wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
PROJECT_NAME = LDB
|
||||
OUTPUT_DIRECTORY = apidocs
|
||||
REPEAT_BRIEF = YES
|
||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
GENERATE_TODOLIST = YES
|
||||
GENERATE_BUGLIST = YES
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
SHOW_USED_FILES = NO
|
||||
SHOW_DIRECTORIES = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
INPUT = include .
|
||||
FILE_PATTERNS = *.h *.dox
|
||||
EXCLUDE = include/config.h include/dlinklist.h \
|
||||
include/includes.h
|
||||
EXAMPLE_PATH = examples
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
GENERATE_MAN = YES
|
||||
ALWAYS_DETAILED_SEC = YES
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
@@ -0,0 +1,173 @@
|
||||
#!gmake
|
||||
#
|
||||
CC = @CC@
|
||||
GCOV = @GCOV@
|
||||
XSLTPROC = @XSLTPROC@
|
||||
DOXYGEN = @DOXYGEN@
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
datarootdir = @datarootdir@
|
||||
includedir = @includedir@
|
||||
libdir = @libdir@
|
||||
bindir = @bindir@
|
||||
mandir = @mandir@
|
||||
VPATH = @srcdir@:@tdbdir@:@tallocdir@:@libreplacedir@:@poptdir@
|
||||
srcdir = @srcdir@
|
||||
builddir = @builddir@
|
||||
SLAPD = @SLAPD@
|
||||
EXTRA_OBJ=@EXTRA_OBJ@
|
||||
TESTS=test-tdb.sh @TESTS@
|
||||
|
||||
CFLAGS=-g -I$(srcdir)/include -Iinclude -I$(srcdir) -I$(srcdir)/.. \
|
||||
@POPT_CFLAGS@ -I@tallocdir@ -I@tdbdir@/include -I@libreplacedir@ \
|
||||
-DLIBDIR=\"$(libdir)\" -DSHLIBEXT=\"@SHLIBEXT@\" -DUSE_MMAP=1 @CFLAGS@
|
||||
|
||||
LIB_FLAGS=@LDFLAGS@ -Llib -lldb @LIBS@ @POPT_LIBS@
|
||||
|
||||
LDB_TDB_DIR=ldb_tdb
|
||||
LDB_TDB_OBJ=$(LDB_TDB_DIR)/ldb_tdb.o \
|
||||
$(LDB_TDB_DIR)/ldb_pack.o $(LDB_TDB_DIR)/ldb_search.o $(LDB_TDB_DIR)/ldb_index.o \
|
||||
$(LDB_TDB_DIR)/ldb_cache.o $(LDB_TDB_DIR)/ldb_tdb_wrap.o
|
||||
|
||||
COMDIR=common
|
||||
COMMON_OBJ=$(COMDIR)/ldb.o $(COMDIR)/ldb_ldif.o \
|
||||
$(COMDIR)/ldb_parse.o $(COMDIR)/ldb_msg.o $(COMDIR)/ldb_utf8.o \
|
||||
$(COMDIR)/ldb_debug.o $(COMDIR)/ldb_modules.o \
|
||||
$(COMDIR)/ldb_dn.o $(COMDIR)/ldb_match.o $(COMDIR)/ldb_attributes.o \
|
||||
$(COMDIR)/attrib_handlers.o $(COMDIR)/ldb_controls.o $(COMDIR)/qsort.o
|
||||
|
||||
MODDIR=modules
|
||||
MODULES_OBJ=$(MODDIR)/operational.o $(MODDIR)/rdn_name.o \
|
||||
$(MODDIR)/objectclass.o \
|
||||
$(MODDIR)/paged_results.o $(MODDIR)/sort.o $(MODDIR)/asq.o
|
||||
|
||||
NSSDIR=nssldb
|
||||
NSS_OBJ= $(NSSDIR)/ldb-nss.o $(NSSDIR)/ldb-pwd.o $(NSSDIR)/ldb-grp.o
|
||||
NSS_LIB = lib/libnss_ldb.so.2
|
||||
|
||||
OBJS = $(MODULES_OBJ) $(COMMON_OBJ) $(LDB_TDB_OBJ) @TDBOBJ@ @TALLOCOBJ@ @POPTOBJ@ @LIBREPLACEOBJ@ $(EXTRA_OBJ)
|
||||
|
||||
LDB_LIB = lib/libldb.a
|
||||
|
||||
BINS = bin/ldbadd bin/ldbsearch bin/ldbdel bin/ldbmodify bin/ldbedit bin/ldbrename bin/ldbtest bin/oLschema2ldif
|
||||
|
||||
LIBS = $(LDB_LIB)
|
||||
|
||||
EXAMPLES = examples/ldbreader examples/ldifreader
|
||||
|
||||
DIRS = lib bin common ldb_tdb ldb_ldap ldb_sqlite3 modules tools examples
|
||||
|
||||
default: all
|
||||
|
||||
nss: nssdir all $(NSS_LIB)
|
||||
|
||||
nssdir:
|
||||
@mkdir -p $(NSSDIR)
|
||||
|
||||
all: showflags dirs $(OBJS) $(LDB_LIB) $(BINS) $(EXAMPLES) manpages
|
||||
|
||||
showflags:
|
||||
@echo 'ldb will be compiled with flags:'
|
||||
@echo ' CFLAGS = $(CFLAGS)'
|
||||
@echo ' LIBS = $(LIBS)'
|
||||
|
||||
.c.o:
|
||||
@echo Compiling $*.c
|
||||
@mkdir -p `dirname $@`
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
dirs:
|
||||
@mkdir -p $(DIRS)
|
||||
|
||||
lib/libldb.a: $(OBJS)
|
||||
ar -rv $@ $(OBJS)
|
||||
@-ranlib $@
|
||||
|
||||
lib/libnss_ldb.so.2: $(NSS_OBJ) $(LIBS)
|
||||
$(CC) -shared -Wl,-soname,libnss_ldb.so.2 -o lib/libnss_ldb.so.2 $(NSS_OBJ) $(OBJS) $(LIB_FLAGS)
|
||||
|
||||
bin/ldbadd: tools/ldbadd.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbadd tools/ldbadd.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbsearch: tools/ldbsearch.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbsearch tools/ldbsearch.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbdel: tools/ldbdel.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbdel tools/ldbdel.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbmodify: tools/ldbmodify.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbmodify tools/ldbmodify.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbedit: tools/ldbedit.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbedit tools/ldbedit.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbrename: tools/ldbrename.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbrename tools/ldbrename.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/ldbtest: tools/ldbtest.o tools/cmdline.o $(LIBS)
|
||||
$(CC) -o bin/ldbtest tools/ldbtest.o tools/cmdline.o $(LIB_FLAGS)
|
||||
|
||||
bin/oLschema2ldif: tools/oLschema2ldif.o tools/cmdline.o tools/convert.o $(LIBS)
|
||||
$(CC) -o bin/oLschema2ldif tools/oLschema2ldif.o tools/cmdline.o tools/convert.o $(LIB_FLAGS)
|
||||
|
||||
examples/ldbreader: examples/ldbreader.o $(LIBS)
|
||||
$(CC) -o examples/ldbreader examples/ldbreader.o $(LIB_FLAGS)
|
||||
|
||||
examples/ldifreader: examples/ldifreader.o $(LIBS)
|
||||
$(CC) -o examples/ldifreader examples/ldifreader.o $(LIB_FLAGS)
|
||||
|
||||
.SUFFIXES: .1 .1.xml .3 .3.xml .xml .html
|
||||
|
||||
manpages:
|
||||
@$(srcdir)/docs/builddocs.sh "$(XSLTPROC)" "$(srcdir)"
|
||||
|
||||
doxygen:
|
||||
test -z "$(DOXYGEN)" || (cd $(srcdir) && "$(DOXYGEN)")
|
||||
|
||||
clean:
|
||||
rm -f *.o */*.o *.gcov */*.gc?? tdbtest.ldb*
|
||||
rm -f $(BINS) $(TDB_OBJ) $(TALLOC_OBJ) $(LDB_LIB) $(NSS_LIB)
|
||||
rm -f man/*.1 man/*.3 man/*.html
|
||||
rm -f $(EXAMPLES)
|
||||
rm -rf apidocs/
|
||||
rm -rf tests/schema/
|
||||
|
||||
distclean: clean
|
||||
rm -f *~ */*~
|
||||
rm -rf bin lib
|
||||
rm -f config.log config.status config.cache include/config.h
|
||||
rm -f ldb.pc
|
||||
rm -f Makefile
|
||||
|
||||
realdistclean: distclean
|
||||
rm -f configure.in include/config.h.in
|
||||
|
||||
test: all
|
||||
for t in $(TESTS); do echo STARTING $${t}; $(srcdir)/tests/$${t} || exit 1; done
|
||||
|
||||
valgrindtest: all
|
||||
for t in $(TESTS); do echo STARTING $${t}; VALGRIND="valgrind -q --db-attach=yes --num-callers=30" $(srcdir)/tests/$${t} || exit 1; done
|
||||
|
||||
installcheck: install test
|
||||
|
||||
install: all
|
||||
mkdir -p $(includedir) $(libdir)/pkgconfig $(libdir) $(bindir)
|
||||
cp $(srcdir)/include/ldb.h $(srcdir)/include/ldb_errors.h $(includedir)
|
||||
cp $(LDB_LIB) $(libdir)
|
||||
cp $(BINS) $(bindir)
|
||||
cp ldb.pc $(libdir)/pkgconfig
|
||||
$(srcdir)/docs/installdocs.sh $(mandir)
|
||||
|
||||
gcov:
|
||||
$(GCOV) -po ldb_sqlite3 $(srcdir)/ldb_sqlite3/*.c 2| tee ldb_sqlite3.report.gcov
|
||||
$(GCOV) -po ldb_ldap $(srcdir)/ldb_ldap/*.c 2| tee ldb_ldap.report.gcov
|
||||
$(GCOV) -po ldb_tdb $(srcdir)/ldb_tdb/*.c 2| tee ldb_tdb.report.gcov
|
||||
$(GCOV) -po common $(srcdir)/common/*.c 2| tee common.report.gcov
|
||||
$(GCOV) -po modules $(srcdir)/modules/*.c 2| tee modules.report.gcov
|
||||
$(GCOV) -po tools $(srcdir)/tools/*.c 2| tee tools.report.gcov
|
||||
|
||||
etags:
|
||||
etags `find $(srcdir) -name "*.[ch]"`
|
||||
|
||||
ctags:
|
||||
ctags `find $(srcdir) -name "*.[ch]"`
|
||||
@@ -0,0 +1,29 @@
|
||||
Here is how to use gcov to test code coverage in ldb.
|
||||
|
||||
Step 1: build ldb with gcov enabled
|
||||
|
||||
make clean all WITH_GCOV=1
|
||||
|
||||
Step 3: run the test suite
|
||||
make test-tdb
|
||||
|
||||
Step 4: produce the gcov report
|
||||
make gcov
|
||||
|
||||
Step 5: read the summary reports
|
||||
less *.report.gcov
|
||||
|
||||
Step 6: examine the per-file reports
|
||||
less ldb_tdb\#ldb_tdb.c.gcov
|
||||
|
||||
You can also combine steps 2 to 4 like this:
|
||||
|
||||
make clean all test-tdb gcov WITH_GCOV=1
|
||||
|
||||
Note that you should not expect 100% coverage, as some error paths
|
||||
(such as memory allocation failures) are very hard to trigger. There
|
||||
are ways of working around this, but they are quite tricky (they
|
||||
involve allocation wrappers that "fork and fail on malloc").
|
||||
|
||||
The lines to look for in the per-file reports are the ones starting
|
||||
with "#####". Those are lines that are never executed.
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
m4_include(libreplace.m4)
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
rm -rf autom4te.cache
|
||||
rm -f configure config.h.in
|
||||
|
||||
IPATHS="-I libreplace -I lib/replace -I ../libreplace -I ../replace"
|
||||
IPATHS="$IPATHS -I lib/talloc -I talloc -I ../talloc"
|
||||
IPATHS="$IPATHS -I lib/tdb -I tdb -I ../tdb"
|
||||
IPATHS="$IPATHS -I lib/popt -I popt -I ../popt"
|
||||
autoheader $IPATHS || exit 1
|
||||
autoconf $IPATHS || exit 1
|
||||
|
||||
rm -rf autom4te.cache
|
||||
|
||||
echo "Now run ./configure and then make."
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
attribute handlers for well known attribute types, selected by syntax OID
|
||||
see rfc2252
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
#include "ldb/include/ldb_handlers.h"
|
||||
|
||||
/*
|
||||
default handler that just copies a ldb_val.
|
||||
*/
|
||||
int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
*out = ldb_val_dup(mem_ctx, in);
|
||||
if (in->length > 0 && out->data == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
a case folding copy handler, removing leading and trailing spaces and
|
||||
multiple internal spaces
|
||||
|
||||
We exploit the fact that utf8 never uses the space octet except for
|
||||
the space itself
|
||||
*/
|
||||
int ldb_handler_fold(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
char *s, *t;
|
||||
int l;
|
||||
if (!in || !out || !(in->data)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
out->data = (uint8_t *)ldb_casefold(ldb, mem_ctx, (const char *)(in->data));
|
||||
if (out->data == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb_handler_fold: unable to casefold string [%s]", in->data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = (char *)(out->data);
|
||||
|
||||
/* remove trailing spaces if any */
|
||||
l = strlen(s);
|
||||
while (l > 0 && s[l - 1] == ' ') l--;
|
||||
s[l] = '\0';
|
||||
|
||||
/* remove leading spaces if any */
|
||||
if (*s == ' ') {
|
||||
for (t = s; *s == ' '; s++) ;
|
||||
|
||||
/* remove leading spaces by moving down the string */
|
||||
memmove(t, s, l);
|
||||
|
||||
s = t;
|
||||
}
|
||||
|
||||
/* check middle spaces */
|
||||
while ((t = strchr(s, ' ')) != NULL) {
|
||||
for (s = t; *s == ' '; s++) ;
|
||||
|
||||
if ((s - t) > 1) {
|
||||
l = strlen(s);
|
||||
|
||||
/* remove all spaces but one by moving down the string */
|
||||
memmove(t + 1, s, l);
|
||||
}
|
||||
}
|
||||
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
canonicalise a ldap Integer
|
||||
rfc2252 specifies it should be in decimal form
|
||||
*/
|
||||
int ldb_canonicalise_Integer(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
char *end;
|
||||
long long i = strtoll((char *)in->data, &end, 0);
|
||||
if (*end != 0) {
|
||||
return -1;
|
||||
}
|
||||
out->data = (uint8_t *)talloc_asprintf(mem_ctx, "%lld", i);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two Integers
|
||||
*/
|
||||
int ldb_comparison_Integer(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
return strtoll((char *)v1->data, NULL, 0) - strtoll((char *)v2->data, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two binary blobs
|
||||
*/
|
||||
int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (v1->length != v2->length) {
|
||||
return v1->length - v2->length;
|
||||
}
|
||||
return memcmp(v1->data, v2->data, v1->length);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two case insensitive strings, ignoring multiple whitespaces
|
||||
and leading and trailing whitespaces
|
||||
see rfc2252 section 8.1
|
||||
|
||||
try to optimize for the ascii case,
|
||||
but if we find out an utf8 codepoint revert to slower but correct function
|
||||
*/
|
||||
int ldb_comparison_fold(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
const char *s1=(const char *)v1->data, *s2=(const char *)v2->data;
|
||||
const char *u1, *u2;
|
||||
char *b1, *b2;
|
||||
int ret;
|
||||
while (*s1 == ' ') s1++;
|
||||
while (*s2 == ' ') s2++;
|
||||
/* TODO: make utf8 safe, possibly with helper function from application */
|
||||
while (*s1 && *s2) {
|
||||
/* the first 127 (0x7F) chars are ascii and utf8 guarantes they
|
||||
* never appear in multibyte sequences */
|
||||
if (((unsigned char)s1[0]) & 0x80) goto utf8str;
|
||||
if (((unsigned char)s2[0]) & 0x80) goto utf8str;
|
||||
if (toupper((unsigned char)*s1) != toupper((unsigned char)*s2))
|
||||
break;
|
||||
if (*s1 == ' ') {
|
||||
while (s1[0] == s1[1]) s1++;
|
||||
while (s2[0] == s2[1]) s2++;
|
||||
}
|
||||
s1++; s2++;
|
||||
}
|
||||
if (! (*s1 && *s2)) {
|
||||
/* check for trailing spaces only if one of the pointers
|
||||
* has reached the end of the strings otherwise we
|
||||
* can mistakenly match.
|
||||
* ex. "domain users" <-> "domainUpdates"
|
||||
*/
|
||||
while (*s1 == ' ') s1++;
|
||||
while (*s2 == ' ') s2++;
|
||||
}
|
||||
return (int)(toupper(*s1)) - (int)(toupper(*s2));
|
||||
|
||||
utf8str:
|
||||
/* no need to recheck from the start, just from the first utf8 char found */
|
||||
b1 = ldb_casefold(ldb, mem_ctx, s1);
|
||||
b2 = ldb_casefold(ldb, mem_ctx, s2);
|
||||
|
||||
if (b1 && b2) {
|
||||
/* Both strings converted correctly */
|
||||
|
||||
u1 = b1;
|
||||
u2 = b2;
|
||||
} else {
|
||||
/* One of the strings was not UTF8, so we have no options but to do a binary compare */
|
||||
|
||||
u1 = s1;
|
||||
u2 = s2;
|
||||
}
|
||||
|
||||
while (*u1 & *u2) {
|
||||
if (*u1 != *u2)
|
||||
break;
|
||||
if (*u1 == ' ') {
|
||||
while (u1[0] == u1[1]) u1++;
|
||||
while (u2[0] == u2[1]) u2++;
|
||||
}
|
||||
u1++; u2++;
|
||||
}
|
||||
if (! (*u1 && *u2)) {
|
||||
while (*u1 == ' ') u1++;
|
||||
while (*u2 == ' ') u2++;
|
||||
}
|
||||
ret = (int)(*u1 - *u2);
|
||||
|
||||
talloc_free(b1);
|
||||
talloc_free(b2);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a attribute in DN format
|
||||
*/
|
||||
int ldb_canonicalise_dn(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct ldb_dn *dn;
|
||||
int ret = -1;
|
||||
|
||||
out->length = 0;
|
||||
out->data = NULL;
|
||||
|
||||
dn = ldb_dn_new(ldb, mem_ctx, (char *)in->data);
|
||||
if ( ! ldb_dn_validate(dn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
out->data = (uint8_t *)ldb_dn_alloc_casefold(mem_ctx, dn);
|
||||
if (out->data == NULL) {
|
||||
goto done;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
talloc_free(dn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two dns
|
||||
*/
|
||||
int ldb_comparison_dn(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
struct ldb_dn *dn1 = NULL, *dn2 = NULL;
|
||||
int ret;
|
||||
|
||||
dn1 = ldb_dn_new(ldb, mem_ctx, (char *)v1->data);
|
||||
if ( ! ldb_dn_validate(dn1)) return -1;
|
||||
|
||||
dn2 = ldb_dn_new(ldb, mem_ctx, (char *)v2->data);
|
||||
if ( ! ldb_dn_validate(dn2)) {
|
||||
talloc_free(dn1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ldb_dn_compare(dn1, dn2);
|
||||
|
||||
talloc_free(dn1);
|
||||
talloc_free(dn2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two objectclasses, looking at subclasses
|
||||
*/
|
||||
int ldb_comparison_objectclass(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
int ret, i;
|
||||
const char **subclasses;
|
||||
ret = ldb_comparison_fold(ldb, mem_ctx, v1, v2);
|
||||
if (ret == 0) {
|
||||
return 0;
|
||||
}
|
||||
subclasses = ldb_subclass_list(ldb, (char *)v1->data);
|
||||
if (subclasses == NULL) {
|
||||
return ret;
|
||||
}
|
||||
for (i=0;subclasses[i];i++) {
|
||||
struct ldb_val vs;
|
||||
vs.data = discard_const(subclasses[i]);
|
||||
vs.length = strlen(subclasses[i]);
|
||||
if (ldb_comparison_objectclass(ldb, mem_ctx, &vs, v2) == 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two utc time values. 1 second resolution
|
||||
*/
|
||||
int ldb_comparison_utctime(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
time_t t1, t2;
|
||||
t1 = ldb_string_to_time((char *)v1->data);
|
||||
t2 = ldb_string_to_time((char *)v2->data);
|
||||
return (int)t2 - (int)t1;
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a utc time
|
||||
*/
|
||||
int ldb_canonicalise_utctime(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
time_t t = ldb_string_to_time((char *)in->data);
|
||||
out->data = (uint8_t *)ldb_timestring(mem_ctx, t);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
table of standard attribute handlers
|
||||
*/
|
||||
static const struct ldb_attrib_handler ldb_standard_attribs[] = {
|
||||
{
|
||||
.attr = LDB_SYNTAX_INTEGER,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_Integer,
|
||||
.comparison_fn = ldb_comparison_Integer
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_OCTET_STRING,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_copy,
|
||||
.comparison_fn = ldb_comparison_binary
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_DIRECTORY_STRING,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_fold,
|
||||
.comparison_fn = ldb_comparison_fold
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_DN,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_OBJECTCLASS,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_fold,
|
||||
.comparison_fn = ldb_comparison_objectclass
|
||||
},
|
||||
{
|
||||
.attr = LDB_SYNTAX_UTC_TIME,
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_utctime,
|
||||
.comparison_fn = ldb_comparison_utctime
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
return the attribute handlers for a given syntax name
|
||||
*/
|
||||
const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *syntax)
|
||||
{
|
||||
int i;
|
||||
unsigned num_handlers = sizeof(ldb_standard_attribs)/sizeof(ldb_standard_attribs[0]);
|
||||
/* TODO: should be replaced with a binary search */
|
||||
for (i=0;i<num_handlers;i++) {
|
||||
if (strcmp(ldb_standard_attribs[i].attr, syntax) == 0) {
|
||||
return &ldb_standard_attribs[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
register handlers for specific attributes and objectclass relationships
|
||||
|
||||
this allows a backend to store its schema information in any format
|
||||
it likes (or to not have any schema information at all) while keeping the
|
||||
message matching logic generic
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
add to the list of ldif handlers for this ldb context
|
||||
*/
|
||||
int ldb_set_attrib_handlers(struct ldb_context *ldb,
|
||||
const struct ldb_attrib_handler *handlers,
|
||||
unsigned num_handlers)
|
||||
{
|
||||
int i, j, n;
|
||||
struct ldb_attrib_handler *h;
|
||||
n = ldb->schema.num_attrib_handlers + num_handlers;
|
||||
h = talloc_realloc(ldb, ldb->schema.attrib_handlers,
|
||||
struct ldb_attrib_handler, n);
|
||||
if (h == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
ldb->schema.attrib_handlers = h;
|
||||
|
||||
for (i = 0; i < num_handlers; i++) {
|
||||
for (j = 0; j < ldb->schema.num_attrib_handlers; j++) {
|
||||
if (ldb_attr_cmp(handlers[i].attr, h[j].attr) < 0) {
|
||||
memmove(h+j+1, h+j, sizeof(*h) * (ldb->schema.num_attrib_handlers-j));
|
||||
break;
|
||||
}
|
||||
}
|
||||
h[j] = handlers[i];
|
||||
if (h[j].flags & LDB_ATTR_FLAG_ALLOCATED) {
|
||||
h[j].attr = talloc_strdup(h, h[j].attr);
|
||||
if (h[j].attr == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ldb->schema.num_attrib_handlers++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
default handler function pointers
|
||||
*/
|
||||
static const struct ldb_attrib_handler ldb_default_attrib_handler = {
|
||||
.attr = NULL,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_handler_copy,
|
||||
.comparison_fn = ldb_comparison_binary,
|
||||
};
|
||||
|
||||
/*
|
||||
return the attribute handlers for a given attribute
|
||||
*/
|
||||
const struct ldb_attrib_handler *ldb_attrib_handler(struct ldb_context *ldb,
|
||||
const char *attrib)
|
||||
{
|
||||
int i, e, b = 0, r;
|
||||
const struct ldb_attrib_handler *def = &ldb_default_attrib_handler;
|
||||
|
||||
/* as handlers are sorted, '*' must be the first if present */
|
||||
if (strcmp(ldb->schema.attrib_handlers[0].attr, "*") == 0) {
|
||||
def = &ldb->schema.attrib_handlers[0];
|
||||
b = 1;
|
||||
}
|
||||
|
||||
/* do a binary search on the array */
|
||||
e = ldb->schema.num_attrib_handlers - 1;
|
||||
|
||||
while (b <= e) {
|
||||
|
||||
i = (b + e) / 2;
|
||||
|
||||
r = ldb_attr_cmp(attrib, ldb->schema.attrib_handlers[i].attr);
|
||||
if (r == 0) {
|
||||
return &ldb->schema.attrib_handlers[i];
|
||||
}
|
||||
if (r < 0) {
|
||||
e = i - 1;
|
||||
} else {
|
||||
b = i + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add to the list of ldif handlers for this ldb context
|
||||
*/
|
||||
void ldb_remove_attrib_handler(struct ldb_context *ldb, const char *attrib)
|
||||
{
|
||||
const struct ldb_attrib_handler *h;
|
||||
int i;
|
||||
h = ldb_attrib_handler(ldb, attrib);
|
||||
if (h == &ldb_default_attrib_handler) {
|
||||
return;
|
||||
}
|
||||
if (h->flags & LDB_ATTR_FLAG_ALLOCATED) {
|
||||
talloc_free(discard_const_p(char, h->attr));
|
||||
}
|
||||
i = h - ldb->schema.attrib_handlers;
|
||||
if (i < ldb->schema.num_attrib_handlers - 1) {
|
||||
memmove(&ldb->schema.attrib_handlers[i],
|
||||
h+1, sizeof(*h) * (ldb->schema.num_attrib_handlers-(i+1)));
|
||||
}
|
||||
ldb->schema.num_attrib_handlers--;
|
||||
}
|
||||
|
||||
/*
|
||||
setup a attribute handler using a standard syntax
|
||||
*/
|
||||
int ldb_set_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *attr, const char *syntax)
|
||||
{
|
||||
const struct ldb_attrib_handler *h = ldb_attrib_handler_syntax(ldb, syntax);
|
||||
struct ldb_attrib_handler h2;
|
||||
if (h == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Unknown syntax '%s'\n", syntax);
|
||||
return -1;
|
||||
}
|
||||
h2 = *h;
|
||||
h2.attr = attr;
|
||||
return ldb_set_attrib_handlers(ldb, &h2, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the attribute handles for well known attributes
|
||||
*/
|
||||
int ldb_setup_wellknown_attributes(struct ldb_context *ldb)
|
||||
{
|
||||
const struct {
|
||||
const char *attr;
|
||||
const char *syntax;
|
||||
} wellknown[] = {
|
||||
{ "dn", LDB_SYNTAX_DN },
|
||||
{ "distinguishedName", LDB_SYNTAX_DN },
|
||||
{ "cn", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "dc", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "ou", LDB_SYNTAX_DIRECTORY_STRING },
|
||||
{ "objectClass", LDB_SYNTAX_OBJECTCLASS }
|
||||
};
|
||||
int i;
|
||||
for (i=0;i<ARRAY_SIZE(wellknown);i++) {
|
||||
if (ldb_set_attrib_handler_syntax(ldb, wellknown[i].attr,
|
||||
wellknown[i].syntax) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the list of subclasses for a class
|
||||
*/
|
||||
const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname)
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
return (const char **)ldb->schema.classes[i].subclasses;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a new subclass
|
||||
*/
|
||||
static int ldb_subclass_new(struct ldb_context *ldb, const char *classname, const char *subclass)
|
||||
{
|
||||
struct ldb_subclass *s, *c;
|
||||
s = talloc_realloc(ldb, ldb->schema.classes, struct ldb_subclass, ldb->schema.num_classes+1);
|
||||
if (s == NULL) goto failed;
|
||||
|
||||
ldb->schema.classes = s;
|
||||
c = &s[ldb->schema.num_classes];
|
||||
c->name = talloc_strdup(s, classname);
|
||||
if (c->name == NULL) goto failed;
|
||||
|
||||
c->subclasses = talloc_array(s, char *, 2);
|
||||
if (c->subclasses == NULL) goto failed;
|
||||
|
||||
c->subclasses[0] = talloc_strdup(c->subclasses, subclass);
|
||||
if (c->subclasses[0] == NULL) goto failed;
|
||||
c->subclasses[1] = NULL;
|
||||
|
||||
ldb->schema.num_classes++;
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
add a subclass
|
||||
*/
|
||||
int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass)
|
||||
{
|
||||
int i, n;
|
||||
struct ldb_subclass *c;
|
||||
char **s;
|
||||
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ldb->schema.num_classes) {
|
||||
return ldb_subclass_new(ldb, classname, subclass);
|
||||
}
|
||||
c = &ldb->schema.classes[i];
|
||||
|
||||
for (n=0;c->subclasses[n];n++) /* noop */;
|
||||
|
||||
s = talloc_realloc(ldb->schema.classes, c->subclasses, char *, n+2);
|
||||
if (s == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
c->subclasses = s;
|
||||
s[n] = talloc_strdup(s, subclass);
|
||||
if (s[n] == NULL) {
|
||||
ldb_oom(ldb);
|
||||
return -1;
|
||||
}
|
||||
s[n+1] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a set of subclasses for a class
|
||||
*/
|
||||
void ldb_subclass_remove(struct ldb_context *ldb, const char *classname)
|
||||
{
|
||||
int i;
|
||||
struct ldb_subclass *c;
|
||||
|
||||
for (i=0;i<ldb->schema.num_classes;i++) {
|
||||
if (ldb_attr_cmp(classname, ldb->schema.classes[i].name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == ldb->schema.num_classes) {
|
||||
return;
|
||||
}
|
||||
|
||||
c = &ldb->schema.classes[i];
|
||||
talloc_free(c->name);
|
||||
talloc_free(c->subclasses);
|
||||
if (ldb->schema.num_classes-(i+1) > 0) {
|
||||
memmove(c, c+1, sizeof(*c) * (ldb->schema.num_classes-(i+1)));
|
||||
}
|
||||
ldb->schema.num_classes--;
|
||||
if (ldb->schema.num_classes == 0) {
|
||||
talloc_free(ldb->schema.classes);
|
||||
ldb->schema.classes = NULL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb_controls.c
|
||||
*
|
||||
* Component: ldb controls utility functions
|
||||
*
|
||||
* Description: helper functions for control modules
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/* check if a control with the specified "oid" exist and return it */
|
||||
/* returns NULL if not found */
|
||||
struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* check if there's a paged request control */
|
||||
if (controls != NULL) {
|
||||
for (i = 0; controls[i]; i++) {
|
||||
if (strcmp(oid, controls[i]->oid) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return controls[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* saves the current controls list into the "saver" and replace the one in req with a new one excluding
|
||||
the "exclude" control */
|
||||
/* returns False on error */
|
||||
int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver)
|
||||
{
|
||||
struct ldb_control **lcs;
|
||||
int i, j;
|
||||
|
||||
*saver = req->controls;
|
||||
for (i = 0; req->controls[i]; i++);
|
||||
if (i == 1) {
|
||||
req->controls = NULL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
lcs = talloc_array(req, struct ldb_control *, i);
|
||||
if (!lcs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; (*saver)[i]; i++) {
|
||||
if (exclude == (*saver)[i]) continue;
|
||||
lcs[j] = (*saver)[i];
|
||||
j++;
|
||||
}
|
||||
lcs[j] = NULL;
|
||||
|
||||
req->controls = lcs;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* check if there's any control marked as critical in the list */
|
||||
/* return True if any, False if none */
|
||||
int check_critical_controls(struct ldb_control **controls)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (controls == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; controls[i]; i++) {
|
||||
if (controls[i]->critical) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb debug
|
||||
*
|
||||
* Description: functions for printing debug messages
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
this allows the user to choose their own debug function
|
||||
*/
|
||||
int ldb_set_debug(struct ldb_context *ldb,
|
||||
void (*debug)(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap),
|
||||
void *context)
|
||||
{
|
||||
ldb->debug_ops.debug = debug;
|
||||
ldb->debug_ops.context = context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
debug function for ldb_set_debug_stderr
|
||||
*/
|
||||
static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
|
||||
static void ldb_debug_stderr(void *context, enum ldb_debug_level level,
|
||||
const char *fmt, va_list ap)
|
||||
{
|
||||
if (level <= LDB_DEBUG_WARNING) {
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
convenience function to setup debug messages on stderr
|
||||
messages of level LDB_DEBUG_WARNING and higher are printed
|
||||
*/
|
||||
int ldb_set_debug_stderr(struct ldb_context *ldb)
|
||||
{
|
||||
return ldb_set_debug(ldb, ldb_debug_stderr, ldb);
|
||||
}
|
||||
|
||||
/*
|
||||
log a message
|
||||
*/
|
||||
void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
if (ldb->debug_ops.debug == NULL) {
|
||||
ldb_set_debug_stderr(ldb);
|
||||
}
|
||||
va_start(ap, fmt);
|
||||
ldb->debug_ops.debug(ldb->debug_ops.context, level, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
log a message, and set the ldb error string to the same message
|
||||
*/
|
||||
void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *msg;
|
||||
va_start(ap, fmt);
|
||||
msg = talloc_vasprintf(ldb, fmt, ap);
|
||||
va_end(ap);
|
||||
if (msg != NULL) {
|
||||
ldb_set_errstring(ldb, msg);
|
||||
ldb_debug(ldb, level, "%s", msg);
|
||||
}
|
||||
talloc_free(msg);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,761 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldif routines
|
||||
*
|
||||
* Description: ldif pack/unpack routines
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
/*
|
||||
see RFC2849 for the LDIF format definition
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
static int ldb_read_data_file(void *mem_ctx, struct ldb_val *value)
|
||||
{
|
||||
struct stat statbuf;
|
||||
char *buf;
|
||||
int count, size, bytes;
|
||||
int ret;
|
||||
int f;
|
||||
const char *fname = (const char *)value->data;
|
||||
|
||||
if (strncmp(fname, "file://", 7) != 0) {
|
||||
return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
|
||||
}
|
||||
fname += 7;
|
||||
|
||||
f = open(fname, O_RDONLY);
|
||||
if (f == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fstat(f, &statbuf) != 0) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (statbuf.st_size == 0) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
value->data = (uint8_t *)talloc_size(mem_ctx, statbuf.st_size + 1);
|
||||
if (value->data == NULL) {
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
value->data[statbuf.st_size] = 0;
|
||||
|
||||
count = 0;
|
||||
size = statbuf.st_size;
|
||||
buf = (char *)value->data;
|
||||
while (count < statbuf.st_size) {
|
||||
bytes = read(f, buf, size);
|
||||
if (bytes == -1) {
|
||||
talloc_free(value->data);
|
||||
ret = -1;
|
||||
goto done;
|
||||
}
|
||||
count += bytes;
|
||||
buf += bytes;
|
||||
size -= bytes;
|
||||
}
|
||||
|
||||
value->length = statbuf.st_size;
|
||||
ret = statbuf.st_size;
|
||||
|
||||
done:
|
||||
close(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
this base64 decoder was taken from jitterbug (written by tridge).
|
||||
we might need to replace it with a new version
|
||||
*/
|
||||
int ldb_base64_decode(char *s)
|
||||
{
|
||||
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int bit_offset=0, byte_offset, idx, i, n;
|
||||
uint8_t *d = (uint8_t *)s;
|
||||
char *p=NULL;
|
||||
|
||||
n=i=0;
|
||||
|
||||
while (*s && (p=strchr(b64,*s))) {
|
||||
idx = (int)(p - b64);
|
||||
byte_offset = (i*6)/8;
|
||||
bit_offset = (i*6)%8;
|
||||
d[byte_offset] &= ~((1<<(8-bit_offset))-1);
|
||||
if (bit_offset < 3) {
|
||||
d[byte_offset] |= (idx << (2-bit_offset));
|
||||
n = byte_offset+1;
|
||||
} else {
|
||||
d[byte_offset] |= (idx >> (bit_offset-2));
|
||||
d[byte_offset+1] = 0;
|
||||
d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF;
|
||||
n = byte_offset+2;
|
||||
}
|
||||
s++; i++;
|
||||
}
|
||||
if (bit_offset >= 3) {
|
||||
n--;
|
||||
}
|
||||
|
||||
if (*s && !p) {
|
||||
/* the only termination allowed */
|
||||
if (*s != '=') {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* null terminate */
|
||||
d[n] = 0;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode as base64
|
||||
caller frees
|
||||
*/
|
||||
char *ldb_base64_encode(void *mem_ctx, const char *buf, int len)
|
||||
{
|
||||
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
int bit_offset, byte_offset, idx, i;
|
||||
const uint8_t *d = (const uint8_t *)buf;
|
||||
int bytes = (len*8 + 5)/6, pad_bytes = (bytes % 4) ? 4 - (bytes % 4) : 0;
|
||||
char *out;
|
||||
|
||||
out = talloc_array(mem_ctx, char, bytes+pad_bytes+1);
|
||||
if (!out) return NULL;
|
||||
|
||||
for (i=0;i<bytes;i++) {
|
||||
byte_offset = (i*6)/8;
|
||||
bit_offset = (i*6)%8;
|
||||
if (bit_offset < 3) {
|
||||
idx = (d[byte_offset] >> (2-bit_offset)) & 0x3F;
|
||||
} else {
|
||||
idx = (d[byte_offset] << (bit_offset-2)) & 0x3F;
|
||||
if (byte_offset+1 < len) {
|
||||
idx |= (d[byte_offset+1] >> (8-(bit_offset-2)));
|
||||
}
|
||||
}
|
||||
out[i] = b64[idx];
|
||||
}
|
||||
|
||||
for (;i<bytes+pad_bytes;i++)
|
||||
out[i] = '=';
|
||||
out[i] = 0;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
see if a buffer should be base64 encoded
|
||||
*/
|
||||
int ldb_should_b64_encode(const struct ldb_val *val)
|
||||
{
|
||||
unsigned int i;
|
||||
uint8_t *p = val->data;
|
||||
|
||||
if (val->length == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (p[0] == ' ' || p[0] == ':') {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i=0; i<val->length; i++) {
|
||||
if (!isprint(p[i]) || p[i] == '\n') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this macro is used to handle the return checking on fprintf_fn() */
|
||||
#define CHECK_RET do { if (ret < 0) return ret; total += ret; } while (0)
|
||||
|
||||
/*
|
||||
write a line folded string onto a file
|
||||
*/
|
||||
static int fold_string(int (*fprintf_fn)(void *, const char *, ...), void *private_data,
|
||||
const char *buf, size_t length, int start_pos)
|
||||
{
|
||||
unsigned int i;
|
||||
int total=0, ret;
|
||||
|
||||
for (i=0;i<length;i++) {
|
||||
ret = fprintf_fn(private_data, "%c", buf[i]);
|
||||
CHECK_RET;
|
||||
if (i != (length-1) && (i + start_pos) % 77 == 0) {
|
||||
ret = fprintf_fn(private_data, "\n ");
|
||||
CHECK_RET;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#undef CHECK_RET
|
||||
|
||||
/*
|
||||
encode as base64 to a file
|
||||
*/
|
||||
static int base64_encode_f(struct ldb_context *ldb,
|
||||
int (*fprintf_fn)(void *, const char *, ...),
|
||||
void *private_data,
|
||||
const char *buf, int len, int start_pos)
|
||||
{
|
||||
char *b = ldb_base64_encode(ldb, buf, len);
|
||||
int ret;
|
||||
|
||||
if (!b) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = fold_string(fprintf_fn, private_data, b, strlen(b), start_pos);
|
||||
|
||||
talloc_free(b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
const char *name;
|
||||
enum ldb_changetype changetype;
|
||||
} ldb_changetypes[] = {
|
||||
{"add", LDB_CHANGETYPE_ADD},
|
||||
{"delete", LDB_CHANGETYPE_DELETE},
|
||||
{"modify", LDB_CHANGETYPE_MODIFY},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
/* this macro is used to handle the return checking on fprintf_fn() */
|
||||
#define CHECK_RET do { if (ret < 0) { talloc_free(mem_ctx); return ret; } total += ret; } while (0)
|
||||
|
||||
/*
|
||||
write to ldif, using a caller supplied write method
|
||||
*/
|
||||
int ldb_ldif_write(struct ldb_context *ldb,
|
||||
int (*fprintf_fn)(void *, const char *, ...),
|
||||
void *private_data,
|
||||
const struct ldb_ldif *ldif)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
unsigned int i, j;
|
||||
int total=0, ret;
|
||||
const struct ldb_message *msg;
|
||||
|
||||
mem_ctx = talloc_named_const(NULL, 0, "ldb_ldif_write");
|
||||
|
||||
msg = ldif->msg;
|
||||
|
||||
ret = fprintf_fn(private_data, "dn: %s\n", ldb_dn_get_linearized(msg->dn));
|
||||
CHECK_RET;
|
||||
|
||||
if (ldif->changetype != LDB_CHANGETYPE_NONE) {
|
||||
for (i=0;ldb_changetypes[i].name;i++) {
|
||||
if (ldb_changetypes[i].changetype == ldif->changetype) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ldb_changetypes[i].name) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Invalid ldif changetype %d\n",
|
||||
ldif->changetype);
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
ret = fprintf_fn(private_data, "changetype: %s\n", ldb_changetypes[i].name);
|
||||
CHECK_RET;
|
||||
}
|
||||
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
const struct ldb_attrib_handler *h;
|
||||
|
||||
h = ldb_attrib_handler(ldb, msg->elements[i].name);
|
||||
|
||||
if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
|
||||
switch (msg->elements[i].flags & LDB_FLAG_MOD_MASK) {
|
||||
case LDB_FLAG_MOD_ADD:
|
||||
fprintf_fn(private_data, "add: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
case LDB_FLAG_MOD_DELETE:
|
||||
fprintf_fn(private_data, "delete: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
case LDB_FLAG_MOD_REPLACE:
|
||||
fprintf_fn(private_data, "replace: %s\n",
|
||||
msg->elements[i].name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (j=0;j<msg->elements[i].num_values;j++) {
|
||||
struct ldb_val v;
|
||||
ret = h->ldif_write_fn(ldb, mem_ctx, &msg->elements[i].values[j], &v);
|
||||
CHECK_RET;
|
||||
if (ldb_should_b64_encode(&v)) {
|
||||
ret = fprintf_fn(private_data, "%s:: ",
|
||||
msg->elements[i].name);
|
||||
CHECK_RET;
|
||||
ret = base64_encode_f(ldb, fprintf_fn, private_data,
|
||||
(char *)v.data, v.length,
|
||||
strlen(msg->elements[i].name)+3);
|
||||
CHECK_RET;
|
||||
ret = fprintf_fn(private_data, "\n");
|
||||
CHECK_RET;
|
||||
} else {
|
||||
ret = fprintf_fn(private_data, "%s: ", msg->elements[i].name);
|
||||
CHECK_RET;
|
||||
ret = fold_string(fprintf_fn, private_data,
|
||||
(char *)v.data, v.length,
|
||||
strlen(msg->elements[i].name)+2);
|
||||
CHECK_RET;
|
||||
ret = fprintf_fn(private_data, "\n");
|
||||
CHECK_RET;
|
||||
}
|
||||
if (v.data != msg->elements[i].values[j].data) {
|
||||
talloc_free(v.data);
|
||||
}
|
||||
}
|
||||
if (ldif->changetype == LDB_CHANGETYPE_MODIFY) {
|
||||
fprintf_fn(private_data, "-\n");
|
||||
}
|
||||
}
|
||||
ret = fprintf_fn(private_data,"\n");
|
||||
CHECK_RET;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
#undef CHECK_RET
|
||||
|
||||
|
||||
/*
|
||||
pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
|
||||
this routine removes any RFC2849 continuations and comments
|
||||
|
||||
caller frees
|
||||
*/
|
||||
static char *next_chunk(struct ldb_context *ldb,
|
||||
int (*fgetc_fn)(void *), void *private_data)
|
||||
{
|
||||
size_t alloc_size=0, chunk_size = 0;
|
||||
char *chunk = NULL;
|
||||
int c;
|
||||
int in_comment = 0;
|
||||
|
||||
while ((c = fgetc_fn(private_data)) != EOF) {
|
||||
if (chunk_size+1 >= alloc_size) {
|
||||
char *c2;
|
||||
alloc_size += 1024;
|
||||
c2 = talloc_realloc(ldb, chunk, char, alloc_size);
|
||||
if (!c2) {
|
||||
talloc_free(chunk);
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
chunk = c2;
|
||||
}
|
||||
|
||||
if (in_comment) {
|
||||
if (c == '\n') {
|
||||
in_comment = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* handle continuation lines - see RFC2849 */
|
||||
if (c == ' ' && chunk_size > 1 && chunk[chunk_size-1] == '\n') {
|
||||
chunk_size--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* chunks are terminated by a double line-feed */
|
||||
if (c == '\n' && chunk_size > 0 && chunk[chunk_size-1] == '\n') {
|
||||
chunk[chunk_size-1] = 0;
|
||||
return chunk;
|
||||
}
|
||||
|
||||
if (c == '#' && (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
|
||||
in_comment = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore leading blank lines */
|
||||
if (chunk_size == 0 && c == '\n') {
|
||||
continue;
|
||||
}
|
||||
|
||||
chunk[chunk_size++] = c;
|
||||
}
|
||||
|
||||
if (chunk) {
|
||||
chunk[chunk_size] = 0;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
|
||||
/* simple ldif attribute parser */
|
||||
static int next_attr(void *mem_ctx, char **s, const char **attr, struct ldb_val *value)
|
||||
{
|
||||
char *p;
|
||||
int base64_encoded = 0;
|
||||
int binary_file = 0;
|
||||
|
||||
if (strncmp(*s, "-\n", 2) == 0) {
|
||||
value->length = 0;
|
||||
*attr = "-";
|
||||
*s += 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strchr(*s, ':');
|
||||
if (!p) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p++ = 0;
|
||||
|
||||
if (*p == ':') {
|
||||
base64_encoded = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p == '<') {
|
||||
binary_file = 1;
|
||||
p++;
|
||||
}
|
||||
|
||||
*attr = *s;
|
||||
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
p++;
|
||||
}
|
||||
|
||||
value->data = (uint8_t *)p;
|
||||
|
||||
p = strchr(p, '\n');
|
||||
|
||||
if (!p) {
|
||||
value->length = strlen((char *)value->data);
|
||||
*s = ((char *)value->data) + value->length;
|
||||
} else {
|
||||
value->length = p - (char *)value->data;
|
||||
*s = p+1;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
if (base64_encoded) {
|
||||
int len = ldb_base64_decode((char *)value->data);
|
||||
if (len == -1) {
|
||||
/* it wasn't valid base64 data */
|
||||
return -1;
|
||||
}
|
||||
value->length = len;
|
||||
}
|
||||
|
||||
if (binary_file) {
|
||||
int len = ldb_read_data_file(mem_ctx, value);
|
||||
if (len == -1) {
|
||||
/* an error occured hile trying to retrieve the file */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
free a message from a ldif_read
|
||||
*/
|
||||
void ldb_ldif_read_free(struct ldb_context *ldb, struct ldb_ldif *ldif)
|
||||
{
|
||||
talloc_free(ldif);
|
||||
}
|
||||
|
||||
/*
|
||||
read from a LDIF source, creating a ldb_message
|
||||
*/
|
||||
struct ldb_ldif *ldb_ldif_read(struct ldb_context *ldb,
|
||||
int (*fgetc_fn)(void *), void *private_data)
|
||||
{
|
||||
struct ldb_ldif *ldif;
|
||||
struct ldb_message *msg;
|
||||
const char *attr=NULL;
|
||||
char *chunk=NULL, *s;
|
||||
struct ldb_val value;
|
||||
unsigned flags = 0;
|
||||
|
||||
value.data = NULL;
|
||||
|
||||
ldif = talloc(ldb, struct ldb_ldif);
|
||||
if (!ldif) return NULL;
|
||||
|
||||
ldif->msg = talloc(ldif, struct ldb_message);
|
||||
if (ldif->msg == NULL) {
|
||||
talloc_free(ldif);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ldif->changetype = LDB_CHANGETYPE_NONE;
|
||||
msg = ldif->msg;
|
||||
|
||||
msg->dn = NULL;
|
||||
msg->elements = NULL;
|
||||
msg->num_elements = 0;
|
||||
msg->private_data = NULL;
|
||||
|
||||
chunk = next_chunk(ldb, fgetc_fn, private_data);
|
||||
if (!chunk) {
|
||||
goto failed;
|
||||
}
|
||||
talloc_steal(ldif, chunk);
|
||||
|
||||
msg->private_data = chunk;
|
||||
s = chunk;
|
||||
|
||||
if (next_attr(ldif, &s, &attr, &value) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* first line must be a dn */
|
||||
if (ldb_attr_cmp(attr, "dn") != 0) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: First line of ldif must be a dn not '%s'\n",
|
||||
attr);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
msg->dn = ldb_dn_new(msg, ldb, (char *)value.data);
|
||||
|
||||
if ( ! ldb_dn_validate(msg->dn)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Error: Unable to parse dn '%s'\n",
|
||||
value.data);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
while (next_attr(ldif, &s, &attr, &value) == 0) {
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_message_element *el;
|
||||
int ret, empty = 0;
|
||||
|
||||
if (ldb_attr_cmp(attr, "changetype") == 0) {
|
||||
int i;
|
||||
for (i=0;ldb_changetypes[i].name;i++) {
|
||||
if (ldb_attr_cmp((char *)value.data, ldb_changetypes[i].name) == 0) {
|
||||
ldif->changetype = ldb_changetypes[i].changetype;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ldb_changetypes[i].name) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR,
|
||||
"Error: Bad ldif changetype '%s'\n",(char *)value.data);
|
||||
}
|
||||
flags = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ldb_attr_cmp(attr, "add") == 0) {
|
||||
flags = LDB_FLAG_MOD_ADD;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "delete") == 0) {
|
||||
flags = LDB_FLAG_MOD_DELETE;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "replace") == 0) {
|
||||
flags = LDB_FLAG_MOD_REPLACE;
|
||||
empty = 1;
|
||||
}
|
||||
if (ldb_attr_cmp(attr, "-") == 0) {
|
||||
flags = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
if (ldb_msg_add_empty(msg, (char *)value.data, flags, NULL) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
el = &msg->elements[msg->num_elements-1];
|
||||
|
||||
h = ldb_attrib_handler(ldb, attr);
|
||||
|
||||
if (msg->num_elements > 0 && ldb_attr_cmp(attr, el->name) == 0 &&
|
||||
flags == el->flags) {
|
||||
/* its a continuation */
|
||||
el->values =
|
||||
talloc_realloc(msg->elements, el->values,
|
||||
struct ldb_val, el->num_values+1);
|
||||
if (!el->values) {
|
||||
goto failed;
|
||||
}
|
||||
ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[el->num_values]);
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
if (value.length == 0) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR,
|
||||
"Error: Attribute value cannot be empty for attribute '%s'\n", el->name);
|
||||
goto failed;
|
||||
}
|
||||
if (value.data != el->values[el->num_values].data) {
|
||||
talloc_steal(el->values, el->values[el->num_values].data);
|
||||
}
|
||||
el->num_values++;
|
||||
} else {
|
||||
/* its a new attribute */
|
||||
msg->elements = talloc_realloc(ldif, msg->elements,
|
||||
struct ldb_message_element,
|
||||
msg->num_elements+1);
|
||||
if (!msg->elements) {
|
||||
goto failed;
|
||||
}
|
||||
el = &msg->elements[msg->num_elements];
|
||||
el->flags = flags;
|
||||
el->name = talloc_strdup(msg->elements, attr);
|
||||
el->values = talloc(msg->elements, struct ldb_val);
|
||||
if (!el->values || !el->name) {
|
||||
goto failed;
|
||||
}
|
||||
el->num_values = 1;
|
||||
ret = h->ldif_read_fn(ldb, ldif, &value, &el->values[0]);
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
if (value.data != el->values[0].data) {
|
||||
talloc_steal(el->values, el->values[0].data);
|
||||
}
|
||||
msg->num_elements++;
|
||||
}
|
||||
}
|
||||
|
||||
return ldif;
|
||||
|
||||
failed:
|
||||
talloc_free(ldif);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
a wrapper around ldif_read() for reading from FILE*
|
||||
*/
|
||||
struct ldif_read_file_state {
|
||||
FILE *f;
|
||||
};
|
||||
|
||||
static int fgetc_file(void *private_data)
|
||||
{
|
||||
struct ldif_read_file_state *state =
|
||||
(struct ldif_read_file_state *)private_data;
|
||||
return fgetc(state->f);
|
||||
}
|
||||
|
||||
struct ldb_ldif *ldb_ldif_read_file(struct ldb_context *ldb, FILE *f)
|
||||
{
|
||||
struct ldif_read_file_state state;
|
||||
state.f = f;
|
||||
return ldb_ldif_read(ldb, fgetc_file, &state);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a wrapper around ldif_read() for reading from const char*
|
||||
*/
|
||||
struct ldif_read_string_state {
|
||||
const char *s;
|
||||
};
|
||||
|
||||
static int fgetc_string(void *private_data)
|
||||
{
|
||||
struct ldif_read_string_state *state =
|
||||
(struct ldif_read_string_state *)private_data;
|
||||
if (state->s[0] != 0) {
|
||||
return *state->s++;
|
||||
}
|
||||
return EOF;
|
||||
}
|
||||
|
||||
struct ldb_ldif *ldb_ldif_read_string(struct ldb_context *ldb, const char **s)
|
||||
{
|
||||
struct ldif_read_string_state state;
|
||||
struct ldb_ldif *ldif;
|
||||
state.s = *s;
|
||||
ldif = ldb_ldif_read(ldb, fgetc_string, &state);
|
||||
*s = state.s;
|
||||
return ldif;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
wrapper around ldif_write() for a file
|
||||
*/
|
||||
struct ldif_write_file_state {
|
||||
FILE *f;
|
||||
};
|
||||
|
||||
static int fprintf_file(void *private_data, const char *fmt, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
|
||||
static int fprintf_file(void *private_data, const char *fmt, ...)
|
||||
{
|
||||
struct ldif_write_file_state *state =
|
||||
(struct ldif_write_file_state *)private_data;
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
ret = vfprintf(state->f, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ldb_ldif_write_file(struct ldb_context *ldb, FILE *f, const struct ldb_ldif *ldif)
|
||||
{
|
||||
struct ldif_write_file_state state;
|
||||
state.f = f;
|
||||
return ldb_ldif_write(ldb, fprintf_file, &state, ldif);
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004-2005
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb expression matching
|
||||
*
|
||||
* Description: ldb expression matching
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
check if the scope matches in a search result
|
||||
*/
|
||||
static int ldb_match_scope(struct ldb_context *ldb,
|
||||
struct ldb_dn *base,
|
||||
struct ldb_dn *dn,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (base == NULL || dn == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (scope) {
|
||||
case LDB_SCOPE_BASE:
|
||||
if (ldb_dn_compare(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LDB_SCOPE_ONELEVEL:
|
||||
if (ldb_dn_get_comp_num(dn) == (ldb_dn_get_comp_num(base) + 1)) {
|
||||
if (ldb_dn_compare_base(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case LDB_SCOPE_SUBTREE:
|
||||
default:
|
||||
if (ldb_dn_compare_base(base, dn) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
match if node is present
|
||||
*/
|
||||
static int ldb_match_present(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
if (ldb_attr_dn(tree->u.present.attr) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (ldb_msg_find_element(msg, tree->u.present.attr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ldb_match_comparison(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope,
|
||||
enum ldb_parse_op comp_op)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
const struct ldb_attrib_handler *h;
|
||||
int ret;
|
||||
|
||||
/* FIXME: APPROX comparison not handled yet */
|
||||
if (comp_op == LDB_OP_APPROX) return 0;
|
||||
|
||||
el = ldb_msg_find_element(msg, tree->u.comparison.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h = ldb_attrib_handler(ldb, el->name);
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
ret = h->comparison_fn(ldb, ldb, &el->values[i], &tree->u.comparison.value);
|
||||
|
||||
if (ret == 0) {
|
||||
return 1;
|
||||
}
|
||||
if (ret > 0 && comp_op == LDB_OP_GREATER) {
|
||||
return 1;
|
||||
}
|
||||
if (ret < 0 && comp_op == LDB_OP_LESS) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
match a simple leaf node
|
||||
*/
|
||||
static int ldb_match_equality(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_dn *valuedn;
|
||||
int ret;
|
||||
|
||||
if (ldb_attr_dn(tree->u.equality.attr) == 0) {
|
||||
valuedn = ldb_dn_new(ldb, ldb, (char *)tree->u.equality.value.data);
|
||||
if (valuedn == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ldb_dn_compare(msg->dn, valuedn);
|
||||
|
||||
talloc_free(valuedn);
|
||||
|
||||
if (ret == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: handle the "*" case derived from an extended search
|
||||
operation without the attibute type defined */
|
||||
el = ldb_msg_find_element(msg, tree->u.equality.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
h = ldb_attrib_handler(ldb, el->name);
|
||||
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
if (h->comparison_fn(ldb, ldb, &tree->u.equality.value,
|
||||
&el->values[i]) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ldb_wildcard_compare(struct ldb_context *ldb,
|
||||
const struct ldb_parse_tree *tree,
|
||||
const struct ldb_val value)
|
||||
{
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_val val;
|
||||
struct ldb_val cnk;
|
||||
struct ldb_val *chunk;
|
||||
char *p, *g;
|
||||
uint8_t *save_p = NULL;
|
||||
int c = 0;
|
||||
|
||||
h = ldb_attrib_handler(ldb, tree->u.substring.attr);
|
||||
|
||||
if(h->canonicalise_fn(ldb, ldb, &value, &val) != 0)
|
||||
return -1;
|
||||
|
||||
save_p = val.data;
|
||||
cnk.data = NULL;
|
||||
|
||||
if ( ! tree->u.substring.start_with_wildcard ) {
|
||||
|
||||
chunk = tree->u.substring.chunks[c];
|
||||
if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
|
||||
|
||||
/* This deals with wildcard prefix searches on binary attributes (eg objectGUID) */
|
||||
if (cnk.length > val.length) {
|
||||
goto failed;
|
||||
}
|
||||
if (memcmp((char *)val.data, (char *)cnk.data, cnk.length) != 0) goto failed;
|
||||
val.length -= cnk.length;
|
||||
val.data += cnk.length;
|
||||
c++;
|
||||
talloc_free(cnk.data);
|
||||
cnk.data = NULL;
|
||||
}
|
||||
|
||||
while (tree->u.substring.chunks[c]) {
|
||||
|
||||
chunk = tree->u.substring.chunks[c];
|
||||
if(h->canonicalise_fn(ldb, ldb, chunk, &cnk) != 0) goto failed;
|
||||
|
||||
/* FIXME: case of embedded nulls */
|
||||
p = strstr((char *)val.data, (char *)cnk.data);
|
||||
if (p == NULL) goto failed;
|
||||
if ( (! tree->u.substring.chunks[c + 1]) && (! tree->u.substring.end_with_wildcard) ) {
|
||||
do { /* greedy */
|
||||
g = strstr((char *)p + cnk.length, (char *)cnk.data);
|
||||
if (g) p = g;
|
||||
} while(g);
|
||||
}
|
||||
val.length = val.length - (p - (char *)(val.data)) - cnk.length;
|
||||
val.data = (uint8_t *)(p + cnk.length);
|
||||
c++;
|
||||
talloc_free(cnk.data);
|
||||
cnk.data = NULL;
|
||||
}
|
||||
|
||||
if ( (! tree->u.substring.end_with_wildcard) && (*(val.data) != 0) ) goto failed; /* last chunk have not reached end of string */
|
||||
talloc_free(save_p);
|
||||
return 1;
|
||||
|
||||
failed:
|
||||
talloc_free(save_p);
|
||||
talloc_free(cnk.data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
match a simple leaf node
|
||||
*/
|
||||
static int ldb_match_substring(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
el = ldb_msg_find_element(msg, tree->u.substring.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
if (ldb_wildcard_compare(ldb, tree, el->values[i]) == 1) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
bitwise-and comparator
|
||||
*/
|
||||
static int ldb_comparator_and(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
uint64_t i1, i2;
|
||||
i1 = strtoull((char *)v1->data, NULL, 0);
|
||||
i2 = strtoull((char *)v2->data, NULL, 0);
|
||||
return ((i1 & i2) == i2);
|
||||
}
|
||||
|
||||
/*
|
||||
bitwise-or comparator
|
||||
*/
|
||||
static int ldb_comparator_or(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
uint64_t i1, i2;
|
||||
i1 = strtoull((char *)v1->data, NULL, 0);
|
||||
i2 = strtoull((char *)v2->data, NULL, 0);
|
||||
return ((i1 & i2) != 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
extended match, handles things like bitops
|
||||
*/
|
||||
static int ldb_match_extended(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
int i;
|
||||
const struct {
|
||||
const char *oid;
|
||||
int (*comparator)(const struct ldb_val *, const struct ldb_val *);
|
||||
} rules[] = {
|
||||
{ LDB_OID_COMPARATOR_AND, ldb_comparator_and},
|
||||
{ LDB_OID_COMPARATOR_OR, ldb_comparator_or}
|
||||
};
|
||||
int (*comp)(const struct ldb_val *, const struct ldb_val *) = NULL;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
if (tree->u.extended.dnAttributes) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: dnAttributes extended match not supported yet");
|
||||
return -1;
|
||||
}
|
||||
if (tree->u.extended.rule_id == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-rule extended matches not supported yet");
|
||||
return -1;
|
||||
}
|
||||
if (tree->u.extended.attr == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: no-attribute extended matches not supported yet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=0;i<ARRAY_SIZE(rules);i++) {
|
||||
if (strcmp(rules[i].oid, tree->u.extended.rule_id) == 0) {
|
||||
comp = rules[i].comparator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comp == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "ldb: unknown extended rule_id %s\n",
|
||||
tree->u.extended.rule_id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* find the message element */
|
||||
el = ldb_msg_find_element(msg, tree->u.extended.attr);
|
||||
if (el == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
int ret = comp(&el->values[i], &tree->u.extended.value);
|
||||
if (ret == -1 || ret == 1) return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
return 0 if the given parse tree matches the given message. Assumes
|
||||
the message is in sorted order
|
||||
|
||||
return 1 if it matches, and 0 if it doesn't match
|
||||
|
||||
this is a recursive function, and does short-circuit evaluation
|
||||
*/
|
||||
static int ldb_match_message(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
unsigned int i;
|
||||
int v;
|
||||
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
|
||||
if (!v) return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
case LDB_OP_OR:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
v = ldb_match_message(ldb, msg, tree->u.list.elements[i], scope);
|
||||
if (v) return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case LDB_OP_NOT:
|
||||
return ! ldb_match_message(ldb, msg, tree->u.isnot.child, scope);
|
||||
|
||||
case LDB_OP_EQUALITY:
|
||||
return ldb_match_equality(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_SUBSTRING:
|
||||
return ldb_match_substring(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_GREATER:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_GREATER);
|
||||
|
||||
case LDB_OP_LESS:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_LESS);
|
||||
|
||||
case LDB_OP_PRESENT:
|
||||
return ldb_match_present(ldb, msg, tree, scope);
|
||||
|
||||
case LDB_OP_APPROX:
|
||||
return ldb_match_comparison(ldb, msg, tree, scope, LDB_OP_APPROX);
|
||||
|
||||
case LDB_OP_EXTENDED:
|
||||
return ldb_match_extended(ldb, msg, tree, scope);
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ldb_match_msg(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
struct ldb_dn *base,
|
||||
enum ldb_scope scope)
|
||||
{
|
||||
if ( ! ldb_match_scope(ldb, base, msg->dn, scope) ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return ldb_match_message(ldb, msg, tree, scope);
|
||||
}
|
||||
@@ -0,0 +1,452 @@
|
||||
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb modules core
|
||||
*
|
||||
* Description: core modules routines
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#if (_SAMBA_BUILD_ >= 4)
|
||||
#include "build.h"
|
||||
#include "dynconfig.h"
|
||||
#endif
|
||||
|
||||
#define LDB_MODULE_PREFIX "modules:"
|
||||
#define LDB_MODULE_PREFIX_LEN 8
|
||||
|
||||
static char *ldb_modules_strdup_no_spaces(TALLOC_CTX *mem_ctx, const char *string)
|
||||
{
|
||||
int i, len;
|
||||
char *trimmed;
|
||||
|
||||
trimmed = talloc_strdup(mem_ctx, string);
|
||||
if (!trimmed) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(trimmed);
|
||||
for (i = 0; trimmed[i] != '\0'; i++) {
|
||||
switch (trimmed[i]) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
memmove(&trimmed[i], &trimmed[i + 1], len -i -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
|
||||
/* modules are called in inverse order on the stack.
|
||||
Lets place them as an admin would think the right order is.
|
||||
Modules order is important */
|
||||
const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string)
|
||||
{
|
||||
char **modules = NULL;
|
||||
const char **m;
|
||||
char *modstr, *p;
|
||||
int i;
|
||||
|
||||
/* spaces not admitted */
|
||||
modstr = ldb_modules_strdup_no_spaces(mem_ctx, string);
|
||||
if ( ! modstr) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_strdup_no_spaces()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
modules = talloc_realloc(mem_ctx, modules, char *, 2);
|
||||
if ( ! modules ) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
|
||||
talloc_free(modstr);
|
||||
return NULL;
|
||||
}
|
||||
talloc_steal(modules, modstr);
|
||||
|
||||
i = 0;
|
||||
/* The str*r*chr walks backwards: This is how we get the inverse order mentioned above */
|
||||
while ((p = strrchr(modstr, ',')) != NULL) {
|
||||
*p = '\0';
|
||||
p++;
|
||||
modules[i] = p;
|
||||
|
||||
i++;
|
||||
modules = talloc_realloc(mem_ctx, modules, char *, i + 2);
|
||||
if ( ! modules ) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Out of Memory in ldb_modules_list_from_string()\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
modules[i] = modstr;
|
||||
|
||||
modules[i + 1] = NULL;
|
||||
|
||||
m = (const char **)modules;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
static struct ops_list_entry {
|
||||
const struct ldb_module_ops *ops;
|
||||
struct ops_list_entry *next;
|
||||
} *registered_modules = NULL;
|
||||
|
||||
static const struct ldb_module_ops *ldb_find_module_ops(const char *name)
|
||||
{
|
||||
struct ops_list_entry *e;
|
||||
|
||||
for (e = registered_modules; e; e = e->next) {
|
||||
if (strcmp(e->ops->name, name) == 0)
|
||||
return e->ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef STATIC_ldb_MODULES
|
||||
|
||||
#ifdef HAVE_LDB_LDAP
|
||||
#define LDAP_INIT ldb_ldap_init,
|
||||
#else
|
||||
#define LDAP_INIT
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LDB_SQLITE3
|
||||
#define SQLITE3_INIT ldb_sqlite3_init,
|
||||
#else
|
||||
#define SQLITE3_INIT
|
||||
#endif
|
||||
|
||||
#define STATIC_ldb_MODULES \
|
||||
{ \
|
||||
LDAP_INIT \
|
||||
SQLITE3_INIT \
|
||||
ldb_tdb_init, \
|
||||
ldb_operational_init, \
|
||||
ldb_rdn_name_init, \
|
||||
ldb_objectclass_init, \
|
||||
ldb_paged_results_init, \
|
||||
ldb_sort_init, \
|
||||
ldb_asq_init, \
|
||||
NULL \
|
||||
}
|
||||
#endif
|
||||
|
||||
int ldb_global_init(void)
|
||||
{
|
||||
static int (*static_init_fns[])(void) = STATIC_ldb_MODULES;
|
||||
|
||||
static int initialized = 0;
|
||||
int ret = 0, i;
|
||||
|
||||
if (initialized)
|
||||
return 0;
|
||||
|
||||
initialized = 1;
|
||||
|
||||
for (i = 0; static_init_fns[i]; i++) {
|
||||
if (static_init_fns[i]() == -1)
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ldb_register_module(const struct ldb_module_ops *ops)
|
||||
{
|
||||
struct ops_list_entry *entry = talloc(talloc_autofree_context(), struct ops_list_entry);
|
||||
|
||||
if (ldb_find_module_ops(ops->name) != NULL)
|
||||
return -1;
|
||||
|
||||
if (entry == NULL)
|
||||
return -1;
|
||||
|
||||
entry->ops = ops;
|
||||
entry->next = registered_modules;
|
||||
registered_modules = entry;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ldb_try_load_dso(struct ldb_context *ldb, const char *name)
|
||||
{
|
||||
char *path;
|
||||
void *handle;
|
||||
int (*init_fn) (void);
|
||||
char *modulesdir;
|
||||
|
||||
#ifdef HAVE_DLOPEN
|
||||
if (getenv("LD_LDB_MODULE_PATH") != NULL) {
|
||||
modulesdir = talloc_strdup(ldb, getenv("LD_LDB_MODULE_PATH"));
|
||||
} else {
|
||||
#ifdef _SAMBA_BUILD_
|
||||
modulesdir = talloc_asprintf(ldb, "%s/ldb", dyn_MODULESDIR);
|
||||
#else
|
||||
modulesdir = talloc_strdup(ldb, MODULESDIR);
|
||||
#endif
|
||||
}
|
||||
|
||||
path = talloc_asprintf(ldb, "%s/%s.%s", modulesdir, name, SHLIBEXT);
|
||||
|
||||
talloc_free(modulesdir);
|
||||
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "trying to load %s from %s\n", name, path);
|
||||
|
||||
handle = dlopen(path, RTLD_NOW);
|
||||
if (handle == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_WARNING, "unable to load %s from %s: %s\n", name, path, dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
init_fn = (int (*)(void))dlsym(handle, "init_module");
|
||||
|
||||
if (init_fn == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "no symbol `init_module' found in %s: %s\n", path, dlerror());
|
||||
return -1;
|
||||
}
|
||||
|
||||
talloc_free(path);
|
||||
|
||||
return init_fn();
|
||||
#else
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "no dlopen() - not trying to load %s module\n", name);
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out)
|
||||
{
|
||||
struct ldb_module *module;
|
||||
int i;
|
||||
|
||||
module = backend;
|
||||
|
||||
for (i = 0; module_list[i] != NULL; i++) {
|
||||
struct ldb_module *current;
|
||||
const struct ldb_module_ops *ops;
|
||||
|
||||
ops = ldb_find_module_ops(module_list[i]);
|
||||
if (ops == NULL) {
|
||||
if (ldb_try_load_dso(ldb, module_list[i]) == 0) {
|
||||
ops = ldb_find_module_ops(module_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ops == NULL) {
|
||||
ldb_debug(ldb, LDB_DEBUG_WARNING, "WARNING: Module [%s] not found\n",
|
||||
module_list[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
current = talloc_zero(ldb, struct ldb_module);
|
||||
if (current == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
talloc_set_name(current, "ldb_module: %s", module_list[i]);
|
||||
|
||||
current->ldb = ldb;
|
||||
current->ops = ops;
|
||||
|
||||
DLIST_ADD(module, current);
|
||||
}
|
||||
*out = module;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module)
|
||||
{
|
||||
while (module && module->ops->init_context == NULL)
|
||||
module = module->next;
|
||||
|
||||
if (module && module->ops->init_context &&
|
||||
module->ops->init_context(module) != LDB_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "module initialization failed\n");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
int ldb_load_modules(struct ldb_context *ldb, const char *options[])
|
||||
{
|
||||
const char **modules = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(ldb);
|
||||
if (!mem_ctx) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* find out which modules we are requested to activate */
|
||||
|
||||
/* check if we have a custom module list passd as ldb option */
|
||||
if (options) {
|
||||
for (i = 0; options[i] != NULL; i++) {
|
||||
if (strncmp(options[i], LDB_MODULE_PREFIX, LDB_MODULE_PREFIX_LEN) == 0) {
|
||||
modules = ldb_modules_list_from_string(ldb, mem_ctx, &options[i][LDB_MODULE_PREFIX_LEN]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if not overloaded by options and the backend is not ldap try to load the modules list from ldb */
|
||||
if ((modules == NULL) && (strcmp("ldap", ldb->modules->ops->name) != 0)) {
|
||||
const char * const attrs[] = { "@LIST" , NULL};
|
||||
struct ldb_result *res = NULL;
|
||||
struct ldb_dn *mods_dn;
|
||||
|
||||
mods_dn = ldb_dn_new(mem_ctx, ldb, "@MODULES");
|
||||
if (mods_dn == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ldb_search(ldb, mods_dn, LDB_SCOPE_BASE, "", attrs, &res);
|
||||
talloc_steal(mods_dn, res);
|
||||
if (ret == LDB_SUCCESS && (res->count == 0 || res->msgs[0]->num_elements == 0)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "no modules required by the db\n");
|
||||
} else {
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "ldb error (%s) occurred searching for modules, bailing out\n", ldb_errstring(ldb));
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
if (res->count > 1) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "Too many records found (%d), bailing out\n", res->count);
|
||||
talloc_free(mem_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
modules = ldb_modules_list_from_string(ldb, mem_ctx,
|
||||
(const char *)res->msgs[0]->elements[0].values[0].data);
|
||||
|
||||
}
|
||||
|
||||
talloc_free(mods_dn);
|
||||
}
|
||||
|
||||
if (modules != NULL) {
|
||||
ret = ldb_load_modules_list(ldb, modules, ldb->modules, &ldb->modules);
|
||||
talloc_free(modules);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ldb_debug(ldb, LDB_DEBUG_TRACE, "No modules specified for this database\n");
|
||||
}
|
||||
|
||||
return ldb_init_module_chain(ldb, ldb->modules);
|
||||
}
|
||||
|
||||
/*
|
||||
by using this we allow ldb modules to only implement the functions they care about,
|
||||
which makes writing a module simpler, and makes it more likely to keep working
|
||||
when ldb is extended
|
||||
*/
|
||||
#define FIND_OP(module, op) do { \
|
||||
struct ldb_context *ldb = module->ldb; \
|
||||
module = module->next; \
|
||||
while (module && module->ops->op == NULL) module = module->next; \
|
||||
if (module == NULL) { \
|
||||
ldb_asprintf_errstring(ldb, "Unable to find backend operation for " #op ); \
|
||||
return LDB_ERR_OPERATIONS_ERROR; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
helper functions to call the next module in chain
|
||||
*/
|
||||
|
||||
int ldb_next_request(struct ldb_module *module, struct ldb_request *request)
|
||||
{
|
||||
switch (request->operation) {
|
||||
case LDB_SEARCH:
|
||||
FIND_OP(module, search);
|
||||
return module->ops->search(module, request);
|
||||
case LDB_ADD:
|
||||
FIND_OP(module, add);
|
||||
return module->ops->add(module, request);
|
||||
case LDB_MODIFY:
|
||||
FIND_OP(module, modify);
|
||||
return module->ops->modify(module, request);
|
||||
case LDB_DELETE:
|
||||
FIND_OP(module, del);
|
||||
return module->ops->del(module, request);
|
||||
case LDB_RENAME:
|
||||
FIND_OP(module, rename);
|
||||
return module->ops->rename(module, request);
|
||||
case LDB_SEQUENCE_NUMBER:
|
||||
FIND_OP(module, sequence_number);
|
||||
return module->ops->sequence_number(module, request);
|
||||
default:
|
||||
FIND_OP(module, request);
|
||||
return module->ops->request(module, request);
|
||||
}
|
||||
}
|
||||
|
||||
int ldb_next_init(struct ldb_module *module)
|
||||
{
|
||||
/* init is different in that it is not an error if modules
|
||||
* do not require initialization */
|
||||
|
||||
module = module->next;
|
||||
|
||||
while (module && module->ops->init_context == NULL)
|
||||
module = module->next;
|
||||
|
||||
if (module == NULL)
|
||||
return LDB_SUCCESS;
|
||||
|
||||
return module->ops->init_context(module);
|
||||
}
|
||||
|
||||
int ldb_next_start_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, start_transaction);
|
||||
return module->ops->start_transaction(module);
|
||||
}
|
||||
|
||||
int ldb_next_end_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, end_transaction);
|
||||
return module->ops->end_transaction(module);
|
||||
}
|
||||
|
||||
int ldb_next_del_trans(struct ldb_module *module)
|
||||
{
|
||||
FIND_OP(module, del_transaction);
|
||||
return module->ops->del_transaction(module);
|
||||
}
|
||||
@@ -0,0 +1,846 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb message component utility functions
|
||||
*
|
||||
* Description: functions for manipulating ldb_message structures
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
create a new ldb_message in a given memory context (NULL for top level)
|
||||
*/
|
||||
struct ldb_message *ldb_msg_new(void *mem_ctx)
|
||||
{
|
||||
return talloc_zero(mem_ctx, struct ldb_message);
|
||||
}
|
||||
|
||||
/*
|
||||
find an element in a message by attribute name
|
||||
*/
|
||||
struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg,
|
||||
const char *attr_name)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
|
||||
return &msg->elements[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
see if two ldb_val structures contain exactly the same data
|
||||
return 1 for a match, 0 for a mis-match
|
||||
*/
|
||||
int ldb_val_equal_exact(const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (v1->length != v2->length) return 0;
|
||||
|
||||
if (v1->length == 0) return 1;
|
||||
|
||||
if (memcmp(v1->data, v2->data, v1->length) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
find a value in an element
|
||||
assumes case sensitive comparison
|
||||
*/
|
||||
struct ldb_val *ldb_msg_find_val(const struct ldb_message_element *el,
|
||||
struct ldb_val *val)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
if (ldb_val_equal_exact(val, &el->values[i])) {
|
||||
return &el->values[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
duplicate a ldb_val structure
|
||||
*/
|
||||
struct ldb_val ldb_val_dup(void *mem_ctx, const struct ldb_val *v)
|
||||
{
|
||||
struct ldb_val v2;
|
||||
v2.length = v->length;
|
||||
if (v->data == NULL) {
|
||||
v2.data = NULL;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/* the +1 is to cope with buggy C library routines like strndup
|
||||
that look one byte beyond */
|
||||
v2.data = talloc_array(mem_ctx, uint8_t, v->length+1);
|
||||
if (!v2.data) {
|
||||
v2.length = 0;
|
||||
return v2;
|
||||
}
|
||||
|
||||
memcpy(v2.data, v->data, v->length);
|
||||
((char *)v2.data)[v->length] = 0;
|
||||
return v2;
|
||||
}
|
||||
|
||||
/*
|
||||
add an empty element to a message
|
||||
*/
|
||||
int ldb_msg_add_empty( struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int flags,
|
||||
struct ldb_message_element **return_el)
|
||||
{
|
||||
struct ldb_message_element *els;
|
||||
|
||||
/* FIXME: we should probably leave this to the schema module to check */
|
||||
if (! ldb_valid_attr_name(attr_name)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
els = talloc_realloc(msg, msg->elements,
|
||||
struct ldb_message_element, msg->num_elements+1);
|
||||
if (!els) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
els[msg->num_elements].values = NULL;
|
||||
els[msg->num_elements].num_values = 0;
|
||||
els[msg->num_elements].flags = flags;
|
||||
els[msg->num_elements].name = talloc_strdup(els, attr_name);
|
||||
if (!els[msg->num_elements].name) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->elements = els;
|
||||
msg->num_elements++;
|
||||
|
||||
if (return_el) {
|
||||
*return_el = &els[msg->num_elements-1];
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
add an empty element to a message
|
||||
*/
|
||||
int ldb_msg_add(struct ldb_message *msg,
|
||||
const struct ldb_message_element *el,
|
||||
int flags)
|
||||
{
|
||||
if (ldb_msg_add_empty(msg, el->name, flags, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->elements[msg->num_elements-1] = *el;
|
||||
msg->elements[msg->num_elements-1].flags = flags;
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
add a value to a message
|
||||
*/
|
||||
int ldb_msg_add_value(struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
const struct ldb_val *val,
|
||||
struct ldb_message_element **return_el)
|
||||
{
|
||||
struct ldb_message_element *el;
|
||||
struct ldb_val *vals;
|
||||
int ret;
|
||||
|
||||
el = ldb_msg_find_element(msg, attr_name);
|
||||
if (!el) {
|
||||
ret = ldb_msg_add_empty(msg, attr_name, 0, &el);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
vals = talloc_realloc(msg, el->values, struct ldb_val, el->num_values+1);
|
||||
if (!vals) {
|
||||
errno = ENOMEM;
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
el->values = vals;
|
||||
el->values[el->num_values] = *val;
|
||||
el->num_values++;
|
||||
|
||||
if (return_el) {
|
||||
*return_el = el;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a value to a message, stealing it into the 'right' place
|
||||
*/
|
||||
int ldb_msg_add_steal_value(struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
struct ldb_val *val)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
ret = ldb_msg_add_value(msg, attr_name, val, &el);
|
||||
if (ret == LDB_SUCCESS) {
|
||||
talloc_steal(el->values, val->data);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a string element to a message
|
||||
*/
|
||||
int ldb_msg_add_string(struct ldb_message *msg,
|
||||
const char *attr_name, const char *str)
|
||||
{
|
||||
struct ldb_val val;
|
||||
|
||||
val.data = discard_const_p(uint8_t, str);
|
||||
val.length = strlen(str);
|
||||
|
||||
if (val.length == 0) {
|
||||
/* allow empty strings as non-existant attributes */
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
return ldb_msg_add_value(msg, attr_name, &val, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
add a string element to a message, stealing it into the 'right' place
|
||||
*/
|
||||
int ldb_msg_add_steal_string(struct ldb_message *msg,
|
||||
const char *attr_name, char *str)
|
||||
{
|
||||
struct ldb_val val;
|
||||
|
||||
val.data = (uint8_t *)str;
|
||||
val.length = strlen(str);
|
||||
|
||||
return ldb_msg_add_steal_value(msg, attr_name, &val);
|
||||
}
|
||||
|
||||
/*
|
||||
add a printf formatted element to a message
|
||||
*/
|
||||
int ldb_msg_add_fmt(struct ldb_message *msg,
|
||||
const char *attr_name, const char *fmt, ...)
|
||||
{
|
||||
struct ldb_val val;
|
||||
va_list ap;
|
||||
char *str;
|
||||
|
||||
va_start(ap, fmt);
|
||||
str = talloc_vasprintf(msg, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (str == NULL) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
val.data = (uint8_t *)str;
|
||||
val.length = strlen(str);
|
||||
|
||||
return ldb_msg_add_steal_value(msg, attr_name, &val);
|
||||
}
|
||||
|
||||
/*
|
||||
compare two ldb_message_element structures
|
||||
assumes case senistive comparison
|
||||
*/
|
||||
int ldb_msg_element_compare(struct ldb_message_element *el1,
|
||||
struct ldb_message_element *el2)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (el1->num_values != el2->num_values) {
|
||||
return el1->num_values - el2->num_values;
|
||||
}
|
||||
|
||||
for (i=0;i<el1->num_values;i++) {
|
||||
if (!ldb_msg_find_val(el2, &el1->values[i])) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two ldb_message_element structures
|
||||
comparing by element name
|
||||
*/
|
||||
int ldb_msg_element_compare_name(struct ldb_message_element *el1,
|
||||
struct ldb_message_element *el2)
|
||||
{
|
||||
return ldb_attr_cmp(el1->name, el2->name);
|
||||
}
|
||||
|
||||
/*
|
||||
convenience functions to return common types from a message
|
||||
these return the first value if the attribute is multi-valued
|
||||
*/
|
||||
const struct ldb_val *ldb_msg_find_ldb_val(const struct ldb_message *msg, const char *attr_name)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr_name);
|
||||
if (!el || el->num_values == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return &el->values[0];
|
||||
}
|
||||
|
||||
int ldb_msg_find_attr_as_int(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtol((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
unsigned int ldb_msg_find_attr_as_uint(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
unsigned int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoul((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
int64_t ldb_msg_find_attr_as_int64(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int64_t default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoll((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
uint64_t ldb_msg_find_attr_as_uint64(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
uint64_t default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtoull((const char *)v->data, NULL, 0);
|
||||
}
|
||||
|
||||
double ldb_msg_find_attr_as_double(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
double default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return strtod((const char *)v->data, NULL);
|
||||
}
|
||||
|
||||
int ldb_msg_find_attr_as_bool(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
int default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
if (strcasecmp((const char *)v->data, "FALSE") == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (strcasecmp((const char *)v->data, "TRUE") == 0) {
|
||||
return 1;
|
||||
}
|
||||
return default_value;
|
||||
}
|
||||
|
||||
const char *ldb_msg_find_attr_as_string(const struct ldb_message *msg,
|
||||
const char *attr_name,
|
||||
const char *default_value)
|
||||
{
|
||||
const struct ldb_val *v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return default_value;
|
||||
}
|
||||
return (const char *)v->data;
|
||||
}
|
||||
|
||||
struct ldb_dn *ldb_msg_find_attr_as_dn(struct ldb_context *ldb,
|
||||
void *mem_ctx,
|
||||
const struct ldb_message *msg,
|
||||
const char *attr_name)
|
||||
{
|
||||
struct ldb_dn *res_dn;
|
||||
const struct ldb_val *v;
|
||||
|
||||
v = ldb_msg_find_ldb_val(msg, attr_name);
|
||||
if (!v || !v->data) {
|
||||
return NULL;
|
||||
}
|
||||
res_dn = ldb_dn_new(mem_ctx, ldb, (const char *)v->data);
|
||||
if ( ! ldb_dn_validate(res_dn)) {
|
||||
talloc_free(res_dn);
|
||||
return NULL;
|
||||
}
|
||||
return res_dn;
|
||||
}
|
||||
|
||||
/*
|
||||
sort the elements of a message by name
|
||||
*/
|
||||
void ldb_msg_sort_elements(struct ldb_message *msg)
|
||||
{
|
||||
qsort(msg->elements, msg->num_elements, sizeof(struct ldb_message_element),
|
||||
(comparison_fn_t)ldb_msg_element_compare_name);
|
||||
}
|
||||
|
||||
/*
|
||||
shallow copy a message - copying only the elements array so that the caller
|
||||
can safely add new elements without changing the message
|
||||
*/
|
||||
struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_message *msg2;
|
||||
int i;
|
||||
|
||||
msg2 = talloc(mem_ctx, struct ldb_message);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
*msg2 = *msg;
|
||||
msg2->private_data = NULL;
|
||||
|
||||
msg2->elements = talloc_array(msg2, struct ldb_message_element,
|
||||
msg2->num_elements);
|
||||
if (msg2->elements == NULL) goto failed;
|
||||
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
msg2->elements[i] = msg->elements[i];
|
||||
}
|
||||
|
||||
return msg2;
|
||||
|
||||
failed:
|
||||
talloc_free(msg2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy a message, allocating new memory for all parts
|
||||
*/
|
||||
struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_message *msg2;
|
||||
int i, j;
|
||||
|
||||
msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
msg2->dn = ldb_dn_copy(msg2, msg2->dn);
|
||||
if (msg2->dn == NULL) goto failed;
|
||||
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
struct ldb_message_element *el = &msg2->elements[i];
|
||||
struct ldb_val *values = el->values;
|
||||
el->name = talloc_strdup(msg2->elements, el->name);
|
||||
if (el->name == NULL) goto failed;
|
||||
el->values = talloc_array(msg2->elements, struct ldb_val, el->num_values);
|
||||
for (j=0;j<el->num_values;j++) {
|
||||
el->values[j] = ldb_val_dup(el->values, &values[j]);
|
||||
if (el->values[j].data == NULL && values[j].length != 0) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return msg2;
|
||||
|
||||
failed:
|
||||
talloc_free(msg2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
canonicalise a message, merging elements of the same name
|
||||
*/
|
||||
struct ldb_message *ldb_msg_canonicalize(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
int i;
|
||||
struct ldb_message *msg2;
|
||||
|
||||
msg2 = ldb_msg_copy(ldb, msg);
|
||||
if (msg2 == NULL) return NULL;
|
||||
|
||||
ldb_msg_sort_elements(msg2);
|
||||
|
||||
for (i=1;i<msg2->num_elements;i++) {
|
||||
struct ldb_message_element *el1 = &msg2->elements[i-1];
|
||||
struct ldb_message_element *el2 = &msg2->elements[i];
|
||||
if (ldb_msg_element_compare_name(el1, el2) == 0) {
|
||||
el1->values = talloc_realloc(msg2->elements, el1->values, struct ldb_val,
|
||||
el1->num_values + el2->num_values);
|
||||
if (el1->values == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
memcpy(el1->values + el1->num_values,
|
||||
el2->values,
|
||||
sizeof(struct ldb_val) * el2->num_values);
|
||||
el1->num_values += el2->num_values;
|
||||
talloc_free(discard_const_p(char, el2->name));
|
||||
if (i+1<msg2->num_elements) {
|
||||
memmove(el2, el2+1, sizeof(struct ldb_message_element) *
|
||||
(msg2->num_elements - (i+1)));
|
||||
}
|
||||
msg2->num_elements--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return msg2;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return a ldb_message representing the differences between msg1 and msg2. If you
|
||||
then use this in a ldb_modify() call it can be used to save edits to a message
|
||||
*/
|
||||
struct ldb_message *ldb_msg_diff(struct ldb_context *ldb,
|
||||
struct ldb_message *msg1,
|
||||
struct ldb_message *msg2)
|
||||
{
|
||||
struct ldb_message *mod;
|
||||
struct ldb_message_element *el;
|
||||
unsigned int i;
|
||||
|
||||
mod = ldb_msg_new(ldb);
|
||||
|
||||
mod->dn = msg1->dn;
|
||||
mod->num_elements = 0;
|
||||
mod->elements = NULL;
|
||||
|
||||
msg2 = ldb_msg_canonicalize(ldb, msg2);
|
||||
if (msg2 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* look in msg2 to find elements that need to be added
|
||||
or modified */
|
||||
for (i=0;i<msg2->num_elements;i++) {
|
||||
el = ldb_msg_find_element(msg1, msg2->elements[i].name);
|
||||
|
||||
if (el && ldb_msg_element_compare(el, &msg2->elements[i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ldb_msg_add(mod,
|
||||
&msg2->elements[i],
|
||||
el?LDB_FLAG_MOD_REPLACE:LDB_FLAG_MOD_ADD) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* look in msg1 to find elements that need to be deleted */
|
||||
for (i=0;i<msg1->num_elements;i++) {
|
||||
el = ldb_msg_find_element(msg2, msg1->elements[i].name);
|
||||
if (!el) {
|
||||
if (ldb_msg_add_empty(mod,
|
||||
msg1->elements[i].name,
|
||||
LDB_FLAG_MOD_DELETE, NULL) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
int ldb_msg_sanity_check(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* basic check on DN */
|
||||
if (msg->dn == NULL) {
|
||||
/* TODO: return also an error string */
|
||||
ldb_set_errstring(ldb, "ldb message lacks a DN!");
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
/* basic syntax checks */
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
for (j = 0; j < msg->elements[i].num_values; j++) {
|
||||
if (msg->elements[i].values[j].length == 0) {
|
||||
TALLOC_CTX *mem_ctx = talloc_new(ldb);
|
||||
/* an attribute cannot be empty */
|
||||
/* TODO: return also an error string */
|
||||
ldb_asprintf_errstring(ldb, "Element %s has empty attribute in ldb message (%s)!",
|
||||
msg->elements[i].name,
|
||||
ldb_dn_get_linearized(msg->dn));
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
copy an attribute list. This only copies the array, not the elements
|
||||
(ie. the elements are left as the same pointers)
|
||||
*/
|
||||
const char **ldb_attr_list_copy(TALLOC_CTX *mem_ctx, const char * const *attrs)
|
||||
{
|
||||
const char **ret;
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) /* noop */ ;
|
||||
ret = talloc_array(mem_ctx, const char *, i+1);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;attrs[i];i++) {
|
||||
ret[i] = attrs[i];
|
||||
}
|
||||
ret[i] = attrs[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy an attribute list. This only copies the array, not the elements
|
||||
(ie. the elements are left as the same pointers). The new attribute is added to the list.
|
||||
*/
|
||||
const char **ldb_attr_list_copy_add(TALLOC_CTX *mem_ctx, const char * const *attrs, const char *new_attr)
|
||||
{
|
||||
const char **ret;
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) /* noop */ ;
|
||||
ret = talloc_array(mem_ctx, const char *, i+2);
|
||||
if (ret == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;attrs[i];i++) {
|
||||
ret[i] = attrs[i];
|
||||
}
|
||||
ret[i] = new_attr;
|
||||
ret[i+1] = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return 1 if an attribute is in a list of attributes, or 0 otherwise
|
||||
*/
|
||||
int ldb_attr_in_list(const char * const *attrs, const char *attr)
|
||||
{
|
||||
int i;
|
||||
for (i=0;attrs[i];i++) {
|
||||
if (ldb_attr_cmp(attrs[i], attr) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename the specified attribute in a search result
|
||||
*/
|
||||
int ldb_msg_rename_attr(struct ldb_message *msg, const char *attr, const char *replace)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el == NULL) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
el->name = talloc_strdup(msg->elements, replace);
|
||||
if (el->name == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy the specified attribute in a search result to a new attribute
|
||||
*/
|
||||
int ldb_msg_copy_attr(struct ldb_message *msg, const char *attr, const char *replace)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el == NULL) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
if (ldb_msg_add(msg, el, 0) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
return ldb_msg_rename_attr(msg, attr, replace);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
remove the specified attribute in a search result
|
||||
*/
|
||||
void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr)
|
||||
{
|
||||
struct ldb_message_element *el = ldb_msg_find_element(msg, attr);
|
||||
if (el) {
|
||||
int n = (el - msg->elements);
|
||||
if (n != msg->num_elements-1) {
|
||||
memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
|
||||
}
|
||||
msg->num_elements--;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
remove the specified element in a search result
|
||||
*/
|
||||
void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el)
|
||||
{
|
||||
int n = (el - msg->elements);
|
||||
if (n != msg->num_elements-1) {
|
||||
memmove(el, el+1, ((msg->num_elements-1) - n)*sizeof(*el));
|
||||
}
|
||||
msg->num_elements--;
|
||||
}
|
||||
|
||||
/*
|
||||
return a LDAP formatted time string
|
||||
*/
|
||||
char *ldb_timestring(TALLOC_CTX *mem_ctx, time_t t)
|
||||
{
|
||||
struct tm *tm = gmtime(&t);
|
||||
char *ts;
|
||||
int r;
|
||||
|
||||
if (!tm) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we now excatly how long this string will be */
|
||||
ts = talloc_array(mem_ctx, char, 18);
|
||||
|
||||
/* formatted like: 20040408072012.0Z */
|
||||
r = snprintf(ts, 18,
|
||||
"%04u%02u%02u%02u%02u%02u.0Z",
|
||||
tm->tm_year+1900, tm->tm_mon+1,
|
||||
tm->tm_mday, tm->tm_hour, tm->tm_min,
|
||||
tm->tm_sec);
|
||||
|
||||
if (r != 17) {
|
||||
talloc_free(ts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert a LDAP time string to a time_t. Return 0 if unable to convert
|
||||
*/
|
||||
time_t ldb_string_to_time(const char *s)
|
||||
{
|
||||
struct tm tm;
|
||||
|
||||
if (s == NULL) return 0;
|
||||
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
if (sscanf(s, "%04u%02u%02u%02u%02u%02u",
|
||||
&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
|
||||
&tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
|
||||
return 0;
|
||||
}
|
||||
tm.tm_year -= 1900;
|
||||
tm.tm_mon -= 1;
|
||||
|
||||
return timegm(&tm);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
dump a set of results to a file. Useful from within gdb
|
||||
*/
|
||||
void ldb_dump_results(struct ldb_context *ldb, struct ldb_result *result, FILE *f)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < result->count; i++) {
|
||||
struct ldb_ldif ldif;
|
||||
fprintf(f, "# record %d\n", i+1);
|
||||
ldif.changetype = LDB_CHANGETYPE_NONE;
|
||||
ldif.msg = result->msgs[i];
|
||||
ldb_ldif_write_file(ldb, f, &ldif);
|
||||
}
|
||||
}
|
||||
|
||||
int ldb_msg_check_string_attribute(const struct ldb_message *msg, const char *name, const char *value)
|
||||
{
|
||||
struct ldb_message_element *el;
|
||||
struct ldb_val val;
|
||||
|
||||
el = ldb_msg_find_element(msg, name);
|
||||
if (el == NULL)
|
||||
return 0;
|
||||
|
||||
val.data = discard_const_p(uint8_t, value);
|
||||
val.length = strlen(value);
|
||||
|
||||
if (ldb_msg_find_val(el, &val))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,817 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb expression parsing
|
||||
*
|
||||
* Description: parse LDAP-like search expressions
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- add RFC2254 binary string handling
|
||||
- possibly add ~=, <= and >= handling
|
||||
- expand the test suite
|
||||
- add better parse error handling
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
/*
|
||||
a filter is defined by:
|
||||
<filter> ::= '(' <filtercomp> ')'
|
||||
<filtercomp> ::= <and> | <or> | <not> | <simple>
|
||||
<and> ::= '&' <filterlist>
|
||||
<or> ::= '|' <filterlist>
|
||||
<not> ::= '!' <filter>
|
||||
<filterlist> ::= <filter> | <filter> <filterlist>
|
||||
<simple> ::= <attributetype> <filtertype> <attributevalue>
|
||||
<filtertype> ::= '=' | '~=' | '<=' | '>='
|
||||
*/
|
||||
|
||||
/*
|
||||
decode a RFC2254 binary string representation of a buffer.
|
||||
Used in LDAP filters.
|
||||
*/
|
||||
struct ldb_val ldb_binary_decode(void *mem_ctx, const char *str)
|
||||
{
|
||||
int i, j;
|
||||
struct ldb_val ret;
|
||||
int slen = str?strlen(str):0;
|
||||
|
||||
ret.data = (uint8_t *)talloc_size(mem_ctx, slen+1);
|
||||
ret.length = 0;
|
||||
if (ret.data == NULL) return ret;
|
||||
|
||||
for (i=j=0;i<slen;i++) {
|
||||
if (str[i] == '\\') {
|
||||
unsigned c;
|
||||
if (sscanf(&str[i+1], "%02X", &c) != 1) {
|
||||
talloc_free(ret.data);
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
return ret;
|
||||
}
|
||||
((uint8_t *)ret.data)[j++] = c;
|
||||
i += 2;
|
||||
} else {
|
||||
((uint8_t *)ret.data)[j++] = str[i];
|
||||
}
|
||||
}
|
||||
ret.length = j;
|
||||
((uint8_t *)ret.data)[j] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
encode a blob as a RFC2254 binary string, escaping any
|
||||
non-printable or '\' characters
|
||||
*/
|
||||
char *ldb_binary_encode(void *mem_ctx, struct ldb_val val)
|
||||
{
|
||||
int i;
|
||||
char *ret;
|
||||
int len = val.length;
|
||||
unsigned char *buf = val.data;
|
||||
|
||||
for (i=0;i<val.length;i++) {
|
||||
if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
|
||||
len += 2;
|
||||
}
|
||||
}
|
||||
ret = talloc_array(mem_ctx, char, len+1);
|
||||
if (ret == NULL) return NULL;
|
||||
|
||||
len = 0;
|
||||
for (i=0;i<val.length;i++) {
|
||||
if (!isprint(buf[i]) || strchr(" *()\\&|!\"", buf[i])) {
|
||||
snprintf(ret+len, 4, "\\%02X", buf[i]);
|
||||
len += 3;
|
||||
} else {
|
||||
ret[len++] = buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
ret[len] = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
encode a string as a RFC2254 binary string, escaping any
|
||||
non-printable or '\' characters. This routine is suitable for use
|
||||
in escaping user data in ldap filters.
|
||||
*/
|
||||
char *ldb_binary_encode_string(void *mem_ctx, const char *string)
|
||||
{
|
||||
struct ldb_val val;
|
||||
val.data = discard_const_p(uint8_t, string);
|
||||
val.length = strlen(string);
|
||||
return ldb_binary_encode(mem_ctx, val);
|
||||
}
|
||||
|
||||
/* find the first matching wildcard */
|
||||
static char *ldb_parse_find_wildcard(char *value)
|
||||
{
|
||||
while (*value) {
|
||||
value = strpbrk(value, "\\*");
|
||||
if (value == NULL) return NULL;
|
||||
|
||||
if (value[0] == '\\') {
|
||||
if (value[1] == '\0') return NULL;
|
||||
value += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value[0] == '*') return value;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return a NULL terminated list of binary strings representing the value
|
||||
chunks separated by wildcards that makes the value portion of the filter
|
||||
*/
|
||||
static struct ldb_val **ldb_wildcard_decode(void *mem_ctx, const char *string)
|
||||
{
|
||||
struct ldb_val **ret = NULL;
|
||||
int val = 0;
|
||||
char *wc, *str;
|
||||
|
||||
wc = talloc_strdup(mem_ctx, string);
|
||||
if (wc == NULL) return NULL;
|
||||
|
||||
while (wc && *wc) {
|
||||
str = wc;
|
||||
wc = ldb_parse_find_wildcard(str);
|
||||
if (wc && *wc) {
|
||||
if (wc == str) {
|
||||
wc++;
|
||||
continue;
|
||||
}
|
||||
*wc = 0;
|
||||
wc++;
|
||||
}
|
||||
|
||||
ret = talloc_realloc(mem_ctx, ret, struct ldb_val *, val + 2);
|
||||
if (ret == NULL) return NULL;
|
||||
|
||||
ret[val] = talloc(mem_ctx, struct ldb_val);
|
||||
if (ret[val] == NULL) return NULL;
|
||||
|
||||
*(ret[val]) = ldb_binary_decode(mem_ctx, str);
|
||||
if ((ret[val])->data == NULL) return NULL;
|
||||
|
||||
val++;
|
||||
}
|
||||
|
||||
if (ret != NULL) {
|
||||
ret[val] = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s);
|
||||
|
||||
|
||||
/*
|
||||
parse an extended match
|
||||
|
||||
possible forms:
|
||||
(attr:oid:=value)
|
||||
(attr:dn:oid:=value)
|
||||
(attr:dn:=value)
|
||||
(:dn:oid:=value)
|
||||
|
||||
the ':dn' part sets the dnAttributes boolean if present
|
||||
the oid sets the rule_id string
|
||||
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_extended(struct ldb_parse_tree *ret,
|
||||
char *attr, char *value)
|
||||
{
|
||||
char *p1, *p2;
|
||||
|
||||
ret->operation = LDB_OP_EXTENDED;
|
||||
ret->u.extended.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.extended.value.data == NULL) goto failed;
|
||||
|
||||
p1 = strchr(attr, ':');
|
||||
if (p1 == NULL) goto failed;
|
||||
p2 = strchr(p1+1, ':');
|
||||
|
||||
*p1 = 0;
|
||||
if (p2) *p2 = 0;
|
||||
|
||||
ret->u.extended.attr = attr;
|
||||
if (strcmp(p1+1, "dn") == 0) {
|
||||
ret->u.extended.dnAttributes = 1;
|
||||
if (p2) {
|
||||
ret->u.extended.rule_id = talloc_strdup(ret, p2+1);
|
||||
if (ret->u.extended.rule_id == NULL) goto failed;
|
||||
} else {
|
||||
ret->u.extended.rule_id = NULL;
|
||||
}
|
||||
} else {
|
||||
ret->u.extended.dnAttributes = 0;
|
||||
ret->u.extended.rule_id = talloc_strdup(ret, p1+1);
|
||||
if (ret->u.extended.rule_id == NULL) goto failed;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
failed:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum ldb_parse_op ldb_parse_filtertype(void *mem_ctx, char **type, char **value, const char **s)
|
||||
{
|
||||
enum ldb_parse_op filter = 0;
|
||||
char *name, *val, *k;
|
||||
const char *p = *s;
|
||||
const char *t, *t1;
|
||||
|
||||
/* retrieve attributetype name */
|
||||
t = p;
|
||||
|
||||
while ((isascii(*p) && isalnum((unsigned char)*p)) || (*p == '-')) { /* attribute names can only be alphanums */
|
||||
p++;
|
||||
}
|
||||
|
||||
if (*p == ':') { /* but extended searches have : and . chars too */
|
||||
p = strstr(p, ":=");
|
||||
if (p == NULL) { /* malformed attribute name */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
t1 = p;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
if (!strchr("=<>~:", *p)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* save name */
|
||||
name = (char *)talloc_memdup(mem_ctx, t, t1 - t + 1);
|
||||
if (name == NULL) return 0;
|
||||
name[t1 - t] = '\0';
|
||||
|
||||
/* retrieve filtertype */
|
||||
|
||||
if (*p == '=') {
|
||||
filter = LDB_OP_EQUALITY;
|
||||
} else if (*(p + 1) == '=') {
|
||||
switch (*p) {
|
||||
case '<':
|
||||
filter = LDB_OP_LESS;
|
||||
p++;
|
||||
break;
|
||||
case '>':
|
||||
filter = LDB_OP_GREATER;
|
||||
p++;
|
||||
break;
|
||||
case '~':
|
||||
filter = LDB_OP_APPROX;
|
||||
p++;
|
||||
break;
|
||||
case ':':
|
||||
filter = LDB_OP_EXTENDED;
|
||||
p++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!filter) {
|
||||
talloc_free(name);
|
||||
return filter;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
/* retieve value */
|
||||
t = p;
|
||||
|
||||
while (*p && ((*p != ')') || ((*p == ')') && (*(p - 1) == '\\')))) p++;
|
||||
|
||||
val = (char *)talloc_memdup(mem_ctx, t, p - t + 1);
|
||||
if (val == NULL) {
|
||||
talloc_free(name);
|
||||
return 0;
|
||||
}
|
||||
val[p - t] = '\0';
|
||||
|
||||
k = &(val[p - t]);
|
||||
|
||||
/* remove trailing spaces from value */
|
||||
while ((k > val) && (isspace((unsigned char)*(k - 1)))) k--;
|
||||
*k = '\0';
|
||||
|
||||
*type = name;
|
||||
*value = val;
|
||||
*s = p;
|
||||
return filter;
|
||||
}
|
||||
|
||||
/*
|
||||
<simple> ::= <attributetype> <filtertype> <attributevalue>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_simple(void *mem_ctx, const char **s)
|
||||
{
|
||||
char *attr, *value;
|
||||
struct ldb_parse_tree *ret;
|
||||
enum ldb_parse_op filtertype;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filtertype = ldb_parse_filtertype(ret, &attr, &value, s);
|
||||
if (!filtertype) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (filtertype) {
|
||||
|
||||
case LDB_OP_PRESENT:
|
||||
ret->operation = LDB_OP_PRESENT;
|
||||
ret->u.present.attr = attr;
|
||||
break;
|
||||
|
||||
case LDB_OP_EQUALITY:
|
||||
|
||||
if (strcmp(value, "*") == 0) {
|
||||
ret->operation = LDB_OP_PRESENT;
|
||||
ret->u.present.attr = attr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ldb_parse_find_wildcard(value) != NULL) {
|
||||
ret->operation = LDB_OP_SUBSTRING;
|
||||
ret->u.substring.attr = attr;
|
||||
ret->u.substring.start_with_wildcard = 0;
|
||||
ret->u.substring.end_with_wildcard = 0;
|
||||
ret->u.substring.chunks = ldb_wildcard_decode(ret, value);
|
||||
if (ret->u.substring.chunks == NULL){
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
if (value[0] == '*')
|
||||
ret->u.substring.start_with_wildcard = 1;
|
||||
if (value[strlen(value) - 1] == '*')
|
||||
ret->u.substring.end_with_wildcard = 1;
|
||||
talloc_free(value);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
ret->operation = LDB_OP_EQUALITY;
|
||||
ret->u.equality.attr = attr;
|
||||
ret->u.equality.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.equality.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_GREATER:
|
||||
ret->operation = LDB_OP_GREATER;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_LESS:
|
||||
ret->operation = LDB_OP_LESS;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_APPROX:
|
||||
ret->operation = LDB_OP_APPROX;
|
||||
ret->u.comparison.attr = attr;
|
||||
ret->u.comparison.value = ldb_binary_decode(ret, value);
|
||||
if (ret->u.comparison.value.data == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
talloc_free(value);
|
||||
break;
|
||||
|
||||
case LDB_OP_EXTENDED:
|
||||
|
||||
ret = ldb_parse_extended(ret, attr, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
parse a filterlist
|
||||
<and> ::= '&' <filterlist>
|
||||
<or> ::= '|' <filterlist>
|
||||
<filterlist> ::= <filter> | <filter> <filterlist>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filterlist(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret, *next;
|
||||
enum ldb_parse_op op;
|
||||
const char *p = *s;
|
||||
|
||||
switch (*p) {
|
||||
case '&':
|
||||
op = LDB_OP_AND;
|
||||
break;
|
||||
case '|':
|
||||
op = LDB_OP_OR;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->operation = op;
|
||||
ret->u.list.num_elements = 1;
|
||||
ret->u.list.elements = talloc(ret, struct ldb_parse_tree *);
|
||||
if (!ret->u.list.elements) {
|
||||
errno = ENOMEM;
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->u.list.elements[0] = ldb_parse_filter(ret->u.list.elements, &p);
|
||||
if (!ret->u.list.elements[0]) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
while (*p && (next = ldb_parse_filter(ret->u.list.elements, &p))) {
|
||||
struct ldb_parse_tree **e;
|
||||
e = talloc_realloc(ret, ret->u.list.elements,
|
||||
struct ldb_parse_tree *,
|
||||
ret->u.list.num_elements + 1);
|
||||
if (!e) {
|
||||
errno = ENOMEM;
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->u.list.elements = e;
|
||||
ret->u.list.elements[ret->u.list.num_elements] = next;
|
||||
ret->u.list.num_elements++;
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<not> ::= '!' <filter>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_not(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
if (*p != '!') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_parse_tree);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->operation = LDB_OP_NOT;
|
||||
ret->u.isnot.child = ldb_parse_filter(ret, &p);
|
||||
if (!ret->u.isnot.child) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a filtercomp
|
||||
<filtercomp> ::= <and> | <or> | <not> | <simple>
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filtercomp(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
while (isspace((unsigned char)*p)) p++;
|
||||
|
||||
switch (*p) {
|
||||
case '&':
|
||||
ret = ldb_parse_filterlist(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '|':
|
||||
ret = ldb_parse_filterlist(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '!':
|
||||
ret = ldb_parse_not(mem_ctx, &p);
|
||||
break;
|
||||
|
||||
case '(':
|
||||
case ')':
|
||||
return NULL;
|
||||
|
||||
default:
|
||||
ret = ldb_parse_simple(mem_ctx, &p);
|
||||
|
||||
}
|
||||
|
||||
*s = p;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
<filter> ::= '(' <filtercomp> ')'
|
||||
*/
|
||||
static struct ldb_parse_tree *ldb_parse_filter(void *mem_ctx, const char **s)
|
||||
{
|
||||
struct ldb_parse_tree *ret;
|
||||
const char *p = *s;
|
||||
|
||||
if (*p != '(') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
ret = ldb_parse_filtercomp(mem_ctx, &p);
|
||||
|
||||
if (*p != ')') {
|
||||
return NULL;
|
||||
}
|
||||
p++;
|
||||
|
||||
while (isspace((unsigned char)*p)) {
|
||||
p++;
|
||||
}
|
||||
|
||||
*s = p;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
main parser entry point. Takes a search string and returns a parse tree
|
||||
|
||||
expression ::= <simple> | <filter>
|
||||
*/
|
||||
struct ldb_parse_tree *ldb_parse_tree(void *mem_ctx, const char *s)
|
||||
{
|
||||
if (s == NULL || *s == 0) {
|
||||
s = "(|(objectClass=*)(distinguishedName=*))";
|
||||
}
|
||||
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
|
||||
if (*s == '(') {
|
||||
return ldb_parse_filter(mem_ctx, &s);
|
||||
}
|
||||
|
||||
return ldb_parse_simple(mem_ctx, &s);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
construct a ldap parse filter given a parse tree
|
||||
*/
|
||||
char *ldb_filter_from_tree(void *mem_ctx, struct ldb_parse_tree *tree)
|
||||
{
|
||||
char *s, *s2, *ret;
|
||||
int i;
|
||||
|
||||
if (tree == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
case LDB_OP_OR:
|
||||
ret = talloc_asprintf(mem_ctx, "(%c", tree->operation==LDB_OP_AND?'&':'|');
|
||||
if (ret == NULL) return NULL;
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
s = ldb_filter_from_tree(mem_ctx, tree->u.list.elements[i]);
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
s2 = talloc_asprintf_append(ret, "%s", s);
|
||||
talloc_free(s);
|
||||
if (s2 == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s2;
|
||||
}
|
||||
s = talloc_asprintf_append(ret, ")");
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
return s;
|
||||
case LDB_OP_NOT:
|
||||
s = ldb_filter_from_tree(mem_ctx, tree->u.isnot.child);
|
||||
if (s == NULL) return NULL;
|
||||
|
||||
ret = talloc_asprintf(mem_ctx, "(!%s)", s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_EQUALITY:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_SUBSTRING:
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=%s", tree->u.substring.attr,
|
||||
tree->u.substring.start_with_wildcard?"*":"");
|
||||
if (ret == NULL) return NULL;
|
||||
for (i = 0; tree->u.substring.chunks[i]; i++) {
|
||||
s2 = ldb_binary_encode(mem_ctx, *(tree->u.substring.chunks[i]));
|
||||
if (s2 == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
if (tree->u.substring.chunks[i+1] ||
|
||||
tree->u.substring.end_with_wildcard) {
|
||||
s = talloc_asprintf_append(ret, "%s*", s2);
|
||||
} else {
|
||||
s = talloc_asprintf_append(ret, "%s", s2);
|
||||
}
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s;
|
||||
}
|
||||
s = talloc_asprintf_append(ret, ")");
|
||||
if (s == NULL) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret = s;
|
||||
return ret;
|
||||
case LDB_OP_GREATER:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s>=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_LESS:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s<=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_PRESENT:
|
||||
ret = talloc_asprintf(mem_ctx, "(%s=*)", tree->u.present.attr);
|
||||
return ret;
|
||||
case LDB_OP_APPROX:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.equality.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s~=%s)",
|
||||
tree->u.equality.attr, s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
case LDB_OP_EXTENDED:
|
||||
s = ldb_binary_encode(mem_ctx, tree->u.extended.value);
|
||||
if (s == NULL) return NULL;
|
||||
ret = talloc_asprintf(mem_ctx, "(%s%s%s%s:=%s)",
|
||||
tree->u.extended.attr?tree->u.extended.attr:"",
|
||||
tree->u.extended.dnAttributes?":dn":"",
|
||||
tree->u.extended.rule_id?":":"",
|
||||
tree->u.extended.rule_id?tree->u.extended.rule_id:"",
|
||||
s);
|
||||
talloc_free(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
replace any occurances of an attribute name in the parse tree with a
|
||||
new name
|
||||
*/
|
||||
void ldb_parse_tree_attr_replace(struct ldb_parse_tree *tree,
|
||||
const char *attr,
|
||||
const char *replace)
|
||||
{
|
||||
int i;
|
||||
switch (tree->operation) {
|
||||
case LDB_OP_AND:
|
||||
case LDB_OP_OR:
|
||||
for (i=0;i<tree->u.list.num_elements;i++) {
|
||||
ldb_parse_tree_attr_replace(tree->u.list.elements[i],
|
||||
attr, replace);
|
||||
}
|
||||
break;
|
||||
case LDB_OP_NOT:
|
||||
ldb_parse_tree_attr_replace(tree->u.isnot.child, attr, replace);
|
||||
break;
|
||||
case LDB_OP_EQUALITY:
|
||||
case LDB_OP_GREATER:
|
||||
case LDB_OP_LESS:
|
||||
case LDB_OP_APPROX:
|
||||
if (ldb_attr_cmp(tree->u.equality.attr, attr) == 0) {
|
||||
tree->u.equality.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_SUBSTRING:
|
||||
if (ldb_attr_cmp(tree->u.substring.attr, attr) == 0) {
|
||||
tree->u.substring.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_PRESENT:
|
||||
if (ldb_attr_cmp(tree->u.present.attr, attr) == 0) {
|
||||
tree->u.present.attr = replace;
|
||||
}
|
||||
break;
|
||||
case LDB_OP_EXTENDED:
|
||||
if (tree->u.extended.attr &&
|
||||
ldb_attr_cmp(tree->u.extended.attr, attr) == 0) {
|
||||
tree->u.extended.attr = replace;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb utf8 handling
|
||||
*
|
||||
* Description: case folding and case comparison for UTF8 strings
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "system/locale.h"
|
||||
|
||||
|
||||
/*
|
||||
this allow the user to pass in a caseless comparison
|
||||
function to handle utf8 caseless comparisons
|
||||
*/
|
||||
void ldb_set_utf8_fns(struct ldb_context *ldb,
|
||||
void *context,
|
||||
char *(*casefold)(void *, void *, const char *))
|
||||
{
|
||||
if (context)
|
||||
ldb->utf8_fns.context = context;
|
||||
if (casefold)
|
||||
ldb->utf8_fns.casefold = casefold;
|
||||
}
|
||||
|
||||
/*
|
||||
a simple case folding function
|
||||
NOTE: does not handle UTF8
|
||||
*/
|
||||
char *ldb_casefold_default(void *context, void *mem_ctx, const char *s)
|
||||
{
|
||||
int i;
|
||||
char *ret = talloc_strdup(mem_ctx, s);
|
||||
if (!s) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
for (i=0;ret[i];i++) {
|
||||
ret[i] = toupper((unsigned char)ret[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ldb_set_utf8_default(struct ldb_context *ldb)
|
||||
{
|
||||
ldb_set_utf8_fns(ldb, NULL, ldb_casefold_default);
|
||||
}
|
||||
|
||||
char *ldb_casefold(struct ldb_context *ldb, void *mem_ctx, const char *s)
|
||||
{
|
||||
return ldb->utf8_fns.casefold(ldb->utf8_fns.context, mem_ctx, s);
|
||||
}
|
||||
|
||||
/*
|
||||
check the attribute name is valid according to rfc2251
|
||||
returns 1 if the name is ok
|
||||
*/
|
||||
|
||||
int ldb_valid_attr_name(const char *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!s || !s[0])
|
||||
return 0;
|
||||
|
||||
/* handle special ldb_tdb wildcard */
|
||||
if (strcmp(s, "*") == 0) return 1;
|
||||
|
||||
for (i = 0; s[i]; i++) {
|
||||
if (! isascii(s[i])) {
|
||||
return 0;
|
||||
}
|
||||
if (i == 0) { /* first char must be an alpha (or our special '@' identifier) */
|
||||
if (! (isalpha(s[i]) || (s[i] == '@'))) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (! (isalnum(s[i]) || (s[i] == '-'))) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *ldb_attr_casefold(void *mem_ctx, const char *s)
|
||||
{
|
||||
int i;
|
||||
char *ret = talloc_strdup(mem_ctx, s);
|
||||
if (!ret) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; ret[i]; i++) {
|
||||
ret[i] = toupper((unsigned char)ret[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
we accept either 'dn' or 'distinguishedName' for a distinguishedName
|
||||
*/
|
||||
int ldb_attr_dn(const char *attr)
|
||||
{
|
||||
if (ldb_attr_cmp(attr, "dn") == 0 ||
|
||||
ldb_attr_cmp(attr, "distinguishedName") == 0) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/* Copyright (C) 1991,1992,1996,1997,1999,2004 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
|
||||
|
||||
The GNU C 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.
|
||||
|
||||
The GNU C 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 the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
/* If you consider tuning this algorithm, you should consult first:
|
||||
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
||||
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
||||
|
||||
/* Modified to be used in samba4 by
|
||||
* Simo Sorce <idra@samba.org> 2005
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/* Byte-wise swap two items of size SIZE. */
|
||||
#define SWAP(a, b, size) \
|
||||
do \
|
||||
{ \
|
||||
register size_t __size = (size); \
|
||||
register char *__a = (a), *__b = (b); \
|
||||
do \
|
||||
{ \
|
||||
char __tmp = *__a; \
|
||||
*__a++ = *__b; \
|
||||
*__b++ = __tmp; \
|
||||
} while (--__size > 0); \
|
||||
} while (0)
|
||||
|
||||
/* Discontinue quicksort algorithm when partition gets below this size.
|
||||
This particular magic number was chosen to work best on a Sun 4/260. */
|
||||
#define MAX_THRESH 4
|
||||
|
||||
/* Stack node declarations used to store unfulfilled partition obligations. */
|
||||
typedef struct
|
||||
{
|
||||
char *lo;
|
||||
char *hi;
|
||||
} stack_node;
|
||||
|
||||
/* The next 4 #defines implement a very fast in-line stack abstraction. */
|
||||
/* The stack needs log (total_elements) entries (we could even subtract
|
||||
log(MAX_THRESH)). Since total_elements has type size_t, we get as
|
||||
upper bound for log (total_elements):
|
||||
bits per byte (CHAR_BIT) * sizeof(size_t). */
|
||||
#ifndef CHAR_BIT
|
||||
#define CHAR_BIT 8
|
||||
#endif
|
||||
#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
|
||||
#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
|
||||
#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
|
||||
#define STACK_NOT_EMPTY (stack < top)
|
||||
|
||||
|
||||
/* Order size using quicksort. This implementation incorporates
|
||||
four optimizations discussed in Sedgewick:
|
||||
|
||||
1. Non-recursive, using an explicit stack of pointer that store the
|
||||
next array partition to sort. To save time, this maximum amount
|
||||
of space required to store an array of SIZE_MAX is allocated on the
|
||||
stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
|
||||
only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
|
||||
Pretty cheap, actually.
|
||||
|
||||
2. Chose the pivot element using a median-of-three decision tree.
|
||||
This reduces the probability of selecting a bad pivot value and
|
||||
eliminates certain extraneous comparisons.
|
||||
|
||||
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
|
||||
insertion sort to order the MAX_THRESH items within each partition.
|
||||
This is a big win, since insertion sort is faster for small, mostly
|
||||
sorted array segments.
|
||||
|
||||
4. The larger of the two sub-partitions is always pushed onto the
|
||||
stack first, with the algorithm then concentrating on the
|
||||
smaller partition. This *guarantees* no more than log (total_elems)
|
||||
stack size is needed (actually O(1) in this case)! */
|
||||
|
||||
void ldb_qsort (void *const pbase, size_t total_elems, size_t size,
|
||||
void *opaque, ldb_qsort_cmp_fn_t cmp)
|
||||
{
|
||||
register char *base_ptr = (char *) pbase;
|
||||
|
||||
const size_t max_thresh = MAX_THRESH * size;
|
||||
|
||||
if (total_elems == 0)
|
||||
/* Avoid lossage with unsigned arithmetic below. */
|
||||
return;
|
||||
|
||||
if (total_elems > MAX_THRESH)
|
||||
{
|
||||
char *lo = base_ptr;
|
||||
char *hi = &lo[size * (total_elems - 1)];
|
||||
stack_node stack[STACK_SIZE];
|
||||
stack_node *top = stack;
|
||||
|
||||
PUSH (NULL, NULL);
|
||||
|
||||
while (STACK_NOT_EMPTY)
|
||||
{
|
||||
char *left_ptr;
|
||||
char *right_ptr;
|
||||
|
||||
/* Select median value from among LO, MID, and HI. Rearrange
|
||||
LO and HI so the three values are sorted. This lowers the
|
||||
probability of picking a pathological pivot value and
|
||||
skips a comparison for both the LEFT_PTR and RIGHT_PTR in
|
||||
the while loops. */
|
||||
|
||||
char *mid = lo + size * ((hi - lo) / size >> 1);
|
||||
|
||||
if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
|
||||
SWAP (mid, lo, size);
|
||||
if ((*cmp) ((void *) hi, (void *) mid, opaque) < 0)
|
||||
SWAP (mid, hi, size);
|
||||
else
|
||||
goto jump_over;
|
||||
if ((*cmp) ((void *) mid, (void *) lo, opaque) < 0)
|
||||
SWAP (mid, lo, size);
|
||||
jump_over:;
|
||||
|
||||
left_ptr = lo + size;
|
||||
right_ptr = hi - size;
|
||||
|
||||
/* Here's the famous ``collapse the walls'' section of quicksort.
|
||||
Gotta like those tight inner loops! They are the main reason
|
||||
that this algorithm runs much faster than others. */
|
||||
do
|
||||
{
|
||||
while ((*cmp) ((void *) left_ptr, (void *) mid, opaque) < 0)
|
||||
left_ptr += size;
|
||||
|
||||
while ((*cmp) ((void *) mid, (void *) right_ptr, opaque) < 0)
|
||||
right_ptr -= size;
|
||||
|
||||
if (left_ptr < right_ptr)
|
||||
{
|
||||
SWAP (left_ptr, right_ptr, size);
|
||||
if (mid == left_ptr)
|
||||
mid = right_ptr;
|
||||
else if (mid == right_ptr)
|
||||
mid = left_ptr;
|
||||
left_ptr += size;
|
||||
right_ptr -= size;
|
||||
}
|
||||
else if (left_ptr == right_ptr)
|
||||
{
|
||||
left_ptr += size;
|
||||
right_ptr -= size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (left_ptr <= right_ptr);
|
||||
|
||||
/* Set up pointers for next iteration. First determine whether
|
||||
left and right partitions are below the threshold size. If so,
|
||||
ignore one or both. Otherwise, push the larger partition's
|
||||
bounds on the stack and continue sorting the smaller one. */
|
||||
|
||||
if ((size_t) (right_ptr - lo) <= max_thresh)
|
||||
{
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore both small partitions. */
|
||||
POP (lo, hi);
|
||||
else
|
||||
/* Ignore small left partition. */
|
||||
lo = left_ptr;
|
||||
}
|
||||
else if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore small right partition. */
|
||||
hi = right_ptr;
|
||||
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
{
|
||||
/* Push larger left partition indices. */
|
||||
PUSH (lo, right_ptr);
|
||||
lo = left_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Push larger right partition indices. */
|
||||
PUSH (left_ptr, hi);
|
||||
hi = right_ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Once the BASE_PTR array is partially sorted by quicksort the rest
|
||||
is completely sorted using insertion sort, since this is efficient
|
||||
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
||||
of the array to sort, and END_PTR points at the very last element in
|
||||
the array (*not* one beyond it!). */
|
||||
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
{
|
||||
char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||
char *tmp_ptr = base_ptr;
|
||||
char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||
register char *run_ptr;
|
||||
|
||||
/* Find smallest element in first threshold and place it at the
|
||||
array's beginning. This is the smallest array element,
|
||||
and the operation speeds up insertion sort's inner loop. */
|
||||
|
||||
for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||
if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
|
||||
tmp_ptr = run_ptr;
|
||||
|
||||
if (tmp_ptr != base_ptr)
|
||||
SWAP (tmp_ptr, base_ptr, size);
|
||||
|
||||
/* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
|
||||
run_ptr = base_ptr + size;
|
||||
while ((run_ptr += size) <= end_ptr)
|
||||
{
|
||||
tmp_ptr = run_ptr - size;
|
||||
while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, opaque) < 0)
|
||||
tmp_ptr -= size;
|
||||
|
||||
tmp_ptr += size;
|
||||
if (tmp_ptr != run_ptr)
|
||||
{
|
||||
char *trav;
|
||||
|
||||
trav = run_ptr + size;
|
||||
while (--trav >= run_ptr)
|
||||
{
|
||||
char c = *trav;
|
||||
char *hi, *lo;
|
||||
|
||||
for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||
*hi = *lo;
|
||||
*hi = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1466
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,316 @@
|
||||
################################################
|
||||
# Start MODULE ldb_asq
|
||||
[MODULE::ldb_asq]
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_asq_init
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/asq.o
|
||||
# End MODULE ldb_asq
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_server_sort
|
||||
[MODULE::ldb_server_sort]
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_sort_init
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/sort.o
|
||||
# End MODULE ldb_sort
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_paged_results
|
||||
[MODULE::ldb_paged_results]
|
||||
INIT_FUNCTION = ldb_paged_results_init
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/paged_results.o
|
||||
# End MODULE ldb_paged_results
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_paged_results
|
||||
[MODULE::ldb_paged_searches]
|
||||
INIT_FUNCTION = ldb_paged_searches_init
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/paged_searches.o
|
||||
# End MODULE ldb_paged_results
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_operational
|
||||
[MODULE::ldb_operational]
|
||||
SUBSYSTEM = ldb
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_operational_init
|
||||
OBJ_FILES = \
|
||||
modules/operational.o
|
||||
# End MODULE ldb_operational
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_objectclass
|
||||
[MODULE::ldb_objectclass]
|
||||
INIT_FUNCTION = ldb_objectclass_init
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/objectclass.o
|
||||
# End MODULE ldb_objectclass
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_rdn_name
|
||||
[MODULE::ldb_rdn_name]
|
||||
SUBSYSTEM = ldb
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_rdn_name_init
|
||||
OBJ_FILES = \
|
||||
modules/rdn_name.o
|
||||
# End MODULE ldb_rdn_name
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_ildap
|
||||
[MODULE::ldb_ildap]
|
||||
SUBSYSTEM = ldb
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_ildap_init
|
||||
ALIASES = ldapi ldaps ldap
|
||||
OBJ_FILES = \
|
||||
ldb_ildap/ldb_ildap.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_LDAP
|
||||
# End MODULE ldb_ildap
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_map
|
||||
[MODULE::ldb_map]
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
SUBSYSTEM = ldb
|
||||
OBJ_FILES = \
|
||||
modules/ldb_map_inbound.o \
|
||||
modules/ldb_map_outbound.o \
|
||||
modules/ldb_map.o
|
||||
# End MODULE ldb_map
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_skel
|
||||
[MODULE::ldb_skel]
|
||||
SUBSYSTEM = ldb
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_skel_init
|
||||
OBJ_FILES = modules/skel.o
|
||||
# End MODULE ldb_skel
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_sqlite3
|
||||
[MODULE::ldb_sqlite3]
|
||||
SUBSYSTEM = ldb
|
||||
PRIVATE_DEPENDENCIES = LIBTALLOC
|
||||
INIT_FUNCTION = ldb_sqlite3_init
|
||||
OBJ_FILES = \
|
||||
ldb_sqlite3/ldb_sqlite3.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
SQLITE3 LIBTALLOC
|
||||
# End MODULE ldb_sqlite3
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ldb_tdb
|
||||
[MODULE::ldb_tdb]
|
||||
SUBSYSTEM = ldb
|
||||
INIT_FUNCTION = ldb_tdb_init
|
||||
OBJ_FILES = \
|
||||
ldb_tdb/ldb_tdb.o \
|
||||
ldb_tdb/ldb_search.o \
|
||||
ldb_tdb/ldb_pack.o \
|
||||
ldb_tdb/ldb_index.o \
|
||||
ldb_tdb/ldb_cache.o \
|
||||
ldb_tdb/ldb_tdb_wrap.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBTDB LIBTALLOC
|
||||
# End MODULE ldb_tdb
|
||||
################################################
|
||||
|
||||
./lib/ldb/common/ldb_modules.o: lib/ldb/common/ldb_modules.c Makefile
|
||||
@echo Compiling $<
|
||||
@$(CC) -Iinclude $(CFLAGS) -Ilib/replace -Ilib/talloc -Ilib/ldb $(PICFLAG) -DLDBMODULESDIR=\"$(MODULESDIR)/ldb\" -DSHLIBEXT=\"$(SHLIBEXT)\" -c $< -o $@
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM ldb
|
||||
[LIBRARY::ldb]
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = LDAP-like embedded database library
|
||||
INIT_FUNCTION_TYPE = int (*) (void)
|
||||
OBJ_FILES = \
|
||||
common/ldb.o \
|
||||
common/ldb_ldif.o \
|
||||
common/ldb_parse.o \
|
||||
common/ldb_msg.o \
|
||||
common/ldb_utf8.o \
|
||||
common/ldb_debug.o \
|
||||
common/ldb_modules.o \
|
||||
common/ldb_match.o \
|
||||
common/ldb_attributes.o \
|
||||
common/attrib_handlers.o \
|
||||
common/ldb_dn.o \
|
||||
common/ldb_controls.o \
|
||||
common/qsort.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBTALLOC
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
DYNCONFIG \
|
||||
SOCKET_WRAPPER
|
||||
MANPAGE = man/ldb.3
|
||||
PUBLIC_HEADERS = include/ldb.h include/ldb_errors.h
|
||||
#
|
||||
# End SUBSYSTEM ldb
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM LDBSAMBA
|
||||
[SUBSYSTEM::LDBSAMBA]
|
||||
PRIVATE_DEPENDENCIES = ldb
|
||||
PRIVATE_PROTO_HEADER = samba/ldif_handlers.h
|
||||
PRIVATE_DEPENDENCIES = LIBSECURITY SAMDB
|
||||
OBJ_FILES = \
|
||||
samba/ldif_handlers.o
|
||||
# End SUBSYSTEM LDBSAMBA
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM LIBLDB_CMDLINE
|
||||
[SUBSYSTEM::LIBLDB_CMDLINE]
|
||||
OBJ_FILES= \
|
||||
tools/cmdline.o
|
||||
PUBLIC_DEPENDENCIES = ldb LIBPOPT
|
||||
PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL POPT_SAMBA POPT_CREDENTIALS gensec
|
||||
# End SUBSYSTEM LIBLDB_CMDLINE
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbadd
|
||||
[BINARY::ldbadd]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES = \
|
||||
tools/ldbadd.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE LIBCLI_RESOLVE
|
||||
MANPAGE = man/ldbadd.1
|
||||
# End BINARY ldbadd
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbdel
|
||||
[BINARY::ldbdel]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES= \
|
||||
tools/ldbdel.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
MANPAGE = man/ldbdel.1
|
||||
# End BINARY ldbdel
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbmodify
|
||||
[BINARY::ldbmodify]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES= \
|
||||
tools/ldbmodify.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
MANPAGE = man/ldbmodify.1
|
||||
# End BINARY ldbmodify
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbsearch
|
||||
[BINARY::ldbsearch]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES= \
|
||||
tools/ldbsearch.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
MANPAGE = man/ldbsearch.1
|
||||
# End BINARY ldbsearch
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbedit
|
||||
[BINARY::ldbedit]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES= \
|
||||
tools/ldbedit.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
MANPAGE = man/ldbedit.1
|
||||
# End BINARY ldbedit
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbrename
|
||||
[BINARY::ldbrename]
|
||||
INSTALLDIR = BINDIR
|
||||
OBJ_FILES= \
|
||||
tools/ldbrename.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
MANPAGE = man/ldbrename.1
|
||||
# End BINARY ldbrename
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ldbtest
|
||||
[BINARY::ldbtest]
|
||||
OBJ_FILES= \
|
||||
tools/ldbtest.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
# End BINARY ldbtest
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY oLschema2ldif
|
||||
[BINARY::oLschema2ldif]
|
||||
INSTALLDIR = BINDIR
|
||||
MANPAGE = man/oLschema2ldif.1
|
||||
OBJ_FILES= \
|
||||
tools/convert.o \
|
||||
tools/oLschema2ldif.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
# End BINARY oLschema2ldif
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start BINARY ad2oLschema
|
||||
[BINARY::ad2oLschema]
|
||||
INSTALLDIR = BINDIR
|
||||
MANPAGE = man/ad2oLschema.1
|
||||
OBJ_FILES= \
|
||||
tools/convert.o \
|
||||
tools/ad2oLschema.o
|
||||
PRIVATE_DEPENDENCIES = \
|
||||
LIBLDB_CMDLINE
|
||||
# End BINARY ad2oLschema
|
||||
################################################
|
||||
|
||||
#######################
|
||||
# Start LIBRARY swig_ldb
|
||||
[LIBRARY::swig_ldb]
|
||||
PUBLIC_DEPENDENCIES = ldb DYNCONFIG
|
||||
LIBRARY_REALNAME = swig/_ldb.$(SHLIBEXT)
|
||||
OBJ_FILES = swig/ldb_wrap.o
|
||||
# End LIBRARY swig_ldb
|
||||
#######################
|
||||
+1579
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
AC_PREREQ(2.50)
|
||||
AC_DEFUN([AC_CHECK_LIB_EXT], [
|
||||
AC_CHECK_LIB([$1],[$3],[$4],[$5],[$7])
|
||||
ac_cv_lib_ext_$1_$3=$ac_cv_lib_$1_$3
|
||||
])
|
||||
AC_DEFUN([AC_CHECK_FUNC_EXT], [
|
||||
AC_CHECK_FUNC([$1],[$3],[$4])
|
||||
ac_cv_func_ext_$1=$ac_cv_func_$1
|
||||
])
|
||||
AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""])
|
||||
AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""])
|
||||
AC_DEFUN([SMB_EXT_LIB], [echo -n ""])
|
||||
AC_DEFUN([SMB_ENABLE], [echo -n ""])
|
||||
AC_INIT(include/ldb.h)
|
||||
AC_CONFIG_SRCDIR([common/ldb.c])
|
||||
|
||||
AC_LIBREPLACE_ALL_CHECKS
|
||||
|
||||
if test "$ac_cv_prog_gcc" = yes; then
|
||||
CFLAGS="$CFLAGS -Wall -Wshadow -Wstrict-prototypes -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings"
|
||||
fi
|
||||
|
||||
WITH_GCOV=0
|
||||
AC_ARG_ENABLE(gcov,
|
||||
AS_HELP_STRING([--enable-gcov],[enable GCOV code coverage tests]),
|
||||
[ WITH_GCOV=1])
|
||||
AC_SUBST(WITH_GCOV)
|
||||
if test x"$with_gcov_support" = x"yes"; then
|
||||
CFLAGS="$CFLAGS -ftest-coverage -fprofile-arcs"
|
||||
LIBS="$LIBS -lgcov"
|
||||
fi
|
||||
|
||||
AC_PATH_PROG(XSLTPROC,xsltproc)
|
||||
AC_PATH_PROG(DOXYGEN,doxygen)
|
||||
AC_PATH_PROG(GCOV,gcov)
|
||||
AC_PATH_PROG(SLAPD,slapd)
|
||||
AC_CHECK_HEADERS(stdint.h dlfcn.h)
|
||||
AC_CONFIG_HEADER(include/config.h)
|
||||
AC_SEARCH_LIBS(dlopen, dl, AC_DEFINE(HAVE_DLOPEN, [1], [have dlopen]))
|
||||
|
||||
SHLIBEXT="so" # Should be set based on OS later on
|
||||
AC_SUBST(SHLIBEXT)
|
||||
|
||||
AC_DEFINE_UNQUOTED(MODULESDIR, LIBDIR "/ldb" , [Modules directory] )
|
||||
AC_SUBST(MODULESDIR)
|
||||
|
||||
TESTS=""
|
||||
EXTRA_OBJ=""
|
||||
|
||||
m4_include(libpopt.m4)
|
||||
m4_include(libtalloc.m4)
|
||||
m4_include(libtdb.m4)
|
||||
|
||||
m4_include(ldap.m4)
|
||||
if test x"$with_ldap_support" = x"yes"; then
|
||||
LIBS="$LIBS -llber -lldap"
|
||||
CFLAGS="$CFLAGS -DHAVE_LDB_LDAP=1"
|
||||
EXTRA_OBJ="$EXTRA_OBJ ldb_ldap/ldb_ldap.o"
|
||||
TESTS="$TESTS test-ldap.sh"
|
||||
fi
|
||||
|
||||
m4_include(sqlite3.m4)
|
||||
if test x"$with_sqlite3_support" = x"yes"; then
|
||||
LIBS="$LIBS -lsqlite3"
|
||||
CFLAGS="$CFLAGS -DHAVE_LDB_SQLITE3=1"
|
||||
EXTRA_OBJ="$EXTRA_OBJ ldb_sqlite3/ldb_sqlite3.o"
|
||||
TESTS="$TESTS test-sqlite3.sh"
|
||||
fi
|
||||
|
||||
AC_SUBST(TESTS)
|
||||
AC_SUBST(EXTRA_OBJ)
|
||||
|
||||
m4_include(libldb.m4)
|
||||
AC_OUTPUT(Makefile ldb.pc)
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
# build ldb docs
|
||||
# tridge@samba.org August 2006
|
||||
|
||||
XSLTPROC="$1"
|
||||
SRCDIR="$2"
|
||||
|
||||
if [ -z "$XSLTPROC" ] || [ ! -x "$XSLTPROC" ]; then
|
||||
echo "xsltproc not installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MANXSL="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"
|
||||
HTMLXSL="http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl"
|
||||
|
||||
mkdir -p man
|
||||
|
||||
for f in $SRCDIR/man/*.xml; do
|
||||
base=`basename $f .xml`
|
||||
out=man/"`basename $base`"
|
||||
if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
|
||||
echo Processing manpage $f
|
||||
$XSLTPROC --nonet -o "$out" "$MANXSL" $f
|
||||
ret=$?
|
||||
if [ "$ret" = "4" ]; then
|
||||
echo "ignoring stylesheet error 4 for $MANXSL"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$ret" != "0" ]; then
|
||||
echo "xsltproc failed with error $ret"
|
||||
exit $ret
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
for f in $SRCDIR/man/*.xml; do
|
||||
base=`basename $f .xml`
|
||||
out=man/"`basename $base`".html
|
||||
if [ ! -f "$out" ] || [ "$f" -nt "$out" ]; then
|
||||
echo Processing html $f
|
||||
$XSLTPROC --nonet -o "$out" "$HTMLXSL" $f
|
||||
ret=$?
|
||||
if [ "$ret" = "4" ]; then
|
||||
echo "ignoring stylesheet error 4 for $HTMLXSL"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$ret" != "0" ]; then
|
||||
echo "xsltproc failed with error $ret"
|
||||
exit $ret
|
||||
fi
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,41 @@
|
||||
The list of indexed fields
|
||||
--------------------------
|
||||
|
||||
dn=@INDEXLIST
|
||||
list of field names that are indexed
|
||||
|
||||
contains fields of type @IDXATTR which contain attriute names
|
||||
of indexed fields
|
||||
|
||||
|
||||
Data records
|
||||
------------
|
||||
|
||||
for each user record in the db there is:
|
||||
main record
|
||||
key: DN=dn
|
||||
data: packed attribute/value list
|
||||
|
||||
a index record for each indexed field in the record
|
||||
|
||||
|
||||
Index Records
|
||||
-------------
|
||||
|
||||
The index records contain the list of dn's that contain records
|
||||
matching the index key
|
||||
|
||||
All index records are of the form:
|
||||
dn=@INDEX:field:value
|
||||
|
||||
and contain fields of type @IDX which are the dns of the records
|
||||
that have that value for some attribute
|
||||
|
||||
|
||||
Search Expressions
|
||||
------------------
|
||||
|
||||
Very similar to LDAP search expressions, but does not allow ~=, <= or >=
|
||||
|
||||
attrib0 := (field=value)
|
||||
attrib := attrib0 | (attrib&&attrib) | (attrib||attrib) | !attrib
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
# install ldb docs
|
||||
# tridge@samba.org August 2006
|
||||
|
||||
MANDIR="$1"
|
||||
|
||||
MAN1="`/bin/ls man/*.1`"
|
||||
MAN3="`/bin/ls man/*.3`"
|
||||
|
||||
if [ -z "$MAN1" ] && [ -z "$MAN3" ]; then
|
||||
echo "No manpages have been built"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$MANDIR/man1" "$MANDIR/man3"
|
||||
cp $MAN1 "$MANDIR/man1/" || exit 1
|
||||
cp $MAN3 "$MANDIR/man3/" || exit 1
|
||||
@@ -0,0 +1,16 @@
|
||||
/** \example ldbreader.c
|
||||
|
||||
The code below shows a simple LDB application.
|
||||
|
||||
It lists / dumps the records in a LDB database to standard output.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** \example ldifreader.c
|
||||
|
||||
The code below shows a simple LDB application.
|
||||
|
||||
It lists / dumps the entries in an LDIF file to standard output.
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
example code for the ldb database library
|
||||
|
||||
Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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
|
||||
*/
|
||||
|
||||
/** \example ldbreader.c
|
||||
|
||||
The code below shows a simple LDB application.
|
||||
|
||||
It lists / dumps the records in a LDB database to standard output.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/ldb.h"
|
||||
#include "ldb/include/ldb_errors.h"
|
||||
|
||||
/*
|
||||
ldb_ldif_write takes a function pointer to a custom output
|
||||
function. This version is about as simple as the output function can
|
||||
be. In a more complex example, you'd likely be doing something with
|
||||
the private data function (e.g. holding a file handle).
|
||||
*/
|
||||
static int vprintf_fn(void *private_data, const char *fmt, ...)
|
||||
{
|
||||
int retval;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
/* We just write to standard output */
|
||||
retval = vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
/* Note that the function should return the number of
|
||||
bytes written, or a negative error code */
|
||||
return retval;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct ldb_context *ldb;
|
||||
const char *expression = "(dn=*)";
|
||||
struct ldb_result *resultMsg;
|
||||
int i;
|
||||
|
||||
/*
|
||||
This is the always the first thing you want to do in an LDB
|
||||
application - initialise up the context structure.
|
||||
|
||||
Note that you can use the context structure as a parent
|
||||
for talloc allocations as well
|
||||
*/
|
||||
ldb = ldb_init(NULL);
|
||||
|
||||
/*
|
||||
We now open the database. In this example we just hard code the connection path.
|
||||
|
||||
Also note that the database is being opened read-only. This means that the
|
||||
call will fail unless the database already exists.
|
||||
*/
|
||||
if (LDB_SUCCESS != ldb_connect(ldb, "tdb://tdbtest.ldb", LDB_FLG_RDONLY, NULL) ){
|
||||
printf("Problem on connection\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*
|
||||
At this stage we have an open database, and can start using it. It is opened
|
||||
read-only, so a query is possible.
|
||||
|
||||
We construct a search that just returns all the (sensible) contents. You can do
|
||||
quite fine grained results with the LDAP search syntax, however it is a bit
|
||||
confusing to start with. See RFC2254.
|
||||
*/
|
||||
if (LDB_SUCCESS != ldb_search(ldb, NULL, LDB_SCOPE_DEFAULT,
|
||||
expression, NULL, &resultMsg) ) {
|
||||
printf("Problem in search\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
printf("%i records returned\n", resultMsg->count);
|
||||
|
||||
/*
|
||||
We can now iterate through the results, writing them out
|
||||
(to standard output) with our custom output routine as defined
|
||||
at the top of this file
|
||||
*/
|
||||
for (i = 0; i < resultMsg->count; ++i) {
|
||||
struct ldb_ldif ldifMsg;
|
||||
|
||||
printf("Message: %i\n", i+1);
|
||||
|
||||
ldifMsg.changetype = LDB_CHANGETYPE_NONE;
|
||||
ldifMsg.msg = resultMsg->msgs[i];
|
||||
ldb_ldif_write(ldb, vprintf_fn, NULL, &ldifMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
There are two objects to clean up - the result from the
|
||||
ldb_search() query, and the original ldb context.
|
||||
*/
|
||||
talloc_free(resultMsg);
|
||||
|
||||
talloc_free(ldb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
example code for the ldb database library
|
||||
|
||||
Copyright (C) Brad Hards (bradh@frogmouth.net) 2005-2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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
|
||||
*/
|
||||
|
||||
/** \example ldifreader.c
|
||||
|
||||
The code below shows a simple LDB application.
|
||||
|
||||
It lists / dumps the entries in an LDIF file to standard output.
|
||||
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/ldb.h"
|
||||
#include "ldb/include/ldb_errors.h"
|
||||
|
||||
/*
|
||||
ldb_ldif_write takes a function pointer to a custom output
|
||||
function. This version is about as simple as the output function can
|
||||
be. In a more complex example, you'd likely be doing something with
|
||||
the private data function (e.g. holding a file handle).
|
||||
*/
|
||||
static int vprintf_fn(void *private_data, const char *fmt, ...)
|
||||
{
|
||||
int retval;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
/* We just write to standard output */
|
||||
retval = vprintf(fmt, ap);
|
||||
va_end(ap);
|
||||
/* Note that the function should return the number of
|
||||
bytes written, or a negative error code */
|
||||
return retval;
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct ldb_context *ldb;
|
||||
FILE *fileStream;
|
||||
struct ldb_ldif *ldifMsg;
|
||||
|
||||
if (argc != 2) {
|
||||
printf("Usage %s filename.ldif\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
This is the always the first thing you want to do in an LDB
|
||||
application - initialise up the context structure.
|
||||
|
||||
Note that you can use the context structure as a parent
|
||||
for talloc allocations as well
|
||||
*/
|
||||
ldb = ldb_init(NULL);
|
||||
|
||||
fileStream = fopen(argv[1], "r");
|
||||
if (0 == fileStream) {
|
||||
perror(argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
We now work through the filestream to get each entry.
|
||||
*/
|
||||
while ( (ldifMsg = ldb_ldif_read_file(ldb, fileStream)) ) {
|
||||
/*
|
||||
Each message has a particular change type. For Add,
|
||||
Modify and Delete, this will also appear in the
|
||||
output listing (as changetype: add, changetype:
|
||||
modify or changetype:delete, respectively).
|
||||
*/
|
||||
switch (ldifMsg->changetype) {
|
||||
case LDB_CHANGETYPE_NONE:
|
||||
printf("ChangeType: None\n");
|
||||
break;
|
||||
case LDB_CHANGETYPE_ADD:
|
||||
printf("ChangeType: Add\n");
|
||||
break;
|
||||
case LDB_CHANGETYPE_MODIFY:
|
||||
printf("ChangeType: Modify\n");
|
||||
break;
|
||||
case LDB_CHANGETYPE_DELETE:
|
||||
printf("ChangeType: Delete\n");
|
||||
break;
|
||||
default:
|
||||
printf("ChangeType: Unknown\n");
|
||||
}
|
||||
|
||||
/*
|
||||
We can now write out the results, using our custom
|
||||
output routine as defined at the top of this file.
|
||||
*/
|
||||
ldb_ldif_write(ldb, vprintf_fn, NULL, ldifMsg);
|
||||
|
||||
/*
|
||||
Clean up the message
|
||||
*/
|
||||
ldb_ldif_read_free(ldb, ldifMsg);
|
||||
}
|
||||
|
||||
/*
|
||||
Clean up the context
|
||||
*/
|
||||
talloc_free(ldb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
some simple double linked list macros
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* To use these macros you must have a structure containing a next and
|
||||
prev pointer */
|
||||
|
||||
|
||||
/* hook into the front of the list */
|
||||
#define DLIST_ADD(list, p) \
|
||||
do { \
|
||||
if (!(list)) { \
|
||||
(list) = (p); \
|
||||
(p)->next = (p)->prev = NULL; \
|
||||
} else { \
|
||||
(list)->prev = (p); \
|
||||
(p)->next = (list); \
|
||||
(p)->prev = NULL; \
|
||||
(list) = (p); \
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
/* remove an element from a list - element doesn't have to be in list. */
|
||||
#ifndef DLIST_REMOVE
|
||||
#define DLIST_REMOVE(list, p) \
|
||||
do { \
|
||||
if ((p) == (list)) { \
|
||||
(list) = (p)->next; \
|
||||
if (list) (list)->prev = NULL; \
|
||||
} else { \
|
||||
if ((p)->prev) (p)->prev->next = (p)->next; \
|
||||
if ((p)->next) (p)->next->prev = (p)->prev; \
|
||||
} \
|
||||
if ((p) && ((p) != (list))) (p)->next = (p)->prev = NULL; \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
/* promote an element to the top of the list */
|
||||
#define DLIST_PROMOTE(list, p) \
|
||||
do { \
|
||||
DLIST_REMOVE(list, p); \
|
||||
DLIST_ADD(list, p); \
|
||||
} while (0)
|
||||
|
||||
/* hook into the end of the list - needs a tmp pointer */
|
||||
#define DLIST_ADD_END(list, p, type) \
|
||||
do { \
|
||||
if (!(list)) { \
|
||||
(list) = (p); \
|
||||
(p)->next = (p)->prev = NULL; \
|
||||
} else { \
|
||||
type tmp; \
|
||||
for (tmp = (list); tmp->next; tmp = tmp->next) ; \
|
||||
tmp->next = (p); \
|
||||
(p)->next = NULL; \
|
||||
(p)->prev = tmp; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* insert 'p' after the given element 'el' in a list. If el is NULL then
|
||||
this is the same as a DLIST_ADD() */
|
||||
#define DLIST_ADD_AFTER(list, p, el) \
|
||||
do { \
|
||||
if (!(list) || !(el)) { \
|
||||
DLIST_ADD(list, p); \
|
||||
} else { \
|
||||
p->prev = el; \
|
||||
p->next = el->next; \
|
||||
el->next = p; \
|
||||
if (p->next) p->next->prev = p; \
|
||||
}\
|
||||
} while (0)
|
||||
|
||||
/* demote an element to the end of the list, needs a tmp pointer */
|
||||
#define DLIST_DEMOTE(list, p, tmp) \
|
||||
do { \
|
||||
DLIST_REMOVE(list, p); \
|
||||
DLIST_ADD_END(list, p, tmp); \
|
||||
} while (0)
|
||||
|
||||
/* concatenate two lists - putting all elements of the 2nd list at the
|
||||
end of the first list */
|
||||
#define DLIST_CONCATENATE(list1, list2, type) \
|
||||
do { \
|
||||
if (!(list1)) { \
|
||||
(list1) = (list2); \
|
||||
} else { \
|
||||
type tmp; \
|
||||
for (tmp = (list1); tmp->next; tmp = tmp->next) ; \
|
||||
tmp->next = (list2); \
|
||||
if (list2) { \
|
||||
(list2)->prev = tmp; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _LDB_PRIVATE_INCLUDES_H_
|
||||
#define _LDB_PRIVATE_INCLUDES_H_
|
||||
/*
|
||||
a temporary includes file until I work on the ldb build system
|
||||
*/
|
||||
|
||||
#if (_SAMBA_BUILD_ >= 4)
|
||||
/* tell ldb we have the internal ldap code */
|
||||
#define HAVE_ILDAP 1
|
||||
#endif
|
||||
|
||||
#if (_SAMBA_BUILD_ <= 3)
|
||||
/* allow forbidden string functions - should be replaced with _m functions */
|
||||
#undef strcasecmp
|
||||
#undef strncasecmp
|
||||
#define dyn_MODULESDIR dyn_LIBDIR
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
|
||||
#define discard_const_p(type, ptr) ((type *)discard_const(ptr))
|
||||
|
||||
#include "replace.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/network.h"
|
||||
#include "system/time.h"
|
||||
#include "talloc.h"
|
||||
#include "ldb.h"
|
||||
#include "ldb_errors.h"
|
||||
#include "ldb_private.h"
|
||||
#include "dlinklist.h"
|
||||
|
||||
#endif /*_LDB_PRIVATE_INCLUDES_H_*/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb header
|
||||
*
|
||||
* Description: defines error codes following RFC 2251 ldap error codes
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#ifndef _LDB_ERRORS_H_
|
||||
|
||||
/*! \cond DOXYGEN_IGNORE */
|
||||
#define _LDB_ERRORS_H_ 1
|
||||
/*! \endcond */
|
||||
|
||||
/**
|
||||
\file ldb_errors.h
|
||||
|
||||
This header provides a set of result codes for LDB function calls.
|
||||
|
||||
Many LDB function calls return an integer value (int). As shown in
|
||||
the function documentation, those return values may indicate
|
||||
whether the function call worked correctly (in which case it
|
||||
returns LDB_SUCCESS) or some problem occurred (in which case some
|
||||
other value will be returned). As a special case,
|
||||
LDB_ERR_COMPARE_FALSE or LDB_ERR_COMPARE_TRUE may be returned,
|
||||
which does not indicate an error.
|
||||
|
||||
\note Not all error codes make sense for LDB, however they are
|
||||
based on the LDAP error codes, and are kept for reference and to
|
||||
avoid overlap.
|
||||
|
||||
\note Some of this documentation is based on information in
|
||||
the OpenLDAP documentation, as developed and maintained by the
|
||||
<a href="http://www.openldap.org/">The OpenLDAP Project</a>.
|
||||
*/
|
||||
|
||||
/**
|
||||
The function call succeeded.
|
||||
|
||||
If a function returns LDB_SUCCESS, then that function, and the
|
||||
underlying transactions that may have been required, completed
|
||||
successfully.
|
||||
*/
|
||||
#define LDB_SUCCESS 0
|
||||
|
||||
/**
|
||||
The function call failed for some non-specific reason.
|
||||
*/
|
||||
#define LDB_ERR_OPERATIONS_ERROR 1
|
||||
|
||||
/**
|
||||
The function call failed because of a protocol violation.
|
||||
*/
|
||||
#define LDB_ERR_PROTOCOL_ERROR 2
|
||||
|
||||
/**
|
||||
The function call failed because a time limit was exceeded.
|
||||
*/
|
||||
#define LDB_ERR_TIME_LIMIT_EXCEEDED 3
|
||||
|
||||
/**
|
||||
The function call failed because a size limit was exceeded.
|
||||
*/
|
||||
#define LDB_ERR_SIZE_LIMIT_EXCEEDED 4
|
||||
|
||||
/**
|
||||
The function was for value comparison, and the comparison operation
|
||||
returned false.
|
||||
|
||||
\note This is a status value, and doesn't normally indicate an
|
||||
error.
|
||||
*/
|
||||
#define LDB_ERR_COMPARE_FALSE 5
|
||||
|
||||
/**
|
||||
The function was for value comparison, and the comparison operation
|
||||
returned true.
|
||||
|
||||
\note This is a status value, and doesn't normally indicate an
|
||||
error.
|
||||
*/
|
||||
#define LDB_ERR_COMPARE_TRUE 6
|
||||
|
||||
/**
|
||||
The function used an authentication method that is not supported by
|
||||
the database.
|
||||
*/
|
||||
#define LDB_ERR_AUTH_METHOD_NOT_SUPPORTED 7
|
||||
|
||||
/**
|
||||
The function call required a underlying operation that required
|
||||
strong authentication.
|
||||
|
||||
This will normally only occur if you are using LDB with a LDAP
|
||||
backend.
|
||||
*/
|
||||
#define LDB_ERR_STRONG_AUTH_REQUIRED 8
|
||||
/* 9 RESERVED */
|
||||
|
||||
/**
|
||||
The function resulted in a referral to another server.
|
||||
*/
|
||||
#define LDB_ERR_REFERRAL 10
|
||||
|
||||
/**
|
||||
The function failed because an administrative / policy limit was
|
||||
exceeded.
|
||||
*/
|
||||
#define LDB_ERR_ADMIN_LIMIT_EXCEEDED 11
|
||||
|
||||
/**
|
||||
The function required an extension or capability that the
|
||||
database cannot provide.
|
||||
*/
|
||||
#define LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION 12
|
||||
|
||||
/**
|
||||
The function involved a transaction or database operation that
|
||||
could not be performed without a secure link.
|
||||
*/
|
||||
#define LDB_ERR_CONFIDENTIALITY_REQUIRED 13
|
||||
|
||||
/**
|
||||
This is an intermediate result code for SASL bind operations that
|
||||
have more than one step.
|
||||
|
||||
\note This is a result code that does not normally indicate an
|
||||
error has occurred.
|
||||
*/
|
||||
#define LDB_ERR_SASL_BIND_IN_PROGRESS 14
|
||||
|
||||
/**
|
||||
The function referred to an attribute type that is not present in
|
||||
the entry.
|
||||
*/
|
||||
#define LDB_ERR_NO_SUCH_ATTRIBUTE 16
|
||||
|
||||
/**
|
||||
The function referred to an attribute type that is invalid
|
||||
*/
|
||||
#define LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE 17
|
||||
|
||||
/**
|
||||
The function required a filter type that is not available for the
|
||||
specified attribute.
|
||||
*/
|
||||
#define LDB_ERR_INAPPROPRIATE_MATCHING 18
|
||||
|
||||
/**
|
||||
The function would have violated an attribute constraint.
|
||||
*/
|
||||
#define LDB_ERR_CONSTRAINT_VIOLATION 19
|
||||
|
||||
/**
|
||||
The function involved an attribute type or attribute value that
|
||||
already exists in the entry.
|
||||
*/
|
||||
#define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS 20
|
||||
/**
|
||||
The function used an invalid (incorrect syntax) attribute value.
|
||||
*/
|
||||
#define LDB_ERR_INVALID_ATTRIBUTE_SYNTAX 21
|
||||
|
||||
/* 22-31 unused */
|
||||
|
||||
/**
|
||||
The function referred to an object that does not exist in the
|
||||
database.
|
||||
*/
|
||||
#define LDB_ERR_NO_SUCH_OBJECT 32
|
||||
|
||||
/**
|
||||
The function referred to an alias which points to a non-existant
|
||||
object in the database.
|
||||
*/
|
||||
#define LDB_ERR_ALIAS_PROBLEM 33
|
||||
|
||||
/**
|
||||
The function used a DN which was invalid (incorrect syntax).
|
||||
*/
|
||||
#define LDB_ERR_INVALID_DN_SYNTAX 34
|
||||
|
||||
/* 35 RESERVED */
|
||||
|
||||
/**
|
||||
The function required dereferencing of an alias, and something went
|
||||
wrong during the dereferencing process.
|
||||
*/
|
||||
#define LDB_ERR_ALIAS_DEREFERENCING_PROBLEM 36
|
||||
|
||||
/* 37-47 unused */
|
||||
|
||||
/**
|
||||
The function passed in the wrong authentication method.
|
||||
*/
|
||||
#define LDB_ERR_INAPPROPRIATE_AUTHENTICATION 48
|
||||
|
||||
/**
|
||||
The function passed in or referenced incorrect credentials during
|
||||
authentication.
|
||||
*/
|
||||
#define LDB_ERR_INVALID_CREDENTIALS 49
|
||||
|
||||
/**
|
||||
The function required access permissions that the user does not
|
||||
possess.
|
||||
*/
|
||||
#define LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS 50
|
||||
|
||||
/**
|
||||
The function required a transaction or call that the database could
|
||||
not perform because it is busy.
|
||||
*/
|
||||
#define LDB_ERR_BUSY 51
|
||||
|
||||
/**
|
||||
The function required a transaction or call to a database that is
|
||||
not available.
|
||||
*/
|
||||
#define LDB_ERR_UNAVAILABLE 52
|
||||
|
||||
/**
|
||||
The function required a transaction or call to a database that the
|
||||
database declined to perform.
|
||||
*/
|
||||
#define LDB_ERR_UNWILLING_TO_PERFORM 53
|
||||
|
||||
/**
|
||||
The function failed because it resulted in a loop being detected.
|
||||
*/
|
||||
#define LDB_ERR_LOOP_DETECT 54
|
||||
|
||||
/* 55-63 unused */
|
||||
|
||||
/**
|
||||
The function failed because it would have violated a naming rule.
|
||||
*/
|
||||
#define LDB_ERR_NAMING_VIOLATION 64
|
||||
|
||||
/**
|
||||
The function failed because it would have violated the schema.
|
||||
*/
|
||||
#define LDB_ERR_OBJECT_CLASS_VIOLATION 65
|
||||
|
||||
/**
|
||||
The function required an operation that is only allowed on leaf
|
||||
objects, but the object is not a leaf.
|
||||
*/
|
||||
#define LDB_ERR_NOT_ALLOWED_ON_NON_LEAF 66
|
||||
|
||||
/**
|
||||
The function required an operation that cannot be performed on a
|
||||
Relative DN, but the object is a Relative DN.
|
||||
*/
|
||||
#define LDB_ERR_NOT_ALLOWED_ON_RDN 67
|
||||
|
||||
/**
|
||||
The function failed because the entry already exists.
|
||||
*/
|
||||
#define LDB_ERR_ENTRY_ALREADY_EXISTS 68
|
||||
|
||||
/**
|
||||
The function failed because modifications to an object class are
|
||||
not allowable.
|
||||
*/
|
||||
#define LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED 69
|
||||
|
||||
/* 70 RESERVED FOR CLDAP */
|
||||
|
||||
/**
|
||||
The function failed because it needed to be applied to multiple
|
||||
databases.
|
||||
*/
|
||||
#define LDB_ERR_AFFECTS_MULTIPLE_DSAS 71
|
||||
|
||||
/* 72-79 unused */
|
||||
|
||||
/**
|
||||
The function failed for unknown reasons.
|
||||
*/
|
||||
#define LDB_ERR_OTHER 80
|
||||
|
||||
/* 81-90 RESERVED for APIs */
|
||||
|
||||
#endif /* _LDB_ERRORS_H_ */
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb header
|
||||
*
|
||||
* Description: defines attribute handlers
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
|
||||
int ldb_handler_copy( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
|
||||
int ldb_handler_fold( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
|
||||
int ldb_canonicalise_Integer( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
|
||||
int ldb_comparison_Integer( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_comparison_binary( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_comparison_fold( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_canonicalise_dn( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
|
||||
int ldb_comparison_dn( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_comparison_objectclass( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_comparison_utctime( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
int ldb_canonicalise_utctime( struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
Copyright (C) Simo Sorce 2004-2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb private header
|
||||
*
|
||||
* Description: defines internal ldb structures used by the subsystem and modules
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
* Author: Stefan Metzmacher
|
||||
*/
|
||||
|
||||
#ifndef _LDB_PRIVATE_H_
|
||||
#define _LDB_PRIVATE_H_ 1
|
||||
|
||||
struct ldb_context;
|
||||
|
||||
struct ldb_module_ops;
|
||||
|
||||
/* basic module structure */
|
||||
struct ldb_module {
|
||||
struct ldb_module *prev, *next;
|
||||
struct ldb_context *ldb;
|
||||
void *private_data;
|
||||
const struct ldb_module_ops *ops;
|
||||
};
|
||||
|
||||
/*
|
||||
these function pointers define the operations that a ldb module must perform
|
||||
they correspond exactly to the ldb_*() interface
|
||||
*/
|
||||
struct ldb_module_ops {
|
||||
const char *name;
|
||||
int (*init_context) (struct ldb_module *);
|
||||
int (*search)(struct ldb_module *, struct ldb_request *); /* search */
|
||||
int (*add)(struct ldb_module *, struct ldb_request *); /* add */
|
||||
int (*modify)(struct ldb_module *, struct ldb_request *); /* modify */
|
||||
int (*del)(struct ldb_module *, struct ldb_request *); /* delete */
|
||||
int (*rename)(struct ldb_module *, struct ldb_request *); /* rename */
|
||||
int (*request)(struct ldb_module *, struct ldb_request *); /* match any other operation */
|
||||
int (*extended)(struct ldb_module *, struct ldb_request *); /* extended operations */
|
||||
int (*start_transaction)(struct ldb_module *);
|
||||
int (*end_transaction)(struct ldb_module *);
|
||||
int (*del_transaction)(struct ldb_module *);
|
||||
int (*wait)(struct ldb_handle *, enum ldb_wait_type);
|
||||
int (*sequence_number)(struct ldb_module *, struct ldb_request *);
|
||||
};
|
||||
|
||||
typedef int (*ldb_connect_fn) (struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[],
|
||||
struct ldb_module **module);
|
||||
|
||||
/*
|
||||
schema related information needed for matching rules
|
||||
*/
|
||||
struct ldb_schema {
|
||||
/* attribute handling table */
|
||||
unsigned num_attrib_handlers;
|
||||
struct ldb_attrib_handler *attrib_handlers;
|
||||
|
||||
/* objectclass information */
|
||||
unsigned num_classes;
|
||||
struct ldb_subclass {
|
||||
char *name;
|
||||
char **subclasses;
|
||||
} *classes;
|
||||
};
|
||||
|
||||
/*
|
||||
every ldb connection is started by establishing a ldb_context
|
||||
*/
|
||||
struct ldb_context {
|
||||
/* the operations provided by the backend */
|
||||
struct ldb_module *modules;
|
||||
|
||||
/* debugging operations */
|
||||
struct ldb_debug_ops debug_ops;
|
||||
|
||||
/* custom utf8 functions */
|
||||
struct ldb_utf8_fns utf8_fns;
|
||||
|
||||
/* backend specific opaque parameters */
|
||||
struct ldb_opaque {
|
||||
struct ldb_opaque *next;
|
||||
const char *name;
|
||||
void *value;
|
||||
} *opaque;
|
||||
|
||||
struct ldb_schema schema;
|
||||
|
||||
char *err_string;
|
||||
|
||||
int transaction_active;
|
||||
|
||||
int default_timeout;
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
unsigned int create_perms;
|
||||
};
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
|
||||
#endif
|
||||
|
||||
/*
|
||||
simplify out of memory handling
|
||||
*/
|
||||
#define ldb_oom(ldb) ldb_debug_set(ldb, LDB_DEBUG_FATAL, "ldb out of memory at %s:%d\n", __FILE__, __LINE__)
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb.c */
|
||||
|
||||
int ldb_connect_backend(struct ldb_context *ldb, const char *url, const char *options[],
|
||||
struct ldb_module **backend_module);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_modules.c */
|
||||
|
||||
const char **ldb_modules_list_from_string(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, const char *string);
|
||||
int ldb_load_modules_list(struct ldb_context *ldb, const char **module_list, struct ldb_module *backend, struct ldb_module **out);
|
||||
int ldb_load_modules(struct ldb_context *ldb, const char *options[]);
|
||||
int ldb_init_module_chain(struct ldb_context *ldb, struct ldb_module *module);
|
||||
int ldb_next_request(struct ldb_module *module, struct ldb_request *request);
|
||||
int ldb_next_start_trans(struct ldb_module *module);
|
||||
int ldb_next_end_trans(struct ldb_module *module);
|
||||
int ldb_next_del_trans(struct ldb_module *module);
|
||||
int ldb_next_init(struct ldb_module *module);
|
||||
|
||||
void ldb_set_errstring(struct ldb_context *ldb, const char *err_string);
|
||||
void ldb_asprintf_errstring(struct ldb_context *ldb, const char *format, ...) PRINTF_ATTRIBUTE(2,3);
|
||||
void ldb_reset_err_string(struct ldb_context *ldb);
|
||||
|
||||
int ldb_register_module(const struct ldb_module_ops *);
|
||||
int ldb_register_backend(const char *url_prefix, ldb_connect_fn);
|
||||
int ldb_try_load_dso(struct ldb_context *ldb, const char *name);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_debug.c */
|
||||
void ldb_debug(struct ldb_context *ldb, enum ldb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
void ldb_debug_set(struct ldb_context *ldb, enum ldb_debug_level level,
|
||||
const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_ldif.c */
|
||||
int ldb_should_b64_encode(const struct ldb_val *val);
|
||||
|
||||
int ldb_objectclass_init(void);
|
||||
int ldb_operational_init(void);
|
||||
int ldb_paged_results_init(void);
|
||||
int ldb_rdn_name_init(void);
|
||||
int ldb_schema_init(void);
|
||||
int ldb_asq_init(void);
|
||||
int ldb_sort_init(void);
|
||||
int ldb_ldap_init(void);
|
||||
int ldb_ildap_init(void);
|
||||
int ldb_tdb_init(void);
|
||||
int ldb_sqlite3_init(void);
|
||||
|
||||
int ldb_match_msg(struct ldb_context *ldb,
|
||||
const struct ldb_message *msg,
|
||||
const struct ldb_parse_tree *tree,
|
||||
struct ldb_dn *base,
|
||||
enum ldb_scope scope);
|
||||
|
||||
void ldb_remove_attrib_handler(struct ldb_context *ldb, const char *attrib);
|
||||
const struct ldb_attrib_handler *ldb_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *syntax);
|
||||
int ldb_set_attrib_handlers(struct ldb_context *ldb,
|
||||
const struct ldb_attrib_handler *handlers,
|
||||
unsigned num_handlers);
|
||||
int ldb_setup_wellknown_attributes(struct ldb_context *ldb);
|
||||
int ldb_set_attrib_handler_syntax(struct ldb_context *ldb,
|
||||
const char *attr, const char *syntax);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_attributes.c */
|
||||
const char **ldb_subclass_list(struct ldb_context *ldb, const char *classname);
|
||||
void ldb_subclass_remove(struct ldb_context *ldb, const char *classname);
|
||||
int ldb_subclass_add(struct ldb_context *ldb, const char *classname, const char *subclass);
|
||||
|
||||
int ldb_handler_copy(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out);
|
||||
int ldb_comparison_binary(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_controls.c */
|
||||
struct ldb_control *get_control_from_list(struct ldb_control **controls, const char *oid);
|
||||
int save_controls(struct ldb_control *exclude, struct ldb_request *req, struct ldb_control ***saver);
|
||||
int check_critical_controls(struct ldb_control **controls);
|
||||
|
||||
/* The following definitions come from lib/ldb/common/ldb_utf8.c */
|
||||
char *ldb_casefold_default(void *context, void *mem_ctx, const char *s);
|
||||
|
||||
void ldb_msg_remove_element(struct ldb_message *msg, struct ldb_message_element *el);
|
||||
|
||||
/**
|
||||
Obtain current/next database sequence number
|
||||
*/
|
||||
int ldb_sequence_number(struct ldb_context *ldb, enum ldb_sequence_type type, uint64_t *seq_num);
|
||||
|
||||
#define LDB_SEQ_GLOBAL_SEQUENCE 0x01
|
||||
#define LDB_SEQ_TIMESTAMP_SEQUENCE 0x02
|
||||
|
||||
|
||||
#endif
|
||||
Executable
+238
@@ -0,0 +1,238 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
#
|
||||
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
transformbasename=""
|
||||
transform_arg=""
|
||||
instcmd="$mvprog"
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
dir_arg=""
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
# this colon is to work around a 386BSD /bin/sh bug
|
||||
:
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
else
|
||||
instcmd=mkdir
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
|
||||
## this sed command emulates the dirname command
|
||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if [ ! -d "$dstdir" ]; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-${defaultIFS}}"
|
||||
|
||||
oIFS="${IFS}"
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
IFS="${oIFS}"
|
||||
|
||||
pathcomp=''
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
pathcomp="${pathcomp}${1}"
|
||||
shift
|
||||
|
||||
if [ ! -d "${pathcomp}" ] ;
|
||||
then
|
||||
$mkdirprog "${pathcomp}"
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
pathcomp="${pathcomp}/"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]
|
||||
then
|
||||
$doit $instcmd $dst &&
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||
else
|
||||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
|
||||
$doit $instcmd $src $dsttmp &&
|
||||
|
||||
trap "rm -f ${dsttmp}" 0 &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,90 @@
|
||||
########################################################
|
||||
# Compile with LDAP support?
|
||||
|
||||
LDAP_LIBS=""
|
||||
with_ldap_support=auto
|
||||
AC_MSG_CHECKING([for LDAP support])
|
||||
|
||||
AC_ARG_WITH(ldap,
|
||||
AS_HELP_STRING([--with-ldap],[LDAP backend support (default=yes)]),
|
||||
[ case "$withval" in
|
||||
yes|no)
|
||||
with_ldap_support=$withval
|
||||
;;
|
||||
esac ])
|
||||
|
||||
AC_MSG_RESULT($with_ldap_support)
|
||||
|
||||
if test x"$with_ldap_support" != x"no"; then
|
||||
|
||||
##################################################################
|
||||
# first test for ldap.h and lber.h
|
||||
# (ldap.h is required for this test)
|
||||
AC_CHECK_HEADERS(ldap.h lber.h)
|
||||
|
||||
if test x"$ac_cv_header_ldap_h" != x"yes"; then
|
||||
if test x"$with_ldap_support" = x"yes"; then
|
||||
AC_MSG_ERROR(ldap.h is needed for LDAP support)
|
||||
else
|
||||
AC_MSG_WARN(ldap.h is needed for LDAP support)
|
||||
fi
|
||||
|
||||
with_ldap_support=no
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$with_ldap_support" != x"no"; then
|
||||
ac_save_LIBS=$LIBS
|
||||
|
||||
##################################################################
|
||||
# we might need the lber lib on some systems. To avoid link errors
|
||||
# this test must be before the libldap test
|
||||
AC_CHECK_LIB_EXT(lber, LDAP_LIBS, ber_scanf)
|
||||
|
||||
########################################################
|
||||
# now see if we can find the ldap libs in standard paths
|
||||
AC_CHECK_LIB_EXT(ldap, LDAP_LIBS, ldap_init)
|
||||
|
||||
AC_CHECK_FUNC_EXT(ldap_domain2hostlist,$LDAP_LIBS)
|
||||
|
||||
########################################################
|
||||
# If we have LDAP, does it's rebind procedure take 2 or 3 arguments?
|
||||
# Check found in pam_ldap 145.
|
||||
AC_CHECK_FUNC_EXT(ldap_set_rebind_proc,$LDAP_LIBS)
|
||||
|
||||
LIBS="$LIBS $LDAP_LIBS"
|
||||
AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, smb_ldap_cv_ldap_set_rebind_proc, [
|
||||
AC_TRY_COMPILE([
|
||||
#include <lber.h>
|
||||
#include <ldap.h>],
|
||||
[ldap_set_rebind_proc(0, 0, 0);],
|
||||
[smb_ldap_cv_ldap_set_rebind_proc=3],
|
||||
[smb_ldap_cv_ldap_set_rebind_proc=2]
|
||||
)
|
||||
])
|
||||
|
||||
AC_DEFINE_UNQUOTED(LDAP_SET_REBIND_PROC_ARGS, $smb_ldap_cv_ldap_set_rebind_proc, [Number of arguments to ldap_set_rebind_proc])
|
||||
|
||||
AC_CHECK_FUNC_EXT(ldap_initialize,$LDAP_LIBS)
|
||||
|
||||
if test x"$ac_cv_lib_ext_ldap_ldap_init" = x"yes" -a x"$ac_cv_func_ext_ldap_domain2hostlist" = x"yes"; then
|
||||
AC_DEFINE(HAVE_LDAP,1,[Whether ldap is available])
|
||||
AC_DEFINE(HAVE_LDB_LDAP,1,[Whether ldb_ldap is available])
|
||||
with_ldap_support=yes
|
||||
AC_MSG_CHECKING(whether LDAP support is used)
|
||||
AC_MSG_RESULT(yes)
|
||||
SMB_ENABLE(LDAP,YES)
|
||||
else
|
||||
if test x"$with_ldap_support" = x"yes"; then
|
||||
AC_MSG_ERROR(libldap is needed for LDAP support)
|
||||
else
|
||||
AC_MSG_WARN(libldap is needed for LDAP support)
|
||||
fi
|
||||
|
||||
LDAP_LIBS=""
|
||||
with_ldap_support=no
|
||||
fi
|
||||
LIBS=$ac_save_LIBS
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(LDAP,[${LDAP_LIBS}],[${LDAP_CFLAGS}],[${LDAP_CPPFLAGS}],[${LDAP_LDFLAGS}])
|
||||
@@ -0,0 +1,15 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
modulesdir=@modulesdir@
|
||||
|
||||
Name: ldb
|
||||
Description: An LDAP-like embedded database
|
||||
Version: 4.0
|
||||
Requires.private: tdb
|
||||
Requires: talloc
|
||||
Libs: -L${libdir} -lldb
|
||||
Cflags: -I${includedir} @CFLAGS@
|
||||
Modulesdir: ${modulesdir}
|
||||
URL: http://ldb.samba.org/
|
||||
@@ -0,0 +1,826 @@
|
||||
/*
|
||||
ldb database library - ildap backend
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb_ildap
|
||||
*
|
||||
* Component: ldb ildap backend
|
||||
*
|
||||
* Description: This is a ldb backend for the internal ldap
|
||||
* client library in Samba4. By using this backend we are
|
||||
* independent of a system ldap library
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*
|
||||
* Modifications:
|
||||
*
|
||||
* - description: make the module use asyncronous calls
|
||||
* date: Feb 2006
|
||||
* author: Simo Sorce
|
||||
*/
|
||||
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "lib/events/events.h"
|
||||
#include "libcli/ldap/ldap.h"
|
||||
#include "libcli/ldap/ldap_client.h"
|
||||
#include "auth/auth.h"
|
||||
#include "auth/credentials/credentials.h"
|
||||
|
||||
struct ildb_private {
|
||||
struct ldap_connection *ldap;
|
||||
struct ldb_context *ldb;
|
||||
};
|
||||
|
||||
struct ildb_context {
|
||||
struct ldb_module *module;
|
||||
struct ldap_request *req;
|
||||
void *context;
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
};
|
||||
|
||||
/*
|
||||
convert a ldb_message structure to a list of ldap_mod structures
|
||||
ready for ildap_add() or ildap_modify()
|
||||
*/
|
||||
static struct ldap_mod **ildb_msg_to_mods(void *mem_ctx, int *num_mods,
|
||||
const struct ldb_message *msg, int use_flags)
|
||||
{
|
||||
struct ldap_mod **mods;
|
||||
unsigned int i;
|
||||
int n = 0;
|
||||
|
||||
/* allocate maximum number of elements needed */
|
||||
mods = talloc_array(mem_ctx, struct ldap_mod *, msg->num_elements+1);
|
||||
if (!mods) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
mods[0] = NULL;
|
||||
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
const struct ldb_message_element *el = &msg->elements[i];
|
||||
|
||||
mods[n] = talloc(mods, struct ldap_mod);
|
||||
if (!mods[n]) {
|
||||
goto failed;
|
||||
}
|
||||
mods[n + 1] = NULL;
|
||||
mods[n]->type = 0;
|
||||
mods[n]->attrib = *el;
|
||||
if (use_flags) {
|
||||
switch (el->flags & LDB_FLAG_MOD_MASK) {
|
||||
case LDB_FLAG_MOD_ADD:
|
||||
mods[n]->type = LDAP_MODIFY_ADD;
|
||||
break;
|
||||
case LDB_FLAG_MOD_DELETE:
|
||||
mods[n]->type = LDAP_MODIFY_DELETE;
|
||||
break;
|
||||
case LDB_FLAG_MOD_REPLACE:
|
||||
mods[n]->type = LDAP_MODIFY_REPLACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
n++;
|
||||
}
|
||||
|
||||
*num_mods = n;
|
||||
return mods;
|
||||
|
||||
failed:
|
||||
talloc_free(mods);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map an ildap NTSTATUS to a ldb error code
|
||||
*/
|
||||
static int ildb_map_error(struct ildb_private *ildb, NTSTATUS status)
|
||||
{
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
ldb_set_errstring(ildb->ldb, ldap_errstr(ildb->ldap, status));
|
||||
if (NT_STATUS_IS_LDAP(status)) {
|
||||
return NT_STATUS_LDAP_CODE(status);
|
||||
}
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static void ildb_request_timeout(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *private_data)
|
||||
{
|
||||
struct ldb_handle *handle = talloc_get_type(private_data, struct ldb_handle);
|
||||
struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
|
||||
|
||||
if (ac->req->state == LDAP_REQUEST_PENDING) {
|
||||
DLIST_REMOVE(ac->req->conn->pending, ac->req);
|
||||
}
|
||||
|
||||
handle->status = LDB_ERR_TIME_LIMIT_EXCEEDED;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void ildb_callback(struct ldap_request *req)
|
||||
{
|
||||
struct ldb_handle *handle = talloc_get_type(req->async.private_data, struct ldb_handle);
|
||||
struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
|
||||
struct ildb_private *ildb = talloc_get_type(ac->module->private_data, struct ildb_private);
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
if (!NT_STATUS_IS_OK(req->status)) {
|
||||
handle->status = ildb_map_error(ildb, req->status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (req->num_replies < 1) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (req->type) {
|
||||
|
||||
case LDAP_TAG_ModifyRequest:
|
||||
if (req->replies[0]->type != LDAP_TAG_ModifyResponse) {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
|
||||
handle->status = ildb_map_error(ildb, status);
|
||||
if (ac->callback && handle->status == LDB_SUCCESS) {
|
||||
/* FIXME: build a corresponding ares to pass on */
|
||||
handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
|
||||
}
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_AddRequest:
|
||||
if (req->replies[0]->type != LDAP_TAG_AddResponse) {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
|
||||
handle->status = ildb_map_error(ildb, status);
|
||||
if (ac->callback && handle->status == LDB_SUCCESS) {
|
||||
/* FIXME: build a corresponding ares to pass on */
|
||||
handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
|
||||
}
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_DelRequest:
|
||||
if (req->replies[0]->type != LDAP_TAG_DelResponse) {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
|
||||
handle->status = ildb_map_error(ildb, status);
|
||||
if (ac->callback && handle->status == LDB_SUCCESS) {
|
||||
/* FIXME: build a corresponding ares to pass on */
|
||||
handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
|
||||
}
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_ModifyDNRequest:
|
||||
if (req->replies[0]->type != LDAP_TAG_ModifyDNResponse) {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
status = ldap_check_response(req->conn, &req->replies[0]->r.GeneralResult);
|
||||
handle->status = ildb_map_error(ildb, status);
|
||||
if (ac->callback && handle->status == LDB_SUCCESS) {
|
||||
/* FIXME: build a corresponding ares to pass on */
|
||||
handle->status = ac->callback(ac->module->ldb, ac->context, NULL);
|
||||
}
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_SearchRequest:
|
||||
/* loop over all messages */
|
||||
for (i = 0; i < req->num_replies; i++) {
|
||||
struct ldap_SearchResEntry *search;
|
||||
struct ldb_reply *ares = NULL;
|
||||
struct ldap_message *msg;
|
||||
int ret;
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
msg = req->replies[i];
|
||||
switch (msg->type) {
|
||||
|
||||
case LDAP_TAG_SearchResultDone:
|
||||
|
||||
status = ldap_check_response(req->conn, &msg->r.GeneralResult);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
handle->status = ildb_map_error(ildb, status);
|
||||
return;
|
||||
}
|
||||
|
||||
ares->controls = talloc_move(ares, &msg->controls);
|
||||
if (msg->r.SearchResultDone.resultcode) {
|
||||
if (msg->r.SearchResultDone.errormessage) {
|
||||
ldb_set_errstring(ac->module->ldb, msg->r.SearchResultDone.errormessage);
|
||||
}
|
||||
}
|
||||
|
||||
handle->status = msg->r.SearchResultDone.resultcode;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_SearchResultEntry:
|
||||
|
||||
|
||||
ares->message = ldb_msg_new(ares);
|
||||
if (!ares->message) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
search = &(msg->r.SearchResultEntry);
|
||||
|
||||
ares->message->dn = ldb_dn_new(ares->message, ac->module->ldb, search->dn);
|
||||
if ( ! ldb_dn_validate(ares->message->dn)) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return;
|
||||
}
|
||||
ares->message->num_elements = search->num_attributes;
|
||||
ares->message->elements = talloc_move(ares->message,
|
||||
&search->attributes);
|
||||
|
||||
handle->status = LDB_SUCCESS;
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
ares->type = LDB_REPLY_ENTRY;
|
||||
break;
|
||||
|
||||
case LDAP_TAG_SearchResultReference:
|
||||
|
||||
ares->referral = talloc_strdup(ares, msg->r.SearchResultReference.referral);
|
||||
|
||||
handle->status = LDB_SUCCESS;
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
ares->type = LDB_REPLY_REFERRAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* TAG not handled, fail ! */
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
if (ret) {
|
||||
handle->status = ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(req->replies);
|
||||
req->replies = NULL;
|
||||
req->num_replies = 0;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static struct ldb_handle *init_ildb_handle(struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ildb_context *ildb_ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(ildb->ldap, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ildb_ac = talloc(h, struct ildb_context);
|
||||
if (ildb_ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ildb_ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ildb_ac->module = module;
|
||||
ildb_ac->context = context;
|
||||
ildb_ac->callback = callback;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int ildb_request_send(struct ldb_module *module, struct ldap_message *msg,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
|
||||
int timeout,
|
||||
struct ldb_handle **handle)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldb_handle *h = init_ildb_handle(module, context, callback);
|
||||
struct ildb_context *ildb_ac;
|
||||
struct ldap_request *req;
|
||||
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ildb_ac = talloc_get_type(h->private_data, struct ildb_context);
|
||||
|
||||
req = ldap_request_send(ildb->ldap, msg);
|
||||
if (req == NULL) {
|
||||
ldb_set_errstring(module->ldb, "async send request failed");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (!req->conn) {
|
||||
ldb_set_errstring(module->ldb, "connection to remote LDAP server dropped?");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
talloc_free(req->time_event);
|
||||
req->time_event = NULL;
|
||||
if (timeout) {
|
||||
req->time_event = event_add_timed(req->conn->event.event_ctx, h,
|
||||
timeval_current_ofs(timeout, 0),
|
||||
ildb_request_timeout, h);
|
||||
}
|
||||
|
||||
req->async.fn = ildb_callback;
|
||||
req->async.private_data = (void *)h;
|
||||
ildb_ac->req = talloc_move(ildb_ac, &req);
|
||||
|
||||
*handle = h;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ildb_request_noop(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_handle *h = init_ildb_handle(module, req->context, req->callback);
|
||||
struct ildb_context *ildb_ac;
|
||||
int ret = LDB_SUCCESS;
|
||||
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ildb_ac = talloc_get_type(h->private_data, struct ildb_context);
|
||||
|
||||
req->handle = h;
|
||||
|
||||
if (ildb_ac->callback) {
|
||||
ret = ildb_ac->callback(module->ldb, ildb_ac->context, NULL);
|
||||
}
|
||||
req->handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
search for matching records using an asynchronous function
|
||||
*/
|
||||
static int ildb_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldap_message *msg;
|
||||
int n;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb, "Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (req->op.search.tree == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Invalid expression parse tree");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg = new_ldap_message(ildb);
|
||||
if (msg == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->type = LDAP_TAG_SearchRequest;
|
||||
|
||||
if (req->op.search.base == NULL) {
|
||||
msg->r.SearchRequest.basedn = talloc_strdup(msg, "");
|
||||
} else {
|
||||
msg->r.SearchRequest.basedn = ldb_dn_alloc_linearized(msg, req->op.search.base);
|
||||
}
|
||||
if (msg->r.SearchRequest.basedn == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Unable to determine baseDN");
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (req->op.search.scope == LDB_SCOPE_DEFAULT) {
|
||||
msg->r.SearchRequest.scope = LDB_SCOPE_SUBTREE;
|
||||
} else {
|
||||
msg->r.SearchRequest.scope = req->op.search.scope;
|
||||
}
|
||||
|
||||
msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
|
||||
msg->r.SearchRequest.timelimit = 0;
|
||||
msg->r.SearchRequest.sizelimit = 0;
|
||||
msg->r.SearchRequest.attributesonly = 0;
|
||||
msg->r.SearchRequest.tree = discard_const(req->op.search.tree);
|
||||
|
||||
for (n = 0; req->op.search.attrs && req->op.search.attrs[n]; n++) /* noop */ ;
|
||||
msg->r.SearchRequest.num_attributes = n;
|
||||
msg->r.SearchRequest.attributes = discard_const(req->op.search.attrs);
|
||||
msg->controls = req->controls;
|
||||
|
||||
return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
|
||||
}
|
||||
|
||||
/*
|
||||
add a record
|
||||
*/
|
||||
static int ildb_add(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldap_message *msg;
|
||||
struct ldap_mod **mods;
|
||||
int i,n;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
/* ignore ltdb specials */
|
||||
if (ldb_dn_is_special(req->op.add.message->dn)) {
|
||||
return ildb_request_noop(module, req);
|
||||
}
|
||||
|
||||
msg = new_ldap_message(ildb->ldap);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->type = LDAP_TAG_AddRequest;
|
||||
|
||||
msg->r.AddRequest.dn = ldb_dn_alloc_linearized(msg, req->op.add.message->dn);
|
||||
if (msg->r.AddRequest.dn == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
mods = ildb_msg_to_mods(msg, &n, req->op.add.message, 0);
|
||||
if (mods == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->r.AddRequest.num_attributes = n;
|
||||
msg->r.AddRequest.attributes = talloc_array(msg, struct ldb_message_element, n);
|
||||
if (msg->r.AddRequest.attributes == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
msg->r.AddRequest.attributes[i] = mods[i]->attrib;
|
||||
}
|
||||
|
||||
return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
|
||||
}
|
||||
|
||||
/*
|
||||
modify a record
|
||||
*/
|
||||
static int ildb_modify(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldap_message *msg;
|
||||
struct ldap_mod **mods;
|
||||
int i,n;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
/* ignore ltdb specials */
|
||||
if (ldb_dn_is_special(req->op.mod.message->dn)) {
|
||||
return ildb_request_noop(module, req);
|
||||
}
|
||||
|
||||
msg = new_ldap_message(ildb->ldap);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->type = LDAP_TAG_ModifyRequest;
|
||||
|
||||
msg->r.ModifyRequest.dn = ldb_dn_alloc_linearized(msg, req->op.mod.message->dn);
|
||||
if (msg->r.ModifyRequest.dn == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
mods = ildb_msg_to_mods(msg, &n, req->op.mod.message, 1);
|
||||
if (mods == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->r.ModifyRequest.num_mods = n;
|
||||
msg->r.ModifyRequest.mods = talloc_array(msg, struct ldap_mod, n);
|
||||
if (msg->r.ModifyRequest.mods == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
msg->r.ModifyRequest.mods[i] = *mods[i];
|
||||
}
|
||||
|
||||
return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
|
||||
}
|
||||
|
||||
/*
|
||||
delete a record
|
||||
*/
|
||||
static int ildb_delete(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldap_message *msg;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
/* ignore ltdb specials */
|
||||
if (ldb_dn_is_special(req->op.del.dn)) {
|
||||
return ildb_request_noop(module, req);
|
||||
}
|
||||
|
||||
msg = new_ldap_message(ildb->ldap);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->type = LDAP_TAG_DelRequest;
|
||||
|
||||
msg->r.DelRequest.dn = ldb_dn_alloc_linearized(msg, req->op.del.dn);
|
||||
if (msg->r.DelRequest.dn == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
|
||||
}
|
||||
|
||||
/*
|
||||
rename a record
|
||||
*/
|
||||
static int ildb_rename(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ildb_private *ildb = talloc_get_type(module->private_data, struct ildb_private);
|
||||
struct ldap_message *msg;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
/* ignore ltdb specials */
|
||||
if (ldb_dn_is_special(req->op.rename.olddn) || ldb_dn_is_special(req->op.rename.newdn)) {
|
||||
return ildb_request_noop(module, req);
|
||||
}
|
||||
|
||||
msg = new_ldap_message(ildb->ldap);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->type = LDAP_TAG_ModifyDNRequest;
|
||||
msg->r.ModifyDNRequest.dn = ldb_dn_alloc_linearized(msg, req->op.rename.olddn);
|
||||
if (msg->r.ModifyDNRequest.dn == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
msg->r.ModifyDNRequest.newrdn =
|
||||
talloc_asprintf(msg, "%s=%s",
|
||||
ldb_dn_get_rdn_name(req->op.rename.newdn),
|
||||
ldb_dn_escape_value(msg, *ldb_dn_get_rdn_val(req->op.rename.newdn)));
|
||||
if (msg->r.ModifyDNRequest.newrdn == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->r.ModifyDNRequest.newsuperior =
|
||||
ldb_dn_alloc_linearized(msg, ldb_dn_get_parent(msg, req->op.rename.newdn));
|
||||
if (msg->r.ModifyDNRequest.newsuperior == NULL) {
|
||||
talloc_free(msg);
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
msg->r.ModifyDNRequest.deleteolddn = True;
|
||||
|
||||
return ildb_request_send(module, msg, req->context, req->callback, req->timeout, &(req->handle));
|
||||
}
|
||||
|
||||
static int ildb_start_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local locking mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ildb_end_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local transaction mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ildb_del_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local locking mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ildb_request(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int ildb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
struct ildb_context *ac = talloc_get_type(handle->private_data, struct ildb_context);
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
if (!ac) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
|
||||
switch(type) {
|
||||
case LDB_WAIT_NONE:
|
||||
if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
|
||||
return LDB_ERR_OTHER;
|
||||
}
|
||||
break;
|
||||
case LDB_WAIT_ALL:
|
||||
while (handle->status == LDB_SUCCESS && handle->state != LDB_ASYNC_DONE) {
|
||||
if (event_loop_once(ac->req->conn->event.event_ctx) != 0) {
|
||||
return LDB_ERR_OTHER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops ildb_ops = {
|
||||
.name = "ldap",
|
||||
.search = ildb_search,
|
||||
.add = ildb_add,
|
||||
.modify = ildb_modify,
|
||||
.del = ildb_delete,
|
||||
.rename = ildb_rename,
|
||||
.request = ildb_request,
|
||||
.start_transaction = ildb_start_trans,
|
||||
.end_transaction = ildb_end_trans,
|
||||
.del_transaction = ildb_del_trans,
|
||||
.wait = ildb_wait
|
||||
};
|
||||
|
||||
/*
|
||||
connect to the database
|
||||
*/
|
||||
static int ildb_connect(struct ldb_context *ldb, const char *url,
|
||||
unsigned int flags, const char *options[],
|
||||
struct ldb_module **module)
|
||||
{
|
||||
struct ildb_private *ildb = NULL;
|
||||
NTSTATUS status;
|
||||
struct cli_credentials *creds;
|
||||
|
||||
ildb = talloc(ldb, struct ildb_private);
|
||||
if (!ildb) {
|
||||
ldb_oom(ldb);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ildb->ldb = ldb;
|
||||
|
||||
ildb->ldap = ldap4_new_connection(ildb, ldb_get_opaque(ldb, "EventContext"));
|
||||
if (!ildb->ldap) {
|
||||
ldb_oom(ldb);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (flags & LDB_FLG_RECONNECT) {
|
||||
ldap_set_reconn_params(ildb->ldap, 10);
|
||||
}
|
||||
|
||||
status = ldap_connect(ildb->ldap, url);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to connect to ldap URL '%s' - %s\n",
|
||||
url, ldap_errstr(ildb->ldap, status));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
|
||||
*module = talloc(ldb, struct ldb_module);
|
||||
if (!module) {
|
||||
ldb_oom(ldb);
|
||||
talloc_free(ildb);
|
||||
return -1;
|
||||
}
|
||||
talloc_set_name_const(*module, "ldb_ildap backend");
|
||||
(*module)->ldb = ldb;
|
||||
(*module)->prev = (*module)->next = NULL;
|
||||
(*module)->private_data = ildb;
|
||||
(*module)->ops = &ildb_ops;
|
||||
|
||||
/* caller can optionally setup credentials using the opaque token 'credentials' */
|
||||
creds = talloc_get_type(ldb_get_opaque(ldb, "credentials"), struct cli_credentials);
|
||||
if (creds == NULL) {
|
||||
struct auth_session_info *session_info = talloc_get_type(ldb_get_opaque(ldb, "sessionInfo"), struct auth_session_info);
|
||||
if (session_info) {
|
||||
creds = session_info->credentials;
|
||||
}
|
||||
}
|
||||
|
||||
if (creds != NULL && cli_credentials_authentication_requested(creds)) {
|
||||
const char *bind_dn = cli_credentials_get_bind_dn(creds);
|
||||
if (bind_dn) {
|
||||
const char *password = cli_credentials_get_password(creds);
|
||||
status = ldap_bind_simple(ildb->ldap, bind_dn, password);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
|
||||
ldap_errstr(ildb->ldap, status));
|
||||
goto failed;
|
||||
}
|
||||
} else {
|
||||
status = ldap_bind_sasl(ildb->ldap, creds);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR, "Failed to bind - %s\n",
|
||||
ldap_errstr(ildb->ldap, status));
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
talloc_free(ildb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ldb_ildap_init(void)
|
||||
{
|
||||
return ldb_register_backend("ldap", ildb_connect) +
|
||||
ldb_register_backend("ldapi", ildb_connect) +
|
||||
ldb_register_backend("ldaps", ildb_connect);
|
||||
}
|
||||
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb_ldap
|
||||
*
|
||||
* Component: ldb ldap backend
|
||||
*
|
||||
* Description: core files for LDAP backend
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*
|
||||
* Modifications:
|
||||
*
|
||||
* - description: make the module use asyncronous calls
|
||||
* date: Feb 2006
|
||||
* author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#define LDAP_DEPRECATED 1
|
||||
#include <ldap.h>
|
||||
|
||||
struct lldb_private {
|
||||
LDAP *ldap;
|
||||
};
|
||||
|
||||
struct lldb_context {
|
||||
struct ldb_module *module;
|
||||
int msgid;
|
||||
int timeout;
|
||||
time_t starttime;
|
||||
void *context;
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
};
|
||||
|
||||
static int lldb_ldap_to_ldb(int err) {
|
||||
/* Ldap errors and ldb errors are defined to the same values */
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct ldb_handle *init_handle(struct lldb_private *lldb, struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *),
|
||||
int timeout, time_t starttime)
|
||||
{
|
||||
struct lldb_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(lldb, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc(h, struct lldb_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->context = context;
|
||||
ac->callback = callback;
|
||||
ac->timeout = timeout;
|
||||
ac->starttime = starttime;
|
||||
ac->msgid = 0;
|
||||
|
||||
return h;
|
||||
}
|
||||
/*
|
||||
convert a ldb_message structure to a list of LDAPMod structures
|
||||
ready for ldap_add() or ldap_modify()
|
||||
*/
|
||||
static LDAPMod **lldb_msg_to_mods(void *mem_ctx, const struct ldb_message *msg, int use_flags)
|
||||
{
|
||||
LDAPMod **mods;
|
||||
unsigned int i, j;
|
||||
int num_mods = 0;
|
||||
|
||||
/* allocate maximum number of elements needed */
|
||||
mods = talloc_array(mem_ctx, LDAPMod *, msg->num_elements+1);
|
||||
if (!mods) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
mods[0] = NULL;
|
||||
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
const struct ldb_message_element *el = &msg->elements[i];
|
||||
|
||||
mods[num_mods] = talloc(mods, LDAPMod);
|
||||
if (!mods[num_mods]) {
|
||||
goto failed;
|
||||
}
|
||||
mods[num_mods+1] = NULL;
|
||||
mods[num_mods]->mod_op = LDAP_MOD_BVALUES;
|
||||
if (use_flags) {
|
||||
switch (el->flags & LDB_FLAG_MOD_MASK) {
|
||||
case LDB_FLAG_MOD_ADD:
|
||||
mods[num_mods]->mod_op |= LDAP_MOD_ADD;
|
||||
break;
|
||||
case LDB_FLAG_MOD_DELETE:
|
||||
mods[num_mods]->mod_op |= LDAP_MOD_DELETE;
|
||||
break;
|
||||
case LDB_FLAG_MOD_REPLACE:
|
||||
mods[num_mods]->mod_op |= LDAP_MOD_REPLACE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mods[num_mods]->mod_type = discard_const_p(char, el->name);
|
||||
mods[num_mods]->mod_vals.modv_bvals = talloc_array(mods[num_mods],
|
||||
struct berval *,
|
||||
1+el->num_values);
|
||||
if (!mods[num_mods]->mod_vals.modv_bvals) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (j=0;j<el->num_values;j++) {
|
||||
mods[num_mods]->mod_vals.modv_bvals[j] = talloc(mods[num_mods]->mod_vals.modv_bvals,
|
||||
struct berval);
|
||||
if (!mods[num_mods]->mod_vals.modv_bvals[j]) {
|
||||
goto failed;
|
||||
}
|
||||
mods[num_mods]->mod_vals.modv_bvals[j]->bv_val = el->values[j].data;
|
||||
mods[num_mods]->mod_vals.modv_bvals[j]->bv_len = el->values[j].length;
|
||||
}
|
||||
mods[num_mods]->mod_vals.modv_bvals[j] = NULL;
|
||||
num_mods++;
|
||||
}
|
||||
|
||||
return mods;
|
||||
|
||||
failed:
|
||||
talloc_free(mods);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
add a single set of ldap message values to a ldb_message
|
||||
*/
|
||||
static int lldb_add_msg_attr(struct ldb_context *ldb,
|
||||
struct ldb_message *msg,
|
||||
const char *attr, struct berval **bval)
|
||||
{
|
||||
int count, i;
|
||||
struct ldb_message_element *el;
|
||||
|
||||
count = ldap_count_values_len(bval);
|
||||
|
||||
if (count <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
el = talloc_realloc(msg, msg->elements, struct ldb_message_element,
|
||||
msg->num_elements + 1);
|
||||
if (!el) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->elements = el;
|
||||
|
||||
el = &msg->elements[msg->num_elements];
|
||||
|
||||
el->name = talloc_strdup(msg->elements, attr);
|
||||
if (!el->name) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
el->flags = 0;
|
||||
|
||||
el->num_values = 0;
|
||||
el->values = talloc_array(msg->elements, struct ldb_val, count);
|
||||
if (!el->values) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=0;i<count;i++) {
|
||||
/* we have to ensure this is null terminated so that
|
||||
ldb_msg_find_attr_as_string() can work */
|
||||
el->values[i].data = talloc_size(el->values, bval[i]->bv_len+1);
|
||||
if (!el->values[i].data) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
memcpy(el->values[i].data, bval[i]->bv_val, bval[i]->bv_len);
|
||||
el->values[i].data[bval[i]->bv_len] = 0;
|
||||
el->values[i].length = bval[i]->bv_len;
|
||||
el->num_values++;
|
||||
}
|
||||
|
||||
msg->num_elements++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
search for matching records
|
||||
*/
|
||||
static int lldb_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
|
||||
struct lldb_context *lldb_ac;
|
||||
struct timeval tv;
|
||||
int ldap_scope;
|
||||
char *search_base;
|
||||
char *expression;
|
||||
int ret;
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb, "Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (req->op.search.tree == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Invalid expression parse tree");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (req->controls != NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "Controls are not yet supported by ldb_ldap backend!\n");
|
||||
}
|
||||
|
||||
req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
|
||||
if (req->handle == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
|
||||
|
||||
search_base = ldb_dn_alloc_linearized(lldb_ac, req->op.search.base);
|
||||
if (req->op.search.base == NULL) {
|
||||
search_base = talloc_strdup(lldb_ac, "");
|
||||
}
|
||||
if (search_base == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
expression = ldb_filter_from_tree(lldb_ac, req->op.search.tree);
|
||||
if (expression == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
switch (req->op.search.scope) {
|
||||
case LDB_SCOPE_BASE:
|
||||
ldap_scope = LDAP_SCOPE_BASE;
|
||||
break;
|
||||
case LDB_SCOPE_ONELEVEL:
|
||||
ldap_scope = LDAP_SCOPE_ONELEVEL;
|
||||
break;
|
||||
default:
|
||||
ldap_scope = LDAP_SCOPE_SUBTREE;
|
||||
break;
|
||||
}
|
||||
|
||||
tv.tv_sec = req->timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
ret = ldap_search_ext(lldb->ldap, search_base, ldap_scope,
|
||||
expression,
|
||||
discard_const_p(char *, req->op.search.attrs),
|
||||
0,
|
||||
NULL,
|
||||
NULL,
|
||||
&tv,
|
||||
LDAP_NO_LIMIT,
|
||||
&lldb_ac->msgid);
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, ldap_err2string(ret));
|
||||
}
|
||||
|
||||
return lldb_ldap_to_ldb(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
add a record
|
||||
*/
|
||||
static int lldb_add(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
|
||||
struct lldb_context *lldb_ac;
|
||||
LDAPMod **mods;
|
||||
char *dn;
|
||||
int ret;
|
||||
|
||||
/* ltdb specials should not reach this point */
|
||||
if (ldb_dn_is_special(req->op.add.message->dn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
|
||||
if (req->handle == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
|
||||
|
||||
mods = lldb_msg_to_mods(lldb_ac, req->op.add.message, 0);
|
||||
if (mods == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
dn = ldb_dn_alloc_linearized(lldb_ac, req->op.add.message->dn);
|
||||
if (dn == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = ldap_add_ext(lldb->ldap, dn, mods,
|
||||
NULL,
|
||||
NULL,
|
||||
&lldb_ac->msgid);
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, ldap_err2string(ret));
|
||||
}
|
||||
|
||||
return lldb_ldap_to_ldb(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
modify a record
|
||||
*/
|
||||
static int lldb_modify(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
|
||||
struct lldb_context *lldb_ac;
|
||||
LDAPMod **mods;
|
||||
char *dn;
|
||||
int ret;
|
||||
|
||||
/* ltdb specials should not reach this point */
|
||||
if (ldb_dn_is_special(req->op.mod.message->dn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
|
||||
if (req->handle == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
|
||||
|
||||
mods = lldb_msg_to_mods(lldb_ac, req->op.mod.message, 1);
|
||||
if (mods == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
dn = ldb_dn_alloc_linearized(lldb_ac, req->op.mod.message->dn);
|
||||
if (dn == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = ldap_modify_ext(lldb->ldap, dn, mods,
|
||||
NULL,
|
||||
NULL,
|
||||
&lldb_ac->msgid);
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, ldap_err2string(ret));
|
||||
}
|
||||
|
||||
return lldb_ldap_to_ldb(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
delete a record
|
||||
*/
|
||||
static int lldb_delete(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
|
||||
struct lldb_context *lldb_ac;
|
||||
char *dnstr;
|
||||
int ret;
|
||||
|
||||
/* ltdb specials should not reach this point */
|
||||
if (ldb_dn_is_special(req->op.del.dn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
|
||||
if (req->handle == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
|
||||
|
||||
dnstr = ldb_dn_alloc_linearized(lldb_ac, req->op.del.dn);
|
||||
|
||||
ret = ldap_delete_ext(lldb->ldap, dnstr,
|
||||
NULL,
|
||||
NULL,
|
||||
&lldb_ac->msgid);
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, ldap_err2string(ret));
|
||||
}
|
||||
|
||||
return lldb_ldap_to_ldb(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
rename a record
|
||||
*/
|
||||
static int lldb_rename(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct lldb_private *lldb = talloc_get_type(module->private_data, struct lldb_private);
|
||||
struct lldb_context *lldb_ac;
|
||||
char *old_dn;
|
||||
char *newrdn;
|
||||
char *parentdn;
|
||||
int ret;
|
||||
|
||||
/* ltdb specials should not reach this point */
|
||||
if (ldb_dn_is_special(req->op.rename.olddn) || ldb_dn_is_special(req->op.rename.newdn)) {
|
||||
return LDB_ERR_INVALID_DN_SYNTAX;
|
||||
}
|
||||
|
||||
req->handle = init_handle(lldb, module, req->context, req->callback, req->timeout, req->starttime);
|
||||
if (req->handle == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
lldb_ac = talloc_get_type(req->handle->private_data, struct lldb_context);
|
||||
|
||||
old_dn = ldb_dn_alloc_linearized(lldb_ac, req->op.rename.olddn);
|
||||
if (old_dn == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
newrdn = talloc_asprintf(lldb_ac, "%s=%s",
|
||||
ldb_dn_get_rdn_name(req->op.rename.newdn),
|
||||
ldb_dn_escape_value(lldb, *(ldb_dn_get_rdn_val(req->op.rename.newdn))));
|
||||
if (!newrdn) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
parentdn = ldb_dn_alloc_linearized(lldb_ac, ldb_dn_get_parent(lldb_ac, req->op.rename.newdn));
|
||||
if (!parentdn) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = ldap_rename(lldb->ldap, old_dn, newrdn, parentdn,
|
||||
1, NULL, NULL,
|
||||
&lldb_ac->msgid);
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, ldap_err2string(ret));
|
||||
}
|
||||
|
||||
return lldb_ldap_to_ldb(ret);
|
||||
}
|
||||
|
||||
static int lldb_parse_result(struct ldb_handle *handle, LDAPMessage *result)
|
||||
{
|
||||
struct lldb_context *ac = talloc_get_type(handle->private_data, struct lldb_context);
|
||||
struct lldb_private *lldb = talloc_get_type(ac->module->private_data, struct lldb_private);
|
||||
struct ldb_reply *ares = NULL;
|
||||
LDAPMessage *msg;
|
||||
int type;
|
||||
char *matcheddnp = NULL;
|
||||
char *errmsgp = NULL;
|
||||
char **referralsp = NULL;
|
||||
LDAPControl **serverctrlsp = NULL;
|
||||
int ret = LDB_SUCCESS;
|
||||
|
||||
type = ldap_msgtype(result);
|
||||
|
||||
handle->status = 0;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case LDAP_RES_SEARCH_ENTRY:
|
||||
msg = ldap_first_entry(lldb->ldap, result);
|
||||
if (msg != NULL) {
|
||||
BerElement *berptr = NULL;
|
||||
char *attr, *dn;
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ares->message = ldb_msg_new(ares);
|
||||
if (!ares->message) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dn = ldap_get_dn(lldb->ldap, msg);
|
||||
if (!dn) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
ares->message->dn = ldb_dn_new(ares->message, ac->module->ldb, dn);
|
||||
if ( ! ldb_dn_validate(ares->message->dn)) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
ldap_memfree(dn);
|
||||
|
||||
ares->message->num_elements = 0;
|
||||
ares->message->elements = NULL;
|
||||
ares->message->private_data = NULL;
|
||||
|
||||
/* loop over all attributes */
|
||||
for (attr=ldap_first_attribute(lldb->ldap, msg, &berptr);
|
||||
attr;
|
||||
attr=ldap_next_attribute(lldb->ldap, msg, berptr)) {
|
||||
struct berval **bval;
|
||||
bval = ldap_get_values_len(lldb->ldap, msg, attr);
|
||||
|
||||
if (bval) {
|
||||
lldb_add_msg_attr(ac->module->ldb, ares->message, attr, bval);
|
||||
ldap_value_free_len(bval);
|
||||
}
|
||||
}
|
||||
if (berptr) ber_free(berptr, 0);
|
||||
|
||||
|
||||
ares->type = LDB_REPLY_ENTRY;
|
||||
ret = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
} else {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
}
|
||||
break;
|
||||
|
||||
case LDAP_RES_SEARCH_REFERENCE:
|
||||
if (ldap_parse_result(lldb->ldap, result, &handle->status,
|
||||
&matcheddnp, &errmsgp,
|
||||
&referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
if (referralsp == NULL) {
|
||||
handle->status = LDB_ERR_PROTOCOL_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ares->referral = talloc_strdup(ares, *referralsp);
|
||||
ares->type = LDB_REPLY_REFERRAL;
|
||||
ret = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
|
||||
break;
|
||||
|
||||
case LDAP_RES_SEARCH_RESULT:
|
||||
if (ldap_parse_result(lldb->ldap, result, &handle->status,
|
||||
&matcheddnp, &errmsgp,
|
||||
&referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (serverctrlsp != NULL) {
|
||||
/* FIXME: transform the LDAPControl list into an ldb_control one */
|
||||
ares->controls = NULL;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
ret = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
|
||||
break;
|
||||
|
||||
case LDAP_RES_MODIFY:
|
||||
case LDAP_RES_ADD:
|
||||
case LDAP_RES_DELETE:
|
||||
case LDAP_RES_MODDN:
|
||||
if (ldap_parse_result(lldb->ldap, result, &handle->status,
|
||||
&matcheddnp, &errmsgp,
|
||||
&referralsp, &serverctrlsp, 0) != LDAP_SUCCESS) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto error;
|
||||
}
|
||||
if (ac->callback && handle->status == LDB_SUCCESS) {
|
||||
ares = NULL; /* FIXME: build a corresponding ares to pass on */
|
||||
ret = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
}
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = LDB_ERR_PROTOCOL_ERROR;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (matcheddnp) ldap_memfree(matcheddnp);
|
||||
if (errmsgp && *errmsgp) {
|
||||
ldb_set_errstring(ac->module->ldb, errmsgp);
|
||||
} else if (handle->status) {
|
||||
ldb_set_errstring(ac->module->ldb, ldap_err2string(handle->status));
|
||||
}
|
||||
if (errmsgp) {
|
||||
ldap_memfree(errmsgp);
|
||||
}
|
||||
if (referralsp) ldap_value_free(referralsp);
|
||||
if (serverctrlsp) ldap_controls_free(serverctrlsp);
|
||||
|
||||
ldap_msgfree(result);
|
||||
return lldb_ldap_to_ldb(handle->status);
|
||||
|
||||
error:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
ldap_msgfree(result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lldb_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
struct lldb_context *ac = talloc_get_type(handle->private_data, struct lldb_context);
|
||||
struct lldb_private *lldb = talloc_get_type(handle->module->private_data, struct lldb_private);
|
||||
struct timeval timeout;
|
||||
LDAPMessage *result;
|
||||
int ret, lret;
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
if (!ac || !ac->msgid) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
switch(type) {
|
||||
case LDB_WAIT_NONE:
|
||||
|
||||
if ((ac->timeout != -1) &&
|
||||
((ac->starttime + ac->timeout) > time(NULL))) {
|
||||
return LDB_ERR_TIME_LIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
lret = ldap_result(lldb->ldap, ac->msgid, 0, &timeout, &result);
|
||||
if (lret == -1) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
if (lret == 0) {
|
||||
ret = LDB_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
return lldb_parse_result(handle, result);
|
||||
|
||||
case LDB_WAIT_ALL:
|
||||
timeout.tv_usec = 0;
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
while (handle->status == LDB_SUCCESS && handle->state != LDB_ASYNC_DONE) {
|
||||
|
||||
if (ac->timeout == -1) {
|
||||
lret = ldap_result(lldb->ldap, ac->msgid, 0, NULL, &result);
|
||||
} else {
|
||||
timeout.tv_sec = ac->timeout - (time(NULL) - ac->starttime);
|
||||
if (timeout.tv_sec <= 0)
|
||||
return LDB_ERR_TIME_LIMIT_EXCEEDED;
|
||||
lret = ldap_result(lldb->ldap, ac->msgid, 0, &timeout, &result);
|
||||
}
|
||||
if (lret == -1) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
if (lret == 0) {
|
||||
return LDB_ERR_TIME_LIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
ret = lldb_parse_result(handle, result);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lldb_start_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local transaction mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int lldb_end_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local transaction mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int lldb_del_trans(struct ldb_module *module)
|
||||
{
|
||||
/* TODO implement a local transaction mechanism here */
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int lldb_request(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops lldb_ops = {
|
||||
.name = "ldap",
|
||||
.search = lldb_search,
|
||||
.add = lldb_add,
|
||||
.modify = lldb_modify,
|
||||
.del = lldb_delete,
|
||||
.rename = lldb_rename,
|
||||
.request = lldb_request,
|
||||
.start_transaction = lldb_start_trans,
|
||||
.end_transaction = lldb_end_trans,
|
||||
.del_transaction = lldb_del_trans,
|
||||
.wait = lldb_wait
|
||||
};
|
||||
|
||||
|
||||
static int lldb_destructor(struct lldb_private *lldb)
|
||||
{
|
||||
ldap_unbind(lldb->ldap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
connect to the database
|
||||
*/
|
||||
static int lldb_connect(struct ldb_context *ldb,
|
||||
const char *url,
|
||||
unsigned int flags,
|
||||
const char *options[],
|
||||
struct ldb_module **module)
|
||||
{
|
||||
struct lldb_private *lldb = NULL;
|
||||
int version = 3;
|
||||
int ret;
|
||||
|
||||
lldb = talloc(ldb, struct lldb_private);
|
||||
if (!lldb) {
|
||||
ldb_oom(ldb);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
lldb->ldap = NULL;
|
||||
|
||||
ret = ldap_initialize(&lldb->ldap, url);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_initialize failed for URL '%s' - %s\n",
|
||||
url, ldap_err2string(ret));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
talloc_set_destructor(lldb, lldb_destructor);
|
||||
|
||||
ret = ldap_set_option(lldb->ldap, LDAP_OPT_PROTOCOL_VERSION, &version);
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
ldb_debug(ldb, LDB_DEBUG_FATAL, "ldap_set_option failed - %s\n",
|
||||
ldap_err2string(ret));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
*module = talloc(ldb, struct ldb_module);
|
||||
if (*module == NULL) {
|
||||
ldb_oom(ldb);
|
||||
talloc_free(lldb);
|
||||
return -1;
|
||||
}
|
||||
talloc_set_name_const(*module, "ldb_ldap backend");
|
||||
(*module)->ldb = ldb;
|
||||
(*module)->prev = (*module)->next = NULL;
|
||||
(*module)->private_data = lldb;
|
||||
(*module)->ops = &lldb_ops;
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
talloc_free(lldb);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ldb_ldap_init(void)
|
||||
{
|
||||
return ldb_register_backend("ldap", lldb_connect) +
|
||||
ldb_register_backend("ldapi", lldb_connect) +
|
||||
ldb_register_backend("ldaps", lldb_connect);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
trees.ps contains an explanation of the Genealogical Representation of Trees
|
||||
in Databases which is being used in ldb_sqlite3. Note that we use fgID
|
||||
representation with 4 bytes per level, so we can represent 6.5E+08 subclasses
|
||||
of any object class. This should be adequate for our purposes. :-)
|
||||
|
||||
The following document is the primary basis for the schema currently being
|
||||
used here: http://www.research.ibm.com/journal/sj/392/shi.html
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
base160 code used by ldb_sqlite3
|
||||
|
||||
Copyright (C) 2004 Derrell Lipman
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ldb_sqlite3_base160()
|
||||
*
|
||||
* Convert an integer value to a string containing the base 160 representation
|
||||
* of the integer. We always convert to a string representation that is 4
|
||||
* bytes in length, and we always null terminate.
|
||||
*
|
||||
* Parameters:
|
||||
* val --
|
||||
* The value to be converted
|
||||
*
|
||||
* result --
|
||||
* Buffer in which the result is to be placed
|
||||
*
|
||||
* Returns:
|
||||
* nothing
|
||||
*/
|
||||
static unsigned char base160tab[161] =
|
||||
{
|
||||
48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , /* 0-9 */
|
||||
58 , 59 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , /* : ; A-H */
|
||||
73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 , 81 , 82 , /* I-R */
|
||||
83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 97 , 98 , /* S-Z , a-b */
|
||||
99 , 100, 101, 102, 103, 104, 105, 106, 107, 108, /* c-l */
|
||||
109, 110, 111, 112, 113, 114, 115, 116, 117, 118, /* m-v */
|
||||
119, 120, 121, 122, 160, 161, 162, 163, 164, 165, /* w-z, latin1 */
|
||||
166, 167, 168, 169, 170, 171, 172, 173, 174, 175, /* latin1 */
|
||||
176, 177, 178, 179, 180, 181, 182, 183, 184, 185, /* latin1 */
|
||||
186, 187, 188, 189, 190, 191, 192, 193, 194, 195, /* latin1 */
|
||||
196, 197, 198, 199, 200, 201, 202, 203, 204, 205, /* latin1 */
|
||||
206, 207, 208, 209, 210, 211, 212, 213, 214, 215, /* latin1 */
|
||||
216, 217, 218, 219, 220, 221, 222, 223, 224, 225, /* latin1 */
|
||||
226, 227, 228, 229, 230, 231, 232, 233, 234, 235, /* latin1 */
|
||||
236, 237, 238, 239, 240, 241, 242, 243, 244, 245, /* latin1 */
|
||||
246, 247, 248, 249, 250, 251, 252, 253, 254, 255, /* latin1 */
|
||||
'\0'
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* lsqlite3_base160()
|
||||
*
|
||||
* Convert an unsigned long integer into a base160 representation of the
|
||||
* number.
|
||||
*
|
||||
* Parameters:
|
||||
* val --
|
||||
* value to be converted
|
||||
*
|
||||
* result --
|
||||
* character array, 5 bytes long, into which the base160 representation
|
||||
* will be placed. The result will be a four-digit representation of the
|
||||
* number (with leading zeros prepended as necessary), and null
|
||||
* terminated.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing
|
||||
*/
|
||||
void
|
||||
lsqlite3_base160(unsigned long val,
|
||||
unsigned char result[5])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 3; i >= 0; i--) {
|
||||
|
||||
result[i] = base160tab[val % 160];
|
||||
val /= 160;
|
||||
}
|
||||
|
||||
result[4] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* lsqlite3_base160Next()
|
||||
*
|
||||
* Retrieve the next-greater number in the base160 sequence for the terminal
|
||||
* tree node (the last four digits). Only one tree level (four digits) are
|
||||
* operated on.
|
||||
*
|
||||
* Parameters:
|
||||
* base160 -- a character array containing either an empty string (in which
|
||||
* case no operation is performed), or a string of base160 digits
|
||||
* with a length of a multiple of four digits.
|
||||
*
|
||||
* Upon return, the trailing four digits (one tree level) will
|
||||
* have been incremented by 1.
|
||||
*
|
||||
* Returns:
|
||||
* base160 -- the modified array
|
||||
*/
|
||||
char *
|
||||
lsqlite3_base160Next(char base160[])
|
||||
{
|
||||
int i;
|
||||
int len;
|
||||
unsigned char * pTab;
|
||||
char * pBase160 = base160;
|
||||
|
||||
/*
|
||||
* We need a minimum of four digits, and we will always get a multiple of
|
||||
* four digits.
|
||||
*/
|
||||
if (len = strlen(pBase160)) >= 4)
|
||||
{
|
||||
pBase160 += strlen(pBase160) - 1;
|
||||
|
||||
/* We only carry through four digits: one level in the tree */
|
||||
for (i = 0; i < 4; i++) {
|
||||
|
||||
/* What base160 value does this digit have? */
|
||||
pTab = strchr(base160tab, *pBase160);
|
||||
|
||||
/* Is there a carry? */
|
||||
if (pTab < base160tab + sizeof(base160tab) - 1) {
|
||||
|
||||
/* Nope. Just increment this value and we're done. */
|
||||
*pBase160 = *++pTab;
|
||||
break;
|
||||
} else {
|
||||
|
||||
/*
|
||||
* There's a carry. This value gets base160tab[0], we
|
||||
* decrement the buffer pointer to get the next higher-order
|
||||
* digit, and continue in the loop.
|
||||
*/
|
||||
*pBase160-- = base160tab[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base160;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,363 @@
|
||||
-- ------------------------------------------------------
|
||||
|
||||
PRAGMA auto_vacuum=1;
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
BEGIN EXCLUSIVE;
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
CREATE TABLE ldb_info AS
|
||||
SELECT 'LDB' AS database_type,
|
||||
'1.0' AS version;
|
||||
|
||||
/*
|
||||
* Get the next USN value with:
|
||||
* BEGIN EXCLUSIVE;
|
||||
* UPDATE usn SET value = value + 1;
|
||||
* SELECT value FROM usn;
|
||||
* COMMIT;
|
||||
*/
|
||||
CREATE TABLE usn
|
||||
(
|
||||
value INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE ldb_object
|
||||
(
|
||||
/* tree_key is auto-generated by the insert trigger */
|
||||
tree_key TEXT PRIMARY KEY,
|
||||
|
||||
parent_tree_key TEXT,
|
||||
dn TEXT,
|
||||
|
||||
attr_name TEXT REFERENCES ldb_attributes,
|
||||
attr_value TEXT,
|
||||
|
||||
/*
|
||||
* object_type can take on these values (to date):
|
||||
* 1: object is a node of a DN
|
||||
* 2: object is an attribute/value pair of its parent DN
|
||||
*/
|
||||
object_type INTEGER,
|
||||
|
||||
/*
|
||||
* if object_type is 1, the node can have children.
|
||||
* this tracks the maximum previously assigned child
|
||||
* number so we can generate a new unique tree key for
|
||||
* a new child object. note that this is always incremented,
|
||||
* so if children are deleted, this will not represent
|
||||
* the _number_ of children.
|
||||
*/
|
||||
max_child_num INTEGER,
|
||||
|
||||
/*
|
||||
* Automatically maintained meta-data (a gift for metze)
|
||||
*/
|
||||
object_guid TEXT UNIQUE,
|
||||
timestamp INTEGER, -- originating_time
|
||||
invoke_id TEXT, -- GUID: originating_invocation_id
|
||||
usn INTEGER, -- hyper: originating_usn
|
||||
|
||||
/* do not allow duplicate name/value pairs */
|
||||
UNIQUE (parent_tree_key, attr_name, attr_value, object_type)
|
||||
);
|
||||
|
||||
CREATE TABLE ldb_attributes
|
||||
(
|
||||
attr_name TEXT PRIMARY KEY,
|
||||
parent_tree_key TEXT,
|
||||
|
||||
objectclass_p BOOLEAN DEFAULT 0,
|
||||
|
||||
case_insensitive_p BOOLEAN DEFAULT 0,
|
||||
wildcard_p BOOLEAN DEFAULT 0,
|
||||
hidden_p BOOLEAN DEFAULT 0,
|
||||
integer_p BOOLEAN DEFAULT 0,
|
||||
|
||||
/* tree_key is auto-generated by the insert trigger */
|
||||
tree_key TEXT, -- null if not a object/sub class
|
||||
-- level 1 if an objectclass
|
||||
-- level 1-n if a subclass
|
||||
max_child_num INTEGER
|
||||
);
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
CREATE INDEX ldb_object_dn_idx
|
||||
ON ldb_object (dn);
|
||||
|
||||
CREATE INDEX ldb_attributes_tree_key_ids
|
||||
ON ldb_attributes (tree_key);
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
/* Gifts for metze. Automatically updated meta-data */
|
||||
CREATE TRIGGER ldb_object_insert_tr
|
||||
AFTER INSERT
|
||||
ON ldb_object
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE ldb_object
|
||||
SET max_child_num = max_child_num + 1
|
||||
WHERE tree_key = new.parent_tree_key;
|
||||
UPDATE usn SET value = value + 1;
|
||||
UPDATE ldb_object
|
||||
SET tree_key =
|
||||
(SELECT
|
||||
new.tree_key ||
|
||||
base160(SELECT max_child_num
|
||||
FROM ldb_object
|
||||
WHERE tree_key =
|
||||
new.parent_tree_key));
|
||||
max_child_num = 0,
|
||||
object_guid = random_guid(),
|
||||
timestamp = strftime('%s', 'now'),
|
||||
usn = (SELECT value FROM usn);
|
||||
WHERE tree_key = new.tree_key;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER ldb_object_update_tr
|
||||
AFTER UPDATE
|
||||
ON ldb_object
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE usn SET value = value + 1;
|
||||
UPDATE ldb_object
|
||||
SET timestamp = strftime('%s', 'now'),
|
||||
usn = (SELECT value FROM usn);
|
||||
WHERE tree_key = new.tree_key;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER ldb_attributes_insert_tr
|
||||
AFTER INSERT
|
||||
ON ldb_attributes
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE ldb_attributes
|
||||
SET max_child_num = max_child_num + 1
|
||||
WHERE tree_key = new.parent_tree_key;
|
||||
UPDATE ldb_attributes
|
||||
SET tree_key =
|
||||
(SELECT
|
||||
new.tree_key ||
|
||||
base160(SELECT max_child_num
|
||||
FROM ldb_attributes
|
||||
WHERE tree_key =
|
||||
new.parent_tree_key));
|
||||
max_child_num = 0
|
||||
WHERE tree_key = new.tree_key;
|
||||
END;
|
||||
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
/* Initialize usn */
|
||||
INSERT INTO usn (value) VALUES (0);
|
||||
|
||||
/* Create root object */
|
||||
INSERT INTO ldb_object
|
||||
(tree_key, parent_tree_key,
|
||||
dn,
|
||||
object_type, max_child_num)
|
||||
VALUES ('', NULL,
|
||||
'',
|
||||
1, 0);
|
||||
|
||||
/* We need an implicit "top" level object class */
|
||||
INSERT INTO ldb_attributes (attr_name,
|
||||
parent_tree_key)
|
||||
SELECT 'top', '';
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ------------------------------------------------------
|
||||
|
||||
/*
|
||||
* dn: o=University of Michigan,c=US
|
||||
* objectclass: organization
|
||||
* objectclass: domainRelatedObject
|
||||
*/
|
||||
-- newDN
|
||||
BEGIN;
|
||||
|
||||
INSERT OR IGNORE INTO ldb_object
|
||||
(parent_tree_key
|
||||
dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('',
|
||||
'c=US',
|
||||
'c', 'US', 1, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key,
|
||||
dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('0001',
|
||||
'o=University of Michigan,c=US',
|
||||
'o', 'University of Michigan', 1, 0);
|
||||
|
||||
-- newObjectClass
|
||||
INSERT OR IGNORE INTO ldb_attributes
|
||||
(attr_name, parent_tree_key, objectclass_p)
|
||||
VALUES
|
||||
('objectclass', '', 1);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key,
|
||||
dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001',
|
||||
NULL,
|
||||
'objectclass', 'organization', 2, 0);
|
||||
|
||||
INSERT OR IGNORE INTO ldb_attributes
|
||||
(attr_name, parent_tree_key, objectclass_p)
|
||||
VALUES
|
||||
('objectclass', '', 1);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key,
|
||||
dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001',
|
||||
NULL,
|
||||
'objectclass', 'domainRelatedObject', 2, 0);
|
||||
|
||||
COMMIT;
|
||||
|
||||
|
||||
/*
|
||||
* dn: o=University of Michigan,c=US
|
||||
* l: Ann Arbor, Michigan
|
||||
* st: Michigan
|
||||
* o: University of Michigan
|
||||
* o: UMICH
|
||||
* seeAlso:
|
||||
* telephonenumber: +1 313 764-1817
|
||||
*/
|
||||
-- addAttrValuePair
|
||||
BEGIN;
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'l', 'Ann Arbor, Michigan', 2, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'st', 'Michigan', 2, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'o', 'University of Michigan', 2, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'o', 'UMICH', 2, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'seeAlso', '', 2, 0);
|
||||
|
||||
INSERT INTO ldb_object
|
||||
(parent_tree_key, dn,
|
||||
attr_name, attr_value, object_type, max_child_num)
|
||||
VALUES ('00010001', NULL,
|
||||
'telephonenumber', '+1 313 764-1817', 2, 0);
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* dn: @ATTRIBUTES
|
||||
* uid: CASE_INSENSITIVE WILDCARD
|
||||
* cn: CASE_INSENSITIVE
|
||||
* ou: CASE_INSENSITIVE
|
||||
* dn: CASE_INSENSITIVE
|
||||
*/
|
||||
-- newAttribute
|
||||
|
||||
BEGIN;
|
||||
|
||||
INSERT OR IGNORE INTO ldb_attributes
|
||||
(attr_name, parent_tree_key, objectclass_p)
|
||||
VALUES
|
||||
('uid', '', 0);
|
||||
|
||||
UPDATE ldb_attributes
|
||||
SET case_insensitive_p = 1,
|
||||
wildcard_p = 1,
|
||||
hidden_p = 0,
|
||||
integer_p = 0
|
||||
WHERE attr_name = 'uid'
|
||||
|
||||
UPDATE ldb_attributes
|
||||
SET case_insensitive_p = 1,
|
||||
wildcard_p = 0,
|
||||
hidden_p = 0,
|
||||
integer_p = 0
|
||||
WHERE attr_name = 'cn'
|
||||
|
||||
UPDATE ldb_attributes
|
||||
SET case_insensitive_p = 1,
|
||||
wildcard_p = 0,
|
||||
hidden_p = 0,
|
||||
integer_p = 0
|
||||
WHERE attr_name = 'ou'
|
||||
|
||||
UPDATE ldb_attributes
|
||||
SET case_insensitive_p = 1,
|
||||
wildcard_p = 0,
|
||||
hidden_p = 0,
|
||||
integer_p = 0
|
||||
WHERE attr_name = 'dn'
|
||||
|
||||
-- ----------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* dn: @SUBCLASSES
|
||||
* top: domain
|
||||
* top: person
|
||||
* domain: domainDNS
|
||||
* person: organizationalPerson
|
||||
* person: fooPerson
|
||||
* organizationalPerson: user
|
||||
* organizationalPerson: OpenLDAPperson
|
||||
* user: computer
|
||||
*/
|
||||
-- insertSubclass
|
||||
|
||||
/* NOT YET UPDATED!!! *
|
||||
|
||||
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'domain', /* next_tree_key('top') */ '00010001';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'person', /* next_tree_key('top') */ '00010002';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'domainDNS', /* next_tree_key('domain') */ '000100010001';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'organizationalPerson', /* next_tree_key('person') */ '000100020001';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'fooPerson', /* next_tree_key('person') */ '000100020002';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'user', /* next_tree_key('organizationalPerson') */ '0001000200010001';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'OpenLDAPperson', /* next_tree_key('organizationPerson') */ '0001000200010002';
|
||||
INSERT OR REPLACE INTO ldb_object_classes (class_name, tree_key)
|
||||
SELECT 'computer', /* next_tree_key('user') */ '0001000200010001';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,550 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb tdb cache functions
|
||||
*
|
||||
* Description: cache special records in a ldb/tdb
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "ldb/ldb_tdb/ldb_tdb.h"
|
||||
|
||||
#define LTDB_FLAG_CASE_INSENSITIVE (1<<0)
|
||||
#define LTDB_FLAG_INTEGER (1<<1)
|
||||
#define LTDB_FLAG_HIDDEN (1<<2)
|
||||
#define LTDB_FLAG_OBJECTCLASS (1<<3)
|
||||
|
||||
/* valid attribute flags */
|
||||
static const struct {
|
||||
const char *name;
|
||||
int value;
|
||||
} ltdb_valid_attr_flags[] = {
|
||||
{ "CASE_INSENSITIVE", LTDB_FLAG_CASE_INSENSITIVE },
|
||||
{ "INTEGER", LTDB_FLAG_INTEGER },
|
||||
{ "HIDDEN", LTDB_FLAG_HIDDEN },
|
||||
{ "NONE", 0 },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
de-register any special handlers for @ATTRIBUTES
|
||||
*/
|
||||
static void ltdb_attributes_unload(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg;
|
||||
int i;
|
||||
|
||||
if (ltdb->cache->attributes == NULL) {
|
||||
/* no previously loaded attributes */
|
||||
return;
|
||||
}
|
||||
|
||||
msg = ltdb->cache->attributes;
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
ldb_remove_attrib_handler(module->ldb, msg->elements[i].name);
|
||||
}
|
||||
|
||||
talloc_free(ltdb->cache->attributes);
|
||||
ltdb->cache->attributes = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
add up the attrib flags for a @ATTRIBUTES element
|
||||
*/
|
||||
static int ltdb_attributes_flags(struct ldb_message_element *el, unsigned *v)
|
||||
{
|
||||
int i;
|
||||
unsigned value = 0;
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
int j;
|
||||
for (j=0;ltdb_valid_attr_flags[j].name;j++) {
|
||||
if (strcmp(ltdb_valid_attr_flags[j].name,
|
||||
(char *)el->values[i].data) == 0) {
|
||||
value |= ltdb_valid_attr_flags[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ltdb_valid_attr_flags[j].name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*v = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
register any special handlers from @ATTRIBUTES
|
||||
*/
|
||||
static int ltdb_attributes_load(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg = ltdb->cache->attributes;
|
||||
struct ldb_dn *dn;
|
||||
int i;
|
||||
|
||||
dn = ldb_dn_new(module, module->ldb, LTDB_ATTRIBUTES);
|
||||
if (dn == NULL) goto failed;
|
||||
|
||||
if (ltdb_search_dn1(module, dn, msg) == -1) {
|
||||
talloc_free(dn);
|
||||
goto failed;
|
||||
}
|
||||
talloc_free(dn);
|
||||
/* mapping these flags onto ldap 'syntaxes' isn't strictly correct,
|
||||
but its close enough for now */
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
unsigned flags;
|
||||
const char *syntax;
|
||||
const struct ldb_attrib_handler *h;
|
||||
struct ldb_attrib_handler h2;
|
||||
|
||||
if (ltdb_attributes_flags(&msg->elements[i], &flags) != 0) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_ERROR, "Invalid @ATTRIBUTES element for '%s'\n", msg->elements[i].name);
|
||||
goto failed;
|
||||
}
|
||||
switch (flags & ~LTDB_FLAG_HIDDEN) {
|
||||
case 0:
|
||||
syntax = LDB_SYNTAX_OCTET_STRING;
|
||||
break;
|
||||
case LTDB_FLAG_CASE_INSENSITIVE:
|
||||
syntax = LDB_SYNTAX_DIRECTORY_STRING;
|
||||
break;
|
||||
case LTDB_FLAG_INTEGER:
|
||||
syntax = LDB_SYNTAX_INTEGER;
|
||||
break;
|
||||
default:
|
||||
ldb_debug(module->ldb, LDB_DEBUG_ERROR,
|
||||
"Invalid flag combination 0x%x for '%s' in @ATTRIBUTES\n",
|
||||
flags, msg->elements[i].name);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
h = ldb_attrib_handler_syntax(module->ldb, syntax);
|
||||
if (h == NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_ERROR,
|
||||
"Invalid attribute syntax '%s' for '%s' in @ATTRIBUTES\n",
|
||||
syntax, msg->elements[i].name);
|
||||
goto failed;
|
||||
}
|
||||
h2 = *h;
|
||||
h2.attr = msg->elements[i].name;
|
||||
h2.flags |= LDB_ATTR_FLAG_ALLOCATED;
|
||||
if (ldb_set_attrib_handlers(module->ldb, &h2, 1) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
register any subclasses from @SUBCLASSES
|
||||
*/
|
||||
static int ltdb_subclasses_load(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg = ltdb->cache->subclasses;
|
||||
struct ldb_dn *dn;
|
||||
int i, j;
|
||||
|
||||
dn = ldb_dn_new(module, module->ldb, LTDB_SUBCLASSES);
|
||||
if (dn == NULL) goto failed;
|
||||
|
||||
if (ltdb_search_dn1(module, dn, msg) == -1) {
|
||||
talloc_free(dn);
|
||||
goto failed;
|
||||
}
|
||||
talloc_free(dn);
|
||||
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
struct ldb_message_element *el = &msg->elements[i];
|
||||
for (j=0;j<el->num_values;j++) {
|
||||
if (ldb_subclass_add(module->ldb, el->name,
|
||||
(char *)el->values[j].data) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
de-register any @SUBCLASSES
|
||||
*/
|
||||
static void ltdb_subclasses_unload(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg;
|
||||
int i;
|
||||
|
||||
if (ltdb->cache->subclasses == NULL) {
|
||||
/* no previously loaded subclasses */
|
||||
return;
|
||||
}
|
||||
|
||||
msg = ltdb->cache->subclasses;
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
ldb_subclass_remove(module->ldb, msg->elements[i].name);
|
||||
}
|
||||
|
||||
talloc_free(ltdb->cache->subclasses);
|
||||
ltdb->cache->subclasses = NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialise the baseinfo record
|
||||
*/
|
||||
static int ltdb_baseinfo_init(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg;
|
||||
struct ldb_message_element el;
|
||||
struct ldb_val val;
|
||||
int ret;
|
||||
/* the initial sequence number must be different from the one
|
||||
set in ltdb_cache_free(). Thanks to Jon for pointing this
|
||||
out. */
|
||||
const char *initial_sequence_number = "1";
|
||||
|
||||
ltdb->sequence_number = atof(initial_sequence_number);
|
||||
|
||||
msg = talloc(ltdb, struct ldb_message);
|
||||
if (msg == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
msg->num_elements = 1;
|
||||
msg->elements = ⪙
|
||||
msg->dn = ldb_dn_new(msg, module->ldb, LTDB_BASEINFO);
|
||||
if (!msg->dn) {
|
||||
goto failed;
|
||||
}
|
||||
el.name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
|
||||
if (!el.name) {
|
||||
goto failed;
|
||||
}
|
||||
el.values = &val;
|
||||
el.num_values = 1;
|
||||
el.flags = 0;
|
||||
val.data = (uint8_t *)talloc_strdup(msg, initial_sequence_number);
|
||||
if (!val.data) {
|
||||
goto failed;
|
||||
}
|
||||
val.length = 1;
|
||||
|
||||
ret = ltdb_store(module, msg, TDB_INSERT);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
return ret;
|
||||
|
||||
failed:
|
||||
talloc_free(msg);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
free any cache records
|
||||
*/
|
||||
static void ltdb_cache_free(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
|
||||
ltdb->sequence_number = 0;
|
||||
talloc_free(ltdb->cache);
|
||||
ltdb->cache = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
force a cache reload
|
||||
*/
|
||||
int ltdb_cache_reload(struct ldb_module *module)
|
||||
{
|
||||
ltdb_attributes_unload(module);
|
||||
ltdb_subclasses_unload(module);
|
||||
ltdb_cache_free(module);
|
||||
return ltdb_cache_load(module);
|
||||
}
|
||||
|
||||
/*
|
||||
load the cache records
|
||||
*/
|
||||
int ltdb_cache_load(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_dn *baseinfo_dn = NULL;
|
||||
struct ldb_dn *indexlist_dn = NULL;
|
||||
uint64_t seq;
|
||||
struct ldb_message *baseinfo;
|
||||
|
||||
/* a very fast check to avoid extra database reads */
|
||||
if (ltdb->cache != NULL &&
|
||||
tdb_get_seqnum(ltdb->tdb) == ltdb->tdb_seqnum) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ltdb->cache == NULL) {
|
||||
ltdb->cache = talloc_zero(ltdb, struct ltdb_cache);
|
||||
if (ltdb->cache == NULL) goto failed;
|
||||
ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
if (ltdb->cache->indexlist == NULL ||
|
||||
ltdb->cache->subclasses == NULL ||
|
||||
ltdb->cache->attributes == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
baseinfo = talloc(ltdb->cache, struct ldb_message);
|
||||
if (baseinfo == NULL) goto failed;
|
||||
|
||||
baseinfo_dn = ldb_dn_new(module, module->ldb, LTDB_BASEINFO);
|
||||
if (baseinfo_dn == NULL) goto failed;
|
||||
|
||||
if (ltdb_search_dn1(module, baseinfo_dn, baseinfo) == -1) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* possibly initialise the baseinfo */
|
||||
if (!baseinfo->dn) {
|
||||
if (ltdb_baseinfo_init(module) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
if (ltdb_search_dn1(module, baseinfo_dn, baseinfo) != 1) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
ltdb->tdb_seqnum = tdb_get_seqnum(ltdb->tdb);
|
||||
|
||||
/* if the current internal sequence number is the same as the one
|
||||
in the database then assume the rest of the cache is OK */
|
||||
seq = ldb_msg_find_attr_as_uint64(baseinfo, LTDB_SEQUENCE_NUMBER, 0);
|
||||
if (seq == ltdb->sequence_number) {
|
||||
goto done;
|
||||
}
|
||||
ltdb->sequence_number = seq;
|
||||
|
||||
talloc_free(ltdb->cache->last_attribute.name);
|
||||
memset(<db->cache->last_attribute, 0, sizeof(ltdb->cache->last_attribute));
|
||||
|
||||
ltdb_attributes_unload(module);
|
||||
ltdb_subclasses_unload(module);
|
||||
|
||||
talloc_free(ltdb->cache->indexlist);
|
||||
talloc_free(ltdb->cache->subclasses);
|
||||
|
||||
ltdb->cache->indexlist = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
ltdb->cache->subclasses = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
ltdb->cache->attributes = talloc_zero(ltdb->cache, struct ldb_message);
|
||||
if (ltdb->cache->indexlist == NULL ||
|
||||
ltdb->cache->subclasses == NULL ||
|
||||
ltdb->cache->attributes == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
indexlist_dn = ldb_dn_new(module, module->ldb, LTDB_INDEXLIST);
|
||||
if (indexlist_dn == NULL) goto failed;
|
||||
|
||||
if (ltdb_search_dn1(module, indexlist_dn, ltdb->cache->indexlist) == -1) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (ltdb_attributes_load(module) == -1) {
|
||||
goto failed;
|
||||
}
|
||||
if (ltdb_subclasses_load(module) == -1) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
done:
|
||||
talloc_free(baseinfo);
|
||||
talloc_free(baseinfo_dn);
|
||||
talloc_free(indexlist_dn);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
talloc_free(baseinfo);
|
||||
talloc_free(baseinfo_dn);
|
||||
talloc_free(indexlist_dn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
increase the sequence number to indicate a database change
|
||||
*/
|
||||
int ltdb_increase_sequence_number(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
struct ldb_message *msg;
|
||||
struct ldb_message_element el[2];
|
||||
struct ldb_val val;
|
||||
struct ldb_val val_time;
|
||||
time_t t = time(NULL);
|
||||
char *s = NULL;
|
||||
int ret;
|
||||
|
||||
msg = talloc(ltdb, struct ldb_message);
|
||||
if (msg == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = talloc_asprintf(msg, "%llu", ltdb->sequence_number+1);
|
||||
if (!s) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg->num_elements = ARRAY_SIZE(el);
|
||||
msg->elements = el;
|
||||
msg->dn = ldb_dn_new(msg, module->ldb, LTDB_BASEINFO);
|
||||
if (msg->dn == NULL) {
|
||||
talloc_free(msg);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
el[0].name = talloc_strdup(msg, LTDB_SEQUENCE_NUMBER);
|
||||
if (el[0].name == NULL) {
|
||||
talloc_free(msg);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
el[0].values = &val;
|
||||
el[0].num_values = 1;
|
||||
el[0].flags = LDB_FLAG_MOD_REPLACE;
|
||||
val.data = (uint8_t *)s;
|
||||
val.length = strlen(s);
|
||||
|
||||
el[1].name = talloc_strdup(msg, LTDB_MOD_TIMESTAMP);
|
||||
if (el[1].name == NULL) {
|
||||
talloc_free(msg);
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
el[1].values = &val_time;
|
||||
el[1].num_values = 1;
|
||||
el[1].flags = LDB_FLAG_MOD_REPLACE;
|
||||
|
||||
s = ldb_timestring(msg, t);
|
||||
if (s == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
val_time.data = (uint8_t *)s;
|
||||
val_time.length = strlen(s);
|
||||
|
||||
ret = ltdb_modify_internal(module, msg);
|
||||
|
||||
talloc_free(msg);
|
||||
|
||||
if (ret == 0) {
|
||||
ltdb->sequence_number += 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the attribute flags from the @ATTRIBUTES record
|
||||
for the given attribute
|
||||
*/
|
||||
int ltdb_attribute_flags(struct ldb_module *module, const char *attr_name)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
const struct ldb_message_element *attr_el;
|
||||
int i, j, ret=0;
|
||||
|
||||
if (ltdb->cache->last_attribute.name &&
|
||||
ldb_attr_cmp(ltdb->cache->last_attribute.name, attr_name) == 0) {
|
||||
return ltdb->cache->last_attribute.flags;
|
||||
}
|
||||
|
||||
/* objectclass is a special default case */
|
||||
if (ldb_attr_cmp(attr_name, LTDB_OBJECTCLASS) == 0) {
|
||||
ret = LTDB_FLAG_OBJECTCLASS | LTDB_FLAG_CASE_INSENSITIVE;
|
||||
}
|
||||
|
||||
attr_el = ldb_msg_find_element(ltdb->cache->attributes, attr_name);
|
||||
|
||||
if (!attr_el) {
|
||||
/* check if theres a wildcard attribute */
|
||||
attr_el = ldb_msg_find_element(ltdb->cache->attributes, "*");
|
||||
|
||||
if (!attr_el) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < attr_el->num_values; i++) {
|
||||
for (j=0; ltdb_valid_attr_flags[j].name; j++) {
|
||||
if (strcmp(ltdb_valid_attr_flags[j].name,
|
||||
(char *)attr_el->values[i].data) == 0) {
|
||||
ret |= ltdb_valid_attr_flags[j].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(ltdb->cache->last_attribute.name);
|
||||
|
||||
ltdb->cache->last_attribute.name = talloc_strdup(ltdb->cache, attr_name);
|
||||
ltdb->cache->last_attribute.flags = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ltdb_check_at_attributes_values(const struct ldb_val *value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ltdb_valid_attr_flags[i].name != NULL; i++) {
|
||||
if ((strcmp(ltdb_valid_attr_flags[i].name, (char *)value->data) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb pack/unpack
|
||||
*
|
||||
* Description: pack/unpack routines for ldb messages as key/value blobs
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "ldb/ldb_tdb/ldb_tdb.h"
|
||||
|
||||
/* change this if the data format ever changes */
|
||||
#define LTDB_PACKING_FORMAT 0x26011967
|
||||
|
||||
/* old packing formats */
|
||||
#define LTDB_PACKING_FORMAT_NODN 0x26011966
|
||||
|
||||
/* use a portable integer format */
|
||||
static void put_uint32(uint8_t *p, int ofs, unsigned int val)
|
||||
{
|
||||
p += ofs;
|
||||
p[0] = val&0xFF;
|
||||
p[1] = (val>>8) & 0xFF;
|
||||
p[2] = (val>>16) & 0xFF;
|
||||
p[3] = (val>>24) & 0xFF;
|
||||
}
|
||||
|
||||
static unsigned int pull_uint32(uint8_t *p, int ofs)
|
||||
{
|
||||
p += ofs;
|
||||
return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
|
||||
}
|
||||
|
||||
static int attribute_storable_values(const struct ldb_message_element *el)
|
||||
{
|
||||
if (el->num_values == 0) return 0;
|
||||
|
||||
if (ldb_attr_cmp(el->name, "dn") == 0) return 0;
|
||||
|
||||
if (ldb_attr_cmp(el->name, "distinguishedName") == 0) return 0;
|
||||
|
||||
return el->num_values;
|
||||
}
|
||||
|
||||
/*
|
||||
pack a ldb message into a linear buffer in a TDB_DATA
|
||||
|
||||
note that this routine avoids saving elements with zero values,
|
||||
as these are equivalent to having no element
|
||||
|
||||
caller frees the data buffer after use
|
||||
*/
|
||||
int ltdb_pack_data(struct ldb_module *module,
|
||||
const struct ldb_message *message,
|
||||
struct TDB_DATA *data)
|
||||
{
|
||||
struct ldb_context *ldb = module->ldb;
|
||||
unsigned int i, j, real_elements=0;
|
||||
size_t size;
|
||||
const char *dn;
|
||||
uint8_t *p;
|
||||
size_t len;
|
||||
|
||||
dn = ldb_dn_get_linearized(message->dn);
|
||||
if (dn == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* work out how big it needs to be */
|
||||
size = 8;
|
||||
|
||||
size += 1 + strlen(dn);
|
||||
|
||||
for (i=0;i<message->num_elements;i++) {
|
||||
if (attribute_storable_values(&message->elements[i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_elements++;
|
||||
|
||||
size += 1 + strlen(message->elements[i].name) + 4;
|
||||
for (j=0;j<message->elements[i].num_values;j++) {
|
||||
size += 4 + message->elements[i].values[j].length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate it */
|
||||
data->dptr = talloc_array(ldb, uint8_t, size);
|
||||
if (!data->dptr) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
data->dsize = size;
|
||||
|
||||
p = data->dptr;
|
||||
put_uint32(p, 0, LTDB_PACKING_FORMAT);
|
||||
put_uint32(p, 4, real_elements);
|
||||
p += 8;
|
||||
|
||||
/* the dn needs to be packed so we can be case preserving
|
||||
while hashing on a case folded dn */
|
||||
len = strlen(dn);
|
||||
memcpy(p, dn, len+1);
|
||||
p += len + 1;
|
||||
|
||||
for (i=0;i<message->num_elements;i++) {
|
||||
if (attribute_storable_values(&message->elements[i]) == 0) {
|
||||
continue;
|
||||
}
|
||||
len = strlen(message->elements[i].name);
|
||||
memcpy(p, message->elements[i].name, len+1);
|
||||
p += len + 1;
|
||||
put_uint32(p, 0, message->elements[i].num_values);
|
||||
p += 4;
|
||||
for (j=0;j<message->elements[i].num_values;j++) {
|
||||
put_uint32(p, 0, message->elements[i].values[j].length);
|
||||
memcpy(p+4, message->elements[i].values[j].data,
|
||||
message->elements[i].values[j].length);
|
||||
p[4+message->elements[i].values[j].length] = 0;
|
||||
p += 4 + message->elements[i].values[j].length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
unpack a ldb message from a linear buffer in TDB_DATA
|
||||
|
||||
Free with ltdb_unpack_data_free()
|
||||
*/
|
||||
int ltdb_unpack_data(struct ldb_module *module,
|
||||
const struct TDB_DATA *data,
|
||||
struct ldb_message *message)
|
||||
{
|
||||
struct ldb_context *ldb = module->ldb;
|
||||
uint8_t *p;
|
||||
unsigned int remaining;
|
||||
unsigned int i, j;
|
||||
unsigned format;
|
||||
size_t len;
|
||||
|
||||
message->elements = NULL;
|
||||
|
||||
p = data->dptr;
|
||||
if (data->dsize < 8) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
format = pull_uint32(p, 0);
|
||||
message->num_elements = pull_uint32(p, 4);
|
||||
p += 8;
|
||||
|
||||
remaining = data->dsize - 8;
|
||||
|
||||
switch (format) {
|
||||
case LTDB_PACKING_FORMAT_NODN:
|
||||
message->dn = NULL;
|
||||
break;
|
||||
|
||||
case LTDB_PACKING_FORMAT:
|
||||
len = strnlen((char *)p, remaining);
|
||||
if (len == remaining) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
message->dn = ldb_dn_new(message, ldb, (char *)p);
|
||||
if (message->dn == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
remaining -= len + 1;
|
||||
p += len + 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (message->num_elements == 0) {
|
||||
message->elements = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (message->num_elements > remaining / 6) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
message->elements = talloc_array(message, struct ldb_message_element, message->num_elements);
|
||||
if (!message->elements) {
|
||||
errno = ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
memset(message->elements, 0,
|
||||
message->num_elements * sizeof(struct ldb_message_element));
|
||||
|
||||
for (i=0;i<message->num_elements;i++) {
|
||||
if (remaining < 10) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
len = strnlen((char *)p, remaining-6);
|
||||
if (len == remaining-6) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
message->elements[i].flags = 0;
|
||||
message->elements[i].name = talloc_strndup(message->elements, (char *)p, len);
|
||||
if (message->elements[i].name == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
remaining -= len + 1;
|
||||
p += len + 1;
|
||||
message->elements[i].num_values = pull_uint32(p, 0);
|
||||
message->elements[i].values = NULL;
|
||||
if (message->elements[i].num_values != 0) {
|
||||
message->elements[i].values = talloc_array(message->elements,
|
||||
struct ldb_val,
|
||||
message->elements[i].num_values);
|
||||
if (!message->elements[i].values) {
|
||||
errno = ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
p += 4;
|
||||
remaining -= 4;
|
||||
for (j=0;j<message->elements[i].num_values;j++) {
|
||||
len = pull_uint32(p, 0);
|
||||
if (len > remaining-5) {
|
||||
errno = EIO;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
message->elements[i].values[j].length = len;
|
||||
message->elements[i].values[j].data = talloc_size(message->elements[i].values, len+1);
|
||||
if (message->elements[i].values[j].data == NULL) {
|
||||
errno = ENOMEM;
|
||||
goto failed;
|
||||
}
|
||||
memcpy(message->elements[i].values[j].data, p+4, len);
|
||||
message->elements[i].values[j].data[len] = 0;
|
||||
|
||||
remaining -= len+4+1;
|
||||
p += len+4+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining != 0) {
|
||||
ldb_debug(ldb, LDB_DEBUG_ERROR,
|
||||
"Error: %d bytes unread in ltdb_unpack_data\n", remaining);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
talloc_free(message->elements);
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb search functions
|
||||
*
|
||||
* Description: functions to search ldb+tdb databases
|
||||
*
|
||||
* Author: Andrew Tridgell
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "ldb/ldb_tdb/ldb_tdb.h"
|
||||
|
||||
/*
|
||||
add one element to a message
|
||||
*/
|
||||
static int msg_add_element(struct ldb_message *ret,
|
||||
const struct ldb_message_element *el,
|
||||
int check_duplicates)
|
||||
{
|
||||
unsigned int i;
|
||||
struct ldb_message_element *e2, *elnew;
|
||||
|
||||
if (check_duplicates && ldb_msg_find_element(ret, el->name)) {
|
||||
/* its already there */
|
||||
return 0;
|
||||
}
|
||||
|
||||
e2 = talloc_realloc(ret, ret->elements, struct ldb_message_element, ret->num_elements+1);
|
||||
if (!e2) {
|
||||
return -1;
|
||||
}
|
||||
ret->elements = e2;
|
||||
|
||||
elnew = &e2[ret->num_elements];
|
||||
|
||||
elnew->name = talloc_strdup(ret->elements, el->name);
|
||||
if (!elnew->name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (el->num_values) {
|
||||
elnew->values = talloc_array(ret->elements, struct ldb_val, el->num_values);
|
||||
if (!elnew->values) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
elnew->values = NULL;
|
||||
}
|
||||
|
||||
for (i=0;i<el->num_values;i++) {
|
||||
elnew->values[i] = ldb_val_dup(elnew->values, &el->values[i]);
|
||||
if (elnew->values[i].length != el->values[i].length) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
elnew->num_values = el->num_values;
|
||||
|
||||
ret->num_elements++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
add the special distinguishedName element
|
||||
*/
|
||||
static int msg_add_distinguished_name(struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_message_element el;
|
||||
struct ldb_val val;
|
||||
int ret;
|
||||
|
||||
el.flags = 0;
|
||||
el.name = "distinguishedName";
|
||||
el.num_values = 1;
|
||||
el.values = &val;
|
||||
val.data = (uint8_t *)ldb_dn_alloc_linearized(msg, msg->dn);
|
||||
val.length = strlen((char *)val.data);
|
||||
|
||||
ret = msg_add_element(msg, &el, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
add all elements from one message into another
|
||||
*/
|
||||
static int msg_add_all_elements(struct ldb_module *module, struct ldb_message *ret,
|
||||
const struct ldb_message *msg)
|
||||
{
|
||||
struct ldb_context *ldb = module->ldb;
|
||||
unsigned int i;
|
||||
int check_duplicates = (ret->num_elements != 0);
|
||||
|
||||
if (msg_add_distinguished_name(ret) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i=0;i<msg->num_elements;i++) {
|
||||
const struct ldb_attrib_handler *h;
|
||||
h = ldb_attrib_handler(ldb, msg->elements[i].name);
|
||||
if (h->flags & LDB_ATTR_FLAG_HIDDEN) {
|
||||
continue;
|
||||
}
|
||||
if (msg_add_element(ret, &msg->elements[i],
|
||||
check_duplicates) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
pull the specified list of attributes from a message
|
||||
*/
|
||||
static struct ldb_message *ltdb_pull_attrs(struct ldb_module *module,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const struct ldb_message *msg,
|
||||
const char * const *attrs)
|
||||
{
|
||||
struct ldb_message *ret;
|
||||
int i;
|
||||
|
||||
ret = talloc(mem_ctx, struct ldb_message);
|
||||
if (!ret) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->dn = ldb_dn_copy(ret, msg->dn);
|
||||
if (!ret->dn) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->num_elements = 0;
|
||||
ret->elements = NULL;
|
||||
|
||||
if (!attrs) {
|
||||
if (msg_add_all_elements(module, ret, msg) != 0) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i=0;attrs[i];i++) {
|
||||
struct ldb_message_element *el;
|
||||
|
||||
if (strcmp(attrs[i], "*") == 0) {
|
||||
if (msg_add_all_elements(module, ret, msg) != 0) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
|
||||
if (msg_add_distinguished_name(ret) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
el = ldb_msg_find_element(msg, attrs[i]);
|
||||
if (!el) {
|
||||
continue;
|
||||
}
|
||||
if (msg_add_element(ret, el, 1) != 0) {
|
||||
talloc_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
search the database for a single simple dn, returning all attributes
|
||||
in a single message
|
||||
|
||||
return 1 on success, 0 on record-not-found and -1 on error
|
||||
*/
|
||||
int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_message *msg)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
int ret;
|
||||
TDB_DATA tdb_key, tdb_data;
|
||||
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
/* form the key */
|
||||
tdb_key = ltdb_key(module, dn);
|
||||
if (!tdb_key.dptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tdb_data = tdb_fetch(ltdb->tdb, tdb_key);
|
||||
talloc_free(tdb_key.dptr);
|
||||
if (!tdb_data.dptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
msg->num_elements = 0;
|
||||
msg->elements = NULL;
|
||||
|
||||
ret = ltdb_unpack_data(module, &tdb_data, msg);
|
||||
free(tdb_data.dptr);
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!msg->dn) {
|
||||
msg->dn = ldb_dn_copy(msg, dn);
|
||||
}
|
||||
if (!msg->dn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
lock the database for read - use by ltdb_search
|
||||
*/
|
||||
static int ltdb_lock_read(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
return tdb_lockall_read(ltdb->tdb);
|
||||
}
|
||||
|
||||
/*
|
||||
unlock the database after a ltdb_lock_read()
|
||||
*/
|
||||
static int ltdb_unlock_read(struct ldb_module *module)
|
||||
{
|
||||
struct ltdb_private *ltdb = module->private_data;
|
||||
return tdb_unlockall_read(ltdb->tdb);
|
||||
}
|
||||
|
||||
/*
|
||||
add a set of attributes from a record to a set of results
|
||||
return 0 on success, -1 on failure
|
||||
*/
|
||||
int ltdb_add_attr_results(struct ldb_module *module,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct ldb_message *msg,
|
||||
const char * const attrs[],
|
||||
unsigned int *count,
|
||||
struct ldb_message ***res)
|
||||
{
|
||||
struct ldb_message *msg2;
|
||||
struct ldb_message **res2;
|
||||
|
||||
/* pull the attributes that the user wants */
|
||||
msg2 = ltdb_pull_attrs(module, mem_ctx, msg, attrs);
|
||||
if (!msg2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* add to the results list */
|
||||
res2 = talloc_realloc(mem_ctx, *res, struct ldb_message *, (*count)+2);
|
||||
if (!res2) {
|
||||
talloc_free(msg2);
|
||||
return -1;
|
||||
}
|
||||
|
||||
(*res) = res2;
|
||||
|
||||
(*res)[*count] = talloc_move(*res, &msg2);
|
||||
(*res)[(*count)+1] = NULL;
|
||||
(*count)++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
filter the specified list of attributes from a message
|
||||
removing not requested attrs.
|
||||
*/
|
||||
int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs)
|
||||
{
|
||||
int i, keep_all = 0;
|
||||
|
||||
if (attrs) {
|
||||
/* check for special attrs */
|
||||
for (i = 0; attrs[i]; i++) {
|
||||
if (strcmp(attrs[i], "*") == 0) {
|
||||
keep_all = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ldb_attr_cmp(attrs[i], "distinguishedName") == 0) {
|
||||
if (msg_add_distinguished_name(msg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
keep_all = 1;
|
||||
}
|
||||
|
||||
if (keep_all) {
|
||||
if (msg_add_distinguished_name(msg) != 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
int j, found;
|
||||
|
||||
for (j = 0, found = 0; attrs[j]; j++) {
|
||||
if (ldb_attr_cmp(msg->elements[i].name, attrs[j]) == 0) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
ldb_msg_remove_attr(msg, msg->elements[i].name);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
search function for a non-indexed search
|
||||
*/
|
||||
static int search_func(struct tdb_context *tdb, TDB_DATA key, TDB_DATA data, void *state)
|
||||
{
|
||||
struct ldb_handle *handle = talloc_get_type(state, struct ldb_handle);
|
||||
struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
|
||||
struct ldb_reply *ares = NULL;
|
||||
int ret;
|
||||
|
||||
if (key.dsize < 4 ||
|
||||
strncmp((char *)key.dptr, "DN=", 3) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ares->message = ldb_msg_new(ares);
|
||||
if (!ares->message) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
talloc_free(ares);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* unpack the record */
|
||||
ret = ltdb_unpack_data(ac->module, &data, ares->message);
|
||||
if (ret == -1) {
|
||||
talloc_free(ares);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ares->message->dn) {
|
||||
ares->message->dn = ldb_dn_new(ares->message, ac->module->ldb, (char *)key.dptr + 3);
|
||||
if (ares->message->dn == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
talloc_free(ares);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* see if it matches the given expression */
|
||||
if (!ldb_match_msg(ac->module->ldb, ares->message, ac->tree,
|
||||
ac->base, ac->scope)) {
|
||||
talloc_free(ares);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* filter the attributes that the user wants */
|
||||
ret = ltdb_filter_attrs(ares->message, ac->attrs);
|
||||
|
||||
if (ret == -1) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
talloc_free(ares);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_ENTRY;
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = ac->callback(ac->module->ldb, ac->context, ares);
|
||||
|
||||
if (handle->status != LDB_SUCCESS) {
|
||||
/* don't try to free ares here, the callback is in charge of that */
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
search the database with a LDAP-like expression.
|
||||
this is the "full search" non-indexed variant
|
||||
*/
|
||||
static int ltdb_search_full(struct ldb_handle *handle)
|
||||
{
|
||||
struct ltdb_context *ac = talloc_get_type(handle->private_data, struct ltdb_context);
|
||||
struct ltdb_private *ltdb = talloc_get_type(ac->module->private_data, struct ltdb_private);
|
||||
int ret;
|
||||
|
||||
ret = tdb_traverse_read(ltdb->tdb, search_func, handle);
|
||||
|
||||
if (ret == -1) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
search the database with a LDAP-like expression.
|
||||
choses a search method
|
||||
*/
|
||||
int ltdb_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ltdb_private *ltdb = talloc_get_type(module->private_data, struct ltdb_private);
|
||||
struct ltdb_context *ltdb_ac;
|
||||
struct ldb_reply *ares;
|
||||
int ret;
|
||||
|
||||
if ((( ! ldb_dn_is_valid(req->op.search.base)) || ldb_dn_is_null(req->op.search.base)) &&
|
||||
(req->op.search.scope == LDB_SCOPE_BASE || req->op.search.scope == LDB_SCOPE_ONELEVEL))
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
if (ltdb_lock_read(module) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (ltdb_cache_load(module) != 0) {
|
||||
ltdb_unlock_read(module);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (req->op.search.tree == NULL) {
|
||||
ltdb_unlock_read(module);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->handle = init_ltdb_handle(ltdb, module, req);
|
||||
if (req->handle == NULL) {
|
||||
ltdb_unlock_read(module);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ltdb_ac = talloc_get_type(req->handle->private_data, struct ltdb_context);
|
||||
|
||||
ltdb_ac->tree = req->op.search.tree;
|
||||
ltdb_ac->scope = req->op.search.scope;
|
||||
ltdb_ac->base = req->op.search.base;
|
||||
ltdb_ac->attrs = req->op.search.attrs;
|
||||
|
||||
ret = ltdb_search_indexed(req->handle);
|
||||
if (ret == -1) {
|
||||
ret = ltdb_search_full(req->handle);
|
||||
}
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, "Indexed and full searches both failed!\n");
|
||||
req->handle->state = LDB_ASYNC_DONE;
|
||||
req->handle->status = ret;
|
||||
}
|
||||
|
||||
/* Finally send an LDB_REPLY_DONE packet when searching is finished */
|
||||
|
||||
ares = talloc_zero(req, struct ldb_reply);
|
||||
if (!ares) {
|
||||
ltdb_unlock_read(module);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->handle->state = LDB_ASYNC_DONE;
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
|
||||
ret = req->callback(module->ldb, req->context, ares);
|
||||
req->handle->status = ret;
|
||||
|
||||
ltdb_unlock_read(module);
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,129 @@
|
||||
|
||||
#ifdef _SAMBA_BUILD_
|
||||
#include "system/filesys.h"
|
||||
#endif
|
||||
|
||||
#if (_SAMBA_BUILD_ >= 4)
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#elif defined(_SAMBA_BUILD_)
|
||||
#include "tdb/include/tdb.h"
|
||||
#else
|
||||
#include "tdb.h"
|
||||
#endif
|
||||
|
||||
/* this private structure is used by the ltdb backend in the
|
||||
ldb_context */
|
||||
struct ltdb_private {
|
||||
TDB_CONTEXT *tdb;
|
||||
unsigned int connect_flags;
|
||||
|
||||
/* a double is used for portability and ease of string
|
||||
handling. It has plenty of digits of precision */
|
||||
unsigned long long sequence_number;
|
||||
|
||||
/* the low level tdb seqnum - used to avoid loading BASEINFO when
|
||||
possible */
|
||||
int tdb_seqnum;
|
||||
|
||||
struct ltdb_cache {
|
||||
struct ldb_message *indexlist;
|
||||
struct ldb_message *attributes;
|
||||
struct ldb_message *subclasses;
|
||||
|
||||
struct {
|
||||
char *name;
|
||||
int flags;
|
||||
} last_attribute;
|
||||
} *cache;
|
||||
};
|
||||
|
||||
/*
|
||||
the async local context
|
||||
holds also internal search state during a full db search
|
||||
*/
|
||||
struct ltdb_context {
|
||||
struct ldb_module *module;
|
||||
|
||||
/* search stuff */
|
||||
const struct ldb_parse_tree *tree;
|
||||
struct ldb_dn *base;
|
||||
enum ldb_scope scope;
|
||||
const char * const *attrs;
|
||||
|
||||
/* async stuff */
|
||||
void *context;
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
};
|
||||
|
||||
/* special record types */
|
||||
#define LTDB_INDEX "@INDEX"
|
||||
#define LTDB_INDEXLIST "@INDEXLIST"
|
||||
#define LTDB_IDX "@IDX"
|
||||
#define LTDB_IDXATTR "@IDXATTR"
|
||||
#define LTDB_BASEINFO "@BASEINFO"
|
||||
#define LTDB_ATTRIBUTES "@ATTRIBUTES"
|
||||
#define LTDB_SUBCLASSES "@SUBCLASSES"
|
||||
|
||||
/* special attribute types */
|
||||
#define LTDB_SEQUENCE_NUMBER "sequenceNumber"
|
||||
#define LTDB_MOD_TIMESTAMP "whenChanged"
|
||||
#define LTDB_OBJECTCLASS "objectClass"
|
||||
|
||||
/* The following definitions come from lib/ldb/ldb_tdb/ldb_cache.c */
|
||||
|
||||
int ltdb_cache_reload(struct ldb_module *module);
|
||||
int ltdb_cache_load(struct ldb_module *module);
|
||||
int ltdb_increase_sequence_number(struct ldb_module *module);
|
||||
int ltdb_check_at_attributes_values(const struct ldb_val *value);
|
||||
|
||||
/* The following definitions come from lib/ldb/ldb_tdb/ldb_index.c */
|
||||
|
||||
struct ldb_parse_tree;
|
||||
|
||||
int ltdb_search_indexed(struct ldb_handle *handle);
|
||||
int ltdb_index_add(struct ldb_module *module, const struct ldb_message *msg);
|
||||
int ltdb_index_del(struct ldb_module *module, const struct ldb_message *msg);
|
||||
int ltdb_reindex(struct ldb_module *module);
|
||||
|
||||
/* The following definitions come from lib/ldb/ldb_tdb/ldb_pack.c */
|
||||
|
||||
int ltdb_pack_data(struct ldb_module *module,
|
||||
const struct ldb_message *message,
|
||||
struct TDB_DATA *data);
|
||||
void ltdb_unpack_data_free(struct ldb_module *module,
|
||||
struct ldb_message *message);
|
||||
int ltdb_unpack_data(struct ldb_module *module,
|
||||
const struct TDB_DATA *data,
|
||||
struct ldb_message *message);
|
||||
|
||||
/* The following definitions come from lib/ldb/ldb_tdb/ldb_search.c */
|
||||
|
||||
int ltdb_has_wildcard(struct ldb_module *module, const char *attr_name,
|
||||
const struct ldb_val *val);
|
||||
void ltdb_search_dn1_free(struct ldb_module *module, struct ldb_message *msg);
|
||||
int ltdb_search_dn1(struct ldb_module *module, struct ldb_dn *dn, struct ldb_message *msg);
|
||||
int ltdb_add_attr_results(struct ldb_module *module,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct ldb_message *msg,
|
||||
const char * const attrs[],
|
||||
unsigned int *count,
|
||||
struct ldb_message ***res);
|
||||
int ltdb_filter_attrs(struct ldb_message *msg, const char * const *attrs);
|
||||
int ltdb_search(struct ldb_module *module, struct ldb_request *req);
|
||||
|
||||
/* The following definitions come from lib/ldb/ldb_tdb/ldb_tdb.c */
|
||||
struct ldb_handle *init_ltdb_handle(struct ltdb_private *ltdb, struct ldb_module *module,
|
||||
struct ldb_request *req);
|
||||
struct TDB_DATA ltdb_key(struct ldb_module *module, struct ldb_dn *dn);
|
||||
int ltdb_store(struct ldb_module *module, const struct ldb_message *msg, int flgs);
|
||||
int ltdb_delete_noindex(struct ldb_module *module, struct ldb_dn *dn);
|
||||
int ltdb_modify_internal(struct ldb_module *module, const struct ldb_message *msg);
|
||||
|
||||
int ltdb_index_del_value(struct ldb_module *module, const char *dn,
|
||||
struct ldb_message_element *el, int v_idx);
|
||||
|
||||
struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
|
||||
const char *path, int hash_size, int tdb_flags,
|
||||
int open_flags, mode_t mode,
|
||||
struct ldb_context *ldb);
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "ldb/ldb_tdb/ldb_tdb.h"
|
||||
|
||||
/*
|
||||
the purpose of this code is to work around the braindead posix locking
|
||||
rules, to allow us to have a ldb open more than once while allowing
|
||||
locking to work
|
||||
*/
|
||||
|
||||
struct ltdb_wrap {
|
||||
struct ltdb_wrap *next, *prev;
|
||||
struct tdb_context *tdb;
|
||||
dev_t device;
|
||||
ino_t inode;
|
||||
};
|
||||
|
||||
static struct ltdb_wrap *tdb_list;
|
||||
|
||||
/* destroy the last connection to a tdb */
|
||||
static int ltdb_wrap_destructor(struct ltdb_wrap *w)
|
||||
{
|
||||
tdb_close(w->tdb);
|
||||
if (w->next) {
|
||||
w->next->prev = w->prev;
|
||||
}
|
||||
if (w->prev) {
|
||||
w->prev->next = w->next;
|
||||
}
|
||||
if (w == tdb_list) {
|
||||
tdb_list = w->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(_SAMBA_BUILD_) && (_SAMBA_BUILD_ <= 3)
|
||||
static void ltdb_log_fn(struct tdb_context *tdb, int level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
static void ltdb_log_fn(struct tdb_context *tdb, int level, const char *fmt, ...)
|
||||
{
|
||||
/* until we merge the tdb debug changes into samba3, we don't know
|
||||
how serious the error is, and we can't go via the ldb loggin code */
|
||||
va_list ap;
|
||||
const char *name = tdb_name(tdb);
|
||||
char *message;
|
||||
va_start(ap, fmt);
|
||||
message = talloc_vasprintf(NULL, fmt, ap);
|
||||
va_end(ap);
|
||||
DEBUG(3, ("ltdb: tdb(%s): %s", name, message));
|
||||
talloc_free(message);
|
||||
}
|
||||
#else
|
||||
static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4);
|
||||
static void ltdb_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
const char *name = tdb_name(tdb);
|
||||
struct ldb_context *ldb = talloc_get_type(tdb_get_logging_private(tdb), struct ldb_context);
|
||||
enum ldb_debug_level ldb_level;
|
||||
char *message;
|
||||
va_start(ap, fmt);
|
||||
message = talloc_vasprintf(ldb, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
switch (level) {
|
||||
case TDB_DEBUG_FATAL:
|
||||
ldb_level = LDB_DEBUG_FATAL;
|
||||
break;
|
||||
case TDB_DEBUG_ERROR:
|
||||
ldb_level = LDB_DEBUG_ERROR;
|
||||
break;
|
||||
case TDB_DEBUG_WARNING:
|
||||
ldb_level = LDB_DEBUG_WARNING;
|
||||
break;
|
||||
case TDB_DEBUG_TRACE:
|
||||
ldb_level = LDB_DEBUG_TRACE;
|
||||
break;
|
||||
default:
|
||||
ldb_level = LDB_DEBUG_FATAL;
|
||||
}
|
||||
|
||||
ldb_debug(ldb, ldb_level, "ltdb: tdb(%s): %s", name, message);
|
||||
talloc_free(message);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
wrapped connection to a tdb database. The caller should _not_ free
|
||||
this as it is not a talloc structure (as tdb does not use talloc
|
||||
yet). It will auto-close when the caller frees the mem_ctx that is
|
||||
passed to this call
|
||||
*/
|
||||
struct tdb_context *ltdb_wrap_open(TALLOC_CTX *mem_ctx,
|
||||
const char *path, int hash_size,
|
||||
int tdb_flags,
|
||||
int open_flags, mode_t mode,
|
||||
struct ldb_context *ldb)
|
||||
{
|
||||
struct ltdb_wrap *w;
|
||||
struct stat st;
|
||||
#if defined(_SAMBA_BUILD_) && (_SAMBA_BUILD_ <= 3)
|
||||
tdb_log_func log_ctx_p = ltdb_log_fn;
|
||||
#else
|
||||
struct tdb_logging_context log_ctx;
|
||||
const struct tdb_logging_context *log_ctx_p = &log_ctx;
|
||||
log_ctx.log_fn = ltdb_log_fn;
|
||||
log_ctx.log_private = ldb;
|
||||
#endif
|
||||
|
||||
if (stat(path, &st) == 0) {
|
||||
for (w=tdb_list;w;w=w->next) {
|
||||
if (st.st_dev == w->device && st.st_ino == w->inode) {
|
||||
if (!talloc_reference(mem_ctx, w)) {
|
||||
return NULL;
|
||||
}
|
||||
return w->tdb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w = talloc(mem_ctx, struct ltdb_wrap);
|
||||
if (w == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
w->tdb = tdb_open_ex(path, hash_size, tdb_flags, open_flags, mode, log_ctx_p, NULL);
|
||||
if (w->tdb == NULL) {
|
||||
talloc_free(w);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fstat(tdb_fd(w->tdb), &st) != 0) {
|
||||
tdb_close(w->tdb);
|
||||
talloc_free(w);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
w->device = st.st_dev;
|
||||
w->inode = st.st_ino;
|
||||
|
||||
talloc_set_destructor(w, ltdb_wrap_destructor);
|
||||
|
||||
w->next = tdb_list;
|
||||
w->prev = NULL;
|
||||
if (tdb_list) {
|
||||
tdb_list->prev = w;
|
||||
}
|
||||
tdb_list = w;
|
||||
|
||||
return w->tdb;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
SMB_ENABLE(ldb_sqlite3,$with_sqlite3_support)
|
||||
|
||||
AC_MSG_CHECKING([for Python])
|
||||
|
||||
PYTHON=
|
||||
|
||||
AC_ARG_WITH(python,
|
||||
[ --with-python=PYTHONNAME build Python libraries],
|
||||
[ case "${withval-python}" in
|
||||
yes)
|
||||
PYTHON=python
|
||||
;;
|
||||
no)
|
||||
PYTHON=
|
||||
;;
|
||||
*)
|
||||
PYTHON=${withval-python}
|
||||
;;
|
||||
esac ])
|
||||
|
||||
if test x"$PYTHON" != "x"; then
|
||||
incdir=`python -c 'import sys; print "%s/include/python%d.%d" % (sys.prefix, sys.version_info[[0]], sys.version_info[[1]])'`
|
||||
CPPFLAGS="$CPPFLAGS -I $incdir"
|
||||
fi
|
||||
|
||||
if test x"$PYTHON" != "x"; then
|
||||
AC_MSG_RESULT([${withval-python}])
|
||||
else
|
||||
AC_MSG_RESULT(no)
|
||||
SMB_ENABLE(swig_ldb, NO)
|
||||
fi
|
||||
|
||||
AC_SUBST(PYTHON)
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
|
||||
\mainpage ldb
|
||||
|
||||
\section Overview
|
||||
|
||||
ldb is a LDAP-like embedded database. It is not at all LDAP standards
|
||||
compliant, so if you want a standards compliant database then please
|
||||
see the excellent <a href="http://www.openldap.org/">OpenLDAP</a>
|
||||
project.<p>
|
||||
|
||||
What ldb does is provide a fast database with an LDAP-like API
|
||||
designed to be used within an application. In some ways it can be seen
|
||||
as a intermediate solution between key-value pair databases and a real
|
||||
LDAP database.<p>
|
||||
|
||||
ldb is the database engine used in Samba4.
|
||||
|
||||
\section Features
|
||||
|
||||
The main features that separate ldb from other solutions are:
|
||||
- Safe multi-reader, multi-writer, using byte range locking
|
||||
- LDAP-like API
|
||||
- fast operation
|
||||
- choice of local tdb, local sqlite3 or remote LDAP backends
|
||||
- integration with <a href="http://talloc.samba.org">talloc</a>
|
||||
- schema-less operation, for trivial setup
|
||||
- modules for extensions (such as schema support)
|
||||
- easy setup of indexes and attribute properties
|
||||
- ldbedit tool for database editing (reminiscent of 'vipw')
|
||||
- ldif for import/export
|
||||
|
||||
\section Documentation
|
||||
|
||||
ldb has limited programmer and administrator documentation:
|
||||
- a list of <a href="globals_func.html">functions</a>
|
||||
- a list of <a href="examples.html">examples</a>
|
||||
- a list of <a href="annotated.html">data structures</a>
|
||||
- a list of <a href="globals_defs.html">constants</a>
|
||||
|
||||
If you need more information than is presented in this document, you
|
||||
may wish to look at the source code, especially the source code in the
|
||||
<a href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/tools/">tools directory</a>.
|
||||
|
||||
ldb makes use of the LDAP Data Interchange Format (LDIF), which is
|
||||
documented in <a href="http://www.ietf.org/rfc/rfc2849.txt">RFC
|
||||
2849</a>.
|
||||
|
||||
\section Support
|
||||
|
||||
ldb does not currently have its own mailing list or bug tracking
|
||||
system. For now, please use the <a
|
||||
href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
|
||||
mailing list, and the <a href="http://bugzilla.samba.org/">Samba
|
||||
bugzilla</a> bug tracking system.
|
||||
|
||||
\section Download
|
||||
|
||||
You can download the latest release either via rsync or anonymous
|
||||
svn. To fetch via svn use the following commands:
|
||||
|
||||
\verbatim
|
||||
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
|
||||
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
|
||||
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
|
||||
\endverbatim
|
||||
|
||||
To fetch via rsync use these commands:
|
||||
|
||||
\verbatim
|
||||
rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/ldb .
|
||||
rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/tdb .
|
||||
rsync -Pavz samba.org::ftp/unpacked/samba4/source/lib/talloc .
|
||||
\endverbatim
|
||||
|
||||
\section Credits
|
||||
|
||||
ldb is another product of the prolific <a href="http://samba.org/~tridge/">Andrew Tridgell</a>.
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ad2oLschema.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ad2oLschema</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ad2oLschema</refname>
|
||||
<refpurpose>Converts AC-like LDAP schemas to OpenLDAP
|
||||
compatible schema files</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ad2oLschema</command>
|
||||
<arg choice="opt">-I INPUT-FILE</arg>
|
||||
<arg choice="opt">-O OUTPUT-FILE</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ad2oLschema is a simple tool that converts AD-like LDIF
|
||||
schema files into OpenLDAP schema files.</para>
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-H url</term>
|
||||
<listitem><para>URL to an LDB or LDAP server with an AD schema to read. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-I input-file</term> <listitem><para>AD schema
|
||||
to read. If neither this nor -H is specified, the
|
||||
schema file will be read from standard input.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-O output-file</term>
|
||||
<listitem><para>File to write OpenLDAP version of schema to.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
ad2oLschema was written by <ulink
|
||||
url="http://samba.org/~abartlet/">Andrew Bartlett</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,262 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldb.3">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldb</refentrytitle>
|
||||
<manvolnum>3</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldb</refname>
|
||||
<refclass>The Samba Project</refclass>
|
||||
<refpurpose>A light-weight database library</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<synopsis>#include <ldb.h></synopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>description</title>
|
||||
|
||||
<para>
|
||||
ldb is a light weight embedded database library and API. With a
|
||||
programming interface that is very similar to LDAP, ldb can store its
|
||||
data either in a tdb(3) database or in a real LDAP database.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
When used with the tdb backend ldb does not require any database
|
||||
daemon. Instead, ldb function calls are processed immediately by the
|
||||
ldb library, which does IO directly on the database, while allowing
|
||||
multiple readers/writers using operating system byte range locks. This
|
||||
leads to an API with very low overheads, often resulting in speeds of
|
||||
more than 10x what can be achieved with a more traditional LDAP
|
||||
architecture.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In a taxonomy of databases ldb would sit half way between key/value
|
||||
pair databases (such as berkley db or tdb) and a full LDAP
|
||||
database. With a structured attribute oriented API like LDAP and good
|
||||
indexing capabilities, ldb can be used for quite sophisticated
|
||||
applications that need a light weight database, without the
|
||||
administrative overhead of a full LDAP installation.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Included with ldb are a number of useful command line tools for
|
||||
manipulating a ldb database. These tools are similar in style to the
|
||||
equivalent ldap command line tools.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In its default mode of operation with a tdb backend, ldb can also be
|
||||
seen as a "schema-less LDAP". By default ldb does not require a
|
||||
schema, which greatly reduces the complexity of getting started with
|
||||
ldb databases. As the complexity of you application grows you can take
|
||||
advantage of some of the optional schema-like attributes that ldb
|
||||
offers, or you can migrate to using the full LDAP api while keeping
|
||||
your exiting ldb code.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you are new to ldb, then I suggest starting with the manual pages
|
||||
for ldbsearch(1) and ldbedit(1), and experimenting with a local
|
||||
database. Then I suggest you look at the ldb_connect(3) and
|
||||
ldb_search(3) manual pages.
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>TOOLS</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
<application>ldbsearch(1)</application>
|
||||
- command line ldb search utility
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<application>ldbedit(1)</application>
|
||||
- edit all or part of a ldb database using your favourite editor
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<application>ldbadd(1)</application>
|
||||
- add records to a ldb database using LDIF formatted input
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<application>ldbdel(1)</application>
|
||||
- delete records from a ldb database
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<application>ldbmodify(1)</application>
|
||||
- modify records in a ldb database using LDIF formatted input
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>FUNCTIONS</title>
|
||||
|
||||
<itemizedlist>
|
||||
<listitem><para>
|
||||
<function>ldb_connect(3)</function>
|
||||
- connect to a ldb backend
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_search(3)</function>
|
||||
- perform a database search
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_add(3)</function>
|
||||
- add a record to the database
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_delete(3)</function>
|
||||
- delete a record from the database
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_modify(3)</function>
|
||||
- modify a record in the database
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_errstring(3)</function>
|
||||
- retrieve extended error information from the last operation
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_write(3)</function>
|
||||
- write a LDIF formatted message
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_write_file(3)</function>
|
||||
- write a LDIF formatted message to a file
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_read(3)</function>
|
||||
- read a LDIF formatted message
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_read_free(3)</function>
|
||||
- free the result of a ldb_ldif_read()
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_read_file(3)</function>
|
||||
- read a LDIF message from a file
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_ldif_read_string(3)</function>
|
||||
- read a LDIF message from a string
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_element(3)</function>
|
||||
- find an element in a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_val_equal_exact(3)</function>
|
||||
- compare two ldb_val structures
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_val(3)</function>
|
||||
- find an element by value
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_add_empty(3)</function>
|
||||
- add an empty message element to a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_add(3)</function>
|
||||
- add a non-empty message element to a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_element_compare(3)</function>
|
||||
- compare two ldb_message_element structures
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_int(3)</function>
|
||||
- return an integer value from a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_uint(3)</function>
|
||||
- return an unsigned integer value from a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_double(3)</function>
|
||||
- return a double value from a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_msg_find_string(3)</function>
|
||||
- return a string value from a ldb_message
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_set_alloc(3)</function>
|
||||
- set the memory allocation function to be used by ldb
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_set_debug(3)</function>
|
||||
- set a debug handler to be used by ldb
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>
|
||||
<function>ldb_set_debug_stderr(3)</function>
|
||||
- set a debug handler for stderr output
|
||||
</para></listitem>
|
||||
</itemizedlist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Author</title>
|
||||
|
||||
<para>
|
||||
ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
ldb is released under the GNU Lesser General Public License version 2
|
||||
or later. Please see the file COPYING for license details.
|
||||
</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbadd.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbadd</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbadd</refname>
|
||||
<refpurpose>Command-line utility for adding records to an LDB</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbadd</command>
|
||||
<arg choice="opt">-h</arg>
|
||||
<arg choice="opt">-H LDB-URL</arg>
|
||||
<arg choice="opt">ldif-file1</arg>
|
||||
<arg choice="opt">ldif-file2</arg>
|
||||
<arg choice="opt">...</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ldbadd adds records to an ldb(7) database. It reads
|
||||
the ldif(5) files specified on the command line and adds
|
||||
the records from these files to the LDB database, which is specified
|
||||
by the -H option or the LDB_URL environment variable.
|
||||
</para>
|
||||
|
||||
<para>If - is specified as a ldb file, the ldif input is read from
|
||||
standard input.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-h</term>
|
||||
<listitem><para>
|
||||
Show list of available options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem><para>
|
||||
LDB URL to connect to. See ldb(7) for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry><term>LDB_URL</term>
|
||||
<listitem><para>LDB URL to connect to (can be overrided by using the
|
||||
-H command-line option.)</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>This manpage was written by Jelmer Vernooij.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,105 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbdel.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbdel</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbdel</refname>
|
||||
<refpurpose>Command-line program for deleting LDB records</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbdel</command>
|
||||
<arg choice="opt">-h</arg>
|
||||
<arg choice="opt">-H LDB-URL</arg>
|
||||
<arg choice="opt">dn</arg>
|
||||
<arg choice="opt">...</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ldbdel deletes records from an ldb(7) database.
|
||||
It deletes the records identified by the dn's specified
|
||||
on the command-line. </para>
|
||||
|
||||
<para>ldbdel uses either the database that is specified with
|
||||
the -H option or the database specified by the LDB_URL environment
|
||||
variable.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-h</term>
|
||||
<listitem><para>
|
||||
Show list of available options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem><para>
|
||||
LDB URL to connect to. See ldb(7) for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry><term>LDB_URL</term>
|
||||
<listitem><para>LDB URL to connect to (can be overrided by using the
|
||||
-H command-line option.)</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify, ldbadd, ldif(5)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>ldbdel was written by Andrew Tridgell.</para>
|
||||
|
||||
<para>This manpage was written by Jelmer Vernooij.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbedit.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbedit</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbedit</refname>
|
||||
<refpurpose>Edit LDB databases using your preferred editor</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbedit</command>
|
||||
<arg choice="opt">-?</arg>
|
||||
<arg choice="opt">--usage</arg>
|
||||
<arg choice="opt">-s base|one|sub</arg>
|
||||
<arg choice="opt">-b basedn</arg>
|
||||
<arg choice="opt">-a</arg>
|
||||
<arg choice="opt">-e editor</arg>
|
||||
<arg choice="opt">-H LDB-URL</arg>
|
||||
<arg choice="opt">expression</arg>
|
||||
<arg rep="repeat" choice="opt">attributes</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ldbedit is a utility that allows you to edit LDB entries (in
|
||||
tdb files, sqlite files or LDAP servers) using your preferred editor.
|
||||
ldbedit generates an LDIF file based on your query, allows you to edit
|
||||
the LDIF, and then merges that LDIF back into the LDB backend.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-?</term>
|
||||
<term>--help</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Show list of available options, and a phrase describing what that option
|
||||
does.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>--usage</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Show list of available options. This is similar to the help option,
|
||||
however it does not provide any description, and is hence shorter.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem>
|
||||
<para>
|
||||
LDB URL to connect to. For a tdb database,
|
||||
this will be of the form
|
||||
tdb://<replaceable>filename</replaceable>.
|
||||
For a LDAP connection over unix domain
|
||||
sockets, this will be of the form
|
||||
ldapi://<replaceable>socket</replaceable>. For
|
||||
a (potentially remote) LDAP connection over
|
||||
TCP, this will be of the form
|
||||
ldap://<replaceable>hostname</replaceable>. For
|
||||
an SQLite database, this will be of the form
|
||||
sqlite://<replaceable>filename</replaceable>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-s one|sub|base</term>
|
||||
<listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-a</term>
|
||||
<term>-all</term>
|
||||
<listitem>
|
||||
<para>Edit all records. This allows you to
|
||||
apply the same change to a number of records
|
||||
at once. You probably want to combine this
|
||||
with an expression of the form
|
||||
"objectclass=*".
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-e editor</term>
|
||||
<term>--editor editor</term>
|
||||
<listitem>
|
||||
<para>Specify the editor that should be used (overrides
|
||||
the VISUAL and EDITOR environment
|
||||
variables). If this option is not used, and
|
||||
neither VISUAL nor EDITOR environment variables
|
||||
are set, then the vi editor will be used.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-b basedn</term>
|
||||
<listitem><para>Specify Base Distinguished Name to use.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-v</term>
|
||||
<term>--verbose</term>
|
||||
<listitem>
|
||||
<para>Make ldbedit more verbose about the
|
||||
operations that are being performed. Without
|
||||
this option, ldbedit will only provide a
|
||||
summary change line.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>LDB_URL</term>
|
||||
<listitem>
|
||||
<para>LDB URL to connect to. This can be
|
||||
overridden by using the -H command-line option.)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
<term>VISUAL and EDITOR</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Environment variables used to determine what
|
||||
editor to use. VISUAL takes precedence over
|
||||
EDITOR, and both are overridden by the
|
||||
-e command-line option.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify(1), ldbdel(1), ldif(5), vi(1)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para>
|
||||
ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This manpage was written by Jelmer Vernooij and updated
|
||||
by Brad Hards.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,93 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbmodify.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbmodify</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbmodify</refname>
|
||||
<refpurpose>Modify records in a LDB database</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbmodify</command>
|
||||
<arg choice="opt">-H LDB-URL</arg>
|
||||
<arg choice="opt">ldif-file</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>
|
||||
ldbmodify changes, adds and deletes records in a LDB database.
|
||||
The changes that should be made to the LDB database are read from
|
||||
the specified LDIF-file. If - is specified as the filename, input is read from stdin.
|
||||
</para>
|
||||
|
||||
<para>For now, see ldapmodify(1) for details on the LDIF file format.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem><para>
|
||||
LDB URL to connect to. See ldb(7) for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry><term>LDB_URL</term>
|
||||
<listitem><para>LDB URL to connect to (can be overrided by using the
|
||||
-H command-line option.)</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbedit</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>This manpage was written by Jelmer Vernooij.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,107 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbrename.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbrename</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbrename</refname>
|
||||
<refpurpose>Edit LDB databases using your favorite editor</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbrename</command>
|
||||
<arg choice="opt">-h</arg>
|
||||
<arg choice="opt">-o options</arg>
|
||||
<arg choice="req">olddn</arg>
|
||||
<arg choice="req">newdb</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ldbrename is a utility that allows you to rename trees in
|
||||
an LDB database based by DN. This utility takes
|
||||
two arguments: the original
|
||||
DN name of the top element and the DN to change it to.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-h</term>
|
||||
<listitem><para>
|
||||
Show list of available options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem><para>
|
||||
LDB URL to connect to. See ldb(7) for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-o options</term>
|
||||
<listitem><para>Extra ldb options, such as
|
||||
modules.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry><term>LDB_URL</term>
|
||||
<listitem><para>LDB URL to connect to (can be overrided by using the
|
||||
-H command-line option.)</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>This manpage was written by Jelmer Vernooij.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="ldbsearch.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>ldbsearch</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>ldbsearch</refname>
|
||||
<refpurpose>Search for records in a LDB database</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>ldbsearch</command>
|
||||
<arg choice="opt">-h</arg>
|
||||
<arg choice="opt">-s base|one|sub</arg>
|
||||
<arg choice="opt">-b basedn</arg>
|
||||
<arg chioce="opt">-i</arg>
|
||||
<arg choice="opt">-H LDB-URL</arg>
|
||||
<arg choice="opt">expression</arg>
|
||||
<arg choice="opt">attributes</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>ldbsearch searches a LDB database for records matching the
|
||||
specified expression (see the ldapsearch(1) manpage for
|
||||
a description of the expression format). For each
|
||||
record, the specified attributes are printed.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-h</term>
|
||||
<listitem><para>
|
||||
Show list of available options.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-H <ldb-url></term>
|
||||
<listitem><para>
|
||||
LDB URL to connect to. See ldb(7) for details.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-s one|sub|base</term>
|
||||
<listitem><para>Search scope to use. One-level, subtree or base.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-i</term>
|
||||
<listitem><para>Read search expressions from stdin. </para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-b basedn</term>
|
||||
<listitem><para>Specify Base DN to use.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>ENVIRONMENT</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry><term>LDB_URL</term>
|
||||
<listitem><para>LDB URL to connect to (can be overrided by using the
|
||||
-H command-line option.)</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbedit(1)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
<para>This manpage was written by Jelmer Vernooij.</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<refentry id="oLschema2ldif.1">
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>oLschema2ldif</refentrytitle>
|
||||
<manvolnum>1</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
|
||||
<refnamediv>
|
||||
<refname>oLschema2ldif</refname>
|
||||
<refpurpose>Converts LDAP schema's to LDB-compatible LDIF</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
<command>oLschema2ldif</command>
|
||||
<arg choice="opt">-I INPUT-FILE</arg>
|
||||
<arg choice="opt">-O OUTPUT-FILE</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>DESCRIPTION</title>
|
||||
|
||||
<para>oLschema2ldif is a simple tool that converts standard OpenLDAP schema files to a LDIF format that is understood by LDB.</para>
|
||||
</refsect1>
|
||||
|
||||
|
||||
<refsect1>
|
||||
<title>OPTIONS</title>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term>-I input-file</term>
|
||||
<listitem><para>OpenLDAP schema to read. If none are specified,
|
||||
the schema file will be read from standard input.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>-O output-file</term>
|
||||
<listitem><para>File to write ldif version of schema to.
|
||||
</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>VERSION</title>
|
||||
|
||||
<para>This man page is correct for version 4.0 of the Samba suite.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>SEE ALSO</title>
|
||||
|
||||
<para>ldb(7), ldbmodify, ldbdel, ldif(5)</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>AUTHOR</title>
|
||||
|
||||
<para> ldb was written by
|
||||
<ulink url="http://samba.org/~tridge/">Andrew Tridgell</ulink>.
|
||||
oLschema2ldif was written by <ulink url="mailto:idra@samba.org">Simo Sorce</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you wish to report a problem or make a suggestion then please see
|
||||
the <ulink url="http://ldb.samba.org/"/> web site for
|
||||
current contact and maintainer information.
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb attribute scoped query control module
|
||||
*
|
||||
* Description: this module searches all the the objects pointed
|
||||
* by the DNs contained in the references attribute
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
struct asq_context {
|
||||
|
||||
enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
|
||||
|
||||
struct ldb_module *module;
|
||||
void *up_context;
|
||||
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
const char * const *req_attrs;
|
||||
char *req_attribute;
|
||||
enum {
|
||||
ASQ_CTRL_SUCCESS = 0,
|
||||
ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX = 21,
|
||||
ASQ_CTRL_UNWILLING_TO_PERFORM = 53,
|
||||
ASQ_CTRL_AFFECTS_MULTIPLE_DSA = 71
|
||||
} asq_ret;
|
||||
|
||||
struct ldb_request *base_req;
|
||||
struct ldb_reply *base_res;
|
||||
|
||||
struct ldb_request **reqs;
|
||||
int num_reqs;
|
||||
int cur_req;
|
||||
|
||||
struct ldb_control **controls;
|
||||
};
|
||||
|
||||
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
|
||||
{
|
||||
struct asq_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(mem_ctx, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->up_context = context;
|
||||
ac->up_callback = callback;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int asq_terminate(struct ldb_handle *handle)
|
||||
{
|
||||
struct asq_context *ac;
|
||||
struct ldb_reply *ares;
|
||||
struct ldb_asq_control *asq;
|
||||
int i;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
handle->status = LDB_SUCCESS;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (ares == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
|
||||
if (ac->controls) {
|
||||
for (i = 0; ac->controls[i]; i++);
|
||||
ares->controls = talloc_move(ares, &ac->controls);
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
|
||||
ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, i + 2);
|
||||
|
||||
if (ares->controls == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ares->controls[i] = talloc(ares->controls, struct ldb_control);
|
||||
if (ares->controls[i] == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ares->controls[i]->oid = LDB_CONTROL_ASQ_OID;
|
||||
ares->controls[i]->critical = 0;
|
||||
|
||||
asq = talloc_zero(ares->controls[i], struct ldb_asq_control);
|
||||
if (asq == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
asq->result = ac->asq_ret;
|
||||
|
||||
ares->controls[i]->data = asq;
|
||||
|
||||
ares->controls[i + 1] = NULL;
|
||||
|
||||
ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int asq_base_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct asq_context *ac;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* we are interested only in the single reply (base search) we receive here */
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
ac->base_res = talloc_move(ac, &ares);
|
||||
} else {
|
||||
talloc_free(ares);
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int asq_reqs_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct asq_context *ac;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* we are interested only in the single reply (base search) we receive here */
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
|
||||
/* pass the message up to the original callback as we
|
||||
* do not have to elaborate on it any further */
|
||||
return ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
|
||||
} else { /* ignore any REFERRAL or DONE reply */
|
||||
talloc_free(ares);
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int asq_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_control *control;
|
||||
struct ldb_asq_control *asq_ctrl;
|
||||
struct asq_context *ac;
|
||||
struct ldb_handle *h;
|
||||
char **base_attrs;
|
||||
int ret;
|
||||
|
||||
/* check if there's a paged request control */
|
||||
control = get_control_from_list(req->controls, LDB_CONTROL_ASQ_OID);
|
||||
if (control == NULL) {
|
||||
/* not found go on */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb,
|
||||
"Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
|
||||
if (!asq_ctrl) {
|
||||
return LDB_ERR_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
h = init_handle(req, module, req->context, req->callback);
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct asq_context);
|
||||
|
||||
req->handle = h;
|
||||
|
||||
/* check the search is well formed */
|
||||
if (req->op.search.scope != LDB_SCOPE_BASE) {
|
||||
ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
|
||||
return asq_terminate(h);
|
||||
}
|
||||
|
||||
ac->req_attrs = req->op.search.attrs;
|
||||
ac->req_attribute = talloc_strdup(ac, asq_ctrl->source_attribute);
|
||||
if (ac->req_attribute == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
/* get the object to retrieve the DNs to search */
|
||||
ac->base_req = talloc_zero(req, struct ldb_request);
|
||||
if (ac->base_req == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
ac->base_req->operation = req->operation;
|
||||
ac->base_req->op.search.base = req->op.search.base;
|
||||
ac->base_req->op.search.scope = LDB_SCOPE_BASE;
|
||||
ac->base_req->op.search.tree = req->op.search.tree;
|
||||
base_attrs = talloc_array(ac->base_req, char *, 2);
|
||||
if (base_attrs == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
base_attrs[0] = talloc_strdup(base_attrs, asq_ctrl->source_attribute);
|
||||
if (base_attrs[0] == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
base_attrs[1] = NULL;
|
||||
ac->base_req->op.search.attrs = (const char * const *)base_attrs;
|
||||
|
||||
ac->base_req->context = ac;
|
||||
ac->base_req->callback = asq_base_callback;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, ac->base_req);
|
||||
|
||||
ac->step = ASQ_SEARCH_BASE;
|
||||
|
||||
ret = ldb_request(module->ldb, ac->base_req);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int asq_requests(struct ldb_handle *handle) {
|
||||
struct asq_context *ac;
|
||||
struct ldb_message_element *el;
|
||||
int i;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* look up the DNs */
|
||||
if (ac->base_res == NULL) {
|
||||
return LDB_ERR_NO_SUCH_OBJECT;
|
||||
}
|
||||
el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
|
||||
/* no values found */
|
||||
if (el == NULL) {
|
||||
ac->asq_ret = ASQ_CTRL_SUCCESS;
|
||||
return asq_terminate(handle);
|
||||
}
|
||||
|
||||
/* build up the requests call chain */
|
||||
ac->num_reqs = el->num_values;
|
||||
ac->cur_req = 0;
|
||||
ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
|
||||
if (ac->reqs == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
|
||||
ac->reqs[i] = talloc_zero(ac->reqs, struct ldb_request);
|
||||
if (ac->reqs[i] == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
ac->reqs[i]->operation = LDB_SEARCH;
|
||||
ac->reqs[i]->op.search.base = ldb_dn_new(ac->reqs[i], ac->module->ldb, (const char *)el->values[i].data);
|
||||
if ( ! ldb_dn_validate(ac->reqs[i]->op.search.base)) {
|
||||
ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
|
||||
return asq_terminate(handle);
|
||||
}
|
||||
ac->reqs[i]->op.search.scope = LDB_SCOPE_BASE;
|
||||
ac->reqs[i]->op.search.tree = ac->base_req->op.search.tree;
|
||||
ac->reqs[i]->op.search.attrs = ac->req_attrs;
|
||||
|
||||
ac->reqs[i]->context = ac;
|
||||
ac->reqs[i]->callback = asq_reqs_callback;
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->base_req, ac->reqs[i]);
|
||||
}
|
||||
|
||||
ac->step = ASQ_SEARCH_MULTI;
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int asq_wait_none(struct ldb_handle *handle)
|
||||
{
|
||||
struct asq_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct asq_context);
|
||||
if (ac == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
switch (ac->step) {
|
||||
case ASQ_SEARCH_BASE:
|
||||
ret = ldb_wait(ac->base_req->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->base_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->base_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
if (ac->base_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
ret = asq_requests(handle);
|
||||
|
||||
/* no break nor return,
|
||||
* the set of requests is performed in ASQ_SEARCH_MULTI
|
||||
*/
|
||||
|
||||
case ASQ_SEARCH_MULTI:
|
||||
|
||||
if (ac->reqs[ac->cur_req]->handle == NULL) {
|
||||
ret = ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ldb_wait(ac->reqs[ac->cur_req]->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->reqs[ac->cur_req]->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->reqs[ac->cur_req]->handle->status;
|
||||
}
|
||||
|
||||
if (ac->reqs[ac->cur_req]->handle->state == LDB_ASYNC_DONE) {
|
||||
ac->cur_req++;
|
||||
}
|
||||
|
||||
if (ac->cur_req < ac->num_reqs) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
return asq_terminate(handle);
|
||||
|
||||
default:
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = LDB_SUCCESS;
|
||||
|
||||
done:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int asq_wait_all(struct ldb_handle *handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (handle->state != LDB_ASYNC_DONE) {
|
||||
ret = asq_wait_none(handle);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
if (type == LDB_WAIT_ALL) {
|
||||
return asq_wait_all(handle);
|
||||
} else {
|
||||
return asq_wait_none(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static int asq_init(struct ldb_module *module)
|
||||
{
|
||||
struct ldb_request *req;
|
||||
int ret;
|
||||
|
||||
req = talloc_zero(module, struct ldb_request);
|
||||
if (req == NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Out of memory!\n");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->operation = LDB_REQ_REGISTER_CONTROL;
|
||||
req->op.reg_control.oid = LDB_CONTROL_ASQ_OID;
|
||||
|
||||
ret = ldb_request(module->ldb, req);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!\n");
|
||||
}
|
||||
|
||||
return ldb_next_init(module);
|
||||
}
|
||||
|
||||
|
||||
static const struct ldb_module_ops asq_ops = {
|
||||
.name = "asq",
|
||||
.search = asq_search,
|
||||
.wait = asq_wait,
|
||||
.init_context = asq_init
|
||||
};
|
||||
|
||||
int ldb_asq_init(void)
|
||||
{
|
||||
return ldb_register_module(&asq_ops);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
ldb database mapping module
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
|
||||
|
||||
* NOTICE: this module is NOT released under the GNU LGPL license as
|
||||
* other ldb code. This module is release under the GNU GPL v2 or
|
||||
* later license.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef __LDB_MAP_H__
|
||||
#define __LDB_MAP_H__
|
||||
|
||||
/* ldb_map is a skeleton LDB module that can be used for any other modules
|
||||
* that need to map attributes.
|
||||
*
|
||||
* The term 'remote' in this header refers to the connection where the
|
||||
* original schema is used on while 'local' means the local connection
|
||||
* that any upper layers will use.
|
||||
*
|
||||
* All local attributes will have to have a definition. Not all remote
|
||||
* attributes need a definition as LDB is a lot less strict than LDAP
|
||||
* (in other words, sending unknown attributes to an LDAP server hurts us,
|
||||
* while returning too many attributes in ldb_search() doesn't)
|
||||
*/
|
||||
|
||||
|
||||
/* Name of the internal attribute pointing from the local to the
|
||||
* remote part of a record */
|
||||
#define IS_MAPPED "isMapped"
|
||||
|
||||
|
||||
struct ldb_map_context;
|
||||
|
||||
/* convert a local ldb_val to a remote ldb_val */
|
||||
typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
|
||||
|
||||
#define LDB_MAP_MAX_REMOTE_NAMES 10
|
||||
|
||||
/* map from local to remote attribute */
|
||||
struct ldb_map_attribute {
|
||||
const char *local_name; /* local name */
|
||||
|
||||
enum ldb_map_attr_type {
|
||||
MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely. */
|
||||
MAP_KEEP, /* Keep as is. Same name locally and remotely. */
|
||||
MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
|
||||
MAP_CONVERT, /* Rename + convert data */
|
||||
MAP_GENERATE /* Use generate function for generating new name/data.
|
||||
Used for generating attributes based on
|
||||
multiple remote attributes. */
|
||||
} type;
|
||||
|
||||
/* if set, will be called for search expressions that contain this attribute */
|
||||
int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *);
|
||||
|
||||
union {
|
||||
struct {
|
||||
const char *remote_name;
|
||||
} rename;
|
||||
|
||||
struct {
|
||||
const char *remote_name;
|
||||
|
||||
/* Convert local to remote data */
|
||||
ldb_map_convert_func convert_local;
|
||||
|
||||
/* Convert remote to local data */
|
||||
/* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name
|
||||
* that is non-NULL before it. */
|
||||
ldb_map_convert_func convert_remote;
|
||||
} convert;
|
||||
|
||||
struct {
|
||||
/* Generate the local attribute from remote message */
|
||||
struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
|
||||
|
||||
/* Update remote message with information from local message */
|
||||
void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
|
||||
|
||||
/* Name(s) for this attribute on the remote server. This is an array since
|
||||
* one local attribute's data can be split up into several attributes
|
||||
* remotely */
|
||||
const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
|
||||
|
||||
/* Names of additional remote attributes
|
||||
* required for the generation. NULL
|
||||
* indicates that `local_attr' suffices. */
|
||||
/*
|
||||
#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
|
||||
const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
|
||||
*/
|
||||
} generate;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
#define LDB_MAP_MAX_SUBCLASSES 10
|
||||
#define LDB_MAP_MAX_MUSTS 10
|
||||
#define LDB_MAP_MAX_MAYS 50
|
||||
|
||||
/* map from local to remote objectClass */
|
||||
struct ldb_map_objectclass {
|
||||
const char *local_name;
|
||||
const char *remote_name;
|
||||
const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
|
||||
const char *musts[LDB_MAP_MAX_MUSTS];
|
||||
const char *mays[LDB_MAP_MAX_MAYS];
|
||||
};
|
||||
|
||||
|
||||
/* private context data */
|
||||
struct ldb_map_context {
|
||||
struct ldb_map_attribute *attribute_maps;
|
||||
/* NOTE: Always declare base classes first here */
|
||||
const struct ldb_map_objectclass *objectclass_maps;
|
||||
|
||||
/* Remote (often operational) attributes that should be added
|
||||
* to any wildcard search */
|
||||
const char * const *wildcard_attributes;
|
||||
|
||||
/* struct ldb_context *mapped_ldb; */
|
||||
struct ldb_dn *local_base_dn;
|
||||
struct ldb_dn *remote_base_dn;
|
||||
};
|
||||
|
||||
/* Global private data */
|
||||
struct map_private {
|
||||
void *caller_private;
|
||||
struct ldb_map_context *context;
|
||||
};
|
||||
|
||||
/* Initialize global private data. */
|
||||
int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
|
||||
const struct ldb_map_objectclass *ocls,
|
||||
const char * const *wildcard_attributes,
|
||||
const char *name);
|
||||
|
||||
/* get copy of map_ops */
|
||||
struct ldb_module_ops
|
||||
ldb_map_get_ops(void);
|
||||
|
||||
#endif /* __LDB_MAP_H__ */
|
||||
@@ -0,0 +1,724 @@
|
||||
/*
|
||||
ldb database mapping module
|
||||
|
||||
Copyright (C) Jelmer Vernooij 2005
|
||||
Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
|
||||
|
||||
* NOTICE: this module is NOT released under the GNU LGPL license as
|
||||
* other ldb code. This module is release under the GNU GPL v2 or
|
||||
* later license.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include "ldb/modules/ldb_map.h"
|
||||
#include "ldb/modules/ldb_map_private.h"
|
||||
|
||||
|
||||
/* Mapping message elements
|
||||
* ======================== */
|
||||
|
||||
/* Map a message element into the remote partition. */
|
||||
static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
|
||||
{
|
||||
struct ldb_message_element *el;
|
||||
int i;
|
||||
|
||||
el = talloc_zero(mem_ctx, struct ldb_message_element);
|
||||
if (el == NULL) {
|
||||
map_oom(module);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
el->num_values = old->num_values;
|
||||
el->values = talloc_array(el, struct ldb_val, el->num_values);
|
||||
if (el->values == NULL) {
|
||||
talloc_free(el);
|
||||
map_oom(module);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
el->name = map_attr_map_local(el, map, old->name);
|
||||
|
||||
for (i = 0; i < el->num_values; i++) {
|
||||
el->values[i] = ldb_val_map_local(module, el->values, map, &old->values[i]);
|
||||
}
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
/* Add a message element either to a local or to a remote message,
|
||||
* depending on whether it goes into the local or remote partition. */
|
||||
static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
|
||||
{
|
||||
const struct ldb_map_context *data = map_get_context(module);
|
||||
const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
|
||||
struct ldb_message_element *el=NULL;
|
||||
|
||||
/* Unknown attribute: ignore */
|
||||
if (map == NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
|
||||
"Not mapping attribute '%s': no mapping found\n",
|
||||
old->name);
|
||||
goto local;
|
||||
}
|
||||
|
||||
switch (map->type) {
|
||||
case MAP_IGNORE:
|
||||
goto local;
|
||||
|
||||
case MAP_CONVERT:
|
||||
if (map->u.convert.convert_local == NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
|
||||
"Not mapping attribute '%s': "
|
||||
"'convert_local' not set\n",
|
||||
map->local_name);
|
||||
goto local;
|
||||
}
|
||||
/* fall through */
|
||||
case MAP_KEEP:
|
||||
case MAP_RENAME:
|
||||
el = ldb_msg_el_map_local(module, remote, map, old);
|
||||
break;
|
||||
|
||||
case MAP_GENERATE:
|
||||
if (map->u.generate.generate_remote == NULL) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
|
||||
"Not mapping attribute '%s': "
|
||||
"'generate_remote' not set\n",
|
||||
map->local_name);
|
||||
goto local;
|
||||
}
|
||||
|
||||
/* TODO: if this attr requires context:
|
||||
* make sure all context attrs are mappable (in 'names')
|
||||
* make sure all context attrs have already been mapped?
|
||||
* maybe postpone generation until they have been mapped?
|
||||
*/
|
||||
|
||||
map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (el == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ldb_msg_add(remote, el, old->flags);
|
||||
|
||||
local:
|
||||
el = talloc(local, struct ldb_message_element);
|
||||
if (el == NULL) {
|
||||
map_oom(module);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*el = *old; /* copy the old element */
|
||||
|
||||
return ldb_msg_add(local, el, old->flags);
|
||||
}
|
||||
|
||||
/* Mapping messages
|
||||
* ================ */
|
||||
|
||||
/* Check whether a message will be (partially) mapped into the remote partition. */
|
||||
static BOOL ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
|
||||
{
|
||||
const struct ldb_map_context *data = map_get_context(module);
|
||||
BOOL ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
ret = map_attr_check_remote(data, msg->elements[i].name);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/* Split message elements that stay in the local partition from those
|
||||
* that are mapped into the remote partition. */
|
||||
static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
|
||||
{
|
||||
/* const char * const names[]; */
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
/* Skip 'IS_MAPPED' */
|
||||
if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
|
||||
"Skipping attribute '%s'\n",
|
||||
msg->elements[i].name);
|
||||
continue;
|
||||
}
|
||||
|
||||
ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Inbound requests: add, modify, rename, delete
|
||||
* ============================================= */
|
||||
|
||||
/* Add the remote record. */
|
||||
int map_add_do_remote(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
|
||||
|
||||
ac->step = MAP_ADD_REMOTE;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_remote_request(ac->module, ac->remote_req);
|
||||
}
|
||||
|
||||
/* Add the local record. */
|
||||
int map_add_do_local(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
||||
|
||||
ac->step = MAP_ADD_LOCAL;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_request(ac->module, ac->local_req);
|
||||
}
|
||||
|
||||
/* Add a record. */
|
||||
int map_add(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
const struct ldb_message *msg = req->op.add.message;
|
||||
struct ldb_handle *h;
|
||||
struct map_context *ac;
|
||||
struct ldb_message *local, *remote;
|
||||
const char *dn;
|
||||
|
||||
/* Do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(msg->dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
|
||||
if (!ldb_dn_check_local(module, msg->dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping needed, fail */
|
||||
if (!ldb_msg_check_remote(module, msg)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Prepare context and handle */
|
||||
h = map_init_handle(req, module);
|
||||
if (h == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct map_context);
|
||||
|
||||
/* Prepare the local operation */
|
||||
ac->local_req = talloc(ac, struct ldb_request);
|
||||
if (ac->local_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->local_req) = *req; /* copy the request */
|
||||
|
||||
ac->local_req->context = NULL;
|
||||
ac->local_req->callback = NULL;
|
||||
|
||||
/* Prepare the remote operation */
|
||||
ac->remote_req = talloc(ac, struct ldb_request);
|
||||
if (ac->remote_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->remote_req) = *req; /* copy the request */
|
||||
|
||||
ac->remote_req->context = NULL;
|
||||
ac->remote_req->callback = NULL;
|
||||
|
||||
/* Prepare the local message */
|
||||
local = ldb_msg_new(ac->local_req);
|
||||
if (local == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
local->dn = msg->dn;
|
||||
|
||||
/* Prepare the remote message */
|
||||
remote = ldb_msg_new(ac->remote_req);
|
||||
if (remote == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
|
||||
|
||||
/* Split local from remote message */
|
||||
ldb_msg_partition(module, local, remote, msg);
|
||||
ac->local_req->op.add.message = local;
|
||||
ac->remote_req->op.add.message = remote;
|
||||
|
||||
if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
|
||||
/* No local data or db, just run the remote request */
|
||||
talloc_free(ac->local_req);
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return map_add_do_remote(h);
|
||||
}
|
||||
|
||||
/* Store remote DN in 'IS_MAPPED' */
|
||||
/* TODO: use GUIDs here instead */
|
||||
dn = ldb_dn_alloc_linearized(local, remote->dn);
|
||||
if (ldb_msg_add_string(local, IS_MAPPED, dn) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return map_add_do_local(h);
|
||||
|
||||
oom:
|
||||
map_oom(module);
|
||||
failed:
|
||||
talloc_free(h);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Modify the remote record. */
|
||||
int map_modify_do_remote(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
|
||||
|
||||
ac->step = MAP_MODIFY_REMOTE;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_remote_request(ac->module, ac->remote_req);
|
||||
}
|
||||
|
||||
/* Modify the local record. */
|
||||
int map_modify_do_local(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
struct ldb_message *msg;
|
||||
char *dn;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
if (ac->local_dn == NULL) {
|
||||
/* No local record present, add it instead */
|
||||
msg = discard_const_p(struct ldb_message, ac->local_req->op.mod.message);
|
||||
|
||||
/* Add local 'IS_MAPPED' */
|
||||
/* TODO: use GUIDs here instead */
|
||||
if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
dn = ldb_dn_alloc_linearized(msg, ac->remote_req->op.mod.message->dn);
|
||||
if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Turn request into 'add' */
|
||||
ac->local_req->operation = LDB_ADD;
|
||||
ac->local_req->op.add.message = msg;
|
||||
/* TODO: Could I just leave msg in there? I think so,
|
||||
* but it looks clearer this way. */
|
||||
}
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
||||
|
||||
ac->step = MAP_MODIFY_LOCAL;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_request(ac->module, ac->local_req);
|
||||
}
|
||||
|
||||
/* Modify a record. */
|
||||
int map_modify(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
const struct ldb_message *msg = req->op.mod.message;
|
||||
struct ldb_handle *h;
|
||||
struct map_context *ac;
|
||||
struct ldb_message *local, *remote;
|
||||
|
||||
/* Do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(msg->dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
|
||||
if (!ldb_dn_check_local(module, msg->dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping needed, skip to next module */
|
||||
/* TODO: What if the remote part exists, the local doesn't,
|
||||
* and this request wants to modify local data and thus
|
||||
* add the local record? */
|
||||
if (!ldb_msg_check_remote(module, msg)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Prepare context and handle */
|
||||
h = map_init_handle(req, module);
|
||||
if (h == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct map_context);
|
||||
|
||||
/* Prepare the local operation */
|
||||
ac->local_req = talloc(ac, struct ldb_request);
|
||||
if (ac->local_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->local_req) = *req; /* copy the request */
|
||||
|
||||
ac->local_req->context = NULL;
|
||||
ac->local_req->callback = NULL;
|
||||
|
||||
/* Prepare the remote operation */
|
||||
ac->remote_req = talloc(ac, struct ldb_request);
|
||||
if (ac->remote_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->remote_req) = *req; /* copy the request */
|
||||
|
||||
ac->remote_req->context = NULL;
|
||||
ac->remote_req->callback = NULL;
|
||||
|
||||
/* Prepare the local message */
|
||||
local = ldb_msg_new(ac->local_req);
|
||||
if (local == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
local->dn = msg->dn;
|
||||
|
||||
/* Prepare the remote message */
|
||||
remote = ldb_msg_new(ac->remote_req);
|
||||
if (remote == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
|
||||
|
||||
/* Split local from remote message */
|
||||
ldb_msg_partition(module, local, remote, msg);
|
||||
ac->local_req->op.mod.message = local;
|
||||
ac->remote_req->op.mod.message = remote;
|
||||
|
||||
if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
|
||||
/* No local data or db, just run the remote request */
|
||||
talloc_free(ac->local_req);
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return map_modify_do_remote(h);
|
||||
}
|
||||
|
||||
/* prepare the search operation */
|
||||
ac->search_req = map_search_self_req(ac, msg->dn);
|
||||
if (ac->search_req == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ac->step = MAP_SEARCH_SELF_MODIFY;
|
||||
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return ldb_next_request(module, ac->search_req);
|
||||
|
||||
oom:
|
||||
map_oom(module);
|
||||
failed:
|
||||
talloc_free(h);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Delete the remote record. */
|
||||
int map_delete_do_remote(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
|
||||
|
||||
ac->step = MAP_DELETE_REMOTE;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_remote_request(ac->module, ac->remote_req);
|
||||
}
|
||||
|
||||
/* Delete the local record. */
|
||||
int map_delete_do_local(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
/* No local record, continue remotely */
|
||||
if (ac->local_dn == NULL) {
|
||||
return map_delete_do_remote(handle);
|
||||
}
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
||||
|
||||
ac->step = MAP_DELETE_LOCAL;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_request(ac->module, ac->local_req);
|
||||
}
|
||||
|
||||
/* Delete a record. */
|
||||
int map_delete(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_handle *h;
|
||||
struct map_context *ac;
|
||||
|
||||
/* Do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(req->op.del.dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
|
||||
if (!ldb_dn_check_local(module, req->op.del.dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* Prepare context and handle */
|
||||
h = map_init_handle(req, module);
|
||||
if (h == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct map_context);
|
||||
|
||||
/* Prepare the local operation */
|
||||
ac->local_req = talloc(ac, struct ldb_request);
|
||||
if (ac->local_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->local_req) = *req; /* copy the request */
|
||||
ac->local_req->op.del.dn = req->op.del.dn;
|
||||
|
||||
ac->local_req->context = NULL;
|
||||
ac->local_req->callback = NULL;
|
||||
|
||||
/* Prepare the remote operation */
|
||||
ac->remote_req = talloc(ac, struct ldb_request);
|
||||
if (ac->remote_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->remote_req) = *req; /* copy the request */
|
||||
ac->remote_req->op.del.dn = ldb_dn_map_local(module, ac->remote_req, req->op.del.dn);
|
||||
|
||||
/* No local db, just run the remote request */
|
||||
if (!map_check_local_db(ac->module)) {
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return map_delete_do_remote(h);
|
||||
}
|
||||
|
||||
ac->remote_req->context = NULL;
|
||||
ac->remote_req->callback = NULL;
|
||||
|
||||
/* Prepare the search operation */
|
||||
ac->search_req = map_search_self_req(ac, req->op.del.dn);
|
||||
if (ac->search_req == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
|
||||
ac->step = MAP_SEARCH_SELF_DELETE;
|
||||
|
||||
return ldb_next_request(module, ac->search_req);
|
||||
|
||||
oom:
|
||||
map_oom(module);
|
||||
failed:
|
||||
talloc_free(h);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* Rename the remote record. */
|
||||
int map_rename_do_remote(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
|
||||
|
||||
ac->step = MAP_RENAME_REMOTE;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_remote_request(ac->module, ac->remote_req);
|
||||
}
|
||||
|
||||
/* Update the local 'IS_MAPPED' attribute. */
|
||||
int map_rename_do_fixup(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
|
||||
|
||||
ac->step = MAP_RENAME_FIXUP;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_request(ac->module, ac->down_req);
|
||||
}
|
||||
|
||||
/* Rename the local record. */
|
||||
int map_rename_do_local(struct ldb_handle *handle)
|
||||
{
|
||||
struct map_context *ac;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct map_context);
|
||||
|
||||
/* No local record, continue remotely */
|
||||
if (ac->local_dn == NULL) {
|
||||
return map_rename_do_remote(handle);
|
||||
}
|
||||
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
|
||||
|
||||
ac->step = MAP_RENAME_LOCAL;
|
||||
|
||||
handle->state = LDB_ASYNC_INIT;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
return ldb_next_request(ac->module, ac->local_req);
|
||||
}
|
||||
|
||||
/* Rename a record. */
|
||||
int map_rename(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_handle *h;
|
||||
struct map_context *ac;
|
||||
|
||||
/* Do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(req->op.rename.olddn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* No mapping requested (perhaps no DN mapping specified), skip to next module */
|
||||
if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
|
||||
(!ldb_dn_check_local(module, req->op.rename.newdn))) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* Rename into/out of the mapped partition requested, bail out */
|
||||
if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
|
||||
!ldb_dn_check_local(module, req->op.rename.newdn)) {
|
||||
return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
|
||||
}
|
||||
|
||||
/* Prepare context and handle */
|
||||
h = map_init_handle(req, module);
|
||||
if (h == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct map_context);
|
||||
|
||||
/* Prepare the local operation */
|
||||
ac->local_req = talloc(ac, struct ldb_request);
|
||||
if (ac->local_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->local_req) = *req; /* copy the request */
|
||||
ac->local_req->op.rename.olddn = req->op.rename.olddn;
|
||||
ac->local_req->op.rename.newdn = req->op.rename.newdn;
|
||||
|
||||
ac->local_req->context = NULL;
|
||||
ac->local_req->callback = NULL;
|
||||
|
||||
/* Prepare the remote operation */
|
||||
ac->remote_req = talloc(ac, struct ldb_request);
|
||||
if (ac->remote_req == NULL) {
|
||||
goto oom;
|
||||
}
|
||||
|
||||
*(ac->remote_req) = *req; /* copy the request */
|
||||
ac->remote_req->op.rename.olddn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.olddn);
|
||||
ac->remote_req->op.rename.newdn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.newdn);
|
||||
|
||||
ac->remote_req->context = NULL;
|
||||
ac->remote_req->callback = NULL;
|
||||
|
||||
/* No local db, just run the remote request */
|
||||
if (!map_check_local_db(ac->module)) {
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
return map_rename_do_remote(h);
|
||||
}
|
||||
|
||||
/* Prepare the fixup operation */
|
||||
/* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
|
||||
ac->down_req = map_build_fixup_req(ac, req->op.rename.newdn, ac->remote_req->op.rename.newdn);
|
||||
if (ac->down_req == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Prepare the search operation */
|
||||
ac->search_req = map_search_self_req(ac, req->op.rename.olddn);
|
||||
if (ac->search_req == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
req->handle = h; /* return our own handle to deal with this call */
|
||||
|
||||
ac->step = MAP_SEARCH_SELF_RENAME;
|
||||
|
||||
return ldb_next_request(module, ac->search_req);
|
||||
|
||||
oom:
|
||||
map_oom(module);
|
||||
failed:
|
||||
talloc_free(h);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
|
||||
/* A handy macro to report Out of Memory conditions */
|
||||
#define map_oom(module) ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
|
||||
|
||||
/* The type of search callback functions */
|
||||
typedef int (*ldb_search_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
/* The special DN from which the local and remote base DNs are fetched */
|
||||
#define MAP_DN_NAME "@MAP"
|
||||
#define MAP_DN_FROM "@FROM"
|
||||
#define MAP_DN_TO "@TO"
|
||||
|
||||
/* Private data structures
|
||||
* ======================= */
|
||||
|
||||
/* Context data for mapped requests */
|
||||
struct map_context {
|
||||
enum map_step {
|
||||
MAP_SEARCH_REMOTE,
|
||||
MAP_ADD_REMOTE,
|
||||
MAP_ADD_LOCAL,
|
||||
MAP_SEARCH_SELF_MODIFY,
|
||||
MAP_MODIFY_REMOTE,
|
||||
MAP_MODIFY_LOCAL,
|
||||
MAP_SEARCH_SELF_DELETE,
|
||||
MAP_DELETE_REMOTE,
|
||||
MAP_DELETE_LOCAL,
|
||||
MAP_SEARCH_SELF_RENAME,
|
||||
MAP_RENAME_REMOTE,
|
||||
MAP_RENAME_FIXUP,
|
||||
MAP_RENAME_LOCAL
|
||||
} step;
|
||||
|
||||
struct ldb_module *module;
|
||||
|
||||
struct ldb_dn *local_dn;
|
||||
const struct ldb_parse_tree *local_tree;
|
||||
const char * const *local_attrs;
|
||||
const char * const *remote_attrs;
|
||||
const char * const *all_attrs;
|
||||
|
||||
struct ldb_request *orig_req;
|
||||
struct ldb_request *local_req;
|
||||
struct ldb_request *remote_req;
|
||||
struct ldb_request *down_req;
|
||||
struct ldb_request *search_req;
|
||||
|
||||
/* for search, we may have a lot of contexts */
|
||||
int num_searches;
|
||||
struct ldb_request **search_reqs;
|
||||
};
|
||||
|
||||
/* Context data for mapped search requests */
|
||||
struct map_search_context {
|
||||
struct map_context *ac;
|
||||
struct ldb_reply *local_res;
|
||||
struct ldb_reply *remote_res;
|
||||
};
|
||||
|
||||
|
||||
/* Common operations
|
||||
* ================= */
|
||||
|
||||
/* The following definitions come from lib/ldb/modules/ldb_map.c */
|
||||
const struct ldb_map_context *map_get_context(struct ldb_module *module);
|
||||
struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares);
|
||||
struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module);
|
||||
|
||||
int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
|
||||
|
||||
BOOL map_check_local_db(struct ldb_module *module);
|
||||
BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
|
||||
BOOL ldb_dn_check_local(struct ldb_module *module, struct ldb_dn *dn);
|
||||
|
||||
const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
|
||||
const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
|
||||
|
||||
const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
|
||||
const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
|
||||
int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
|
||||
|
||||
struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
|
||||
struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_val *val);
|
||||
|
||||
struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
|
||||
struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
|
||||
struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, struct ldb_dn *dn);
|
||||
|
||||
struct ldb_request *map_search_base_req(struct map_context *ac, struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback);
|
||||
struct ldb_request *map_search_self_req(struct map_context *ac, struct ldb_dn *dn);
|
||||
struct ldb_request *map_build_fixup_req(struct map_context *ac, struct ldb_dn *olddn, struct ldb_dn *newdn);
|
||||
|
||||
int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map);
|
||||
|
||||
/* LDB Requests
|
||||
* ============ */
|
||||
|
||||
/* The following definitions come from lib/ldb/modules/ldb_map_inbound.c */
|
||||
int map_add_do_remote(struct ldb_handle *handle);
|
||||
int map_add_do_local(struct ldb_handle *handle);
|
||||
int map_add(struct ldb_module *module, struct ldb_request *req);
|
||||
|
||||
int map_modify_do_remote(struct ldb_handle *handle);
|
||||
int map_modify_do_local(struct ldb_handle *handle);
|
||||
int map_modify(struct ldb_module *module, struct ldb_request *req);
|
||||
|
||||
int map_delete_do_remote(struct ldb_handle *handle);
|
||||
int map_delete_do_local(struct ldb_handle *handle);
|
||||
int map_delete(struct ldb_module *module, struct ldb_request *req);
|
||||
|
||||
int map_rename_do_remote(struct ldb_handle *handle);
|
||||
int map_rename_do_fixup(struct ldb_handle *handle);
|
||||
int map_rename_do_local(struct ldb_handle *handle);
|
||||
int map_rename(struct ldb_module *module, struct ldb_request *req);
|
||||
|
||||
/* The following definitions come from lib/ldb/modules/ldb_map_outbound.c */
|
||||
int map_search(struct ldb_module *module, struct ldb_request *req);
|
||||
@@ -0,0 +1,694 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2006
|
||||
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: objectClass sorting module
|
||||
*
|
||||
* Description: sort the objectClass attribute into the class hierarchy
|
||||
*
|
||||
* Author: Andrew Bartlett
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
struct oc_context {
|
||||
|
||||
enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
|
||||
|
||||
struct ldb_module *module;
|
||||
struct ldb_request *orig_req;
|
||||
|
||||
struct ldb_request *down_req;
|
||||
|
||||
struct ldb_request *search_req;
|
||||
struct ldb_reply *search_res;
|
||||
|
||||
struct ldb_request *mod_req;
|
||||
};
|
||||
|
||||
struct class_list {
|
||||
struct class_list *prev, *next;
|
||||
const char *objectclass;
|
||||
};
|
||||
|
||||
static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
|
||||
{
|
||||
struct oc_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(req, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct oc_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->orig_req = req;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int objectclass_sort(struct ldb_module *module,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct ldb_message_element *objectclass_element,
|
||||
struct class_list **sorted_out)
|
||||
{
|
||||
int i;
|
||||
int layer;
|
||||
struct class_list *sorted = NULL, *parent_class = NULL,
|
||||
*subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
|
||||
/* DESIGN:
|
||||
*
|
||||
* We work on 4 different 'bins' (implemented here as linked lists):
|
||||
*
|
||||
* * sorted: the eventual list, in the order we wish to push
|
||||
* into the database. This is the only ordered list.
|
||||
*
|
||||
* * parent_class: The current parent class 'bin' we are
|
||||
* trying to find subclasses for
|
||||
*
|
||||
* * subclass: The subclasses we have found so far
|
||||
*
|
||||
* * unsorted: The remaining objectClasses
|
||||
*
|
||||
* The process is a matter of filtering objectClasses up from
|
||||
* unsorted into sorted. Order is irrelevent in the later 3 'bins'.
|
||||
*
|
||||
* We start with 'top' (found and promoted to parent_class
|
||||
* initially). Then we find (in unsorted) all the direct
|
||||
* subclasses of 'top'. parent_classes is concatenated onto
|
||||
* the end of 'sorted', and subclass becomes the list in
|
||||
* parent_class.
|
||||
*
|
||||
* We then repeat, until we find no more subclasses. Any left
|
||||
* over classes are added to the end.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Firstly, dump all the objectClass elements into the
|
||||
* unsorted bin, except for 'top', which is special */
|
||||
for (i=0; i < objectclass_element->num_values; i++) {
|
||||
current = talloc(mem_ctx, struct class_list);
|
||||
if (!current) {
|
||||
ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list");
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
current->objectclass = (const char *)objectclass_element->values[i].data;
|
||||
|
||||
/* this is the root of the tree. We will start
|
||||
* looking for subclasses from here */
|
||||
if (ldb_attr_cmp("top", current->objectclass) == 0) {
|
||||
DLIST_ADD(parent_class, current);
|
||||
} else {
|
||||
DLIST_ADD(unsorted, current);
|
||||
}
|
||||
}
|
||||
|
||||
/* DEBUGGING aid: how many layers are we down now? */
|
||||
layer = 0;
|
||||
do {
|
||||
layer++;
|
||||
/* Find all the subclasses of classes in the
|
||||
* parent_classes. Push them onto the subclass list */
|
||||
|
||||
/* Ensure we don't bother if there are no unsorted entries left */
|
||||
for (current = parent_class; unsorted && current; current = current->next) {
|
||||
const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass);
|
||||
|
||||
/* Walk the list of possible subclasses in unsorted */
|
||||
for (poss_subclass = unsorted; poss_subclass; ) {
|
||||
struct class_list *next;
|
||||
|
||||
/* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
|
||||
next = poss_subclass->next;
|
||||
|
||||
for (i = 0; subclasses && subclasses[i]; i++) {
|
||||
if (ldb_attr_cmp(poss_subclass->objectclass, subclasses[i]) == 0) {
|
||||
DLIST_REMOVE(unsorted, poss_subclass);
|
||||
DLIST_ADD(subclass, poss_subclass);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
poss_subclass = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now push the parent_classes as sorted, we are done with
|
||||
these. Add to the END of the list by concatenation */
|
||||
DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
|
||||
|
||||
/* and now find subclasses of these */
|
||||
parent_class = subclass;
|
||||
subclass = NULL;
|
||||
|
||||
/* If we didn't find any subclasses we will fall out
|
||||
* the bottom here */
|
||||
} while (parent_class);
|
||||
|
||||
/* This shouldn't happen, and would break MMC, but we can't
|
||||
* afford to loose objectClasses. Perhaps there was no 'top',
|
||||
* or some other schema error?
|
||||
*
|
||||
* Detecting schema errors is the job of the schema module, so
|
||||
* at this layer we just try not to loose data
|
||||
*/
|
||||
DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
|
||||
|
||||
*sorted_out = sorted;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_message_element *objectclass_element;
|
||||
struct class_list *sorted, *current;
|
||||
struct ldb_request *down_req;
|
||||
struct ldb_message *msg;
|
||||
int ret;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
|
||||
|
||||
if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass");
|
||||
|
||||
/* If no part of this add has an objectClass, then we don't
|
||||
* need to make any changes. cn=rootdse doesn't have an objectClass */
|
||||
if (!objectclass_element) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(req);
|
||||
if (mem_ctx == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* prepare the first operation */
|
||||
down_req = talloc(req, struct ldb_request);
|
||||
if (down_req == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of memory!");
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
*down_req = *req; /* copy the request */
|
||||
|
||||
down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
|
||||
|
||||
if (down_req->op.add.message == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ldb_msg_remove_attr(msg, "objectClass");
|
||||
ret = ldb_msg_add_empty(msg, "objectClass", 0, NULL);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We must completely replace the existing objectClass entry,
|
||||
* because we need it sorted */
|
||||
|
||||
/* Move from the linked list back into an ldb msg */
|
||||
for (current = sorted; current; current = current->next) {
|
||||
ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
ret = ldb_msg_sanity_check(module->ldb, msg);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* go on with the call chain */
|
||||
ret = ldb_next_request(module, down_req);
|
||||
|
||||
/* do not free down_req as the call results may be linked to it,
|
||||
* it will be freed when the upper level request get freed */
|
||||
if (ret == LDB_SUCCESS) {
|
||||
req->handle = down_req->handle;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_message_element *objectclass_element;
|
||||
struct ldb_message *msg;
|
||||
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
|
||||
|
||||
if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
|
||||
|
||||
/* If no part of this touches the objectClass, then we don't
|
||||
* need to make any changes. */
|
||||
/* If the only operation is the deletion of the objectClass then go on */
|
||||
if (!objectclass_element) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
|
||||
case LDB_FLAG_MOD_DELETE:
|
||||
/* Delete everything? Probably totally illigal, but hey! */
|
||||
if (objectclass_element->num_values == 0) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
break;
|
||||
case LDB_FLAG_MOD_REPLACE:
|
||||
{
|
||||
struct ldb_request *down_req;
|
||||
struct class_list *sorted, *current;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int ret;
|
||||
mem_ctx = talloc_new(req);
|
||||
if (mem_ctx == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* prepare the first operation */
|
||||
down_req = talloc(req, struct ldb_request);
|
||||
if (down_req == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of memory!");
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
*down_req = *req; /* copy the request */
|
||||
|
||||
down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
|
||||
|
||||
if (down_req->op.add.message == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We must completely replace the existing objectClass entry,
|
||||
* because we need it sorted */
|
||||
|
||||
ldb_msg_remove_attr(msg, "objectClass");
|
||||
ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Move from the linked list back into an ldb msg */
|
||||
for (current = sorted; current; current = current->next) {
|
||||
ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
ret = ldb_msg_sanity_check(module->ldb, msg);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* go on with the call chain */
|
||||
ret = ldb_next_request(module, down_req);
|
||||
|
||||
/* do not free down_req as the call results may be linked to it,
|
||||
* it will be freed when the upper level request get freed */
|
||||
if (ret == LDB_SUCCESS) {
|
||||
req->handle = down_req->handle;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
struct ldb_handle *h;
|
||||
struct oc_context *ac;
|
||||
|
||||
h = oc_init_handle(req, module);
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct oc_context);
|
||||
|
||||
/* return or own handle to deal with this call */
|
||||
req->handle = h;
|
||||
|
||||
/* prepare the first operation */
|
||||
ac->down_req = talloc(ac, struct ldb_request);
|
||||
if (ac->down_req == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of memory!");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
*(ac->down_req) = *req; /* copy the request */
|
||||
|
||||
ac->down_req->context = NULL;
|
||||
ac->down_req->callback = NULL;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
|
||||
|
||||
ac->step = OC_DO_REQ;
|
||||
|
||||
return ldb_next_request(module, ac->down_req);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct oc_context *ac;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct oc_context);
|
||||
|
||||
/* we are interested only in the single reply (base search) we receive here */
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
if (ac->search_res != NULL) {
|
||||
ldb_set_errstring(ldb, "Too many results");
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->search_res = talloc_move(ac, &ares);
|
||||
} else {
|
||||
talloc_free(ares);
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int objectclass_search_self(struct ldb_handle *h) {
|
||||
|
||||
struct oc_context *ac;
|
||||
static const char * const attrs[] = { "objectClass", NULL };
|
||||
|
||||
ac = talloc_get_type(h->private_data, struct oc_context);
|
||||
|
||||
/* prepare the search operation */
|
||||
ac->search_req = talloc_zero(ac, struct ldb_request);
|
||||
if (ac->search_req == NULL) {
|
||||
ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->search_req->operation = LDB_SEARCH;
|
||||
ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
|
||||
ac->search_req->op.search.scope = LDB_SCOPE_BASE;
|
||||
ac->search_req->op.search.tree = ldb_parse_tree(ac->search_req, NULL);
|
||||
if (ac->search_req->op.search.tree == NULL) {
|
||||
ldb_set_errstring(ac->module->ldb, "objectclass: Internal error producing null search");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac->search_req->op.search.attrs = attrs;
|
||||
ac->search_req->controls = NULL;
|
||||
ac->search_req->context = ac;
|
||||
ac->search_req->callback = get_self_callback;
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
|
||||
|
||||
ac->step = OC_SEARCH_SELF;
|
||||
|
||||
return ldb_next_request(ac->module, ac->search_req);
|
||||
}
|
||||
|
||||
static int objectclass_do_mod(struct ldb_handle *h) {
|
||||
|
||||
struct oc_context *ac;
|
||||
struct ldb_message_element *objectclass_element;
|
||||
struct ldb_message *msg;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
struct class_list *sorted, *current;
|
||||
int ret;
|
||||
|
||||
ac = talloc_get_type(h->private_data, struct oc_context);
|
||||
|
||||
mem_ctx = talloc_new(ac);
|
||||
if (mem_ctx == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->mod_req = talloc(ac, struct ldb_request);
|
||||
if (ac->mod_req == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->mod_req->operation = LDB_MODIFY;
|
||||
ac->mod_req->controls = NULL;
|
||||
ac->mod_req->context = ac;
|
||||
ac->mod_req->callback = NULL;
|
||||
ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
|
||||
|
||||
/* use a new message structure */
|
||||
ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
|
||||
if (msg == NULL) {
|
||||
ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg");
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
/* This is now the objectClass list from the database */
|
||||
objectclass_element = ldb_msg_find_element(ac->search_res->message,
|
||||
"objectClass");
|
||||
if (!objectclass_element) {
|
||||
/* Where did it go? Move along now, nothing to see here */
|
||||
talloc_free(mem_ctx);
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* modify dn */
|
||||
msg->dn = ac->orig_req->op.mod.message->dn;
|
||||
|
||||
ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We must completely replace the existing objectClass entry.
|
||||
* We could do a constrained add/del, but we are meant to be
|
||||
* in a transaction... */
|
||||
|
||||
ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_set_errstring(ac->module->ldb, "objectclass: could not clear objectclass in modify msg");
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Move from the linked list back into an ldb msg */
|
||||
for (current = sorted; current; current = current->next) {
|
||||
ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ldb_msg_sanity_check(ac->module->ldb, msg);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
talloc_free(mem_ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->step = OC_DO_MOD;
|
||||
|
||||
talloc_free(mem_ctx);
|
||||
/* perform the search */
|
||||
return ldb_next_request(ac->module, ac->mod_req);
|
||||
}
|
||||
|
||||
static int oc_wait(struct ldb_handle *handle) {
|
||||
struct oc_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct oc_context);
|
||||
|
||||
switch (ac->step) {
|
||||
case OC_DO_REQ:
|
||||
ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->down_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->down_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* mods done, go on */
|
||||
return objectclass_search_self(handle);
|
||||
|
||||
case OC_SEARCH_SELF:
|
||||
ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->search_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->search_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* self search done, go on */
|
||||
return objectclass_do_mod(handle);
|
||||
|
||||
case OC_DO_MOD:
|
||||
ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->mod_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->mod_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = LDB_SUCCESS;
|
||||
|
||||
done:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int oc_wait_all(struct ldb_handle *handle) {
|
||||
|
||||
int ret;
|
||||
|
||||
while (handle->state != LDB_ASYNC_DONE) {
|
||||
ret = oc_wait(handle);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
if (type == LDB_WAIT_ALL) {
|
||||
return oc_wait_all(handle);
|
||||
} else {
|
||||
return oc_wait(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops objectclass_ops = {
|
||||
.name = "objectclass",
|
||||
.add = objectclass_add,
|
||||
.modify = objectclass_modify,
|
||||
.wait = objectclass_wait
|
||||
};
|
||||
|
||||
int ldb_objectclass_init(void)
|
||||
{
|
||||
return ldb_register_module(&objectclass_ops);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
/*
|
||||
handle operational attributes
|
||||
*/
|
||||
|
||||
/*
|
||||
createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
|
||||
modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
|
||||
|
||||
for the above two, we do the search as normal, and if
|
||||
createTimestamp or modifyTimestamp is asked for, then do
|
||||
additional searches for whenCreated and whenChanged and fill in
|
||||
the resulting values
|
||||
|
||||
we also need to replace these with the whenCreated/whenChanged
|
||||
equivalent in the search expression trees
|
||||
|
||||
whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
|
||||
whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
|
||||
|
||||
on init we need to setup attribute handlers for these so
|
||||
comparisons are done correctly. The resolution is 1 second.
|
||||
|
||||
on add we need to add both the above, for current time
|
||||
|
||||
on modify we need to change whenChanged
|
||||
|
||||
|
||||
subschemaSubentry: HIDDEN, not-searchable,
|
||||
points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
|
||||
|
||||
for this one we do the search as normal, then add the static
|
||||
value if requested. How do we work out the $BASEDN from inside a
|
||||
module?
|
||||
|
||||
|
||||
structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
|
||||
|
||||
for this one we do the search as normal, then if requested ask
|
||||
for objectclass, change the attribute name, and add it
|
||||
|
||||
allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
|
||||
list of attributes that can be modified - requires schema lookup
|
||||
|
||||
|
||||
attributeTypes: in schema only
|
||||
objectClasses: in schema only
|
||||
matchingRules: in schema only
|
||||
matchingRuleUse: in schema only
|
||||
creatorsName: not supported by w2k3?
|
||||
modifiersName: not supported by w2k3?
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
/*
|
||||
construct a canonical name from a message
|
||||
*/
|
||||
static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
|
||||
{
|
||||
char *canonicalName;
|
||||
canonicalName = ldb_dn_canonical_string(msg, msg->dn);
|
||||
if (canonicalName == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
|
||||
}
|
||||
|
||||
/*
|
||||
a list of attribute names that should be substituted in the parse
|
||||
tree before the search is done
|
||||
*/
|
||||
static const struct {
|
||||
const char *attr;
|
||||
const char *replace;
|
||||
} parse_tree_sub[] = {
|
||||
{ "createTimestamp", "whenCreated" },
|
||||
{ "modifyTimestamp", "whenChanged" }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
a list of attribute names that are hidden, but can be searched for
|
||||
using another (non-hidden) name to produce the correct result
|
||||
*/
|
||||
static const struct {
|
||||
const char *attr;
|
||||
const char *replace;
|
||||
int (*constructor)(struct ldb_module *, struct ldb_message *);
|
||||
} search_sub[] = {
|
||||
{ "createTimestamp", "whenCreated", NULL },
|
||||
{ "modifyTimestamp", "whenChanged", NULL },
|
||||
{ "structuralObjectClass", "objectClass", NULL },
|
||||
{ "canonicalName", "distinguishedName", construct_canonical_name }
|
||||
};
|
||||
|
||||
/*
|
||||
post process a search result record. For any search_sub[] attributes that were
|
||||
asked for, we need to call the appropriate copy routine to copy the result
|
||||
into the message, then remove any attributes that we added to the search but were
|
||||
not asked for by the user
|
||||
*/
|
||||
static int operational_search_post_process(struct ldb_module *module,
|
||||
struct ldb_message *msg,
|
||||
const char * const *attrs)
|
||||
{
|
||||
int i, a=0;
|
||||
|
||||
for (a=0;attrs && attrs[a];a++) {
|
||||
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
|
||||
if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* construct the new attribute, using either a supplied
|
||||
constructor or a simple copy */
|
||||
if (search_sub[i].constructor) {
|
||||
if (search_sub[i].constructor(module, msg) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
} else if (ldb_msg_copy_attr(msg,
|
||||
search_sub[i].replace,
|
||||
search_sub[i].attr) != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* remove the added search attribute, unless it was asked for
|
||||
by the user */
|
||||
if (search_sub[i].replace == NULL ||
|
||||
ldb_attr_in_list(attrs, search_sub[i].replace) ||
|
||||
ldb_attr_in_list(attrs, "*")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ldb_msg_remove_attr(msg, search_sub[i].replace);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
|
||||
"operational_search_post_process failed for attribute '%s'\n",
|
||||
attrs[a]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
hook search operations
|
||||
*/
|
||||
|
||||
struct operational_context {
|
||||
|
||||
struct ldb_module *module;
|
||||
void *up_context;
|
||||
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
const char * const *attrs;
|
||||
};
|
||||
|
||||
static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct operational_context *ac;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct operational_context);
|
||||
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
/* for each record returned post-process to add any derived
|
||||
attributes that have been asked for */
|
||||
if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return ac->up_callback(ldb, ac->up_context, ares);
|
||||
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int operational_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct operational_context *ac;
|
||||
struct ldb_request *down_req;
|
||||
const char **search_attrs = NULL;
|
||||
int i, a, ret;
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
ac = talloc(req, struct operational_context);
|
||||
if (ac == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->module = module;
|
||||
ac->up_context = req->context;
|
||||
ac->up_callback = req->callback;
|
||||
ac->attrs = req->op.search.attrs;
|
||||
|
||||
down_req = talloc_zero(req, struct ldb_request);
|
||||
if (down_req == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
down_req->operation = req->operation;
|
||||
down_req->op.search.base = req->op.search.base;
|
||||
down_req->op.search.scope = req->op.search.scope;
|
||||
down_req->op.search.tree = req->op.search.tree;
|
||||
|
||||
/* FIXME: I hink we should copy the tree and keep the original
|
||||
* unmodified. SSS */
|
||||
/* replace any attributes in the parse tree that are
|
||||
searchable, but are stored using a different name in the
|
||||
backend */
|
||||
for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
|
||||
ldb_parse_tree_attr_replace(req->op.search.tree,
|
||||
parse_tree_sub[i].attr,
|
||||
parse_tree_sub[i].replace);
|
||||
}
|
||||
|
||||
/* in the list of attributes we are looking for, rename any
|
||||
attributes to the alias for any hidden attributes that can
|
||||
be fetched directly using non-hidden names */
|
||||
for (a=0;ac->attrs && ac->attrs[a];a++) {
|
||||
for (i=0;i<ARRAY_SIZE(search_sub);i++) {
|
||||
if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
|
||||
search_sub[i].replace) {
|
||||
if (!search_attrs) {
|
||||
search_attrs = ldb_attr_list_copy(req, ac->attrs);
|
||||
if (search_attrs == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
}
|
||||
search_attrs[a] = search_sub[i].replace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* use new set of attrs if any */
|
||||
if (search_attrs) down_req->op.search.attrs = search_attrs;
|
||||
else down_req->op.search.attrs = req->op.search.attrs;
|
||||
|
||||
down_req->controls = req->controls;
|
||||
|
||||
down_req->context = ac;
|
||||
down_req->callback = operational_callback;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
|
||||
|
||||
/* perform the search */
|
||||
ret = ldb_next_request(module, down_req);
|
||||
|
||||
/* do not free down_req as the call results may be linked to it,
|
||||
* it will be freed when the upper level request get freed */
|
||||
if (ret == LDB_SUCCESS) {
|
||||
req->handle = down_req->handle;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int operational_init(struct ldb_module *ctx)
|
||||
{
|
||||
/* setup some standard attribute handlers */
|
||||
ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
|
||||
ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
|
||||
ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
|
||||
ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
|
||||
|
||||
return ldb_next_init(ctx);
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops operational_ops = {
|
||||
.name = "operational",
|
||||
.search = operational_search,
|
||||
.init_context = operational_init
|
||||
};
|
||||
|
||||
int ldb_operational_init(void)
|
||||
{
|
||||
return ldb_register_module(&operational_ops);
|
||||
}
|
||||
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005-2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: paged_result
|
||||
*
|
||||
* Component: ldb paged results control module
|
||||
*
|
||||
* Description: this module caches a complete search and sends back
|
||||
* results in chunks as asked by the client
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
struct message_store {
|
||||
/* keep the whole ldb_reply as an optimization
|
||||
* instead of freeing and talloc-ing the container
|
||||
* on each result */
|
||||
struct ldb_reply *r;
|
||||
struct message_store *next;
|
||||
};
|
||||
|
||||
struct private_data;
|
||||
|
||||
struct results_store {
|
||||
|
||||
struct private_data *priv;
|
||||
|
||||
char *cookie;
|
||||
time_t timestamp;
|
||||
|
||||
struct results_store *prev;
|
||||
struct results_store *next;
|
||||
|
||||
struct message_store *first;
|
||||
struct message_store *last;
|
||||
int num_entries;
|
||||
|
||||
struct message_store *first_ref;
|
||||
struct message_store *last_ref;
|
||||
|
||||
struct ldb_control **controls;
|
||||
|
||||
struct ldb_request *req;
|
||||
};
|
||||
|
||||
struct private_data {
|
||||
|
||||
int next_free_id;
|
||||
struct results_store *store;
|
||||
|
||||
};
|
||||
|
||||
int store_destructor(struct results_store *store)
|
||||
{
|
||||
if (store->prev) {
|
||||
store->prev->next = store->next;
|
||||
}
|
||||
if (store->next) {
|
||||
store->next->prev = store->prev;
|
||||
}
|
||||
|
||||
if (store == store->priv->store) {
|
||||
store->priv->store = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct results_store *new_store(struct private_data *priv)
|
||||
{
|
||||
struct results_store *newr;
|
||||
int new_id = priv->next_free_id++;
|
||||
|
||||
/* TODO: we should have a limit on the number of
|
||||
* outstanding paged searches
|
||||
*/
|
||||
|
||||
newr = talloc(priv, struct results_store);
|
||||
if (!newr) return NULL;
|
||||
|
||||
newr->priv = priv;
|
||||
|
||||
newr->cookie = talloc_asprintf(newr, "%d", new_id);
|
||||
if (!newr->cookie) {
|
||||
talloc_free(newr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newr->timestamp = time(NULL);
|
||||
|
||||
newr->first = NULL;
|
||||
newr->num_entries = 0;
|
||||
newr->first_ref = NULL;
|
||||
newr->controls = NULL;
|
||||
|
||||
/* put this entry as first */
|
||||
newr->prev = NULL;
|
||||
newr->next = priv->store;
|
||||
if (priv->store != NULL) priv->store->prev = newr;
|
||||
priv->store = newr;
|
||||
|
||||
talloc_set_destructor(newr, store_destructor);
|
||||
|
||||
return newr;
|
||||
}
|
||||
|
||||
struct paged_context {
|
||||
struct ldb_module *module;
|
||||
void *up_context;
|
||||
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
int size;
|
||||
|
||||
struct results_store *store;
|
||||
};
|
||||
|
||||
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
|
||||
{
|
||||
struct paged_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(mem_ctx, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct paged_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->up_context = context;
|
||||
ac->up_callback = callback;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct paged_context *ac = NULL;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct paged_context);
|
||||
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
if (ac->store->first == NULL) {
|
||||
ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
|
||||
} else {
|
||||
ac->store->last->next = talloc(ac->store, struct message_store);
|
||||
ac->store->last = ac->store->last->next;
|
||||
}
|
||||
if (ac->store->last == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac->store->num_entries++;
|
||||
|
||||
ac->store->last->r = talloc_steal(ac->store->last, ares);
|
||||
ac->store->last->next = NULL;
|
||||
}
|
||||
|
||||
if (ares->type == LDB_REPLY_REFERRAL) {
|
||||
if (ac->store->first_ref == NULL) {
|
||||
ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
|
||||
} else {
|
||||
ac->store->last_ref->next = talloc(ac->store, struct message_store);
|
||||
ac->store->last_ref = ac->store->last_ref->next;
|
||||
}
|
||||
if (ac->store->last_ref == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
|
||||
ac->store->last_ref->next = NULL;
|
||||
}
|
||||
|
||||
if (ares->type == LDB_REPLY_DONE) {
|
||||
ac->store->controls = talloc_move(ac->store, &ares->controls);
|
||||
talloc_free(ares);
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int paged_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_control *control;
|
||||
struct private_data *private_data;
|
||||
struct ldb_paged_control *paged_ctrl;
|
||||
struct ldb_control **saved_controls;
|
||||
struct paged_context *ac;
|
||||
struct ldb_handle *h;
|
||||
int ret;
|
||||
|
||||
/* check if there's a paged request control */
|
||||
control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
|
||||
if (control == NULL) {
|
||||
/* not found go on */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
private_data = talloc_get_type(module->private_data, struct private_data);
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb,
|
||||
"Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
|
||||
if (!paged_ctrl) {
|
||||
return LDB_ERR_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
h = init_handle(req, module, req->context, req->callback);
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct paged_context);
|
||||
|
||||
ac->size = paged_ctrl->size;
|
||||
|
||||
/* check if it is a continuation search the store */
|
||||
if (paged_ctrl->cookie_len == 0) {
|
||||
|
||||
ac->store = new_store(private_data);
|
||||
if (ac->store == NULL) {
|
||||
talloc_free(h);
|
||||
return LDB_ERR_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
|
||||
ac->store->req = talloc(ac->store, struct ldb_request);
|
||||
if (!ac->store->req)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ac->store->req->operation = req->operation;
|
||||
ac->store->req->op.search.base = req->op.search.base;
|
||||
ac->store->req->op.search.scope = req->op.search.scope;
|
||||
ac->store->req->op.search.tree = req->op.search.tree;
|
||||
ac->store->req->op.search.attrs = req->op.search.attrs;
|
||||
ac->store->req->controls = req->controls;
|
||||
|
||||
/* save it locally and remove it from the list */
|
||||
/* we do not need to replace them later as we
|
||||
* are keeping the original req intact */
|
||||
if (!save_controls(control, ac->store->req, &saved_controls)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->store->req->context = ac;
|
||||
ac->store->req->callback = paged_search_callback;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
|
||||
|
||||
ret = ldb_next_request(module, ac->store->req);
|
||||
|
||||
} else {
|
||||
struct results_store *current = NULL;
|
||||
|
||||
for (current = private_data->store; current; current = current->next) {
|
||||
if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
|
||||
current->timestamp = time(NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current == NULL) {
|
||||
talloc_free(h);
|
||||
return LDB_ERR_UNWILLING_TO_PERFORM;
|
||||
}
|
||||
|
||||
ac->store = current;
|
||||
ret = LDB_SUCCESS;
|
||||
}
|
||||
|
||||
req->handle = h;
|
||||
|
||||
/* check if it is an abandon */
|
||||
if (ac->size == 0) {
|
||||
talloc_free(ac->store);
|
||||
h->status = LDB_SUCCESS;
|
||||
h->state = LDB_ASYNC_DONE;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* TODO: age out old outstanding requests */
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int paged_results(struct ldb_handle *handle)
|
||||
{
|
||||
struct paged_context *ac;
|
||||
struct ldb_paged_control *paged;
|
||||
struct ldb_reply *ares;
|
||||
struct message_store *msg;
|
||||
int i, num_ctrls, ret;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct paged_context);
|
||||
|
||||
if (ac->store == NULL)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
while (ac->store->num_entries > 0 && ac->size > 0) {
|
||||
msg = ac->store->first;
|
||||
ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ac->store->first = msg->next;
|
||||
talloc_free(msg);
|
||||
ac->store->num_entries--;
|
||||
ac->size--;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
|
||||
while (ac->store->first_ref != NULL) {
|
||||
msg = ac->store->first_ref;
|
||||
ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ac->store->first_ref = msg->next;
|
||||
talloc_free(msg);
|
||||
}
|
||||
|
||||
ares = talloc_zero(ac->store, struct ldb_reply);
|
||||
if (ares == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
num_ctrls = 2;
|
||||
i = 0;
|
||||
|
||||
if (ac->store->controls != NULL) {
|
||||
ares->controls = ac->store->controls;
|
||||
while (ares->controls[i]) i++; /* counting */
|
||||
|
||||
ares->controls = talloc_move(ares, &ac->store->controls);
|
||||
num_ctrls += i;
|
||||
}
|
||||
|
||||
ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
|
||||
if (ares->controls == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->controls[i] = talloc(ares->controls, struct ldb_control);
|
||||
if (ares->controls[i] == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
|
||||
if (ares->controls[i]->oid == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->controls[i]->critical = 0;
|
||||
ares->controls[i + 1] = NULL;
|
||||
|
||||
paged = talloc(ares->controls[i], struct ldb_paged_control);
|
||||
if (paged == NULL) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->controls[i]->data = paged;
|
||||
|
||||
if (ac->size > 0) {
|
||||
paged->size = 0;
|
||||
paged->cookie = NULL;
|
||||
paged->cookie_len = 0;
|
||||
} else {
|
||||
paged->size = ac->store->num_entries;
|
||||
paged->cookie = talloc_strdup(paged, ac->store->cookie);
|
||||
paged->cookie_len = strlen(paged->cookie) + 1;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
|
||||
ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
|
||||
handle->status = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
struct paged_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct paged_context);
|
||||
|
||||
if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
|
||||
/* if lower level is finished we do not need to call it anymore */
|
||||
/* return all we have until size == 0 or we empty storage */
|
||||
ret = paged_results(handle);
|
||||
|
||||
/* we are done, if num_entries is zero free the storage
|
||||
* as that mean we delivered the last batch */
|
||||
if (ac->store->num_entries == 0) {
|
||||
talloc_free(ac->store);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type == LDB_WAIT_ALL) {
|
||||
while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
|
||||
ret = ldb_wait(ac->store->req->handle, type);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
handle->status = ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = paged_results(handle);
|
||||
|
||||
/* we are done, if num_entries is zero free the storage
|
||||
* as that mean we delivered the last batch */
|
||||
if (ac->store->num_entries == 0) {
|
||||
talloc_free(ac->store);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ldb_wait(ac->store->req->handle, type);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
handle->status = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
handle->status = ret;
|
||||
|
||||
if (ac->store->num_entries >= ac->size ||
|
||||
ac->store->req->handle->state == LDB_ASYNC_DONE) {
|
||||
|
||||
ret = paged_results(handle);
|
||||
|
||||
/* we are done, if num_entries is zero free the storage
|
||||
* as that mean we delivered the last batch */
|
||||
if (ac->store->num_entries == 0) {
|
||||
talloc_free(ac->store);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int paged_request_init(struct ldb_module *module)
|
||||
{
|
||||
struct private_data *data;
|
||||
struct ldb_request *req;
|
||||
int ret;
|
||||
|
||||
data = talloc(module, struct private_data);
|
||||
if (data == NULL) {
|
||||
return LDB_ERR_OTHER;
|
||||
}
|
||||
|
||||
data->next_free_id = 1;
|
||||
data->store = NULL;
|
||||
module->private_data = data;
|
||||
|
||||
req = talloc(module, struct ldb_request);
|
||||
if (req == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->operation = LDB_REQ_REGISTER_CONTROL;
|
||||
req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
|
||||
req->controls = NULL;
|
||||
|
||||
ret = ldb_request(module->ldb, req);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "paged_request: Unable to register control with rootdse!\n");
|
||||
}
|
||||
|
||||
talloc_free(req);
|
||||
return ldb_next_init(module);
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops paged_ops = {
|
||||
.name = "paged_results",
|
||||
.search = paged_search,
|
||||
.wait = paged_wait,
|
||||
.init_context = paged_request_init
|
||||
};
|
||||
|
||||
int ldb_paged_results_init(void)
|
||||
{
|
||||
return ldb_register_module(&paged_ops);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005-2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: paged_searches
|
||||
*
|
||||
* Component: ldb paged searches module
|
||||
*
|
||||
* Description: this module detects if the remote ldap server supports
|
||||
* paged results and use them to transparently access all objects
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#define PS_DEFAULT_PAGE_SIZE 500
|
||||
/* 500 objects per query seem to be a decent compromise
|
||||
* the default AD limit per request is 1000 entries */
|
||||
|
||||
struct private_data {
|
||||
|
||||
bool paged_supported;
|
||||
};
|
||||
|
||||
struct ps_context {
|
||||
struct ldb_module *module;
|
||||
void *up_context;
|
||||
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
struct ldb_request *orig_req;
|
||||
|
||||
struct ldb_request *new_req;
|
||||
|
||||
bool pending;
|
||||
|
||||
char **saved_referrals;
|
||||
int num_referrals;
|
||||
};
|
||||
|
||||
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
|
||||
{
|
||||
struct ps_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(mem_ctx, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct ps_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->up_context = context;
|
||||
ac->up_callback = callback;
|
||||
|
||||
ac->pending = False;
|
||||
ac->saved_referrals = NULL;
|
||||
ac->num_referrals = 0;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int check_ps_continuation(struct ldb_reply *ares, struct ps_context *ac)
|
||||
{
|
||||
struct ldb_paged_control *rep_control, *req_control;
|
||||
|
||||
/* look up our paged control */
|
||||
if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) {
|
||||
/* something wrong here */
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control);
|
||||
if (rep_control->cookie_len == 0) {
|
||||
/* we are done */
|
||||
ac->pending = False;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* more processing required */
|
||||
/* let's fill in the request control with the new cookie */
|
||||
/* if there's a reply control we must find a request
|
||||
* control matching it */
|
||||
|
||||
if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ac->new_req->controls[0]->oid) != 0) {
|
||||
/* something wrong here */
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req_control = talloc_get_type(ac->new_req->controls[0]->data, struct ldb_paged_control);
|
||||
|
||||
if (req_control->cookie) {
|
||||
talloc_free(req_control->cookie);
|
||||
}
|
||||
|
||||
req_control->cookie = talloc_memdup(req_control,
|
||||
rep_control->cookie,
|
||||
rep_control->cookie_len);
|
||||
req_control->cookie_len = rep_control->cookie_len;
|
||||
|
||||
ac->pending = True;
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int store_referral(char *referral, struct ps_context *ac)
|
||||
{
|
||||
ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
|
||||
if (!ac->saved_referrals) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
|
||||
if (!ac->saved_referrals[ac->num_referrals]) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->num_referrals++;
|
||||
ac->saved_referrals[ac->num_referrals] = NULL;
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int send_referrals(struct ldb_context *ldb, struct ps_context *ac)
|
||||
{
|
||||
struct ldb_reply *ares;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ac->num_referrals; i++) {
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_REFERRAL;
|
||||
ares->referral = ac->saved_referrals[i];
|
||||
|
||||
ac->up_callback(ldb, ac->up_context, ares);
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int ps_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct ps_context *ac = NULL;
|
||||
int ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct ps_context);
|
||||
|
||||
switch (ares->type) {
|
||||
case LDB_REPLY_ENTRY:
|
||||
ac->up_callback(ldb, ac->up_context, ares);
|
||||
break;
|
||||
|
||||
case LDB_REPLY_REFERRAL:
|
||||
ret = store_referral(ares->referral, ac);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case LDB_REPLY_DONE:
|
||||
ret = check_ps_continuation(ares, ac);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
if (!ac->pending) {
|
||||
/* send referrals */
|
||||
ret = send_referrals(ldb, ac);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* send REPLY_DONE */
|
||||
ac->up_callback(ldb, ac->up_context, ares);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ps_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct private_data *private_data;
|
||||
struct ldb_paged_control *control;
|
||||
struct ps_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
private_data = talloc_get_type(module->private_data, struct private_data);
|
||||
|
||||
/* check if paging is supported and if there is a any control */
|
||||
if (!private_data || !private_data->paged_supported || req->controls) {
|
||||
/* do not touch this request paged controls not
|
||||
* supported or explicit controls have been set or we
|
||||
* are just not setup yet */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb,
|
||||
"Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
h = init_handle(req, module, req->context, req->callback);
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct ps_context);
|
||||
|
||||
ac->new_req = talloc(ac, struct ldb_request);
|
||||
if (!ac->new_req) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ac->new_req->controls = talloc_array(ac->new_req, struct ldb_control *, 2);
|
||||
if (!ac->new_req->controls) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ac->new_req->controls[0] = talloc(ac->new_req->controls, struct ldb_control);
|
||||
if (!ac->new_req->controls[0]) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
control = talloc(ac->new_req->controls[0], struct ldb_paged_control);
|
||||
if (!control) return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
control->size = PS_DEFAULT_PAGE_SIZE;
|
||||
control->cookie = NULL;
|
||||
control->cookie_len = 0;
|
||||
|
||||
ac->new_req->controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
|
||||
ac->new_req->controls[0]->critical = 1;
|
||||
ac->new_req->controls[0]->data = control;
|
||||
|
||||
ac->new_req->controls[1] = NULL;
|
||||
|
||||
ac->new_req->operation = req->operation;
|
||||
ac->new_req->op.search.base = req->op.search.base;
|
||||
ac->new_req->op.search.scope = req->op.search.scope;
|
||||
ac->new_req->op.search.tree = req->op.search.tree;
|
||||
ac->new_req->op.search.attrs = req->op.search.attrs;
|
||||
ac->new_req->context = ac;
|
||||
ac->new_req->callback = ps_callback;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, ac->new_req);
|
||||
|
||||
req->handle = h;
|
||||
|
||||
return ldb_next_request(module, ac->new_req);
|
||||
}
|
||||
|
||||
static int ps_continuation(struct ldb_handle *handle)
|
||||
{
|
||||
struct ps_context *ac;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct ps_context);
|
||||
|
||||
/* reset the requests handle */
|
||||
ac->new_req->handle = NULL;
|
||||
|
||||
return ldb_next_request(handle->module, ac->new_req);
|
||||
}
|
||||
|
||||
static int ps_wait_none(struct ldb_handle *handle)
|
||||
{
|
||||
struct ps_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct ps_context);
|
||||
|
||||
ret = ldb_wait(ac->new_req->handle, LDB_WAIT_NONE);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->new_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->new_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->new_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* see if we need to send another request for the next batch */
|
||||
if (ac->pending) {
|
||||
ret = ps_continuation(handle);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* continue the search with the next request */
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
ret = LDB_SUCCESS;
|
||||
|
||||
done:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ps_wait_all(struct ldb_handle *handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (handle->state != LDB_ASYNC_DONE) {
|
||||
ret = ps_wait_none(handle);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
static int ps_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
if (type == LDB_WAIT_ALL) {
|
||||
return ps_wait_all(handle);
|
||||
} else {
|
||||
return ps_wait_none(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static int check_supported_paged(struct ldb_context *ldb, void *context,
|
||||
struct ldb_reply *ares)
|
||||
{
|
||||
struct private_data *data;
|
||||
data = talloc_get_type(context,
|
||||
struct private_data);
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
if (ldb_msg_check_string_attribute(ares->message,
|
||||
"supportedControl",
|
||||
LDB_CONTROL_PAGED_RESULTS_OID)) {
|
||||
data->paged_supported = True;
|
||||
}
|
||||
}
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static int ps_init(struct ldb_module *module)
|
||||
{
|
||||
static const char *attrs[] = { "supportedControl", NULL };
|
||||
struct private_data *data;
|
||||
int ret;
|
||||
struct ldb_request *req;
|
||||
|
||||
data = talloc(module, struct private_data);
|
||||
if (data == NULL) {
|
||||
return LDB_ERR_OTHER;
|
||||
}
|
||||
module->private_data = data;
|
||||
data->paged_supported = False;
|
||||
|
||||
req = talloc(module, struct ldb_request);
|
||||
if (req == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->operation = LDB_SEARCH;
|
||||
req->op.search.base = ldb_dn_new(req, module->ldb, NULL);
|
||||
req->op.search.scope = LDB_SCOPE_BASE;
|
||||
|
||||
req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
|
||||
if (req->op.search.tree == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Unable to parse search expression");
|
||||
talloc_free(req);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->op.search.attrs = attrs;
|
||||
req->controls = NULL;
|
||||
req->context = data;
|
||||
req->callback = check_supported_paged;
|
||||
ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
|
||||
|
||||
ret = ldb_next_request(module, req);
|
||||
|
||||
if (ret == LDB_SUCCESS) {
|
||||
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
|
||||
}
|
||||
|
||||
talloc_free(req);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ldb_next_init(module);
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops ps_ops = {
|
||||
.name = "paged_searches",
|
||||
.search = ps_search,
|
||||
.wait = ps_wait,
|
||||
.init_context = ps_init
|
||||
};
|
||||
|
||||
int ldb_paged_searches_init(void)
|
||||
{
|
||||
return ldb_register_module(&ps_ops);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Andrew Bartlet 2005
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: rdb_name
|
||||
*
|
||||
* Component: ldb rdn name module
|
||||
*
|
||||
* Description: keep a consistent name attribute on objects manpulations
|
||||
*
|
||||
* Author: Andrew Bartlet
|
||||
*
|
||||
* Modifications:
|
||||
* - made the module async
|
||||
* Simo Sorce Mar 2006
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < msg->num_elements; i++) {
|
||||
if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
|
||||
return &msg->elements[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_request *down_req;
|
||||
struct ldb_message *msg;
|
||||
struct ldb_message_element *attribute;
|
||||
const char *rdn_name;
|
||||
struct ldb_val rdn_val;
|
||||
int i, ret;
|
||||
|
||||
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
|
||||
|
||||
/* do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(req->op.add.message->dn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
down_req = talloc(req, struct ldb_request);
|
||||
if (down_req == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
*down_req = *req;
|
||||
|
||||
down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
rdn_name = ldb_dn_get_rdn_name(msg->dn);
|
||||
if (rdn_name == NULL) {
|
||||
talloc_free(down_req);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(msg->dn));
|
||||
|
||||
/* Perhaps someone above us tried to set this? */
|
||||
if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
|
||||
attribute->num_values = 0;
|
||||
}
|
||||
|
||||
if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
|
||||
talloc_free(down_req);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
attribute = rdn_name_find_attribute(msg, rdn_name);
|
||||
|
||||
if (!attribute) {
|
||||
if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
|
||||
talloc_free(down_req);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
} else {
|
||||
const struct ldb_attrib_handler *handler = ldb_attrib_handler(module->ldb, rdn_name);
|
||||
|
||||
for (i = 0; i < attribute->num_values; i++) {
|
||||
if (handler->comparison_fn(module->ldb, msg, &rdn_val, &attribute->values[i]) == 0) {
|
||||
/* overwrite so it matches in case */
|
||||
attribute->values[i] = rdn_val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == attribute->num_values) {
|
||||
ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
|
||||
"RDN mismatch on %s: %s (%s)",
|
||||
ldb_dn_get_linearized(msg->dn), rdn_name, rdn_val.data);
|
||||
talloc_free(down_req);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* go on with the call chain */
|
||||
ret = ldb_next_request(module, down_req);
|
||||
|
||||
/* do not free down_req as the call results may be linked to it,
|
||||
* it will be freed when the upper level request get freed */
|
||||
if (ret == LDB_SUCCESS) {
|
||||
req->handle = down_req->handle;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct rename_context {
|
||||
|
||||
enum {RENAME_RENAME, RENAME_MODIFY} step;
|
||||
struct ldb_request *orig_req;
|
||||
struct ldb_request *down_req;
|
||||
struct ldb_request *mod_req;
|
||||
};
|
||||
|
||||
static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_handle *h;
|
||||
struct rename_context *ac;
|
||||
|
||||
ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n");
|
||||
|
||||
/* do not manipulate our control entries */
|
||||
if (ldb_dn_is_special(req->op.rename.newdn)) {
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
h = talloc_zero(req, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct rename_context);
|
||||
if (ac == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->orig_req = req;
|
||||
ac->down_req = talloc(req, struct ldb_request);
|
||||
if (ac->down_req == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
*(ac->down_req) = *req;
|
||||
|
||||
ac->step = RENAME_RENAME;
|
||||
|
||||
req->handle = h;
|
||||
|
||||
/* rename first, modify "name" if rename is ok */
|
||||
return ldb_next_request(module, ac->down_req);
|
||||
}
|
||||
|
||||
static int rdn_name_rename_do_mod(struct ldb_handle *h) {
|
||||
|
||||
struct rename_context *ac;
|
||||
const char *rdn_name;
|
||||
struct ldb_val rdn_val;
|
||||
struct ldb_message *msg;
|
||||
|
||||
ac = talloc_get_type(h->private_data, struct rename_context);
|
||||
|
||||
ac->mod_req = talloc_zero(ac, struct ldb_request);
|
||||
|
||||
ac->mod_req->operation = LDB_MODIFY;
|
||||
ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
|
||||
if (msg == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
msg->dn = ldb_dn_copy(msg, ac->orig_req->op.rename.newdn);
|
||||
if (msg->dn == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
rdn_name = ldb_dn_get_rdn_name(ac->orig_req->op.rename.newdn);
|
||||
if (rdn_name == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
rdn_val = ldb_val_dup(msg, ldb_dn_get_rdn_val(ac->orig_req->op.rename.newdn));
|
||||
|
||||
if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ldb_set_timeout_from_prev_req(h->module->ldb, ac->orig_req, ac->mod_req);
|
||||
|
||||
ac->step = RENAME_MODIFY;
|
||||
|
||||
/* do the mod call */
|
||||
return ldb_request(h->module->ldb, ac->mod_req);
|
||||
}
|
||||
|
||||
static int rename_wait(struct ldb_handle *handle)
|
||||
{
|
||||
struct rename_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
handle->state = LDB_ASYNC_PENDING;
|
||||
handle->status = LDB_SUCCESS;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct rename_context);
|
||||
|
||||
switch(ac->step) {
|
||||
case RENAME_RENAME:
|
||||
ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->down_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->down_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
/* rename operation done */
|
||||
return rdn_name_rename_do_mod(handle);
|
||||
|
||||
case RENAME_MODIFY:
|
||||
ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
goto done;
|
||||
}
|
||||
if (ac->mod_req->handle->status != LDB_SUCCESS) {
|
||||
handle->status = ac->mod_req->handle->status;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = LDB_ERR_OPERATIONS_ERROR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = LDB_SUCCESS;
|
||||
|
||||
done:
|
||||
handle->state = LDB_ASYNC_DONE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rename_wait_all(struct ldb_handle *handle) {
|
||||
|
||||
int ret;
|
||||
|
||||
while (handle->state != LDB_ASYNC_DONE) {
|
||||
ret = rename_wait(handle);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
static int rdn_name_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
if (type == LDB_WAIT_ALL) {
|
||||
return rename_wait_all(handle);
|
||||
} else {
|
||||
return rename_wait(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops rdn_name_ops = {
|
||||
.name = "rdn_name",
|
||||
.add = rdn_name_add,
|
||||
.rename = rdn_name_rename,
|
||||
.wait = rdn_name_wait
|
||||
};
|
||||
|
||||
|
||||
int ldb_rdn_name_init(void)
|
||||
{
|
||||
return ldb_register_module(&rdn_name_ops);
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2004
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb skel module
|
||||
*
|
||||
* Description: example module
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
struct private_data {
|
||||
|
||||
char *some_private_data;
|
||||
};
|
||||
|
||||
/* search */
|
||||
static int skel_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* add */
|
||||
static int skel_add(struct ldb_module *module, struct ldb_request *req){
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* modify */
|
||||
static int skel_modify(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* delete */
|
||||
static int skel_delete(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* rename */
|
||||
static int skel_rename(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
/* start a transaction */
|
||||
static int skel_start_trans(struct ldb_module *module)
|
||||
{
|
||||
return ldb_next_start_trans(module);
|
||||
}
|
||||
|
||||
/* end a transaction */
|
||||
static int skel_end_trans(struct ldb_module *module)
|
||||
{
|
||||
return ldb_next_end_trans(module);
|
||||
}
|
||||
|
||||
/* delete a transaction */
|
||||
static int skel_del_trans(struct ldb_module *module)
|
||||
{
|
||||
return ldb_next_del_trans(module);
|
||||
}
|
||||
|
||||
static int skel_destructor(struct ldb_module *ctx)
|
||||
{
|
||||
struct private_data *data = talloc_get_type(ctx->private_data, struct private_data);
|
||||
/* put your clean-up functions here */
|
||||
if (data->some_private_data) talloc_free(data->some_private_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int skel_request(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
static int skel_init(struct ldb_module *ctx)
|
||||
{
|
||||
struct private_data *data;
|
||||
|
||||
data = talloc(ctx, struct private_data);
|
||||
if (data == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
data->some_private_data = NULL;
|
||||
ctx->private_data = data;
|
||||
|
||||
talloc_set_destructor (ctx, skel_destructor);
|
||||
|
||||
return ldb_next_init(ctx);
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops skel_ops = {
|
||||
.name = "skel",
|
||||
.init_context = skel_init,
|
||||
.search = skel_search,
|
||||
.add = skel_add,
|
||||
.modify = skel_modify,
|
||||
.del = skel_delete,
|
||||
.rename = skel_rename,
|
||||
.request = skel_request,
|
||||
.start_transaction = skel_start_trans,
|
||||
.end_transaction = skel_end_trans,
|
||||
.del_transaction = skel_del_trans,
|
||||
};
|
||||
|
||||
int ldb_skel_init(void)
|
||||
{
|
||||
return ldb_register_module(&skel_ops);
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*
|
||||
ldb database library
|
||||
|
||||
Copyright (C) Simo Sorce 2005
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Name: ldb
|
||||
*
|
||||
* Component: ldb server side sort control module
|
||||
*
|
||||
* Description: this module sorts the results of a search
|
||||
*
|
||||
* Author: Simo Sorce
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
struct opaque {
|
||||
struct ldb_context *ldb;
|
||||
const struct ldb_attrib_handler *h;
|
||||
const char *attribute;
|
||||
int reverse;
|
||||
int result;
|
||||
};
|
||||
|
||||
struct sort_context {
|
||||
struct ldb_module *module;
|
||||
void *up_context;
|
||||
int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
|
||||
|
||||
char *attributeName;
|
||||
char *orderingRule;
|
||||
int reverse;
|
||||
|
||||
struct ldb_request *req;
|
||||
struct ldb_message **msgs;
|
||||
char **referrals;
|
||||
struct ldb_control **controls;
|
||||
int num_msgs;
|
||||
int num_refs;
|
||||
|
||||
const struct ldb_attrib_handler *h;
|
||||
int sort_result;
|
||||
};
|
||||
|
||||
static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
|
||||
void *context,
|
||||
int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
|
||||
{
|
||||
struct sort_context *ac;
|
||||
struct ldb_handle *h;
|
||||
|
||||
h = talloc_zero(mem_ctx, struct ldb_handle);
|
||||
if (h == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->module = module;
|
||||
|
||||
ac = talloc_zero(h, struct sort_context);
|
||||
if (ac == NULL) {
|
||||
ldb_set_errstring(module->ldb, "Out of Memory");
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->private_data = (void *)ac;
|
||||
|
||||
h->state = LDB_ASYNC_INIT;
|
||||
h->status = LDB_SUCCESS;
|
||||
|
||||
ac->module = module;
|
||||
ac->up_context = context;
|
||||
ac->up_callback = callback;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
|
||||
{
|
||||
struct ldb_control **controls;
|
||||
struct ldb_sort_resp_control *resp;
|
||||
int i;
|
||||
|
||||
if (*ctrls) {
|
||||
controls = *ctrls;
|
||||
for (i = 0; controls[i]; i++);
|
||||
controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
|
||||
} else {
|
||||
i = 0;
|
||||
controls = talloc_array(mem_ctx, struct ldb_control *, 2);
|
||||
}
|
||||
if (! controls )
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
*ctrls = controls;
|
||||
|
||||
controls[i+1] = NULL;
|
||||
controls[i] = talloc(controls, struct ldb_control);
|
||||
if (! controls[i] )
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
|
||||
controls[i]->critical = 0;
|
||||
|
||||
resp = talloc(controls[i], struct ldb_sort_resp_control);
|
||||
if (! resp )
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
resp->result = result;
|
||||
resp->attr_desc = talloc_strdup(resp, desc);
|
||||
|
||||
if (! resp->attr_desc )
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
controls[i]->data = resp;
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
|
||||
{
|
||||
struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
|
||||
struct ldb_message_element *el1, *el2;
|
||||
|
||||
if (ac->sort_result != 0) {
|
||||
/* an error occurred previously,
|
||||
* let's exit the sorting by returning always 0 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
el1 = ldb_msg_find_element(*msg1, ac->attributeName);
|
||||
el2 = ldb_msg_find_element(*msg2, ac->attributeName);
|
||||
|
||||
if (!el1 || !el2) {
|
||||
/* the attribute was not found return and
|
||||
* set an error */
|
||||
ac->sort_result = 53;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ac->reverse)
|
||||
return ac->h->comparison_fn(ac->module->ldb, ac, &el2->values[0], &el1->values[0]);
|
||||
|
||||
return ac->h->comparison_fn(ac->module->ldb, ac, &el1->values[0], &el2->values[0]);
|
||||
}
|
||||
|
||||
static int server_sort_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
|
||||
{
|
||||
struct sort_context *ac = NULL;
|
||||
|
||||
if (!context || !ares) {
|
||||
ldb_set_errstring(ldb, "NULL Context or Result in callback");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(context, struct sort_context);
|
||||
|
||||
if (ares->type == LDB_REPLY_ENTRY) {
|
||||
ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
|
||||
if (! ac->msgs) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac->msgs[ac->num_msgs + 1] = NULL;
|
||||
|
||||
ac->msgs[ac->num_msgs] = talloc_move(ac->msgs, &ares->message);
|
||||
ac->num_msgs++;
|
||||
}
|
||||
|
||||
if (ares->type == LDB_REPLY_REFERRAL) {
|
||||
ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
|
||||
if (! ac->referrals) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
ac->referrals[ac->num_refs + 1] = NULL;
|
||||
ac->referrals[ac->num_refs] = talloc_move(ac->referrals, &ares->referral);
|
||||
|
||||
ac->num_refs++;
|
||||
}
|
||||
|
||||
if (ares->type == LDB_REPLY_DONE) {
|
||||
ac->controls = talloc_move(ac, &ares->controls);
|
||||
}
|
||||
|
||||
talloc_free(ares);
|
||||
return LDB_SUCCESS;
|
||||
|
||||
error:
|
||||
talloc_free(ares);
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
|
||||
{
|
||||
struct ldb_control *control;
|
||||
struct ldb_server_sort_control **sort_ctrls;
|
||||
struct ldb_control **saved_controls;
|
||||
struct sort_context *ac;
|
||||
struct ldb_handle *h;
|
||||
int ret;
|
||||
|
||||
/* check if there's a paged request control */
|
||||
control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
|
||||
if (control == NULL) {
|
||||
/* not found go on */
|
||||
return ldb_next_request(module, req);
|
||||
}
|
||||
|
||||
req->handle = NULL;
|
||||
|
||||
if (!req->callback || !req->context) {
|
||||
ldb_set_errstring(module->ldb,
|
||||
"Async interface called with NULL callback function or NULL context");
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
h = init_handle(req, module, req->context, req->callback);
|
||||
if (!h) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
ac = talloc_get_type(h->private_data, struct sort_context);
|
||||
|
||||
sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
|
||||
if (!sort_ctrls) {
|
||||
return LDB_ERR_PROTOCOL_ERROR;
|
||||
}
|
||||
|
||||
/* FIXME: we do not support more than one attribute for sorting right now */
|
||||
/* FIXME: we need to check if the attribute type exist or return an error */
|
||||
|
||||
if (sort_ctrls[1] != NULL) {
|
||||
if (control->critical) {
|
||||
struct ldb_reply *ares;
|
||||
|
||||
ares = talloc_zero(req, struct ldb_reply);
|
||||
if (!ares)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
/* 53 = unwilling to perform */
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
if ((ret = build_response(ares, &ares->controls, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
h->status = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
|
||||
h->state = LDB_ASYNC_DONE;
|
||||
ret = ac->up_callback(module->ldb, ac->up_context, ares);
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
/* just pass the call down and don't do any sorting */
|
||||
ldb_next_request(module, req);
|
||||
}
|
||||
}
|
||||
|
||||
ac->attributeName = sort_ctrls[0]->attributeName;
|
||||
ac->orderingRule = sort_ctrls[0]->orderingRule;
|
||||
ac->reverse = sort_ctrls[0]->reverse;
|
||||
|
||||
ac->req = talloc(req, struct ldb_request);
|
||||
if (!ac->req)
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
|
||||
ac->req->operation = req->operation;
|
||||
ac->req->op.search.base = req->op.search.base;
|
||||
ac->req->op.search.scope = req->op.search.scope;
|
||||
ac->req->op.search.tree = req->op.search.tree;
|
||||
ac->req->op.search.attrs = req->op.search.attrs;
|
||||
ac->req->controls = req->controls;
|
||||
|
||||
/* save it locally and remove it from the list */
|
||||
/* we do not need to replace them later as we
|
||||
* are keeping the original req intact */
|
||||
if (!save_controls(control, ac->req, &saved_controls)) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac->req->context = ac;
|
||||
ac->req->callback = server_sort_search_callback;
|
||||
ldb_set_timeout_from_prev_req(module->ldb, req, ac->req);
|
||||
|
||||
req->handle = h;
|
||||
|
||||
return ldb_next_request(module, ac->req);
|
||||
}
|
||||
|
||||
static int server_sort_results(struct ldb_handle *handle)
|
||||
{
|
||||
struct sort_context *ac;
|
||||
struct ldb_reply *ares;
|
||||
int i, ret;
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct sort_context);
|
||||
|
||||
ac->h = ldb_attrib_handler(ac->module->ldb, ac->attributeName);
|
||||
ac->sort_result = 0;
|
||||
|
||||
ldb_qsort(ac->msgs, ac->num_msgs,
|
||||
sizeof(struct ldb_message *),
|
||||
ac, (ldb_qsort_cmp_fn_t)sort_compare);
|
||||
|
||||
for (i = 0; i < ac->num_msgs; i++) {
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_ENTRY;
|
||||
ares->message = talloc_move(ares, &ac->msgs[i]);
|
||||
|
||||
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
if (handle->status != LDB_SUCCESS) {
|
||||
return handle->status;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < ac->num_refs; i++) {
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_REFERRAL;
|
||||
ares->referral = talloc_move(ares, &ac->referrals[i]);
|
||||
|
||||
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
if (handle->status != LDB_SUCCESS) {
|
||||
return handle->status;
|
||||
}
|
||||
}
|
||||
|
||||
ares = talloc_zero(ac, struct ldb_reply);
|
||||
if (!ares) {
|
||||
handle->status = LDB_ERR_OPERATIONS_ERROR;
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
ares->type = LDB_REPLY_DONE;
|
||||
ares->controls = talloc_move(ares, &ac->controls);
|
||||
|
||||
handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
|
||||
if (handle->status != LDB_SUCCESS) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
if ((ret = build_response(ac, &ac->controls, ac->sort_result, "sort control is not complete yet")) != LDB_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return LDB_SUCCESS;
|
||||
}
|
||||
|
||||
static int server_sort_wait(struct ldb_handle *handle, enum ldb_wait_type type)
|
||||
{
|
||||
struct sort_context *ac;
|
||||
int ret;
|
||||
|
||||
if (!handle || !handle->private_data) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
ac = talloc_get_type(handle->private_data, struct sort_context);
|
||||
|
||||
ret = ldb_wait(ac->req->handle, type);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
handle->status = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
handle->state = ac->req->handle->state;
|
||||
handle->status = ac->req->handle->status;
|
||||
|
||||
if (handle->status != LDB_SUCCESS) {
|
||||
return handle->status;
|
||||
}
|
||||
|
||||
if (handle->state == LDB_ASYNC_DONE) {
|
||||
ret = server_sort_results(handle);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int server_sort_init(struct ldb_module *module)
|
||||
{
|
||||
struct ldb_request *req;
|
||||
int ret;
|
||||
|
||||
req = talloc(module, struct ldb_request);
|
||||
if (req == NULL) {
|
||||
return LDB_ERR_OPERATIONS_ERROR;
|
||||
}
|
||||
|
||||
req->operation = LDB_REQ_REGISTER_CONTROL;
|
||||
req->op.reg_control.oid = LDB_CONTROL_SERVER_SORT_OID;
|
||||
req->controls = NULL;
|
||||
|
||||
ret = ldb_request(module->ldb, req);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
ldb_debug(module->ldb, LDB_DEBUG_WARNING, "server_sort: Unable to register control with rootdse!\n");
|
||||
}
|
||||
|
||||
talloc_free(req);
|
||||
return ldb_next_init(module);
|
||||
}
|
||||
|
||||
static const struct ldb_module_ops server_sort_ops = {
|
||||
.name = "server_sort",
|
||||
.search = server_sort_search,
|
||||
.wait = server_sort_wait,
|
||||
.init_context = server_sort_init
|
||||
};
|
||||
|
||||
int ldb_sort_init(void)
|
||||
{
|
||||
return ldb_register_module(&server_sort_ops);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
This test code requires a tdb that is configured for to use the asq module.
|
||||
You can do that adding the following record to a tdb:
|
||||
|
||||
dn: @MODULES
|
||||
@LIST: asq
|
||||
|
||||
Other modules can be used as well (like rdn_name for example)
|
||||
|
||||
The uidNumber 0 and the gidNumber 0 are considered invalid.
|
||||
|
||||
The user records should contain the followin attributes:
|
||||
uid (required) the user name
|
||||
userPassword (optional) the user password (if not present "LDB" is
|
||||
returned in the password field)
|
||||
uidNumber (required) the user uid
|
||||
gidNumber (required) the user primary gid
|
||||
gecos (optional) the GECOS
|
||||
homeDirectory (required) the home directory
|
||||
loginShell (required) the login shell
|
||||
memberOf (required) all the groups the user is member of should
|
||||
be reported here using their DNs. The
|
||||
primary group as well.
|
||||
|
||||
The group accounts should contain the following attributes:
|
||||
cn (required) the group name
|
||||
uesrPassword (optional) the group password (if not present "LDB" is
|
||||
returned in the password field)
|
||||
gidNumber (required) the group gid
|
||||
member (optional) the DNs of the member users, also the ones
|
||||
that have this group as primary
|
||||
|
||||
|
||||
SSS
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
LDB nsswitch module
|
||||
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "ldb-nss.h"
|
||||
|
||||
extern struct _ldb_nss_context *_ldb_nss_ctx;
|
||||
|
||||
const char *_ldb_nss_gr_attrs[] = {
|
||||
"cn",
|
||||
"userPassword",
|
||||
"gidNumber",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *_ldb_nss_mem_attrs[] = {
|
||||
"uid",
|
||||
NULL
|
||||
};
|
||||
|
||||
#define _NSS_LDB_ENOMEM(amem) \
|
||||
do { \
|
||||
if ( ! amem) { \
|
||||
errno = ENOMEM; \
|
||||
talloc_free(memctx); \
|
||||
return NSS_STATUS_UNAVAIL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* This setgrent, getgrent, endgrent is not very efficient */
|
||||
|
||||
NSS_STATUS _nss_ldb_setgrent(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->gr_cur = 0;
|
||||
if (_ldb_nss_ctx->gr_res != NULL) {
|
||||
talloc_free(_ldb_nss_ctx->gr_res);
|
||||
_ldb_nss_ctx->gr_res = NULL;
|
||||
}
|
||||
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
_LDB_NSS_GRENT_FILTER,
|
||||
_ldb_nss_gr_attrs,
|
||||
&_ldb_nss_ctx->gr_res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_endgrent(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->gr_cur = 0;
|
||||
if (_ldb_nss_ctx->gr_res) {
|
||||
talloc_free(_ldb_nss_ctx->gr_res);
|
||||
_ldb_nss_ctx->gr_res = NULL;
|
||||
}
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getgrent_r(struct group *result_buf, char *buffer, size_t buflen, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
struct ldb_result *res;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*errnop = 0;
|
||||
|
||||
if (_ldb_nss_ctx->gr_cur >= _ldb_nss_ctx->gr_res->count) {
|
||||
/* already returned all entries */
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
res = talloc_zero(_ldb_nss_ctx->gr_res, struct ldb_result);
|
||||
if ( ! res) {
|
||||
errno = *errnop = ENOMEM;
|
||||
_ldb_nss_ctx->gr_cur++; /* skip this entry */
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_group_request(&res,
|
||||
_ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur]->dn,
|
||||
_ldb_nss_mem_attrs,
|
||||
"member");
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
*errnop = errno;
|
||||
talloc_free(res);
|
||||
_ldb_nss_ctx->gr_cur++; /* skip this entry */
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_fill_group(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
_ldb_nss_ctx->gr_res->msgs[_ldb_nss_ctx->gr_cur],
|
||||
res);
|
||||
|
||||
talloc_free(res);
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
if (ret != NSS_STATUS_TRYAGAIN) {
|
||||
_ldb_nss_ctx->gr_cur++; /* skip this entry */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* this entry is ok, increment counter to nex entry */
|
||||
_ldb_nss_ctx->gr_cur++;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getgrnam_r(const char *name, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
char *filter;
|
||||
TALLOC_CTX *ctx;
|
||||
struct ldb_result *gr_res;
|
||||
struct ldb_result *mem_res;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx = talloc_new(_ldb_nss_ctx->ldb);
|
||||
if ( ! ctx) {
|
||||
*errnop = errno = ENOMEM;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
/* build the filter for this uid */
|
||||
filter = talloc_asprintf(ctx, _LDB_NSS_GRNAM_FILTER, name);
|
||||
if (filter == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* search the entry */
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
filter,
|
||||
_ldb_nss_gr_attrs,
|
||||
&gr_res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
talloc_steal(ctx, gr_res);
|
||||
|
||||
/* if none found return */
|
||||
if (gr_res->count == 0) {
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_NOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (gr_res->count != 1) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mem_res = talloc_zero(ctx, struct ldb_result);
|
||||
if ( ! mem_res) {
|
||||
errno = *errnop = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_group_request(&mem_res,
|
||||
gr_res->msgs[0]->dn,
|
||||
_ldb_nss_mem_attrs,
|
||||
"member");
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
*errnop = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_fill_group(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
gr_res->msgs[0],
|
||||
mem_res);
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = NSS_STATUS_SUCCESS;
|
||||
done:
|
||||
talloc_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getgrgid_r(gid_t gid, struct group *result_buf, char *buffer, size_t buflen, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
char *filter;
|
||||
TALLOC_CTX *ctx;
|
||||
struct ldb_result *gr_res;
|
||||
struct ldb_result *mem_res;
|
||||
|
||||
if (gid == 0) { /* we don't serve root gid by policy */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx = talloc_new(_ldb_nss_ctx->ldb);
|
||||
if ( ! ctx) {
|
||||
*errnop = errno = ENOMEM;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
/* build the filter for this uid */
|
||||
filter = talloc_asprintf(ctx, _LDB_NSS_GRGID_FILTER, gid);
|
||||
if (filter == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* search the entry */
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
filter,
|
||||
_ldb_nss_gr_attrs,
|
||||
&gr_res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
talloc_steal(ctx, gr_res);
|
||||
|
||||
/* if none found return */
|
||||
if (gr_res->count == 0) {
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_NOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (gr_res->count != 1) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
mem_res = talloc_zero(ctx, struct ldb_result);
|
||||
if ( ! mem_res) {
|
||||
errno = *errnop = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_group_request(&mem_res,
|
||||
gr_res->msgs[0]->dn,
|
||||
_ldb_nss_mem_attrs,
|
||||
"member");
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
*errnop = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_fill_group(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
gr_res->msgs[0],
|
||||
mem_res);
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = NSS_STATUS_SUCCESS;
|
||||
done:
|
||||
talloc_free(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_initgroups_dyn(const char *user, gid_t group, long int *start, long int *size, gid_t **groups, long int limit, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
char *filter;
|
||||
const char * attrs[] = { "uidNumber", "gidNumber", NULL };
|
||||
struct ldb_result *uid_res;
|
||||
struct ldb_result *mem_res;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
mem_res = talloc_zero(_ldb_nss_ctx, struct ldb_result);
|
||||
if ( ! mem_res) {
|
||||
errno = *errnop = ENOMEM;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
/* build the filter for this name */
|
||||
filter = talloc_asprintf(mem_res, _LDB_NSS_PWNAM_FILTER, user);
|
||||
if (filter == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* search the entry */
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
filter,
|
||||
attrs,
|
||||
&uid_res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
talloc_steal(mem_res, uid_res);
|
||||
|
||||
/* if none found return */
|
||||
if (uid_res->count == 0) {
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_NOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (uid_res->count != 1) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_group_request(&mem_res,
|
||||
uid_res->msgs[0]->dn,
|
||||
attrs,
|
||||
"memberOf");
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
*errnop = errno;
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_fill_initgr(group,
|
||||
limit,
|
||||
start,
|
||||
size,
|
||||
groups,
|
||||
errnop,
|
||||
mem_res);
|
||||
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
ret = NSS_STATUS_SUCCESS;
|
||||
|
||||
done:
|
||||
talloc_free(mem_res);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
LDB nsswitch module
|
||||
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "ldb-nss.h"
|
||||
|
||||
struct _ldb_nss_context *_ldb_nss_ctx = NULL;
|
||||
|
||||
NSS_STATUS _ldb_nss_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pid_t mypid = getpid();
|
||||
|
||||
if (_ldb_nss_ctx != NULL) {
|
||||
if (_ldb_nss_ctx->pid == mypid) {
|
||||
/* already initialized */
|
||||
return NSS_STATUS_SUCCESS;
|
||||
} else {
|
||||
/* we are in a forked child now, reinitialize */
|
||||
talloc_free(_ldb_nss_ctx);
|
||||
_ldb_nss_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
_ldb_nss_ctx = talloc_named(NULL, 0, "_ldb_nss_ctx(%u)", mypid);
|
||||
if (_ldb_nss_ctx == NULL) {
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->pid = mypid;
|
||||
|
||||
ret = ldb_global_init();
|
||||
if (ret != 0) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->ldb = ldb_init(_ldb_nss_ctx);
|
||||
if (_ldb_nss_ctx->ldb == NULL) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
ret = ldb_connect(_ldb_nss_ctx->ldb, _LDB_NSS_URL, LDB_FLG_RDONLY, NULL);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->base = ldb_dn_new(_ldb_nss_ctx, _ldb_nss_ctx->ldb, _LDB_NSS_BASEDN);
|
||||
if ( ! ldb_dn_validate(_ldb_nss_ctx->base)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->pw_cur = 0;
|
||||
_ldb_nss_ctx->pw_res = NULL;
|
||||
_ldb_nss_ctx->gr_cur = 0;
|
||||
_ldb_nss_ctx->gr_res = NULL;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
|
||||
failed:
|
||||
/* talloc_free(_ldb_nss_ctx); */
|
||||
_ldb_nss_ctx = NULL;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
|
||||
char *buffer,
|
||||
int buflen,
|
||||
int *errnop,
|
||||
struct ldb_message *msg)
|
||||
{
|
||||
int len;
|
||||
int bufpos;
|
||||
const char *tmp;
|
||||
|
||||
bufpos = 0;
|
||||
|
||||
/* get username */
|
||||
tmp = ldb_msg_find_attr_as_string(msg, "uid", NULL);
|
||||
if (tmp == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->pw_name = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
/* get userPassword */
|
||||
tmp = ldb_msg_find_attr_as_string(msg, "userPassword", NULL);
|
||||
if (tmp == NULL) {
|
||||
tmp = "LDB";
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->pw_passwd = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
/* this backend never serves an uid 0 user */
|
||||
result->pw_uid = ldb_msg_find_attr_as_int(msg, "uidNumber", 0);
|
||||
if (result->pw_uid == 0) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
result->pw_gid = ldb_msg_find_attr_as_int(msg, "gidNumber", 0);
|
||||
if (result->pw_gid == 0) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
/* get gecos */
|
||||
tmp = ldb_msg_find_attr_as_string(msg, "gecos", NULL);
|
||||
if (tmp == NULL) {
|
||||
tmp = "";
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->pw_gecos = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
/* get homeDirectory */
|
||||
tmp = ldb_msg_find_attr_as_string(msg, "homeDirectory", NULL);
|
||||
if (tmp == NULL) {
|
||||
tmp = "";
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->pw_dir = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
/* get shell */
|
||||
tmp = ldb_msg_find_attr_as_string(msg, "loginShell", NULL);
|
||||
if (tmp == NULL) {
|
||||
tmp = "";
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->pw_shell = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_group(struct group *result,
|
||||
char *buffer,
|
||||
int buflen,
|
||||
int *errnop,
|
||||
struct ldb_message *group,
|
||||
struct ldb_result *members)
|
||||
{
|
||||
const char *tmp;
|
||||
size_t len;
|
||||
size_t bufpos;
|
||||
size_t lsize;
|
||||
int i;
|
||||
|
||||
bufpos = 0;
|
||||
|
||||
/* get group name */
|
||||
tmp = ldb_msg_find_attr_as_string(group, "cn", NULL);
|
||||
if (tmp == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->gr_name = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
/* get userPassword */
|
||||
tmp = ldb_msg_find_attr_as_string(group, "userPassword", NULL);
|
||||
if (tmp == NULL) {
|
||||
tmp = "LDB";
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->gr_passwd = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
|
||||
result->gr_gid = ldb_msg_find_attr_as_int(group, "gidNumber", 0);
|
||||
if (result->gr_gid == 0) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
/* check if there is enough memory for the list of pointers */
|
||||
lsize = (members->count + 1) * sizeof(char *);
|
||||
|
||||
/* align buffer on pointer boundary */
|
||||
bufpos += (sizeof(char*) - ((unsigned long)(buffer) % sizeof(char*)));
|
||||
if ((buflen - bufpos) < lsize) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
|
||||
result->gr_mem = (char **)&buffer[bufpos];
|
||||
bufpos += lsize;
|
||||
|
||||
for (i = 0; i < members->count; i++) {
|
||||
tmp = ldb_msg_find_attr_as_string(members->msgs[i], "uid", NULL);
|
||||
if (tmp == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
len = strlen(tmp)+1;
|
||||
if (bufpos + len > buflen) {
|
||||
/* buffer too small */
|
||||
*errnop = errno = EAGAIN;
|
||||
return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
memcpy(&buffer[bufpos], tmp, len);
|
||||
result->gr_mem[i] = &buffer[bufpos];
|
||||
bufpos += len;
|
||||
}
|
||||
|
||||
result->gr_mem[i] = NULL;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
|
||||
long int limit,
|
||||
long int *start,
|
||||
long int *size,
|
||||
gid_t **groups,
|
||||
int *errnop,
|
||||
struct ldb_result *grlist)
|
||||
{
|
||||
NSS_STATUS ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < grlist->count; i++) {
|
||||
|
||||
if (limit && (*start > limit)) {
|
||||
/* TODO: warn no all groups were reported */
|
||||
*errnop = 0;
|
||||
ret = NSS_STATUS_SUCCESS;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (*start == *size) {
|
||||
/* buffer full, enlarge it */
|
||||
long int gs;
|
||||
gid_t *gm;
|
||||
|
||||
gs = (*size) + 32;
|
||||
if (limit && (gs > limit)) {
|
||||
gs = limit;
|
||||
}
|
||||
|
||||
gm = (gid_t *)realloc((*groups), gs * sizeof(gid_t));
|
||||
if ( ! gm) {
|
||||
*errnop = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
*groups = gm;
|
||||
*size = gs;
|
||||
}
|
||||
|
||||
(*groups)[*start] = ldb_msg_find_attr_as_int(grlist->msgs[i], "gidNumber", 0);
|
||||
if ((*groups)[*start] == 0 || (*groups)[*start] == group) {
|
||||
/* skip root group or primary group */
|
||||
continue;
|
||||
}
|
||||
(*start)++;
|
||||
|
||||
}
|
||||
|
||||
*errnop = 0;
|
||||
ret = NSS_STATUS_SUCCESS;
|
||||
done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define _LDB_NSS_ALLOC_CHECK(mem) do { if (!mem) { errno = ENOMEM; return NSS_STATUS_UNAVAIL; } } while(0)
|
||||
|
||||
NSS_STATUS _ldb_nss_group_request(struct ldb_result **_res,
|
||||
struct ldb_dn *group_dn,
|
||||
const char * const *attrs,
|
||||
const char *mattr)
|
||||
{
|
||||
struct ldb_control **ctrls;
|
||||
struct ldb_control *ctrl;
|
||||
struct ldb_asq_control *asqc;
|
||||
struct ldb_request *req;
|
||||
int ret;
|
||||
struct ldb_result *res = *_res;
|
||||
|
||||
ctrls = talloc_array(res, struct ldb_control *, 2);
|
||||
_LDB_NSS_ALLOC_CHECK(ctrls);
|
||||
|
||||
ctrl = talloc(ctrls, struct ldb_control);
|
||||
_LDB_NSS_ALLOC_CHECK(ctrl);
|
||||
|
||||
asqc = talloc(ctrl, struct ldb_asq_control);
|
||||
_LDB_NSS_ALLOC_CHECK(asqc);
|
||||
|
||||
asqc->source_attribute = talloc_strdup(asqc, mattr);
|
||||
_LDB_NSS_ALLOC_CHECK(asqc->source_attribute);
|
||||
|
||||
asqc->request = 1;
|
||||
asqc->src_attr_len = strlen(asqc->source_attribute);
|
||||
ctrl->oid = LDB_CONTROL_ASQ_OID;
|
||||
ctrl->critical = 1;
|
||||
ctrl->data = asqc;
|
||||
ctrls[0] = ctrl;
|
||||
ctrls[1] = NULL;
|
||||
|
||||
ret = ldb_build_search_req(
|
||||
&req,
|
||||
_ldb_nss_ctx->ldb,
|
||||
res,
|
||||
group_dn,
|
||||
LDB_SCOPE_BASE,
|
||||
"(objectClass=*)",
|
||||
attrs,
|
||||
ctrls,
|
||||
res,
|
||||
ldb_search_default_callback);
|
||||
|
||||
if (ret != LDB_SUCCESS) {
|
||||
errno = ENOENT;
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
ldb_set_timeout(_ldb_nss_ctx->ldb, req, 0);
|
||||
|
||||
ret = ldb_request(_ldb_nss_ctx->ldb, req);
|
||||
|
||||
if (ret == LDB_SUCCESS) {
|
||||
ret = ldb_wait(req->handle, LDB_WAIT_ALL);
|
||||
} else {
|
||||
talloc_free(req);
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
talloc_free(req);
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
LDB nsswitch module
|
||||
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef _LDB_NSS
|
||||
#define _LDB_NSS
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
|
||||
#include <nss.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#define _LDB_NSS_URL "etc/users.ldb"
|
||||
#define _LDB_NSS_BASEDN "CN=Users,CN=System"
|
||||
#define _LDB_NSS_PWENT_FILTER "(&(objectClass=posixAccount)(!(uidNumber=0))(!(gidNumber=0)))"
|
||||
#define _LDB_NSS_PWUID_FILTER "(&(objectClass=posixAccount)(uidNumber=%d)(!(gidNumber=0)))"
|
||||
#define _LDB_NSS_PWNAM_FILTER "(&(objectClass=posixAccount)(uid=%s)(!(uidNumber=0))(!(gidNumber=0)))"
|
||||
|
||||
#define _LDB_NSS_GRENT_FILTER "(&(objectClass=posixGroup)(!(gidNumber=0)))"
|
||||
#define _LDB_NSS_GRGID_FILTER "(&(objectClass=posixGroup)(gidNumber=%d)))"
|
||||
#define _LDB_NSS_GRNAM_FILTER "(&(objectClass=posixGroup)(cn=%s)(!(gidNumber=0)))"
|
||||
|
||||
typedef enum nss_status NSS_STATUS;
|
||||
|
||||
struct _ldb_nss_context {
|
||||
|
||||
pid_t pid;
|
||||
|
||||
struct ldb_context *ldb;
|
||||
struct ldb_dn *base;
|
||||
|
||||
int pw_cur;
|
||||
struct ldb_result *pw_res;
|
||||
|
||||
int gr_cur;
|
||||
struct ldb_result *gr_res;
|
||||
};
|
||||
|
||||
NSS_STATUS _ldb_nss_init(void);
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_passwd(struct passwd *result,
|
||||
char *buffer,
|
||||
int buflen,
|
||||
int *errnop,
|
||||
struct ldb_message *msg);
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_group(struct group *result,
|
||||
char *buffer,
|
||||
int buflen,
|
||||
int *errnop,
|
||||
struct ldb_message *group,
|
||||
struct ldb_result *members);
|
||||
|
||||
NSS_STATUS _ldb_nss_fill_initgr(gid_t group,
|
||||
long int limit,
|
||||
long int *start,
|
||||
long int *size,
|
||||
gid_t **groups,
|
||||
int *errnop,
|
||||
struct ldb_result *grlist);
|
||||
|
||||
NSS_STATUS _ldb_nss_group_request(struct ldb_result **res,
|
||||
struct ldb_dn *group_dn,
|
||||
const char * const *attrs,
|
||||
const char *mattr);
|
||||
|
||||
#endif /* _LDB_NSS */
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
LDB nsswitch module
|
||||
|
||||
Copyright (C) Simo Sorce 2006
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the
|
||||
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "ldb-nss.h"
|
||||
|
||||
extern struct _ldb_nss_context *_ldb_nss_ctx;
|
||||
|
||||
const char *_ldb_nss_pw_attrs[] = {
|
||||
"uid",
|
||||
"userPassword",
|
||||
"uidNumber",
|
||||
"gidNumber",
|
||||
"gecos",
|
||||
"homeDirectory",
|
||||
"loginShell",
|
||||
NULL
|
||||
};
|
||||
|
||||
NSS_STATUS _nss_ldb_setpwent(void)
|
||||
{
|
||||
int ret;
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->pw_cur = 0;
|
||||
if (_ldb_nss_ctx->pw_res != NULL) {
|
||||
talloc_free(_ldb_nss_ctx->pw_res);
|
||||
_ldb_nss_ctx->pw_res = NULL;
|
||||
}
|
||||
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
_LDB_NSS_PWENT_FILTER,
|
||||
_ldb_nss_pw_attrs,
|
||||
&_ldb_nss_ctx->pw_res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
}
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_endpwent(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->pw_cur = 0;
|
||||
if (_ldb_nss_ctx->pw_res) {
|
||||
talloc_free(_ldb_nss_ctx->pw_res);
|
||||
_ldb_nss_ctx->pw_res = NULL;
|
||||
}
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getpwent_r(struct passwd *result_buf,
|
||||
char *buffer,
|
||||
int buflen,
|
||||
int *errnop)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*errnop = 0;
|
||||
|
||||
if (_ldb_nss_ctx->pw_cur >= _ldb_nss_ctx->pw_res->count) {
|
||||
/* already returned all entries */
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_fill_passwd(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
_ldb_nss_ctx->pw_res->msgs[_ldb_nss_ctx->pw_cur]);
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_ldb_nss_ctx->pw_cur++;
|
||||
|
||||
return NSS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getpwuid_r(uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
char *filter;
|
||||
struct ldb_result *res;
|
||||
|
||||
if (uid == 0) { /* we don't serve root uid by policy */
|
||||
*errnop = errno = ENOENT;
|
||||
return NSS_STATUS_NOTFOUND;
|
||||
}
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* build the filter for this uid */
|
||||
filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWUID_FILTER, uid);
|
||||
if (filter == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOMEM;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* search the entry */
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
filter,
|
||||
_ldb_nss_pw_attrs,
|
||||
&res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* if none found return */
|
||||
if (res->count == 0) {
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_NOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (res->count != 1) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* fill in the passwd struct */
|
||||
ret = _ldb_nss_fill_passwd(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
res->msgs[0]);
|
||||
|
||||
done:
|
||||
talloc_free(filter);
|
||||
talloc_free(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
NSS_STATUS _nss_ldb_getpwnam_r(const char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop)
|
||||
{
|
||||
int ret;
|
||||
char *filter;
|
||||
struct ldb_result *res;
|
||||
|
||||
ret = _ldb_nss_init();
|
||||
if (ret != NSS_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* build the filter for this name */
|
||||
filter = talloc_asprintf(_ldb_nss_ctx, _LDB_NSS_PWNAM_FILTER, name);
|
||||
if (filter == NULL) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* search the entry */
|
||||
ret = ldb_search(_ldb_nss_ctx->ldb,
|
||||
_ldb_nss_ctx->base,
|
||||
LDB_SCOPE_SUBTREE,
|
||||
filter,
|
||||
_ldb_nss_pw_attrs,
|
||||
&res);
|
||||
if (ret != LDB_SUCCESS) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* if none found return */
|
||||
if (res->count == 0) {
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_NOTFOUND;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (res->count != 1) {
|
||||
/* this is a fatal error */
|
||||
*errnop = errno = ENOENT;
|
||||
ret = NSS_STATUS_UNAVAIL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* fill in the passwd struct */
|
||||
ret = _ldb_nss_fill_passwd(result_buf,
|
||||
buffer,
|
||||
buflen,
|
||||
errnop,
|
||||
res->msgs[0]);
|
||||
|
||||
done:
|
||||
talloc_free(filter);
|
||||
talloc_free(res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
This directory contains Samba specific extensions to ldb. It also
|
||||
serves as example code on how to extend ldb for your own application.
|
||||
|
||||
The main extension Samba uses is to provide ldif encode/decode
|
||||
routines for specific attributes, so users can get nice pretty
|
||||
printing of attributes in ldbedit, while the attributes are stored in
|
||||
the standard NDR format in the database.
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
ldb database library - ldif handlers for Samba
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Andrew Bartlett 2006
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ldb/include/includes.h"
|
||||
#include "ldb/include/ldb_handlers.h"
|
||||
|
||||
#include "librpc/gen_ndr/ndr_security.h"
|
||||
#include "librpc/gen_ndr/ndr_misc.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
#include "libcli/security/security.h"
|
||||
|
||||
/*
|
||||
convert a ldif formatted objectSid to a NDR formatted blob
|
||||
*/
|
||||
static int ldif_read_objectSid(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct dom_sid *sid;
|
||||
NTSTATUS status;
|
||||
sid = dom_sid_parse_talloc(mem_ctx, (const char *)in->data);
|
||||
if (sid == NULL) {
|
||||
return -1;
|
||||
}
|
||||
status = ndr_push_struct_blob(out, mem_ctx, sid,
|
||||
(ndr_push_flags_fn_t)ndr_push_dom_sid);
|
||||
talloc_free(sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
convert a NDR formatted blob to a ldif formatted objectSid
|
||||
*/
|
||||
static int ldif_write_objectSid(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct dom_sid *sid;
|
||||
NTSTATUS status;
|
||||
sid = talloc(mem_ctx, struct dom_sid);
|
||||
if (sid == NULL) {
|
||||
return -1;
|
||||
}
|
||||
status = ndr_pull_struct_blob(in, sid, sid,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_dom_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(sid);
|
||||
return -1;
|
||||
}
|
||||
out->data = (uint8_t *)dom_sid_string(mem_ctx, sid);
|
||||
talloc_free(sid);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((const char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL ldb_comparision_objectSid_isString(const struct ldb_val *v)
|
||||
{
|
||||
if (v->length < 3) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (strncmp("S-", (const char *)v->data, 2) != 0) return False;
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two objectSids
|
||||
*/
|
||||
static int ldb_comparison_objectSid(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (ldb_comparision_objectSid_isString(v1) && ldb_comparision_objectSid_isString(v2)) {
|
||||
return strcmp((const char *)v1->data, (const char *)v2->data);
|
||||
} else if (ldb_comparision_objectSid_isString(v1)
|
||||
&& !ldb_comparision_objectSid_isString(v2)) {
|
||||
struct ldb_val v;
|
||||
int ret;
|
||||
if (ldif_read_objectSid(ldb, mem_ctx, v1, &v) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
|
||||
talloc_free(v.data);
|
||||
return ret;
|
||||
} else if (!ldb_comparision_objectSid_isString(v1)
|
||||
&& ldb_comparision_objectSid_isString(v2)) {
|
||||
struct ldb_val v;
|
||||
int ret;
|
||||
if (ldif_read_objectSid(ldb, mem_ctx, v2, &v) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
|
||||
talloc_free(v.data);
|
||||
return ret;
|
||||
}
|
||||
return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a objectSid
|
||||
*/
|
||||
static int ldb_canonicalise_objectSid(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
if (ldb_comparision_objectSid_isString(in)) {
|
||||
return ldif_read_objectSid(ldb, mem_ctx, in, out);
|
||||
}
|
||||
return ldb_handler_copy(ldb, mem_ctx, in, out);
|
||||
}
|
||||
|
||||
/*
|
||||
convert a ldif formatted objectGUID to a NDR formatted blob
|
||||
*/
|
||||
static int ldif_read_objectGUID(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct GUID guid;
|
||||
NTSTATUS status;
|
||||
|
||||
status = GUID_from_string((const char *)in->data, &guid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = ndr_push_struct_blob(out, mem_ctx, &guid,
|
||||
(ndr_push_flags_fn_t)ndr_push_GUID);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
convert a NDR formatted blob to a ldif formatted objectGUID
|
||||
*/
|
||||
static int ldif_write_objectGUID(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct GUID guid;
|
||||
NTSTATUS status;
|
||||
status = ndr_pull_struct_blob(in, mem_ctx, &guid,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_GUID);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return -1;
|
||||
}
|
||||
out->data = (uint8_t *)GUID_string(mem_ctx, &guid);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((const char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL ldb_comparision_objectGUID_isString(const struct ldb_val *v)
|
||||
{
|
||||
struct GUID guid;
|
||||
NTSTATUS status;
|
||||
|
||||
if (v->length < 33) return False;
|
||||
|
||||
/* see if the input if null-terninated (safety check for the below) */
|
||||
if (v->data[v->length] != '\0') return False;
|
||||
|
||||
status = GUID_from_string((const char *)v->data, &guid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
compare two objectGUIDs
|
||||
*/
|
||||
static int ldb_comparison_objectGUID(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1, const struct ldb_val *v2)
|
||||
{
|
||||
if (ldb_comparision_objectGUID_isString(v1) && ldb_comparision_objectGUID_isString(v2)) {
|
||||
return strcmp((const char *)v1->data, (const char *)v2->data);
|
||||
} else if (ldb_comparision_objectGUID_isString(v1)
|
||||
&& !ldb_comparision_objectGUID_isString(v2)) {
|
||||
struct ldb_val v;
|
||||
int ret;
|
||||
if (ldif_read_objectGUID(ldb, mem_ctx, v1, &v) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ret = ldb_comparison_binary(ldb, mem_ctx, &v, v2);
|
||||
talloc_free(v.data);
|
||||
return ret;
|
||||
} else if (!ldb_comparision_objectGUID_isString(v1)
|
||||
&& ldb_comparision_objectGUID_isString(v2)) {
|
||||
struct ldb_val v;
|
||||
int ret;
|
||||
if (ldif_read_objectGUID(ldb, mem_ctx, v2, &v) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ret = ldb_comparison_binary(ldb, mem_ctx, v1, &v);
|
||||
talloc_free(v.data);
|
||||
return ret;
|
||||
}
|
||||
return ldb_comparison_binary(ldb, mem_ctx, v1, v2);
|
||||
}
|
||||
|
||||
/*
|
||||
canonicalise a objectGUID
|
||||
*/
|
||||
static int ldb_canonicalise_objectGUID(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
if (ldb_comparision_objectGUID_isString(in)) {
|
||||
return ldif_read_objectGUID(ldb, mem_ctx, in, out);
|
||||
}
|
||||
return ldb_handler_copy(ldb, mem_ctx, in, out);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert a ldif (SDDL) formatted ntSecurityDescriptor to a NDR formatted blob
|
||||
*/
|
||||
static int ldif_read_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct security_descriptor *sd;
|
||||
NTSTATUS status;
|
||||
|
||||
sd = sddl_decode(mem_ctx, (const char *)in->data, NULL);
|
||||
if (sd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
status = ndr_push_struct_blob(out, mem_ctx, sd,
|
||||
(ndr_push_flags_fn_t)ndr_push_security_descriptor);
|
||||
talloc_free(sd);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
convert a NDR formatted blob to a ldif formatted ntSecurityDescriptor (SDDL format)
|
||||
*/
|
||||
static int ldif_write_ntSecurityDescriptor(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct security_descriptor *sd;
|
||||
NTSTATUS status;
|
||||
|
||||
sd = talloc(mem_ctx, struct security_descriptor);
|
||||
if (sd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
status = ndr_pull_struct_blob(in, sd, sd,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(sd);
|
||||
return -1;
|
||||
}
|
||||
out->data = (uint8_t *)sddl_encode(mem_ctx, sd, NULL);
|
||||
talloc_free(sd);
|
||||
if (out->data == NULL) {
|
||||
return -1;
|
||||
}
|
||||
out->length = strlen((const char *)out->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
canonicolise an objectCategory. We use the short form as the cannoical form:
|
||||
cn=Person,cn=Schema,cn=Configuration,<basedn> becomes 'person'
|
||||
*/
|
||||
|
||||
static int ldif_canonicalise_objectCategory(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *in, struct ldb_val *out)
|
||||
{
|
||||
struct ldb_dn *dn1 = NULL;
|
||||
char *oc1, *oc2;
|
||||
|
||||
dn1 = ldb_dn_new(mem_ctx, ldb, (char *)in->data);
|
||||
if ( ! ldb_dn_validate(dn1)) {
|
||||
oc1 = talloc_strndup(mem_ctx, (char *)in->data, in->length);
|
||||
} else if (ldb_dn_get_comp_num(dn1) >= 1 && strcasecmp(ldb_dn_get_rdn_name(dn1), "cn") == 0) {
|
||||
const struct ldb_val *val = ldb_dn_get_rdn_val(dn1);
|
||||
oc1 = talloc_strndup(mem_ctx, (char *)val->data, val->length);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
oc2 = ldb_casefold(ldb, mem_ctx, oc1);
|
||||
out->data = (void *)oc2;
|
||||
out->length = strlen(oc2);
|
||||
talloc_free(oc1);
|
||||
talloc_free(dn1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ldif_comparison_objectCategory(struct ldb_context *ldb, void *mem_ctx,
|
||||
const struct ldb_val *v1,
|
||||
const struct ldb_val *v2)
|
||||
{
|
||||
struct ldb_dn *dn1 = NULL, *dn2 = NULL;
|
||||
const char *oc1, *oc2;
|
||||
|
||||
dn1 = ldb_dn_new(mem_ctx, ldb, (char *)v1->data);
|
||||
if ( ! ldb_dn_validate(dn1)) {
|
||||
oc1 = talloc_strndup(mem_ctx, (char *)v1->data, v1->length);
|
||||
} else if (ldb_dn_get_comp_num(dn1) >= 1 && strcasecmp(ldb_dn_get_rdn_name(dn1), "cn") == 0) {
|
||||
const struct ldb_val *val = ldb_dn_get_rdn_val(dn1);
|
||||
oc1 = talloc_strndup(mem_ctx, (char *)val->data, val->length);
|
||||
} else {
|
||||
oc1 = NULL;
|
||||
}
|
||||
|
||||
dn2 = ldb_dn_new(mem_ctx, ldb, (char *)v2->data);
|
||||
if ( ! ldb_dn_validate(dn2)) {
|
||||
oc2 = talloc_strndup(mem_ctx, (char *)v2->data, v2->length);
|
||||
} else if (ldb_dn_get_comp_num(dn2) >= 2 && strcasecmp(ldb_dn_get_rdn_name(dn2), "cn") == 0) {
|
||||
const struct ldb_val *val = ldb_dn_get_rdn_val(dn2);
|
||||
oc2 = talloc_strndup(mem_ctx, (char *)val->data, val->length);
|
||||
} else {
|
||||
oc2 = NULL;
|
||||
}
|
||||
|
||||
oc1 = ldb_casefold(ldb, mem_ctx, oc1);
|
||||
oc2 = ldb_casefold(ldb, mem_ctx, oc2);
|
||||
if (!oc1 && oc2) {
|
||||
return -1;
|
||||
}
|
||||
if (oc1 && !oc2) {
|
||||
return 1;
|
||||
}
|
||||
if (!oc1 && !oc2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return strcmp(oc1, oc2);
|
||||
}
|
||||
|
||||
static const struct ldb_attrib_handler samba_handlers[] = {
|
||||
{
|
||||
.attr = "objectSid",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectSid,
|
||||
.ldif_write_fn = ldif_write_objectSid,
|
||||
.canonicalise_fn = ldb_canonicalise_objectSid,
|
||||
.comparison_fn = ldb_comparison_objectSid
|
||||
},
|
||||
{
|
||||
.attr = "securityIdentifier",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectSid,
|
||||
.ldif_write_fn = ldif_write_objectSid,
|
||||
.canonicalise_fn = ldb_canonicalise_objectSid,
|
||||
.comparison_fn = ldb_comparison_objectSid
|
||||
},
|
||||
{
|
||||
.attr = "ntSecurityDescriptor",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_ntSecurityDescriptor,
|
||||
.ldif_write_fn = ldif_write_ntSecurityDescriptor,
|
||||
.canonicalise_fn = ldb_handler_copy,
|
||||
.comparison_fn = ldb_comparison_binary
|
||||
},
|
||||
{
|
||||
.attr = "objectGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "invocationId",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "schemaIDGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "attributeSecurityGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "parentGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "siteGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "pKTGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "fRSVersionGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "fRSReplicaSetGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "netbootGUID",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldif_read_objectGUID,
|
||||
.ldif_write_fn = ldif_write_objectGUID,
|
||||
.canonicalise_fn = ldb_canonicalise_objectGUID,
|
||||
.comparison_fn = ldb_comparison_objectGUID
|
||||
},
|
||||
{
|
||||
.attr = "objectCategory",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldif_canonicalise_objectCategory,
|
||||
.comparison_fn = ldif_comparison_objectCategory,
|
||||
},
|
||||
{
|
||||
.attr = "member",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "memberOf",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "nCName",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "schemaNamingContext",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "configurationNamingContext",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "rootDomainNamingContext",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "defaultNamingContext",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "subRefs",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "dMDLocation",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "serverReference",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "masteredBy",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "msDs-masteredBy",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "subRefs",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
},
|
||||
{
|
||||
.attr = "fSMORoleOwner",
|
||||
.flags = 0,
|
||||
.ldif_read_fn = ldb_handler_copy,
|
||||
.ldif_write_fn = ldb_handler_copy,
|
||||
.canonicalise_fn = ldb_canonicalise_dn,
|
||||
.comparison_fn = ldb_comparison_dn,
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
register the samba ldif handlers
|
||||
*/
|
||||
int ldb_register_samba_handlers(struct ldb_context *ldb)
|
||||
{
|
||||
return ldb_set_attrib_handlers(ldb, samba_handlers, ARRAY_SIZE(samba_handlers));
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
########################################################
|
||||
# Compile with SQLITE3 support?
|
||||
|
||||
SQLITE3_LIBS=""
|
||||
with_sqlite3_support=no
|
||||
AC_MSG_CHECKING([for SQLITE3 support])
|
||||
|
||||
AC_ARG_WITH(sqlite3,
|
||||
AS_HELP_STRING([--with-sqlite3],[SQLITE3 backend support (default=no)]),
|
||||
[ case "$withval" in
|
||||
yes|no|auto)
|
||||
with_sqlite3_support=$withval
|
||||
;;
|
||||
esac ])
|
||||
|
||||
AC_MSG_RESULT($with_sqlite3_support)
|
||||
|
||||
if test x"$with_sqlite3_support" != x"no"; then
|
||||
##################################################################
|
||||
# first test for sqlite3.h
|
||||
AC_CHECK_HEADERS(sqlite3.h)
|
||||
|
||||
if test x"$ac_cv_header_sqlite3_h" != x"yes"; then
|
||||
if test x"$with_sqlite3_support" = x"yes"; then
|
||||
AC_MSG_ERROR(sqlite3.h is needed for SQLITE3 support)
|
||||
else
|
||||
AC_MSG_WARN(sqlite3.h is needed for SQLITE3 support)
|
||||
fi
|
||||
|
||||
with_sqlite3_support=no
|
||||
fi
|
||||
fi
|
||||
|
||||
if test x"$with_sqlite3_support" != x"no"; then
|
||||
ac_save_LIBS=$LIBS
|
||||
|
||||
########################################################
|
||||
# now see if we can find the sqlite3 libs in standard paths
|
||||
AC_CHECK_LIB_EXT(sqlite3, SQLITE3_LIBS, sqlite3_open)
|
||||
|
||||
if test x"$ac_cv_lib_ext_sqlite3_sqlite3_open" = x"yes"; then
|
||||
AC_DEFINE(HAVE_SQLITE3,1,[Whether sqlite3 is available])
|
||||
AC_DEFINE(HAVE_LDB_SQLITE3,1,[Whether ldb_sqlite3 is available])
|
||||
AC_MSG_CHECKING(whether SQLITE3 support is used)
|
||||
AC_MSG_RESULT(yes)
|
||||
with_sqlite3_support=yes
|
||||
SMB_ENABLE(SQLITE3,YES)
|
||||
else
|
||||
if test x"$with_sqlite3_support" = x"yes"; then
|
||||
AC_MSG_ERROR(libsqlite3 is needed for SQLITE3 support)
|
||||
else
|
||||
AC_MSG_WARN(libsqlite3 is needed for SQLITE3 support)
|
||||
fi
|
||||
|
||||
SQLITE3_LIBS=""
|
||||
with_sqlite3_support=no
|
||||
fi
|
||||
|
||||
LIBS=$ac_save_LIBS;
|
||||
fi
|
||||
|
||||
SMB_EXT_LIB(SQLITE3,[${SQLITE3_LIBS}],[${SQLITE3_CFLAGS}],[${SQLITE3_CPPFLAGS}],[${SQLITE3_LDFLAGS}])
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
|
||||
cd ../replace
|
||||
make clean
|
||||
|
||||
cd ../talloc
|
||||
make clean
|
||||
|
||||
cd ../tdb
|
||||
make clean
|
||||
|
||||
cd ../ldb
|
||||
make clean
|
||||
|
||||
./autogen.sh
|
||||
|
||||
rm -fr build
|
||||
mkdir build
|
||||
cd build
|
||||
|
||||
../configure $*
|
||||
make dirs
|
||||
make all
|
||||
|
||||
cd ..
|
||||
@@ -0,0 +1,179 @@
|
||||
"""Provide a more Pythonic and object-oriented interface to ldb."""
|
||||
|
||||
#
|
||||
# Swig interface to Samba
|
||||
#
|
||||
# Copyright (C) Tim Potter 2006
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#
|
||||
|
||||
#
|
||||
# Interface notes:
|
||||
#
|
||||
# - should an empty dn be represented as None, or an empty string?
|
||||
#
|
||||
# - should single-valued attributes be a string, or a list with one
|
||||
# element?
|
||||
#
|
||||
|
||||
from ldb import *
|
||||
|
||||
# Global initialisation
|
||||
|
||||
result = ldb_global_init()
|
||||
|
||||
if result != 0:
|
||||
raise LdbError, (result, 'ldb_global_init failed')
|
||||
|
||||
# Ldb exceptions
|
||||
|
||||
class LdbError(Exception):
|
||||
"""An exception raised when a ldb error occurs.
|
||||
The exception data is a tuple consisting of the ldb number and a
|
||||
string description of the error."""
|
||||
pass
|
||||
|
||||
# Ldb classes
|
||||
|
||||
class LdbMessage:
|
||||
"""A class representing a ldb message as a Python dictionary."""
|
||||
|
||||
def __init__(self):
|
||||
self.mem_ctx = talloc_init(None)
|
||||
self.msg = ldb_msg_new(self.mem_ctx)
|
||||
|
||||
def __del__(self):
|
||||
if self.mem_ctx is not None:
|
||||
talloc_free(self.mem_ctx)
|
||||
self.mem_ctx = None
|
||||
self.msg = None
|
||||
|
||||
# Make the dn attribute of the object dynamic
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr == 'dn':
|
||||
return ldb_dn_linearize(None, self.msg.dn)
|
||||
return self.__dict__[attr]
|
||||
|
||||
def __setattr__(self, attr, value):
|
||||
if attr == 'dn':
|
||||
self.msg.dn = ldb_dn_explode(self.msg, value)
|
||||
if self.msg.dn == None:
|
||||
err = LDB_ERR_INVALID_DN_SYNTAX
|
||||
raise LdbError(err, ldb_strerror(err))
|
||||
return
|
||||
self.__dict__[attr] = value
|
||||
|
||||
# Get and set individual elements
|
||||
|
||||
def __getitem__(self, key):
|
||||
|
||||
elt = ldb_msg_find_element(self.msg, key)
|
||||
|
||||
if elt is None:
|
||||
raise KeyError, "No such attribute '%s'" % key
|
||||
|
||||
return [ldb_val_array_getitem(elt.values, i)
|
||||
for i in range(elt.num_values)]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
ldb_msg_remove_attr(self.msg, key)
|
||||
if type(value) in (list, tuple):
|
||||
[ldb_msg_add_value(self.msg, key, v) for v in value]
|
||||
else:
|
||||
ldb_msg_add_value(self.msg, key, value)
|
||||
|
||||
# Dictionary interface
|
||||
# TODO: move to iterator based interface
|
||||
|
||||
def len(self):
|
||||
return self.msg.num_elements
|
||||
|
||||
def keys(self):
|
||||
return [ldb_message_element_array_getitem(self.msg.elements, i).name
|
||||
for i in range(self.msg.num_elements)]
|
||||
|
||||
def values(self):
|
||||
return [self[k] for k in self.keys()]
|
||||
|
||||
def items(self):
|
||||
return [(k, self[k]) for k in self.keys()]
|
||||
|
||||
# Misc stuff
|
||||
|
||||
def sanity_check(self):
|
||||
return ldb_msg_sanity_check(self.msg)
|
||||
|
||||
class Ldb:
|
||||
"""A class representing a binding to a ldb file."""
|
||||
|
||||
def __init__(self, url, flags = 0):
|
||||
"""Initialise underlying ldb."""
|
||||
|
||||
self.mem_ctx = talloc_init('mem_ctx for ldb 0x%x' % id(self))
|
||||
self.ldb_ctx = ldb_init(self.mem_ctx)
|
||||
|
||||
result = ldb_connect(self.ldb_ctx, url, flags, None)
|
||||
|
||||
if result != LDB_SUCCESS:
|
||||
raise LdbError, (result, ldb_strerror(result))
|
||||
|
||||
def __del__(self):
|
||||
"""Called when the object is to be garbage collected."""
|
||||
self.close()
|
||||
|
||||
def close(self):
|
||||
"""Close down a ldb."""
|
||||
if self.mem_ctx is not None:
|
||||
talloc_free(self.mem_ctx)
|
||||
self.mem_ctx = None
|
||||
self.ldb_ctx = None
|
||||
|
||||
def _ldb_call(self, fn, *args):
|
||||
"""Call a ldb function with args. Raise a LdbError exception
|
||||
if the function returns a non-zero return value."""
|
||||
|
||||
result = fn(*args)
|
||||
|
||||
if result != LDB_SUCCESS:
|
||||
raise LdbError, (result, ldb_strerror(result))
|
||||
|
||||
def search(self, expression):
|
||||
"""Search a ldb for a given expression."""
|
||||
|
||||
self._ldb_call(ldb_search, self.ldb_ctx, None, LDB_SCOPE_DEFAULT,
|
||||
expression, None);
|
||||
|
||||
return [LdbMessage(ldb_message_ptr_array_getitem(result.msgs, ndx))
|
||||
for ndx in range(result.count)]
|
||||
|
||||
def delete(self, dn):
|
||||
"""Delete a dn."""
|
||||
|
||||
_dn = ldb_dn_explode(self.ldb_ctx, dn)
|
||||
|
||||
self._ldb_call(ldb_delete, self.ldb_ctx, _dn)
|
||||
|
||||
def rename(self, olddn, newdn):
|
||||
"""Rename a dn."""
|
||||
|
||||
_olddn = ldb_dn_explode(self.ldb_ctx, olddn)
|
||||
_newdn = ldb_dn_explode(self.ldb_ctx, newdn)
|
||||
|
||||
self._ldb_call(ldb_rename, self.ldb_ctx, _olddn, _newdn)
|
||||
|
||||
def add(self, m):
|
||||
self._ldb_call(ldb_add, self.ldb_ctx, m.msg)
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Swig interface to ldb.
|
||||
|
||||
Copyright (C) 2005,2006 Tim Potter <tpot@samba.org>
|
||||
Copyright (C) 2006 Simo Sorce <idra@samba.org>
|
||||
|
||||
** NOTE! The following LGPL license applies to the ldb
|
||||
** library. This does NOT imply that all of Samba is released
|
||||
** under the LGPL
|
||||
|
||||
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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
%module ldb
|
||||
|
||||
%{
|
||||
|
||||
/* Some typedefs to help swig along */
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef long long int64_t;
|
||||
|
||||
/* Include headers */
|
||||
|
||||
#include "lib/ldb/include/ldb.h"
|
||||
#include "lib/talloc/talloc.h"
|
||||
|
||||
%}
|
||||
|
||||
%include "carrays.i"
|
||||
%include "exception.i"
|
||||
|
||||
/*
|
||||
* Constants
|
||||
*/
|
||||
|
||||
#define LDB_SUCCESS 0
|
||||
#define LDB_ERR_OPERATIONS_ERROR 1
|
||||
#define LDB_ERR_PROTOCOL_ERROR 2
|
||||
#define LDB_ERR_TIME_LIMIT_EXCEEDED 3
|
||||
#define LDB_ERR_SIZE_LIMIT_EXCEEDED 4
|
||||
#define LDB_ERR_COMPARE_FALSE 5
|
||||
#define LDB_ERR_COMPARE_TRUE 6
|
||||
#define LDB_ERR_AUTH_METHOD_NOT_SUPPORTED 7
|
||||
#define LDB_ERR_STRONG_AUTH_REQUIRED 8
|
||||
/* 9 RESERVED */
|
||||
#define LDB_ERR_REFERRAL 10
|
||||
#define LDB_ERR_ADMIN_LIMIT_EXCEEDED 11
|
||||
#define LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION 12
|
||||
#define LDB_ERR_CONFIDENTIALITY_REQUIRED 13
|
||||
#define LDB_ERR_SASL_BIND_IN_PROGRESS 14
|
||||
#define LDB_ERR_NO_SUCH_ATTRIBUTE 16
|
||||
#define LDB_ERR_UNDEFINED_ATTRIBUTE_TYPE 17
|
||||
#define LDB_ERR_INAPPROPRIATE_MATCHING 18
|
||||
#define LDB_ERR_CONSTRAINT_VIOLATION 19
|
||||
#define LDB_ERR_ATTRIBUTE_OR_VALUE_EXISTS 20
|
||||
#define LDB_ERR_INVALID_ATTRIBUTE_SYNTAX 21
|
||||
/* 22-31 unused */
|
||||
#define LDB_ERR_NO_SUCH_OBJECT 32
|
||||
#define LDB_ERR_ALIAS_PROBLEM 33
|
||||
#define LDB_ERR_INVALID_DN_SYNTAX 34
|
||||
/* 35 RESERVED */
|
||||
#define LDB_ERR_ALIAS_DEREFERENCING_PROBLEM 36
|
||||
/* 37-47 unused */
|
||||
#define LDB_ERR_INAPPROPRIATE_AUTHENTICATION 48
|
||||
#define LDB_ERR_INVALID_CREDENTIALS 49
|
||||
#define LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS 50
|
||||
#define LDB_ERR_BUSY 51
|
||||
#define LDB_ERR_UNAVAILABLE 52
|
||||
#define LDB_ERR_UNWILLING_TO_PERFORM 53
|
||||
#define LDB_ERR_LOOP_DETECT 54
|
||||
/* 55-63 unused */
|
||||
#define LDB_ERR_NAMING_VIOLATION 64
|
||||
#define LDB_ERR_OBJECT_CLASS_VIOLATION 65
|
||||
#define LDB_ERR_NOT_ALLOWED_ON_NON_LEAF 66
|
||||
#define LDB_ERR_NOT_ALLOWED_ON_RDN 67
|
||||
#define LDB_ERR_ENTRY_ALREADY_EXISTS 68
|
||||
#define LDB_ERR_OBJECT_CLASS_MODS_PROHIBITED 69
|
||||
/* 70 RESERVED FOR CLDAP */
|
||||
#define LDB_ERR_AFFECTS_MULTIPLE_DSAS 71
|
||||
/* 72-79 unused */
|
||||
#define LDB_ERR_OTHER 80
|
||||
|
||||
enum ldb_scope {LDB_SCOPE_DEFAULT=-1,
|
||||
LDB_SCOPE_BASE=0,
|
||||
LDB_SCOPE_ONELEVEL=1,
|
||||
LDB_SCOPE_SUBTREE=2};
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_context
|
||||
*/
|
||||
|
||||
/* The ldb functions will crash if a NULL ldb context is passed so
|
||||
catch this before it happens. */
|
||||
|
||||
%typemap(check) struct ldb_context* {
|
||||
if ($1 == NULL)
|
||||
SWIG_exception(SWIG_ValueError,
|
||||
"ldb context must be non-NULL");
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrap a small bit of talloc
|
||||
*/
|
||||
|
||||
/* Use talloc_init() to create a parameter to pass to ldb_init(). Don't
|
||||
forget to free it using talloc_free() afterwards. */
|
||||
|
||||
TALLOC_CTX *talloc_init(char *name);
|
||||
int talloc_free(TALLOC_CTX *ptr);
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_val
|
||||
*/
|
||||
|
||||
%typemap(in) struct ldb_val *INPUT (struct ldb_val temp) {
|
||||
$1 = &temp;
|
||||
if (!PyString_Check($input)) {
|
||||
PyErr_SetString(PyExc_TypeError, "string arg expected");
|
||||
return NULL;
|
||||
}
|
||||
$1->length = PyString_Size($input);
|
||||
$1->data = PyString_AsString($input);
|
||||
}
|
||||
|
||||
%typemap(out) struct ldb_val {
|
||||
$result = PyString_FromStringAndSize($1.data, $1.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_result
|
||||
*/
|
||||
|
||||
%typemap(in, numinputs=0) struct ldb_result **OUT (struct ldb_result *temp_ldb_result) {
|
||||
$1 = &temp_ldb_result;
|
||||
}
|
||||
|
||||
%typemap(argout) struct ldb_result ** {
|
||||
resultobj = SWIG_NewPointerObj(*$1, SWIGTYPE_p_ldb_result, 0);
|
||||
}
|
||||
|
||||
%types(struct ldb_result *);
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_message_element
|
||||
*/
|
||||
|
||||
%array_functions(struct ldb_val, ldb_val_array);
|
||||
|
||||
struct ldb_message_element {
|
||||
unsigned int flags;
|
||||
const char *name;
|
||||
unsigned int num_values;
|
||||
struct ldb_val *values;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_message
|
||||
*/
|
||||
|
||||
%array_functions(struct ldb_message_element, ldb_message_element_array);
|
||||
|
||||
struct ldb_message {
|
||||
struct ldb_dn *dn;
|
||||
unsigned int num_elements;
|
||||
struct ldb_message_element *elements;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wrap struct ldb_result
|
||||
*/
|
||||
|
||||
%array_functions(struct ldb_message *, ldb_message_ptr_array);
|
||||
|
||||
struct ldb_result {
|
||||
unsigned int count;
|
||||
struct ldb_message **msgs;
|
||||
char **refs;
|
||||
struct ldb_control **controls;
|
||||
};
|
||||
|
||||
/*
|
||||
* Wrap ldb functions
|
||||
*/
|
||||
|
||||
/* Initialisation */
|
||||
|
||||
int ldb_global_init(void);
|
||||
struct ldb_context *ldb_init(TALLOC_CTX *mem_ctx);
|
||||
|
||||
/* Error handling */
|
||||
|
||||
const char *ldb_errstring(struct ldb_context *ldb);
|
||||
const char *ldb_strerror(int ldb_err);
|
||||
|
||||
/* Top-level ldb operations */
|
||||
|
||||
int ldb_connect(struct ldb_context *ldb, const char *url, unsigned int flags, const char *options[]);
|
||||
|
||||
int ldb_search(struct ldb_context *ldb, const struct ldb_dn *base, enum ldb_scope scope, const char *expression, const char * const *attrs, struct ldb_result **OUT);
|
||||
|
||||
int ldb_delete(struct ldb_context *ldb, const struct ldb_dn *dn);
|
||||
|
||||
int ldb_rename(struct ldb_context *ldb, const struct ldb_dn *olddn, const struct ldb_dn *newdn);
|
||||
|
||||
int ldb_add(struct ldb_context *ldb, const struct ldb_message *message);
|
||||
|
||||
/* Ldb message operations */
|
||||
|
||||
struct ldb_message *ldb_msg_new(void *mem_ctx);
|
||||
|
||||
struct ldb_message_element *ldb_msg_find_element(const struct ldb_message *msg, const char *attr_name);
|
||||
|
||||
int ldb_msg_add_value(struct ldb_message *msg, const char *attr_name, const struct ldb_val *INPUT);
|
||||
|
||||
void ldb_msg_remove_attr(struct ldb_message *msg, const char *attr);
|
||||
|
||||
int ldb_msg_sanity_check(struct ldb_message *msg);
|
||||
|
||||
/* DN operations */
|
||||
|
||||
struct ldb_dn *ldb_dn_explode(void *mem_ctx, const char *dn);
|
||||
|
||||
char *ldb_dn_linearize(void *mem_ctx, const struct ldb_dn *dn);
|
||||
@@ -0,0 +1,40 @@
|
||||
dn: o=University of Michigan,c=TEST
|
||||
objectclass: organization
|
||||
objectclass: domainRelatedObject
|
||||
l: Ann Arbor, Michigan
|
||||
st: Michigan
|
||||
o: University of Michigan
|
||||
o: UMICH
|
||||
o: UM
|
||||
o: U-M
|
||||
o: U of M
|
||||
description: The University of Michigan at Ann Arbor
|
||||
seeAlso:
|
||||
postaladdress: University of Michigan $ 535 W. William St. $ Ann Arbor, MI 481
|
||||
09 $ US
|
||||
telephonenumber: +1 313 764-1817
|
||||
associateddomain: example.com
|
||||
|
||||
dn: ou=People,o=University of Michigan,c=TEST
|
||||
objectclass: organizationalUnit
|
||||
objectclass: extensibleObject
|
||||
ou: People
|
||||
uidNumber: 0
|
||||
gidNumber: 0
|
||||
|
||||
dn: ou=Ldb Test,ou=People,o=University of Michigan,c=TEST
|
||||
objectclass: organizationalUnit
|
||||
objectclass: extensibleObject
|
||||
ou: People
|
||||
ou: Ldb Test
|
||||
uidNumber: 0
|
||||
gidNumber: 0
|
||||
|
||||
dn: ou=LdbTspace,ou=People,o=University of Michigan,c=TEST
|
||||
objectclass: organizationalUnit
|
||||
objectclass: extensibleObject
|
||||
ou: People
|
||||
ou: LdbTspace
|
||||
description: test white space removal in comparisons
|
||||
uidNumber: 0
|
||||
gidNumber: 0
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$LDBDIR" ]; then
|
||||
LDBDIR=`dirname $0`/..
|
||||
export LDBDIR
|
||||
fi
|
||||
|
||||
rm -rf tests/tmp/db
|
||||
mkdir -p tests/tmp/db
|
||||
|
||||
if [ -f tests/tmp/slapd.pid ]; then
|
||||
kill `cat tests/tmp/slapd.pid`
|
||||
sleep 1
|
||||
fi
|
||||
if [ -f tests/tmp/slapd.pid ]; then
|
||||
kill -9 `cat tests/tmp/slapd.pid`
|
||||
rm -f tests/tmp/slapd.pid
|
||||
fi
|
||||
|
||||
# we don't consider a slapadd failure as a test suite failure, as it
|
||||
# has nothing to do with ldb
|
||||
|
||||
MODCONF=tests/tmp/modules.conf
|
||||
rm -f $MODCONF
|
||||
touch $MODCONF || exit 1
|
||||
|
||||
slaptest -u -f $LDBDIR/tests/slapd.conf > /dev/null 2>&1 || {
|
||||
echo "enabling sladp modules"
|
||||
cat > $MODCONF <<EOF
|
||||
modulepath /usr/lib/ldap
|
||||
moduleload back_bdb
|
||||
EOF
|
||||
}
|
||||
|
||||
slaptest -u -f $LDBDIR/tests/slapd.conf || {
|
||||
echo "slaptest failed - skipping ldap tests"
|
||||
exit 0
|
||||
}
|
||||
|
||||
slapadd -f $LDBDIR/tests/slapd.conf < $LDBDIR/tests/init.ldif || exit 0
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
if [ -z "$LDBDIR" ]; then
|
||||
LDBDIR=`dirname $0`/..
|
||||
export LDBDIR
|
||||
fi
|
||||
|
||||
if [ -f tests/tmp/slapd.pid ]; then
|
||||
echo "killing slapd process `cat tests/tmp/slapd.pid`"
|
||||
kill -9 `cat tests/tmp/slapd.pid`
|
||||
rm -f tests/tmp/slapd.pid
|
||||
fi
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# aargh, did LDAP ever have to expose this crap to users ...
|
||||
|
||||
BASE=`pwd`
|
||||
|
||||
TMPDIR=$BASE/tests/tmp
|
||||
|
||||
LDAPI_ESCAPE=`echo $TMPDIR/ldapi | sed 's|/|%2F|g'`
|
||||
|
||||
echo "ldapi://$LDAPI_ESCAPE"
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: cn=Hampster Ursula,ou=Alumni Association,ou=People,o=University of Michigan,c=TEST
|
||||
changetype: modify
|
||||
add: jpegPhoto
|
||||
jpegPhoto:< file://tests/tmp/samba4.png
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
@@ -0,0 +1,66 @@
|
||||
dn: CN=Users,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: container
|
||||
cn: Users
|
||||
description: Default container for upgraded user accounts
|
||||
instanceType: 4
|
||||
whenCreated: 20050116175504.0Z
|
||||
whenChanged: 20050116175504.0Z
|
||||
uSNCreated: 1
|
||||
uSNChanged: 1
|
||||
showInAdvancedViewOnly: FALSE
|
||||
name: Users
|
||||
objectGUID: b847056a-9934-d87b-8a1a-99fabe0863c8
|
||||
systemFlags: 0x8c000000
|
||||
objectCategory: CN=Container,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
isCriticalSystemObject: TRUE
|
||||
nTSecurityDescriptor: foo
|
||||
|
||||
dn: CN=Administrator,CN=Users,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: person
|
||||
objectClass: organizationalPerson
|
||||
objectClass: user
|
||||
cn: Administrator
|
||||
description: Built-in account for administering the computer/domain
|
||||
instanceType: 4
|
||||
whenCreated: 20050116175504.0Z
|
||||
whenChanged: 20050116175504.0Z
|
||||
uSNCreated: 1
|
||||
memberOf: CN=Group Policy Creator Owners,CN=Users,DC=schema,DC=test
|
||||
memberOf: CN=Domain Admins,CN=Users,DC=schema,DC=test
|
||||
memberOf: CN=Enterprise Admins,CN=Users,DC=schema,DC=test
|
||||
memberOf: CN=Schema Admins,CN=Users,DC=schema,DC=test
|
||||
memberOf: CN=Administrators,CN=Builtin,DC=schema,DC=test
|
||||
uSNChanged: 1
|
||||
name: Administrator
|
||||
objectGUID: 6c02f98c-46c6-aa38-5f13-a510cac04e6c
|
||||
userAccountControl: 0x10200
|
||||
badPwdCount: 0
|
||||
codePage: 0
|
||||
countryCode: 0
|
||||
badPasswordTime: 0
|
||||
lastLogoff: 0
|
||||
lastLogon: 0
|
||||
pwdLastSet: 0
|
||||
primaryGroupID: 513
|
||||
objectSid: S-1-5-21-43662522-77495566-38969261-500
|
||||
adminCount: 1
|
||||
accountExpires: -1
|
||||
logonCount: 0
|
||||
sAMAccountName: Administrator
|
||||
sAMAccountType: 0x30000000
|
||||
objectCategory: CN=Person,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
isCriticalSystemObject: TRUE
|
||||
unicodePwd: samba
|
||||
nTSecurityDescriptor: foo
|
||||
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: test
|
||||
cn: Test
|
||||
description: This is a test
|
||||
objectCategory: CN=Test,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
nTSecurityDescriptor: foo
|
||||
instanceType: 4
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
changetype: modify
|
||||
replace: description
|
||||
description: this test must not fail
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
changetype: modify
|
||||
delete: description
|
||||
# this test must not fail
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
changetype: modify
|
||||
add: description
|
||||
description: this test must not fail
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
changetype: modify
|
||||
add: foo
|
||||
foo: this test must fail
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
dn: CN=Test,CN=Users,DC=schema,DC=test
|
||||
changetype: modify
|
||||
delete: nTSecurityDescriptor
|
||||
# this test must fail
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
dn: @INDEXLIST
|
||||
@IDXATTR: name
|
||||
@IDXATTR: sAMAccountName
|
||||
@IDXATTR: objectSid
|
||||
@IDXATTR: objectClass
|
||||
@IDXATTR: member
|
||||
@IDXATTR: uidNumber
|
||||
@IDXATTR: gidNumber
|
||||
@IDXATTR: unixName
|
||||
@IDXATTR: privilege
|
||||
@IDXATTR: lDAPDisplayName
|
||||
|
||||
dn: @ATTRIBUTES
|
||||
realm: CASE_INSENSITIVE
|
||||
userPrincipalName: CASE_INSENSITIVE
|
||||
servicePrincipalName: CASE_INSENSITIVE
|
||||
name: CASE_INSENSITIVE
|
||||
dn: CASE_INSENSITIVE
|
||||
sAMAccountName: CASE_INSENSITIVE
|
||||
objectClass: CASE_INSENSITIVE
|
||||
unicodePwd: HIDDEN
|
||||
ntPwdHash: HIDDEN
|
||||
ntPwdHistory: HIDDEN
|
||||
lmPwdHash: HIDDEN
|
||||
lmPwdHistory: HIDDEN
|
||||
createTimestamp: HIDDEN
|
||||
modifyTimestamp: HIDDEN
|
||||
|
||||
dn: @SUBCLASSES
|
||||
top: domain
|
||||
top: person
|
||||
top: group
|
||||
domain: domainDNS
|
||||
domain: builtinDomain
|
||||
person: organizationalPerson
|
||||
organizationalPerson: user
|
||||
user: computer
|
||||
template: userTemplate
|
||||
template: groupTemplate
|
||||
|
||||
dn: @MODULES
|
||||
@LIST: timestamps,schema
|
||||
|
||||
dn: CN=Top,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: classSchema
|
||||
lDAPDisplayName: top
|
||||
cn: Top
|
||||
uSNCreated: 1
|
||||
uSNChanged: 1
|
||||
subClassOf: top
|
||||
systemMustContain: objectClass
|
||||
systemMayContain: structuralObjectClass
|
||||
systemMayContain: createTimeStamp
|
||||
systemMayContain: modifyTimeStamp
|
||||
systemMayContain: creatorsName
|
||||
systemMayContain: modifiersName
|
||||
systemMayContain: hasSubordinates
|
||||
systemMayContain: subschemaSubentry
|
||||
systemMayContain: collectiveSubentry
|
||||
systemMayContain: entryUUID
|
||||
systemMayContain: entryCSN
|
||||
systemMayContain: namingCSN
|
||||
systemMayContain: superiorUUID
|
||||
systemMayContain: contextCSN
|
||||
systemMayContain: whenCreated
|
||||
systemMayContain: whenChanged
|
||||
systemMayContain: uSNCreated
|
||||
systemMayContain: uSNChanged
|
||||
systemMayContain: distinguishedName
|
||||
systemMayContain: name
|
||||
systemMayContain: cn
|
||||
systemMayContain: userPassword
|
||||
systemMayContain: labeledURI
|
||||
|
||||
dn: CN=Class-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: classSchema
|
||||
lDAPDisplayName: classSchema
|
||||
cn: Class-Schema
|
||||
uSNCreated: 2
|
||||
uSNChanged: 2
|
||||
lDAPDisplayName: classSchema
|
||||
subClassOf: top
|
||||
systemMustContain: cn
|
||||
systemMustContain: subClassOf
|
||||
systemMayContain: systemPossSuperiors
|
||||
systemMayContain: systemOnly
|
||||
systemMayContain: systemMustContain
|
||||
systemMayContain: systemMayContain
|
||||
systemMayContain: systemAuxiliaryClass
|
||||
systemMayContain: possSuperiors
|
||||
systemMayContain: mustContain
|
||||
systemMayContain: mayContain
|
||||
systemMayContain: lDAPDisplayName
|
||||
systemMayContain: auxiliaryClass
|
||||
|
||||
dn: CN=Attribute-Schema,CN=Schema,CN=Configuration,DC=schema,DC=test
|
||||
objectClass: top
|
||||
objectClass: classSchema
|
||||
cn: Attribute-Schema
|
||||
uSNCreated: 3
|
||||
uSNChanged: 3
|
||||
lDAPDisplayName: attributeSchema
|
||||
subClassOf: top
|
||||
systemMustContain: oMSyntax
|
||||
systemMustContain: lDAPDisplayName
|
||||
systemMustContain: isSingleValued
|
||||
systemMustContain: cn
|
||||
systemMustContain: attributeSyntax
|
||||
systemMustContain: attributeID
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
loglevel 0
|
||||
|
||||
include tests/schema/core.schema
|
||||
include tests/schema/cosine.schema
|
||||
include tests/schema/inetorgperson.schema
|
||||
include tests/schema/openldap.schema
|
||||
include tests/schema/nis.schema
|
||||
|
||||
|
||||
pidfile tests/tmp/slapd.pid
|
||||
argsfile tests/tmp/slapd.args
|
||||
|
||||
access to * by * write
|
||||
|
||||
allow update_anon bind_anon_dn
|
||||
|
||||
include tests/tmp/modules.conf
|
||||
|
||||
defaultsearchbase "o=University of Michigan,c=TEST"
|
||||
|
||||
backend bdb
|
||||
database bdb
|
||||
suffix "o=University of Michigan,c=TEST"
|
||||
directory tests/tmp/db
|
||||
index objectClass eq
|
||||
index uid eq
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user