summaryrefslogtreecommitdiffstats
path: root/libraries/libldap
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 11:11:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 11:11:40 +0000
commit7731832751ab9f3c6ddeb66f186d3d7fa1934a6d (patch)
treee91015872543a59be2aad26c2fea02e41b57005d /libraries/libldap
parentInitial commit. (diff)
downloadopenldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.tar.xz
openldap-7731832751ab9f3c6ddeb66f186d3d7fa1934a6d.zip
Adding upstream version 2.4.57+dfsg.upstream/2.4.57+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--libraries/libldap/Makefile.in84
-rw-r--r--libraries/libldap/abandon.c460
-rw-r--r--libraries/libldap/add.c263
-rw-r--r--libraries/libldap/addentry.c72
-rw-r--r--libraries/libldap/apitest.c241
-rw-r--r--libraries/libldap/assertion.c100
-rw-r--r--libraries/libldap/bind.c127
-rw-r--r--libraries/libldap/cancel.c76
-rw-r--r--libraries/libldap/charray.c275
-rw-r--r--libraries/libldap/compare.c197
-rw-r--r--libraries/libldap/controls.c552
-rw-r--r--libraries/libldap/cyrus.c1237
-rw-r--r--libraries/libldap/dds.c156
-rw-r--r--libraries/libldap/delete.c174
-rw-r--r--libraries/libldap/deref.c282
-rw-r--r--libraries/libldap/dnssrv.c431
-rw-r--r--libraries/libldap/dntest.c296
-rw-r--r--libraries/libldap/error.c398
-rw-r--r--libraries/libldap/extended.c419
-rw-r--r--libraries/libldap/fetch.c146
-rw-r--r--libraries/libldap/filter.c1124
-rw-r--r--libraries/libldap/free.c107
-rw-r--r--libraries/libldap/ftest.c119
-rw-r--r--libraries/libldap/getattr.c157
-rw-r--r--libraries/libldap/getdn.c3305
-rw-r--r--libraries/libldap/getentry.c124
-rw-r--r--libraries/libldap/getvalues.c211
-rw-r--r--libraries/libldap/gssapi.c1011
-rw-r--r--libraries/libldap/init.c728
-rw-r--r--libraries/libldap/ldap-int.h889
-rw-r--r--libraries/libldap/ldap-tls.h77
-rw-r--r--libraries/libldap/ldap.conf13
-rw-r--r--libraries/libldap/ldap_sync.c928
-rw-r--r--libraries/libldap/ldif.c944
-rw-r--r--libraries/libldap/messages.c68
-rw-r--r--libraries/libldap/modify.c233
-rw-r--r--libraries/libldap/modrdn.c273
-rw-r--r--libraries/libldap/open.c611
-rw-r--r--libraries/libldap/options.c940
-rw-r--r--libraries/libldap/os-ip.c1154
-rw-r--r--libraries/libldap/os-local.c363
-rw-r--r--libraries/libldap/pagectrl.c271
-rw-r--r--libraries/libldap/passwd.c170
-rw-r--r--libraries/libldap/ppolicy.c256
-rw-r--r--libraries/libldap/print.c62
-rw-r--r--libraries/libldap/references.c147
-rw-r--r--libraries/libldap/request.c1699
-rw-r--r--libraries/libldap/result.c1382
-rw-r--r--libraries/libldap/sasl.c868
-rw-r--r--libraries/libldap/sbind.c115
-rw-r--r--libraries/libldap/schema.c3385
-rw-r--r--libraries/libldap/search.c545
-rw-r--r--libraries/libldap/sort.c183
-rw-r--r--libraries/libldap/sortctrl.c552
-rw-r--r--libraries/libldap/stctrl.c302
-rw-r--r--libraries/libldap/string.c177
-rw-r--r--libraries/libldap/t61.c692
-rw-r--r--libraries/libldap/test.c807
-rw-r--r--libraries/libldap/tls2.c1413
-rw-r--r--libraries/libldap/tls_g.c940
-rw-r--r--libraries/libldap/tls_m.c3324
-rw-r--r--libraries/libldap/tls_o.c1378
-rw-r--r--libraries/libldap/turn.c96
-rw-r--r--libraries/libldap/txn.c155
-rw-r--r--libraries/libldap/unbind.c309
-rw-r--r--libraries/libldap/url.c1622
-rw-r--r--libraries/libldap/urltest.c128
-rw-r--r--libraries/libldap/utf-8-conv.c485
-rw-r--r--libraries/libldap/utf-8.c562
-rw-r--r--libraries/libldap/util-int.c923
-rw-r--r--libraries/libldap/vlvctrl.c361
-rw-r--r--libraries/libldap/whoami.c102
-rw-r--r--libraries/libldap_r/Makefile.in88
-rw-r--r--libraries/libldap_r/ldap_thr_debug.h191
-rw-r--r--libraries/libldap_r/rdwr.c458
-rw-r--r--libraries/libldap_r/rmutex.c219
-rw-r--r--libraries/libldap_r/rq.c221
-rw-r--r--libraries/libldap_r/thr_cthreads.c180
-rw-r--r--libraries/libldap_r/thr_debug.c1292
-rw-r--r--libraries/libldap_r/thr_nt.c245
-rw-r--r--libraries/libldap_r/thr_posix.c388
-rw-r--r--libraries/libldap_r/thr_pth.c231
-rw-r--r--libraries/libldap_r/thr_stub.c305
-rw-r--r--libraries/libldap_r/thr_thr.c186
-rw-r--r--libraries/libldap_r/threads.c113
-rw-r--r--libraries/libldap_r/tpool.c1032
86 files changed, 47925 insertions, 0 deletions
diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
new file mode 100644
index 0000000..92856d2
--- /dev/null
+++ b/libraries/libldap/Makefile.in
@@ -0,0 +1,84 @@
+# Makefile.in for LDAP -lldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2021 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = libldap.la
+
+PROGRAMS = apitest dntest ftest ltest urltest
+
+SRCS = bind.c open.c result.c error.c compare.c search.c \
+ controls.c messages.c references.c extended.c cyrus.c \
+ modify.c add.c modrdn.c delete.c abandon.c \
+ sasl.c gssapi.c sbind.c unbind.c cancel.c \
+ filter.c free.c sort.c passwd.c whoami.c \
+ getdn.c getentry.c getattr.c getvalues.c addentry.c \
+ request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
+ init.c options.c print.c string.c util-int.c schema.c \
+ charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
+ tls2.c tls_o.c tls_g.c tls_m.c \
+ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
+ assertion.c deref.c ldif.c fetch.c
+
+OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
+ controls.lo messages.lo references.lo extended.lo cyrus.lo \
+ modify.lo add.lo modrdn.lo delete.lo abandon.lo \
+ sasl.lo gssapi.lo sbind.lo unbind.lo cancel.lo \
+ filter.lo free.lo sort.lo passwd.lo whoami.lo \
+ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \
+ request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
+ init.lo options.lo print.lo string.lo util-int.lo schema.lo \
+ charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
+ tls2.lo tls_o.lo tls_g.lo tls_m.lo \
+ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
+ assertion.lo deref.lo ldif.lo fetch.lo
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIB_DEFS = -DLDAP_LIBRARY
+
+XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS)
+UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS)
+
+apitest: $(XLIBS) apitest.o
+ $(LTLINK) -o $@ apitest.o $(LIBS)
+dntest: $(XLIBS) dntest.o
+ $(LTLINK) -o $@ dntest.o $(LIBS)
+ftest: $(XLIBS) ftest.o
+ $(LTLINK) -o $@ ftest.o $(LIBS)
+ltest: $(XLIBS) test.o
+ $(LTLINK) -o $@ test.o $(LIBS)
+urltest: $(XLIBS) urltest.o
+ $(LTLINK) -o $@ urltest.o $(LIBS)
+
+CFFILES=ldap.conf
+
+install-local: $(CFFILES) FORCE
+ -$(MKDIR) $(DESTDIR)$(libdir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir)
+ $(LTFINISH) $(DESTDIR)$(libdir)
+ -$(MKDIR) $(DESTDIR)$(sysconfdir)
+ @for i in $(CFFILES); do \
+ if test ! -f $(DESTDIR)$(sysconfdir)/$$i; then \
+ echo "installing $$i in $(sysconfdir)"; \
+ echo "$(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i"; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i; \
+ else \
+ echo "PRESERVING EXISTING CONFIGURATION FILE $(sysconfdir)/$$i" ; \
+ fi; \
+ $(INSTALL) $(INSTALLFLAGS) -m 644 $(srcdir)/$$i $(DESTDIR)$(sysconfdir)/$$i.default; \
+ done
+
diff --git a/libraries/libldap/abandon.c b/libraries/libldap/abandon.c
new file mode 100644
index 0000000..1b8912e
--- /dev/null
+++ b/libraries/libldap/abandon.c
@@ -0,0 +1,460 @@
+/* abandon.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * An abandon request looks like this:
+ * AbandonRequest ::= [APPLICATION 16] MessageID
+ * and has no response. (Source: RFC 4511)
+ */
+#include "lutil.h"
+
+static int
+do_abandon(
+ LDAP *ld,
+ ber_int_t origid,
+ ber_int_t msgid,
+ LDAPControl **sctrls,
+ int sendabandon );
+
+/*
+ * ldap_abandon_ext - perform an ldap extended abandon operation.
+ *
+ * Parameters:
+ * ld LDAP descriptor
+ * msgid The message id of the operation to abandon
+ * scntrls Server Controls
+ * ccntrls Client Controls
+ *
+ * ldap_abandon_ext returns a LDAP error code.
+ * (LDAP_SUCCESS if everything went ok)
+ *
+ * Example:
+ * ldap_abandon_ext( ld, msgid, scntrls, ccntrls );
+ */
+int
+ldap_abandon_ext(
+ LDAP *ld,
+ int msgid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_abandon_ext %d\n", msgid, 0, 0 );
+
+ /* check client controls */
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+
+ rc = ldap_int_client_controls( ld, cctrls );
+ if ( rc == LDAP_SUCCESS ) {
+ rc = do_abandon( ld, msgid, msgid, sctrls, 1 );
+ }
+
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+
+ return rc;
+}
+
+
+/*
+ * ldap_abandon - perform an ldap abandon operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * msgid The message id of the operation to abandon
+ *
+ * ldap_abandon returns 0 if everything went ok, -1 otherwise.
+ *
+ * Example:
+ * ldap_abandon( ld, msgid );
+ */
+int
+ldap_abandon( LDAP *ld, int msgid )
+{
+ Debug( LDAP_DEBUG_TRACE, "ldap_abandon %d\n", msgid, 0, 0 );
+ return ldap_abandon_ext( ld, msgid, NULL, NULL ) == LDAP_SUCCESS
+ ? 0 : -1;
+}
+
+
+int
+ldap_pvt_discard(
+ LDAP *ld,
+ ber_int_t msgid )
+{
+ int rc;
+
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ rc = do_abandon( ld, msgid, msgid, NULL, 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ return rc;
+}
+
+static int
+do_abandon(
+ LDAP *ld,
+ ber_int_t origid,
+ ber_int_t msgid,
+ LDAPControl **sctrls,
+ int sendabandon )
+{
+ BerElement *ber;
+ int i, err;
+ Sockbuf *sb;
+ LDAPRequest *lr;
+
+ Debug( LDAP_DEBUG_TRACE, "do_abandon origid %d, msgid %d\n",
+ origid, msgid, 0 );
+
+ /* find the request that we are abandoning */
+start_again:;
+ lr = ld->ld_requests;
+ while ( lr != NULL ) {
+ /* this message */
+ if ( lr->lr_msgid == msgid ) {
+ break;
+ }
+
+ /* child: abandon it */
+ if ( lr->lr_origid == msgid && !lr->lr_abandoned ) {
+ (void)do_abandon( ld, lr->lr_origid, lr->lr_msgid,
+ sctrls, sendabandon );
+
+ /* restart, as lr may now be dangling... */
+ goto start_again;
+ }
+
+ lr = lr->lr_next;
+ }
+
+ if ( lr != NULL ) {
+ if ( origid == msgid && lr->lr_parent != NULL ) {
+ /* don't let caller abandon child requests! */
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return( LDAP_PARAM_ERROR );
+ }
+ if ( lr->lr_status != LDAP_REQST_INPROGRESS ) {
+ /* no need to send abandon message */
+ sendabandon = 0;
+ }
+ }
+
+ /* ldap_msgdelete locks the res_mutex. Give up the req_mutex
+ * while we're in there.
+ */
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ err = ldap_msgdelete( ld, msgid );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ if ( err == 0 ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ return LDAP_SUCCESS;
+ }
+
+ /* fetch again the request that we are abandoning */
+ if ( lr != NULL ) {
+ for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
+ /* this message */
+ if ( lr->lr_msgid == msgid ) {
+ break;
+ }
+ }
+ }
+
+ err = 0;
+ if ( sendabandon ) {
+ if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, NULL ) == -1 ) {
+ /* not connected */
+ err = -1;
+ ld->ld_errno = LDAP_SERVER_DOWN;
+
+ } else if ( ( ber = ldap_alloc_ber_with_options( ld ) ) == NULL ) {
+ /* BER element allocation failed */
+ err = -1;
+ ld->ld_errno = LDAP_NO_MEMORY;
+
+ } else {
+ /*
+ * We already have the mutex in LDAP_R_COMPILE, so
+ * don't try to get it again.
+ * LDAP_NEXT_MSGID(ld, i);
+ */
+
+ LDAP_NEXT_MSGID(ld, i);
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage sa = {0};
+ /* dummy, filled with ldo_peer in request.c */
+ err = ber_write( ber, (char *) &sa, sizeof(sa), 0 );
+ }
+ if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version ==
+ LDAP_VERSION2 )
+ {
+ char *dn;
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ dn = ld->ld_options.ldo_cldapdn;
+ if (!dn) dn = "";
+ err = ber_printf( ber, "{isti", /* '}' */
+ i, dn,
+ LDAP_REQ_ABANDON, msgid );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ } else
+#endif
+ {
+ /* create a message to send */
+ err = ber_printf( ber, "{iti", /* '}' */
+ i,
+ LDAP_REQ_ABANDON, msgid );
+ }
+
+ if ( err == -1 ) {
+ /* encoding error */
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+
+ } else {
+ /* Put Server Controls */
+ if ( ldap_int_put_controls( ld, sctrls, ber )
+ != LDAP_SUCCESS )
+ {
+ err = -1;
+
+ } else {
+ /* close '{' */
+ err = ber_printf( ber, /*{*/ "N}" );
+
+ if ( err == -1 ) {
+ /* encoding error */
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+ }
+ }
+
+ if ( err == -1 ) {
+ ber_free( ber, 1 );
+
+ } else {
+ /* send the message */
+ if ( lr != NULL ) {
+ assert( lr->lr_conn != NULL );
+ sb = lr->lr_conn->lconn_sb;
+ } else {
+ sb = ld->ld_sb;
+ }
+
+ if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) != 0 ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ err = -1;
+ } else {
+ err = 0;
+ }
+ }
+ }
+ }
+
+ if ( lr != NULL ) {
+ LDAPConn *lc;
+ int freeconn = 0;
+ if ( sendabandon || lr->lr_status == LDAP_REQST_WRITING ) {
+ freeconn = 1;
+ lc = lr->lr_conn;
+ }
+ if ( origid == msgid ) {
+ ldap_free_request( ld, lr );
+
+ } else {
+ lr->lr_abandoned = 1;
+ }
+
+ if ( freeconn ) {
+ /* release ld_req_mutex while grabbing ld_conn_mutex to
+ * prevent deadlock.
+ */
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ ldap_free_connection( ld, lc, 0, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ }
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+
+ /* use bisection */
+ i = 0;
+ if ( ld->ld_nabandoned == 0 ||
+ ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &i ) == 0 )
+ {
+ ldap_int_bisect_insert( &ld->ld_abandoned, &ld->ld_nabandoned, msgid, i );
+ }
+
+ if ( err != -1 ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ }
+
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return( ld->ld_errno );
+}
+
+/*
+ * ldap_int_bisect_find
+ *
+ * args:
+ * v: array of length n (in)
+ * n: length of array v (in)
+ * id: value to look for (in)
+ * idxp: pointer to location of value/insert point
+ *
+ * return:
+ * 0: not found
+ * 1: found
+ * -1: error
+ */
+int
+ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp )
+{
+ int begin,
+ end,
+ rc = 0;
+
+ assert( id >= 0 );
+
+ begin = 0;
+ end = n - 1;
+
+ if ( n <= 0 || id < v[ begin ] ) {
+ *idxp = 0;
+
+ } else if ( id > v[ end ] ) {
+ *idxp = n;
+
+ } else {
+ int pos;
+ ber_int_t curid;
+
+ do {
+ pos = (begin + end)/2;
+ curid = v[ pos ];
+
+ if ( id < curid ) {
+ end = pos - 1;
+
+ } else if ( id > curid ) {
+ begin = ++pos;
+
+ } else {
+ /* already abandoned? */
+ rc = 1;
+ break;
+ }
+ } while ( end >= begin );
+
+ *idxp = pos;
+ }
+
+ return rc;
+}
+
+/*
+ * ldap_int_bisect_insert
+ *
+ * args:
+ * vp: pointer to array of length *np (in/out)
+ * np: pointer to length of array *vp (in/out)
+ * id: value to insert (in)
+ * idx: location of insert point (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ * 0: inserted
+ * -1: error
+ */
+int
+ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+ ber_int_t *v;
+ ber_len_t n;
+ int i;
+
+ assert( vp != NULL );
+ assert( np != NULL );
+ assert( idx >= 0 );
+ assert( (unsigned) idx <= *np );
+
+ n = *np;
+
+ v = ber_memrealloc( *vp, sizeof( ber_int_t ) * ( n + 1 ) );
+ if ( v == NULL ) {
+ return -1;
+ }
+ *vp = v;
+
+ for ( i = n; i > idx; i-- ) {
+ v[ i ] = v[ i - 1 ];
+ }
+ v[ idx ] = id;
+ ++(*np);
+
+ return 0;
+}
+
+/*
+ * ldap_int_bisect_delete
+ *
+ * args:
+ * vp: pointer to array of length *np (in/out)
+ * np: pointer to length of array *vp (in/out)
+ * id: value to delete (in)
+ * idx: location of value to delete (as computed by ldap_int_bisect_find())
+ *
+ * return:
+ * 0: deleted
+ */
+int
+ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx )
+{
+ ber_int_t *v;
+ ber_len_t i, n;
+
+ assert( vp != NULL );
+ assert( np != NULL );
+ assert( idx >= 0 );
+ assert( (unsigned) idx < *np );
+
+ v = *vp;
+
+ assert( v[ idx ] == id );
+
+ --(*np);
+ n = *np;
+
+ for ( i = idx; i < n; i++ ) {
+ v[ i ] = v[ i + 1 ];
+ }
+
+ return 0;
+}
diff --git a/libraries/libldap/add.c b/libraries/libldap/add.c
new file mode 100644
index 0000000..e9453fd
--- /dev/null
+++ b/libraries/libldap/add.c
@@ -0,0 +1,263 @@
+/* add.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* An LDAP Add Request/Response looks like this:
+ * AddRequest ::= [APPLICATION 8] SEQUENCE {
+ * entry LDAPDN,
+ * attributes AttributeList }
+ *
+ * AttributeList ::= SEQUENCE OF attribute Attribute
+ *
+ * Attribute ::= PartialAttribute(WITH COMPONENTS {
+ * ...,
+ * vals (SIZE(1..MAX))})
+ *
+ * PartialAttribute ::= SEQUENCE {
+ * type AttributeDescription,
+ * vals SET OF value AttributeValue }
+ *
+ * AttributeDescription ::= LDAPString
+ * -- Constrained to <attributedescription> [RFC4512]
+ *
+ * AttributeValue ::= OCTET STRING
+ *
+ * AddResponse ::= [APPLICATION 9] LDAPResult
+ * (Source: RFC 4511)
+ */
+
+/*
+ * ldap_add - initiate an ldap add operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the entry to add
+ * mods List of attributes for the entry. This is a null-
+ * terminated array of pointers to LDAPMod structures.
+ * only the type and values in the structures need be
+ * filled in.
+ *
+ * Example:
+ * LDAPMod *attrs[] = {
+ * { 0, "cn", { "babs jensen", "babs", 0 } },
+ * { 0, "sn", { "jensen", 0 } },
+ * { 0, "objectClass", { "person", 0 } },
+ * 0
+ * }
+ * msgid = ldap_add( ld, dn, attrs );
+ */
+int
+ldap_add( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs )
+{
+ int rc;
+ int msgid;
+
+ rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return -1;
+
+ return msgid;
+}
+
+
+BerElement *
+ldap_build_add_req(
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int i, rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID(ld, *msgidp);
+ rc = ber_printf( ber, "{it{s{", /* '}}}' */
+ *msgidp, LDAP_REQ_ADD, dn );
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* allow attrs to be NULL ("touch"; should fail...) */
+ if ( attrs ) {
+ /* for each attribute in the entry... */
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( ( attrs[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) {
+ int j;
+
+ if ( attrs[i]->mod_bvalues == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ for ( j = 0; attrs[i]->mod_bvalues[ j ] != NULL; j++ ) {
+ if ( attrs[i]->mod_bvalues[ j ]->bv_val == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+
+ rc = ber_printf( ber, "{s[V]N}", attrs[i]->mod_type,
+ attrs[i]->mod_bvalues );
+
+ } else {
+ if ( attrs[i]->mod_values == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ rc = ber_printf( ber, "{s[v]N}", attrs[i]->mod_type,
+ attrs[i]->mod_values );
+ }
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_add_ext - initiate an ldap extended add operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the entry to add
+ * mods List of attributes for the entry. This is a null-
+ * terminated array of pointers to LDAPMod structures.
+ * only the type and values in the structures need be
+ * filled in.
+ * sctrl Server Controls
+ * cctrl Client Controls
+ * msgidp Message ID pointer
+ *
+ * Example:
+ * LDAPMod *attrs[] = {
+ * { 0, "cn", { "babs jensen", "babs", 0 } },
+ * { 0, "sn", { "jensen", 0 } },
+ * { 0, "objectClass", { "person", 0 } },
+ * 0
+ * }
+ * rc = ldap_add_ext( ld, dn, attrs, NULL, NULL, &msgid );
+ */
+int
+ldap_add_ext(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_add_ext\n", 0, 0, 0 );
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_add_req( ld, dn, attrs, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_ADD, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_add_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int msgid, rc;
+ LDAPMessage *res;
+
+ rc = ldap_add_ext( ld, dn, attrs, sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_add_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **attrs )
+{
+ return ldap_add_ext_s( ld, dn, attrs, NULL, NULL );
+}
+
diff --git a/libraries/libldap/addentry.c b/libraries/libldap/addentry.c
new file mode 100644
index 0000000..514833b
--- /dev/null
+++ b/libraries/libldap/addentry.c
@@ -0,0 +1,72 @@
+/* addentry.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_delete_result_entry( LDAPMessage **list, LDAPMessage *e )
+{
+ LDAPMessage *tmp, *prev = NULL;
+
+ assert( list != NULL );
+ assert( e != NULL );
+
+ for ( tmp = *list; tmp != NULL && tmp != e; tmp = tmp->lm_chain )
+ prev = tmp;
+
+ if ( tmp == NULL )
+ return( NULL );
+
+ if ( prev == NULL ) {
+ if ( tmp->lm_chain )
+ tmp->lm_chain->lm_chain_tail = (*list)->lm_chain_tail;
+ *list = tmp->lm_chain;
+ } else {
+ prev->lm_chain = tmp->lm_chain;
+ if ( prev->lm_chain == NULL )
+ (*list)->lm_chain_tail = prev;
+ }
+ tmp->lm_chain = NULL;
+
+ return( tmp );
+}
+
+void
+ldap_add_result_entry( LDAPMessage **list, LDAPMessage *e )
+{
+ assert( list != NULL );
+ assert( e != NULL );
+
+ e->lm_chain = *list;
+ if ( *list )
+ e->lm_chain_tail = (*list)->lm_chain_tail;
+ else
+ e->lm_chain_tail = e;
+ *list = e;
+}
diff --git a/libraries/libldap/apitest.c b/libraries/libldap/apitest.c
new file mode 100644
index 0000000..b15b848
--- /dev/null
+++ b/libraries/libldap/apitest.c
@@ -0,0 +1,241 @@
+/* apitest.c -- OpenLDAP API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Portions Copyright 1998-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was orignally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <stdio.h>
+
+#include <ldap.h>
+
+int
+main(int argc, char **argv)
+{
+ LDAPAPIInfo api;
+ int ival;
+ char *sval;
+
+ printf("Compile time API Information\n");
+
+#ifdef LDAP_API_INFO_VERSION
+ api.ldapai_info_version = LDAP_API_INFO_VERSION;
+ printf(" API Info version: %d\n", (int) api.ldapai_info_version);
+#else
+ api.ldapai_info_version = 1;
+ printf(" API Info version: unknown\n");
+#endif
+
+#ifdef LDAP_FEATURE_INFO_VERSION
+ printf(" Feature Info version: %d\n", (int) LDAP_FEATURE_INFO_VERSION);
+#else
+ printf(" Feature Info version: unknown\n");
+ api.ldapai_info_version = 1;
+#endif
+
+#ifdef LDAP_API_VERSION
+ printf(" API version: %d\n", (int) LDAP_API_VERSION);
+#else
+ printf(" API version: unknown\n");
+#endif
+
+#ifdef LDAP_VERSION
+ printf(" Protocol Version: %d\n", (int) LDAP_VERSION);
+#else
+ printf(" Protocol Version: unknown\n");
+#endif
+#ifdef LDAP_VERSION_MIN
+ printf(" Protocol Min: %d\n", (int) LDAP_VERSION_MIN);
+#else
+ printf(" Protocol Min: unknown\n");
+#endif
+#ifdef LDAP_VERSION_MAX
+ printf(" Protocol Max: %d\n", (int) LDAP_VERSION_MAX);
+#else
+ printf(" Protocol Max: unknown\n");
+#endif
+#ifdef LDAP_VENDOR_NAME
+ printf(" Vendor Name: %s\n", LDAP_VENDOR_NAME);
+#else
+ printf(" Vendor Name: unknown\n");
+#endif
+#ifdef LDAP_VENDOR_VERSION
+ printf(" Vendor Version: %d\n", (int) LDAP_VENDOR_VERSION);
+#else
+ printf(" Vendor Version: unknown\n");
+#endif
+
+ if(ldap_get_option(NULL, LDAP_OPT_API_INFO, &api) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(API_INFO) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+
+ printf("\nExecution time API Information\n");
+ printf(" API Info version: %d\n", api.ldapai_info_version);
+
+ if (api.ldapai_info_version != LDAP_API_INFO_VERSION) {
+ printf(" API INFO version mismatch: got %d, expected %d\n",
+ api.ldapai_info_version, LDAP_API_INFO_VERSION);
+ return EXIT_FAILURE;
+ }
+
+ printf(" API Version: %d\n", api.ldapai_api_version);
+ printf(" Protocol Max: %d\n", api.ldapai_protocol_version);
+
+ if(api.ldapai_extensions == NULL) {
+ printf(" Extensions: none\n");
+
+ } else {
+ int i;
+ for(i=0; api.ldapai_extensions[i] != NULL; i++) /* empty */;
+ printf(" Extensions: %d\n", i);
+ for(i=0; api.ldapai_extensions[i] != NULL; i++) {
+#ifdef LDAP_OPT_API_FEATURE_INFO
+ LDAPAPIFeatureInfo fi;
+ fi.ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+ fi.ldapaif_name = api.ldapai_extensions[i];
+ fi.ldapaif_version = 0;
+
+ if( ldap_get_option(NULL, LDAP_OPT_API_FEATURE_INFO, &fi) == LDAP_SUCCESS ) {
+ if(fi.ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) {
+ printf(" %s feature info mismatch: got %d, expected %d\n",
+ api.ldapai_extensions[i],
+ LDAP_FEATURE_INFO_VERSION,
+ fi.ldapaif_info_version);
+
+ } else {
+ printf(" %s: version %d\n",
+ fi.ldapaif_name,
+ fi.ldapaif_version);
+ }
+
+ } else {
+ printf(" %s (NO FEATURE INFO)\n",
+ api.ldapai_extensions[i]);
+ }
+
+#else
+ printf(" %s\n",
+ api.ldapai_extensions[i]);
+#endif
+
+ ldap_memfree(api.ldapai_extensions[i]);
+ }
+ ldap_memfree(api.ldapai_extensions);
+ }
+
+ printf(" Vendor Name: %s\n", api.ldapai_vendor_name);
+ ldap_memfree(api.ldapai_vendor_name);
+
+ printf(" Vendor Version: %d\n", api.ldapai_vendor_version);
+
+ printf("\nExecution time Default Options\n");
+
+ if(ldap_get_option(NULL, LDAP_OPT_DEREF, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(api) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" DEREF: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_SIZELIMIT, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(sizelimit) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" SIZELIMIT: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_TIMELIMIT, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(timelimit) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" TIMELIMIT: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_REFERRALS, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(referrals) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" REFERRALS: %s\n", ival ? "on" : "off");
+
+ if(ldap_get_option(NULL, LDAP_OPT_RESTART, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(restart) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" RESTART: %s\n", ival ? "on" : "off");
+
+ if(ldap_get_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &ival) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(protocol version) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ printf(" PROTOCOL VERSION: %d\n", ival);
+
+ if(ldap_get_option(NULL, LDAP_OPT_HOST_NAME, &sval) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: ldap_get_option(host name) failed\n", argv[0]);
+ return EXIT_FAILURE;
+ }
+ if( sval != NULL ) {
+ printf(" HOST NAME: %s\n", sval);
+ ldap_memfree(sval);
+ } else {
+ puts(" HOST NAME: <not set>");
+ }
+
+#if 0
+ /* API tests */
+ { /* bindless unbind */
+ LDAP *ld;
+ int rc;
+
+ ld = ldap_init( "localhost", 389 );
+ if( ld == NULL ) {
+ perror("ldap_init");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_unbind( ld );
+ if( rc != LDAP_SUCCESS ) {
+ perror("ldap_unbind");
+ return EXIT_FAILURE;
+ }
+ }
+ { /* bindless unbind */
+ LDAP *ld;
+ int rc;
+
+ ld = ldap_init( "localhost", 389 );
+ if( ld == NULL ) {
+ perror("ldap_init");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_abandon_ext( ld, 0, NULL, NULL );
+ if( rc != LDAP_SERVER_DOWN ) {
+ ldap_perror( ld, "ldap_abandon");
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_unbind( ld );
+ if( rc != LDAP_SUCCESS ) {
+ perror("ldap_unbind");
+ return EXIT_FAILURE;
+ }
+ }
+#endif
+
+ return EXIT_SUCCESS;
+}
diff --git a/libraries/libldap/assertion.c b/libraries/libldap/assertion.c
new file mode 100644
index 0000000..d888d79
--- /dev/null
+++ b/libraries/libldap/assertion.c
@@ -0,0 +1,100 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_create_assertion_control_value(
+ LDAP *ld,
+ char *assertion,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ int err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( assertion == NULL || assertion[ 0 ] == '\0' ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if ( value == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ BER_BVZERO( value );
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ err = ldap_pvt_put_filter( ber, assertion );
+ if ( err < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ err = ber_flatten2( ber, value, 1 );
+ if ( err < 0 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_assertion_control(
+ LDAP *ld,
+ char *assertion,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_assertion_control_value( ld,
+ assertion, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_ASSERT,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/bind.c b/libraries/libldap/bind.c
new file mode 100644
index 0000000..d533a66
--- /dev/null
+++ b/libraries/libldap/bind.c
@@ -0,0 +1,127 @@
+/* bind.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ * (Source: RFC 2251)
+ */
+
+/*
+ * ldap_bind - bind to the ldap server (and X.500). The dn and password
+ * of the entry to which to bind are supplied, along with the authentication
+ * method to use. The msgid of the bind request is returned on success,
+ * -1 if there's trouble. ldap_result() should be called to find out the
+ * outcome of the bind request.
+ *
+ * Example:
+ * ldap_bind( ld, "cn=manager, o=university of michigan, c=us", "secret",
+ * LDAP_AUTH_SIMPLE )
+ */
+
+int
+ldap_bind( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd, int authmethod )
+{
+ Debug( LDAP_DEBUG_TRACE, "ldap_bind\n", 0, 0, 0 );
+
+ switch ( authmethod ) {
+ case LDAP_AUTH_SIMPLE:
+ return( ldap_simple_bind( ld, dn, passwd ) );
+
+#ifdef HAVE_GSSAPI
+ case LDAP_AUTH_NEGOTIATE:
+ return( ldap_gssapi_bind_s( ld, dn, passwd) );
+#endif
+
+ case LDAP_AUTH_SASL:
+ /* user must use ldap_sasl_bind */
+ /* FALL-THRU */
+
+ default:
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ return( -1 );
+ }
+}
+
+/*
+ * ldap_bind_s - bind to the ldap server (and X.500). The dn and password
+ * of the entry to which to bind are supplied, along with the authentication
+ * method to use. This routine just calls whichever bind routine is
+ * appropriate and returns the result of the bind (e.g. LDAP_SUCCESS or
+ * some other error indication).
+ *
+ * Examples:
+ * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret", LDAP_AUTH_SIMPLE )
+ * ldap_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * NULL, LDAP_AUTH_KRBV4 )
+ */
+int
+ldap_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *passwd,
+ int authmethod )
+{
+ Debug( LDAP_DEBUG_TRACE, "ldap_bind_s\n", 0, 0, 0 );
+
+ switch ( authmethod ) {
+ case LDAP_AUTH_SIMPLE:
+ return( ldap_simple_bind_s( ld, dn, passwd ) );
+
+#ifdef HAVE_GSSAPI
+ case LDAP_AUTH_NEGOTIATE:
+ return( ldap_gssapi_bind_s( ld, dn, passwd) );
+#endif
+
+ case LDAP_AUTH_SASL:
+ /* user must use ldap_sasl_bind */
+ /* FALL-THRU */
+
+ default:
+ return( ld->ld_errno = LDAP_AUTH_UNKNOWN );
+ }
+}
diff --git a/libraries/libldap/cancel.c b/libraries/libldap/cancel.c
new file mode 100644
index 0000000..13b0b35
--- /dev/null
+++ b/libraries/libldap/cancel.c
@@ -0,0 +1,76 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Cancel Operation Request
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+int
+ldap_cancel(
+ LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *cancelidber = NULL;
+ struct berval cancelidvalp = { 0, NULL };
+ int rc;
+
+ cancelidber = ber_alloc_t( LBER_USE_DER );
+ ber_printf( cancelidber, "{i}", cancelid );
+ ber_flatten2( cancelidber, &cancelidvalp, 0 );
+ rc = ldap_extended_operation( ld, LDAP_EXOP_CANCEL,
+ &cancelidvalp, sctrls, cctrls, msgidp );
+ ber_free( cancelidber, 1 );
+ return rc;
+}
+
+int
+ldap_cancel_s(
+ LDAP *ld,
+ int cancelid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ BerElement *cancelidber = NULL;
+ struct berval cancelidvalp = { 0, NULL };
+ int rc;
+
+ cancelidber = ber_alloc_t( LBER_USE_DER );
+ ber_printf( cancelidber, "{i}", cancelid );
+ ber_flatten2( cancelidber, &cancelidvalp, 0 );
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_CANCEL,
+ &cancelidvalp, sctrls, cctrls, NULL, NULL );
+ ber_free( cancelidber, 1 );
+ return rc;
+}
+
diff --git a/libraries/libldap/charray.c b/libraries/libldap/charray.c
new file mode 100644
index 0000000..5731edb
--- /dev/null
+++ b/libraries/libldap/charray.c
@@ -0,0 +1,275 @@
+/* charray.c - routines for dealing with char * arrays */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+
+#include "ldap-int.h"
+
+int
+ldap_charray_add(
+ char ***a,
+ const char *s
+)
+{
+ int n;
+
+ if ( *a == NULL ) {
+ *a = (char **) LDAP_MALLOC( 2 * sizeof(char *) );
+ n = 0;
+
+ if( *a == NULL ) {
+ return -1;
+ }
+
+ } else {
+ char **new;
+
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+
+ new = (char **) LDAP_REALLOC( (char *) *a,
+ (n + 2) * sizeof(char *) );
+
+ if( new == NULL ) {
+ /* caller is required to call ldap_charray_free(*a) */
+ return -1;
+ }
+
+ *a = new;
+ }
+
+ (*a)[n] = LDAP_STRDUP(s);
+
+ if( (*a)[n] == NULL ) {
+ return 1;
+ }
+
+ (*a)[++n] = NULL;
+
+ return 0;
+}
+
+int
+ldap_charray_merge(
+ char ***a,
+ char **s
+)
+{
+ int i, n, nn;
+ char **aa;
+
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+ for ( nn = 0; s[nn] != NULL; nn++ ) {
+ ; /* NULL */
+ }
+
+ aa = (char **) LDAP_REALLOC( (char *) *a, (n + nn + 1) * sizeof(char *) );
+
+ if( aa == NULL ) {
+ return -1;
+ }
+
+ *a = aa;
+
+ for ( i = 0; i < nn; i++ ) {
+ (*a)[n + i] = LDAP_STRDUP(s[i]);
+
+ if( (*a)[n + i] == NULL ) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( (*a)[n + i] );
+ (*a)[n + i] = NULL;
+ }
+ return -1;
+ }
+ }
+
+ (*a)[n + nn] = NULL;
+ return 0;
+}
+
+void
+ldap_charray_free( char **a )
+{
+ char **p;
+
+ if ( a == NULL ) {
+ return;
+ }
+
+ for ( p = a; *p != NULL; p++ ) {
+ if ( *p != NULL ) {
+ LDAP_FREE( *p );
+ }
+ }
+
+ LDAP_FREE( (char *) a );
+}
+
+int
+ldap_charray_inlist(
+ char **a,
+ const char *s
+)
+{
+ int i;
+
+ if( a == NULL ) return 0;
+
+ for ( i=0; a[i] != NULL; i++ ) {
+ if ( strcasecmp( s, a[i] ) == 0 ) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+char **
+ldap_charray_dup( char **a )
+{
+ int i;
+ char **new;
+
+ for ( i = 0; a[i] != NULL; i++ )
+ ; /* NULL */
+
+ new = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
+
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ new[i] = LDAP_STRDUP( a[i] );
+
+ if( new[i] == NULL ) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( new[i] );
+ }
+ LDAP_FREE( new );
+ return NULL;
+ }
+ }
+ new[i] = NULL;
+
+ return( new );
+}
+
+char **
+ldap_str2charray( const char *str_in, const char *brkstr )
+{
+ char **res;
+ char *str, *s;
+ char *lasts;
+ int i;
+
+ /* protect the input string from strtok */
+ str = LDAP_STRDUP( str_in );
+ if( str == NULL ) {
+ return NULL;
+ }
+
+ i = 1;
+ for ( s = str; ; LDAP_UTF8_INCR(s) ) {
+ s = ldap_utf8_strpbrk( s, brkstr );
+ if ( !s ) break;
+ i++;
+ }
+
+ res = (char **) LDAP_MALLOC( (i + 1) * sizeof(char *) );
+
+ if( res == NULL ) {
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i = 0;
+
+ for ( s = ldap_utf8_strtok( str, brkstr, &lasts );
+ s != NULL;
+ s = ldap_utf8_strtok( NULL, brkstr, &lasts ) )
+ {
+ res[i] = LDAP_STRDUP( s );
+
+ if(res[i] == NULL) {
+ for( --i ; i >= 0 ; i-- ) {
+ LDAP_FREE( res[i] );
+ }
+ LDAP_FREE( res );
+ LDAP_FREE( str );
+ return NULL;
+ }
+
+ i++;
+ }
+
+ res[i] = NULL;
+
+ LDAP_FREE( str );
+ return( res );
+}
+
+char * ldap_charray2str( char **a, const char *sep )
+{
+ char *s, **v, *p;
+ int len;
+ int slen;
+
+ if( sep == NULL ) sep = " ";
+
+ slen = strlen( sep );
+ len = 0;
+
+ for ( v = a; *v != NULL; v++ ) {
+ len += strlen( *v ) + slen;
+ }
+
+ if ( len == 0 ) {
+ return NULL;
+ }
+
+ /* trim extra sep len */
+ len -= slen;
+
+ s = LDAP_MALLOC ( len + 1 );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ p = s;
+ for ( v = a; *v != NULL; v++ ) {
+ if ( v != a ) {
+ strncpy( p, sep, slen );
+ p += slen;
+ }
+
+ len = strlen( *v );
+ strncpy( p, *v, len );
+ p += len;
+ }
+
+ *p = '\0';
+ return s;
+}
diff --git a/libraries/libldap/compare.c b/libraries/libldap/compare.c
new file mode 100644
index 0000000..9ff4f87
--- /dev/null
+++ b/libraries/libldap/compare.c
@@ -0,0 +1,197 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/* The compare request looks like this:
+ * CompareRequest ::= SEQUENCE {
+ * entry DistinguishedName,
+ * ava SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * }
+ */
+
+BerElement *
+ldap_build_compare_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID(ld, *msgidp);
+ rc = ber_printf( ber, "{it{s{sON}N}", /* '}' */
+ *msgidp,
+ LDAP_REQ_COMPARE, dn, attr, bvalue );
+ if ( rc == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_compare_ext - perform an ldap extended compare operation. The dn
+ * of the entry to compare to and the attribute and value to compare (in
+ * attr and value) are supplied. The msgid of the response is returned.
+ *
+ * Example:
+ * struct berval bvalue = { "secret", sizeof("secret")-1 };
+ * rc = ldap_compare( ld, "c=us@cn=bob",
+ * "userPassword", &bvalue,
+ * sctrl, cctrl, &msgid )
+ */
+int
+ldap_compare_ext(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_compare\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( attr != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_compare_req(
+ ld, dn, attr, bvalue, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_COMPARE, dn, ber, id );
+ return ( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+/*
+ * ldap_compare_ext - perform an ldap extended compare operation. The dn
+ * of the entry to compare to and the attribute and value to compare (in
+ * attr and value) are supplied. The msgid of the response is returned.
+ *
+ * Example:
+ * msgid = ldap_compare( ld, "c=us@cn=bob", "userPassword", "secret" )
+ */
+int
+ldap_compare(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value )
+{
+ int msgid;
+ struct berval bvalue;
+
+ assert( value != NULL );
+
+ bvalue.bv_val = (char *) value;
+ bvalue.bv_len = (value == NULL) ? 0 : strlen( value );
+
+ return ldap_compare_ext( ld, dn, attr, &bvalue, NULL, NULL, &msgid ) == LDAP_SUCCESS
+ ? msgid : -1;
+}
+
+int
+ldap_compare_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrl,
+ LDAPControl **cctrl )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_compare_ext( ld, dn, attr, bvalue, sctrl, cctrl, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_compare_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *attr,
+ LDAP_CONST char *value )
+{
+ struct berval bvalue;
+
+ assert( value != NULL );
+
+ bvalue.bv_val = (char *) value;
+ bvalue.bv_len = (value == NULL) ? 0 : strlen( value );
+
+ return ldap_compare_ext_s( ld, dn, attr, &bvalue, NULL, NULL );
+}
diff --git a/libraries/libldap/controls.c b/libraries/libldap/controls.c
new file mode 100644
index 0000000..184866d
--- /dev/null
+++ b/libraries/libldap/controls.c
@@ -0,0 +1,552 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/time.h>
+#include <ac/string.h>
+
+#include "ldap-int.h"
+
+/* LDAPv3 Controls (RFC 4511)
+ *
+ * Controls ::= SEQUENCE OF control Control
+ *
+ * Control ::= SEQUENCE {
+ * controlType LDAPOID,
+ * criticality BOOLEAN DEFAULT FALSE,
+ * controlValue OCTET STRING OPTIONAL
+ * }
+ */
+
+int
+ldap_pvt_put_control(
+ const LDAPControl *c,
+ BerElement *ber )
+{
+ if ( ber_printf( ber, "{s" /*}*/, c->ldctl_oid ) == -1 ) {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( c->ldctl_iscritical /* only if true */
+ && ( ber_printf( ber, "b",
+ (ber_int_t) c->ldctl_iscritical ) == -1 ) )
+ {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( !BER_BVISNULL( &c->ldctl_value ) /* only if we have a value */
+ && ( ber_printf( ber, "O", &c->ldctl_value ) == -1 ) )
+ {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber_printf( ber, /*{*/"N}" ) == -1 ) {
+ return LDAP_ENCODING_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * ldap_int_put_controls
+ */
+
+int
+ldap_int_put_controls(
+ LDAP *ld,
+ LDAPControl *const *ctrls,
+ BerElement *ber )
+{
+ LDAPControl *const *c;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ber != NULL );
+
+ if( ctrls == NULL ) {
+ /* use default server controls */
+ ctrls = ld->ld_sctrls;
+ }
+
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ /* LDAPv2 doesn't support controls,
+ * error if any control is critical
+ */
+ for( c = ctrls ; *c != NULL; c++ ) {
+ if( (*c)->ldctl_iscritical ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+ }
+
+ /* Controls are encoded as a sequence of sequences */
+ if( ber_printf( ber, "t{"/*}*/, LDAP_TAG_CONTROLS ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ for( c = ctrls ; *c != NULL; c++ ) {
+ ld->ld_errno = ldap_pvt_put_control( *c, ber );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+ }
+
+
+ if( ber_printf( ber, /*{*/ "}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int ldap_pvt_get_controls(
+ BerElement *ber,
+ LDAPControl ***ctrls )
+{
+ int nctrls;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *opaque;
+
+ assert( ber != NULL );
+
+ if( ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+ *ctrls = NULL;
+
+ len = ber_pvt_ber_remaining( ber );
+
+ if( len == 0) {
+ /* no controls */
+ return LDAP_SUCCESS;
+ }
+
+ if(( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
+ if( tag == LBER_ERROR ) {
+ /* decoding error */
+ return LDAP_DECODING_ERROR;
+ }
+
+ /* ignore unexpected input */
+ return LDAP_SUCCESS;
+ }
+
+ /* set through each element */
+ nctrls = 0;
+ *ctrls = LDAP_MALLOC( 1 * sizeof(LDAPControl *) );
+
+ if( *ctrls == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ *ctrls[nctrls] = NULL;
+
+ for( tag = ber_first_element( ber, &len, &opaque );
+ tag != LBER_ERROR;
+ tag = ber_next_element( ber, &len, opaque ) )
+ {
+ LDAPControl *tctrl;
+ LDAPControl **tctrls;
+
+ tctrl = LDAP_CALLOC( 1, sizeof(LDAPControl) );
+
+ /* allocate pointer space for current controls (nctrls)
+ * + this control + extra NULL
+ */
+ tctrls = (tctrl == NULL) ? NULL :
+ LDAP_REALLOC(*ctrls, (nctrls+2) * sizeof(LDAPControl *));
+
+ if( tctrls == NULL ) {
+ /* one of the above allocation failed */
+
+ if( tctrl != NULL ) {
+ LDAP_FREE( tctrl );
+ }
+
+ ldap_controls_free(*ctrls);
+ *ctrls = NULL;
+
+ return LDAP_NO_MEMORY;
+ }
+
+
+ tctrls[nctrls++] = tctrl;
+ tctrls[nctrls] = NULL;
+
+ tag = ber_scanf( ber, "{a" /*}*/, &tctrl->ldctl_oid );
+
+ if( tag == LBER_ERROR ) {
+ *ctrls = NULL;
+ ldap_controls_free( tctrls );
+ return LDAP_DECODING_ERROR;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LBER_BOOLEAN ) {
+ ber_int_t crit;
+ tag = ber_scanf( ber, "b", &crit );
+ tctrl->ldctl_iscritical = crit ? (char) 0 : (char) ~0;
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LBER_OCTETSTRING ) {
+ tag = ber_scanf( ber, "o", &tctrl->ldctl_value );
+ } else {
+ BER_BVZERO( &tctrl->ldctl_value );
+ }
+
+ *ctrls = tctrls;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Free a LDAPControl
+ */
+void
+ldap_control_free( LDAPControl *c )
+{
+ LDAP_MEMORY_DEBUG_ASSERT( c != NULL );
+
+ if ( c != NULL ) {
+ if( c->ldctl_oid != NULL) {
+ LDAP_FREE( c->ldctl_oid );
+ }
+
+ if( c->ldctl_value.bv_val != NULL ) {
+ LDAP_FREE( c->ldctl_value.bv_val );
+ }
+
+ LDAP_FREE( c );
+ }
+}
+
+/*
+ * Free an array of LDAPControl's
+ */
+void
+ldap_controls_free( LDAPControl **controls )
+{
+ LDAP_MEMORY_DEBUG_ASSERT( controls != NULL );
+
+ if ( controls != NULL ) {
+ int i;
+
+ for( i=0; controls[i] != NULL; i++) {
+ ldap_control_free( controls[i] );
+ }
+
+ LDAP_FREE( controls );
+ }
+}
+
+/*
+ * Duplicate an array of LDAPControl
+ */
+LDAPControl **
+ldap_controls_dup( LDAPControl *const *controls )
+{
+ LDAPControl **new;
+ int i;
+
+ if ( controls == NULL ) {
+ return NULL;
+ }
+
+ /* count the controls */
+ for(i=0; controls[i] != NULL; i++) /* empty */ ;
+
+ if( i < 1 ) {
+ /* no controls to duplicate */
+ return NULL;
+ }
+
+ new = (LDAPControl **) LDAP_MALLOC( (i+1) * sizeof(LDAPControl *) );
+
+ if( new == NULL ) {
+ /* memory allocation failure */
+ return NULL;
+ }
+
+ /* duplicate the controls */
+ for(i=0; controls[i] != NULL; i++) {
+ new[i] = ldap_control_dup( controls[i] );
+
+ if( new[i] == NULL ) {
+ ldap_controls_free( new );
+ return NULL;
+ }
+ }
+
+ new[i] = NULL;
+
+ return new;
+}
+
+/*
+ * Duplicate a LDAPControl
+ */
+LDAPControl *
+ldap_control_dup( const LDAPControl *c )
+{
+ LDAPControl *new;
+
+ if ( c == NULL || c->ldctl_oid == NULL ) {
+ return NULL;
+ }
+
+ new = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) );
+
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ new->ldctl_oid = LDAP_STRDUP( c->ldctl_oid );
+
+ if(new->ldctl_oid == NULL) {
+ LDAP_FREE( new );
+ return NULL;
+ }
+
+ if( c->ldctl_value.bv_val != NULL ) {
+ new->ldctl_value.bv_val =
+ (char *) LDAP_MALLOC( c->ldctl_value.bv_len + 1 );
+
+ if(new->ldctl_value.bv_val == NULL) {
+ if(new->ldctl_oid != NULL) {
+ LDAP_FREE( new->ldctl_oid );
+ }
+ LDAP_FREE( new );
+ return NULL;
+ }
+
+ new->ldctl_value.bv_len = c->ldctl_value.bv_len;
+
+ AC_MEMCPY( new->ldctl_value.bv_val, c->ldctl_value.bv_val,
+ c->ldctl_value.bv_len );
+
+ new->ldctl_value.bv_val[new->ldctl_value.bv_len] = '\0';
+
+ } else {
+ new->ldctl_value.bv_len = 0;
+ new->ldctl_value.bv_val = NULL;
+ }
+
+ new->ldctl_iscritical = c->ldctl_iscritical;
+ return new;
+}
+
+/*
+ * Find a LDAPControl - deprecated
+ */
+LDAPControl *
+ldap_find_control(
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls )
+{
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return NULL;
+ }
+
+ for( ; *ctrls != NULL; ctrls++ ) {
+ if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) {
+ return *ctrls;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Find a LDAPControl
+ */
+LDAPControl *
+ldap_control_find(
+ LDAP_CONST char *oid,
+ LDAPControl **ctrls,
+ LDAPControl ***nextctrlp )
+{
+ if ( oid == NULL || ctrls == NULL || *ctrls == NULL ) {
+ return NULL;
+ }
+
+ for( ; *ctrls != NULL; ctrls++ ) {
+ if( strcmp( (*ctrls)->ldctl_oid, oid ) == 0 ) {
+ if ( nextctrlp != NULL ) {
+ *nextctrlp = ctrls + 1;
+ }
+
+ return *ctrls;
+ }
+ }
+
+ if ( nextctrlp != NULL ) {
+ *nextctrlp = NULL;
+ }
+
+ return NULL;
+}
+
+/*
+ * Create a LDAPControl, optionally from ber - deprecated
+ */
+int
+ldap_create_control(
+ LDAP_CONST char *requestOID,
+ BerElement *ber,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ LDAPControl *ctrl;
+
+ assert( requestOID != NULL );
+ assert( ctrlp != NULL );
+
+ ctrl = (LDAPControl *) LDAP_MALLOC( sizeof(LDAPControl) );
+ if ( ctrl == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ BER_BVZERO(&ctrl->ldctl_value);
+ if ( ber && ( ber_flatten2( ber, &ctrl->ldctl_value, 1 ) == -1 )) {
+ LDAP_FREE( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ ctrl->ldctl_oid = LDAP_STRDUP( requestOID );
+ ctrl->ldctl_iscritical = iscritical;
+
+ if ( requestOID != NULL && ctrl->ldctl_oid == NULL ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ *ctrlp = ctrl;
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Create a LDAPControl, optionally from value
+ */
+int
+ldap_control_create(
+ LDAP_CONST char *requestOID,
+ int iscritical,
+ struct berval *value,
+ int dupval,
+ LDAPControl **ctrlp )
+{
+ LDAPControl *ctrl;
+
+ assert( requestOID != NULL );
+ assert( ctrlp != NULL );
+
+ ctrl = (LDAPControl *) LDAP_CALLOC( sizeof(LDAPControl), 1 );
+ if ( ctrl == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+
+ ctrl->ldctl_iscritical = iscritical;
+ if ( requestOID != NULL ) {
+ ctrl->ldctl_oid = LDAP_STRDUP( requestOID );
+ if ( ctrl->ldctl_oid == NULL ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ if ( value && !BER_BVISNULL( value ) ) {
+ if ( dupval ) {
+ ber_dupbv( &ctrl->ldctl_value, value );
+ if ( BER_BVISNULL( &ctrl->ldctl_value ) ) {
+ ldap_control_free( ctrl );
+ return LDAP_NO_MEMORY;
+ }
+
+ } else {
+ ctrl->ldctl_value = *value;
+ }
+ }
+
+ *ctrlp = ctrl;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * check for critical client controls and bitch if present
+ * if we ever support critical controls, we'll have to
+ * find a means for maintaining per API call control
+ * information.
+ */
+int ldap_int_client_controls( LDAP *ld, LDAPControl **ctrls )
+{
+ LDAPControl *const *c;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if( ctrls == NULL ) {
+ /* use default client controls */
+ ctrls = ld->ld_cctrls;
+ }
+
+ if( ctrls == NULL || *ctrls == NULL ) {
+ return LDAP_SUCCESS;
+ }
+
+ for( c = ctrls ; *c != NULL; c++ ) {
+ if( (*c)->ldctl_iscritical ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c
new file mode 100644
index 0000000..4c0089d
--- /dev/null
+++ b/libraries/libldap/cyrus.c
@@ -0,0 +1,1237 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include "ldap-int.h"
+
+#ifdef HAVE_CYRUS_SASL
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX 2147483647 /* 32 bit signed max */
+#endif
+
+#if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
+#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
+#endif
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#if SASL_VERSION_MAJOR >= 2
+#define SASL_CONST const
+#else
+#define SASL_CONST
+#endif
+
+/*
+* Various Cyrus SASL related stuff.
+*/
+
+static const sasl_callback_t client_callbacks[] = {
+#ifdef SASL_CB_GETREALM
+ { SASL_CB_GETREALM, NULL, NULL },
+#endif
+ { SASL_CB_USER, NULL, NULL },
+ { SASL_CB_AUTHNAME, NULL, NULL },
+ { SASL_CB_PASS, NULL, NULL },
+ { SASL_CB_ECHOPROMPT, NULL, NULL },
+ { SASL_CB_NOECHOPROMPT, NULL, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+
+/*
+ * ldap_int_initialize is responsible for calling this only once.
+ */
+int ldap_int_sasl_init( void )
+{
+#ifdef HAVE_SASL_VERSION
+ /* stringify the version number, sasl.h doesn't do it for us */
+#define VSTR0(maj, min, pat) #maj "." #min "." #pat
+#define VSTR(maj, min, pat) VSTR0(maj, min, pat)
+#define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
+ SASL_VERSION_STEP)
+ { int rc;
+ sasl_version( NULL, &rc );
+ if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
+ (rc & 0xffff) < SASL_VERSION_STEP) {
+ char version[sizeof("xxx.xxx.xxxxx")];
+ sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
+ rc & 0xffff );
+
+ Debug( LDAP_DEBUG_ANY,
+ "ldap_int_sasl_init: SASL library version mismatch:"
+ " expected " SASL_VERSION_STRING ","
+ " got %s\n", version, 0, 0 );
+ return -1;
+ }
+ }
+#endif
+
+/* SASL 2 takes care of its own memory completely internally */
+#if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC)
+ sasl_set_alloc(
+ ber_memalloc,
+ ber_memcalloc,
+ ber_memrealloc,
+ ber_memfree );
+#endif /* CSRIMALLOC */
+
+#ifdef LDAP_R_COMPILE
+ sasl_set_mutex(
+ ldap_pvt_sasl_mutex_new,
+ ldap_pvt_sasl_mutex_lock,
+ ldap_pvt_sasl_mutex_unlock,
+ ldap_pvt_sasl_mutex_dispose );
+#endif
+
+ if ( sasl_client_init( NULL ) == SASL_OK ) {
+ return 0;
+ }
+
+#if SASL_VERSION_MAJOR < 2
+ /* A no-op to make sure we link with Cyrus 1.5 */
+ sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL );
+#endif
+ return -1;
+}
+
+static void
+sb_sasl_cyrus_init(
+ struct sb_sasl_generic_data *p,
+ ber_len_t *min_send,
+ ber_len_t *max_send,
+ ber_len_t *max_recv)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_len_t maxbuf;
+
+ sasl_getprop( sasl_context, SASL_MAXOUTBUF,
+ (SASL_CONST void **)(char *) &maxbuf );
+
+ *min_send = SASL_MIN_BUFF_SIZE;
+ *max_send = maxbuf;
+ *max_recv = SASL_MAX_BUFF_SIZE;
+}
+
+static ber_int_t
+sb_sasl_cyrus_encode(
+ struct sb_sasl_generic_data *p,
+ unsigned char *buf,
+ ber_len_t len,
+ Sockbuf_Buf *dst)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_int_t ret;
+ unsigned tmpsize = dst->buf_size;
+
+ ret = sasl_encode( sasl_context, (char *)buf, len,
+ (SASL_CONST char **)&dst->buf_base,
+ &tmpsize );
+
+ dst->buf_size = tmpsize;
+ dst->buf_end = dst->buf_size;
+
+ if ( ret != SASL_OK ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_cyrus_encode: failed to encode packet: %s\n",
+ sasl_errstring( ret, NULL, NULL ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+static ber_int_t
+sb_sasl_cyrus_decode(
+ struct sb_sasl_generic_data *p,
+ const Sockbuf_Buf *src,
+ Sockbuf_Buf *dst)
+{
+ sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
+ ber_int_t ret;
+ unsigned tmpsize = dst->buf_size;
+
+ ret = sasl_decode( sasl_context,
+ src->buf_base, src->buf_end,
+ (SASL_CONST char **)&dst->buf_base,
+ (unsigned *)&tmpsize );
+
+
+ dst->buf_size = tmpsize;
+ dst->buf_end = dst->buf_size;
+
+ if ( ret != SASL_OK ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_cyrus_decode: failed to decode packet: %s\n",
+ sasl_errstring( ret, NULL, NULL ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+sb_sasl_cyrus_reset_buf(
+ struct sb_sasl_generic_data *p,
+ Sockbuf_Buf *buf)
+{
+#if SASL_VERSION_MAJOR >= 2
+ ber_pvt_sb_buf_init( buf );
+#else
+ ber_pvt_sb_buf_destroy( buf );
+#endif
+}
+
+static void
+sb_sasl_cyrus_fini(
+ struct sb_sasl_generic_data *p)
+{
+#if SASL_VERSION_MAJOR >= 2
+ /*
+ * SASLv2 encode/decode buffers are managed by
+ * libsasl2. Ensure they are not freed by liblber.
+ */
+ p->buf_in.buf_base = NULL;
+ p->buf_out.buf_base = NULL;
+#endif
+}
+
+static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = {
+ sb_sasl_cyrus_init,
+ sb_sasl_cyrus_encode,
+ sb_sasl_cyrus_decode,
+ sb_sasl_cyrus_reset_buf,
+ sb_sasl_cyrus_fini
+ };
+
+int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
+{
+ struct sb_sasl_generic_install install_arg;
+
+ install_arg.ops = &sb_sasl_cyrus_ops;
+ install_arg.ops_private = ctx_arg;
+
+ return ldap_pvt_sasl_generic_install( sb, &install_arg );
+}
+
+void ldap_pvt_sasl_remove( Sockbuf *sb )
+{
+ ldap_pvt_sasl_generic_remove( sb );
+}
+
+static int
+sasl_err2ldap( int saslerr )
+{
+ int rc;
+
+ /* map SASL errors to LDAP API errors returned by:
+ * sasl_client_new()
+ * SASL_OK, SASL_NOMECH, SASL_NOMEM
+ * sasl_client_start()
+ * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT
+ * sasl_client_step()
+ * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV
+ */
+
+ switch (saslerr) {
+ case SASL_CONTINUE:
+ rc = LDAP_MORE_RESULTS_TO_RETURN;
+ break;
+ case SASL_INTERACT:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ case SASL_OK:
+ rc = LDAP_SUCCESS;
+ break;
+ case SASL_NOMEM:
+ rc = LDAP_NO_MEMORY;
+ break;
+ case SASL_NOMECH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_BADPROT:
+ rc = LDAP_DECODING_ERROR;
+ break;
+ case SASL_BADSERV:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+
+ /* other codes */
+ case SASL_BADAUTH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_NOAUTHZ:
+ rc = LDAP_PARAM_ERROR;
+ break;
+ case SASL_FAIL:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ case SASL_TOOWEAK:
+ case SASL_ENCRYPT:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ default:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ }
+
+ assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
+ return rc;
+}
+
+int
+ldap_int_sasl_open(
+ LDAP *ld,
+ LDAPConn *lc,
+ const char * host )
+{
+ int rc;
+ sasl_conn_t *ctx;
+
+ assert( lc->lconn_sasl_authctx == NULL );
+
+ if ( host == NULL ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ return ld->ld_errno;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ rc = sasl_client_new( "ldap", host, NULL, NULL,
+ client_callbacks, 0, &ctx );
+#else
+ rc = sasl_client_new( "ldap", host, client_callbacks,
+ SASL_SECURITY_LAYER, &ctx );
+#endif
+
+ if ( rc != SASL_OK ) {
+ ld->ld_errno = sasl_err2ldap( rc );
+ return ld->ld_errno;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n",
+ host, 0, 0 );
+
+ lc->lconn_sasl_authctx = ctx;
+
+ return LDAP_SUCCESS;
+}
+
+int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
+{
+ sasl_conn_t *ctx = lc->lconn_sasl_authctx;
+
+ if( ctx != NULL ) {
+ sasl_dispose( &ctx );
+ if ( lc->lconn_sasl_sockctx &&
+ lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) {
+ ctx = lc->lconn_sasl_sockctx;
+ sasl_dispose( &ctx );
+ }
+ lc->lconn_sasl_sockctx = NULL;
+ lc->lconn_sasl_authctx = NULL;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_int_sasl_bind(
+ LDAP *ld,
+ const char *dn,
+ const char *mechs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{
+ const char *mech;
+ sasl_ssf_t *ssf;
+ sasl_conn_t *ctx;
+ sasl_interact_t *prompts = NULL;
+ struct berval ccred = BER_BVNULL;
+ int saslrc, rc;
+ unsigned credlen;
+#if !defined(_WIN32)
+ char my_hostname[HOST_NAME_MAX + 1];
+#endif
+ int free_saslhost = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
+ mechs ? mechs : "<null>", 0, 0 );
+
+ /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
+ if (ld->ld_version < LDAP_VERSION3) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ /* Starting a Bind */
+ if ( !result ) {
+ const char *pmech = NULL;
+ sasl_conn_t *oldctx;
+ ber_socket_t sd;
+ void *ssl;
+
+ rc = 0;
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
+
+ if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) {
+ /* not connected yet */
+
+ rc = ldap_open_defconn( ld );
+
+ if ( rc == 0 ) {
+ ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
+ LBER_SB_OPT_GET_FD, &sd );
+
+ if( sd == AC_SOCKET_INVALID ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ rc = ld->ld_errno;
+ }
+ }
+ }
+ if ( rc == 0 && ld->ld_defconn &&
+ ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) {
+ rc = ldap_int_check_async_open( ld, sd );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ if( rc != 0 ) return ld->ld_errno;
+
+ oldctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ /* If we already have an authentication context, clear it out */
+ if( oldctx ) {
+ if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
+ sasl_dispose( &oldctx );
+ }
+ ld->ld_defconn->lconn_sasl_authctx = NULL;
+ }
+
+ {
+ char *saslhost;
+ int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
+ LDAP_BOOL_SASL_NOCANON );
+
+ /* If we don't need to canonicalize just use the host
+ * from the LDAP URI.
+ * Always use the result of gethostname() for LDAPI.
+ * Skip for Windows which doesn't support LDAPI.
+ */
+#if !defined(_WIN32)
+ if (ld->ld_defconn->lconn_server->lud_scheme != NULL &&
+ strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) {
+ rc = gethostname(my_hostname, HOST_NAME_MAX + 1);
+ if (rc == 0) {
+ saslhost = my_hostname;
+ } else {
+ saslhost = "localhost";
+ }
+ } else
+#endif
+ if ( nocanon )
+ saslhost = ld->ld_defconn->lconn_server->lud_host;
+ else {
+ saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
+ "localhost" );
+ free_saslhost = 1;
+ }
+ rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
+ if ( free_saslhost )
+ LDAP_FREE( saslhost );
+ }
+
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+#ifdef HAVE_TLS
+ /* Check for TLS */
+ ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
+ if ( ssl ) {
+ struct berval authid = BER_BVNULL;
+ ber_len_t fac;
+
+ fac = ldap_pvt_tls_get_strength( ssl );
+ /* failure is OK, we just can't use SASL EXTERNAL */
+ (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
+
+ (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
+ LDAP_FREE( authid.bv_val );
+ }
+#endif
+
+#if !defined(_WIN32)
+ /* Check for local */
+ if ( ldap_pvt_url_scheme2proto(
+ ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
+ {
+ char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
+ "cn=peercred,cn=external,cn=auth")];
+ sprintf( authid, "gidNumber=%u+uidNumber=%u,"
+ "cn=peercred,cn=external,cn=auth",
+ getegid(), geteuid() );
+ (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
+ LDAP_PVT_SASL_LOCAL_SSF );
+ }
+#endif
+
+ /* (re)set security properties */
+ sasl_setprop( ctx, SASL_SEC_PROPS,
+ &ld->ld_options.ldo_sasl_secprops );
+
+ mech = NULL;
+
+ do {
+ saslrc = sasl_client_start( ctx,
+ mechs,
+#if SASL_VERSION_MAJOR < 2
+ NULL,
+#endif
+ &prompts,
+ (SASL_CONST char **)&ccred.bv_val,
+ &credlen,
+ &mech );
+
+ if( pmech == NULL && mech != NULL ) {
+ pmech = mech;
+ *rmech = mech;
+
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf(stderr,
+ "SASL/%s authentication started\n",
+ pmech );
+ }
+ }
+
+ if( saslrc == SASL_INTERACT ) {
+ int res;
+ if( !interact ) break;
+ res = (interact)( ld, flags, defaults, prompts );
+
+ if( res != LDAP_SUCCESS ) break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+ rc = LDAP_SASL_BIND_IN_PROGRESS;
+
+ } else {
+ /* continuing an in-progress Bind */
+ struct berval *scred = NULL;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ if ( scred )
+ ber_bvfree( scred );
+ goto done;
+ }
+
+ rc = ldap_result2error( ld, result, 0 );
+ if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
+ if( scred ) {
+ /* and server provided us with data? */
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_int_sasl_bind: rc=%d len=%ld\n",
+ rc, scred ? (long) scred->bv_len : -1L, 0 );
+ ber_bvfree( scred );
+ scred = NULL;
+ }
+ goto done;
+ }
+
+ mech = *rmech;
+ if ( rc == LDAP_SUCCESS && mech == NULL ) {
+ if ( scred )
+ ber_bvfree( scred );
+ goto success;
+ }
+
+ do {
+ if( ! scred ) {
+ /* no data! */
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_int_sasl_bind: no data in step!\n",
+ 0, 0, 0 );
+ }
+
+ saslrc = sasl_client_step( ctx,
+ (scred == NULL) ? NULL : scred->bv_val,
+ (scred == NULL) ? 0 : scred->bv_len,
+ &prompts,
+ (SASL_CONST char **)&ccred.bv_val,
+ &credlen );
+
+ Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
+ saslrc, 0, 0 );
+
+ if( saslrc == SASL_INTERACT ) {
+ int res;
+ if( !interact ) break;
+ res = (interact)( ld, flags, defaults, prompts );
+ if( res != LDAP_SUCCESS ) break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ber_bvfree( scred );
+ }
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ rc = ld->ld_errno = sasl_err2ldap( saslrc );
+#if SASL_VERSION_MAJOR >= 2
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
+#endif
+ goto done;
+ }
+
+ if ( saslrc == SASL_OK )
+ *rmech = NULL;
+
+ ccred.bv_len = credlen;
+
+ if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
+ rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid );
+
+ if ( ccred.bv_val != NULL ) {
+#if SASL_VERSION_MAJOR < 2
+ LDAP_FREE( ccred.bv_val );
+#endif
+ ccred.bv_val = NULL;
+ }
+ if ( rc == LDAP_SUCCESS )
+ rc = LDAP_SASL_BIND_IN_PROGRESS;
+ goto done;
+ }
+
+success:
+ /* Conversation was completed successfully by now */
+ if( flags != LDAP_SASL_QUIET ) {
+ char *data;
+ saslrc = sasl_getprop( ctx, SASL_USERNAME,
+ (SASL_CONST void **)(char *) &data );
+ if( saslrc == SASL_OK && data && *data ) {
+ fprintf( stderr, "SASL username: %s\n", data );
+ }
+
+#if SASL_VERSION_MAJOR < 2
+ saslrc = sasl_getprop( ctx, SASL_REALM,
+ (SASL_CONST void **) &data );
+ if( saslrc == SASL_OK && data && *data ) {
+ fprintf( stderr, "SASL realm: %s\n", data );
+ }
+#endif
+ }
+
+ ssf = NULL;
+ saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
+ if( saslrc == SASL_OK ) {
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf( stderr, "SASL SSF: %lu\n",
+ (unsigned long) *ssf );
+ }
+
+ if( ssf && *ssf ) {
+ if ( ld->ld_defconn->lconn_sasl_sockctx ) {
+ sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx;
+ sasl_dispose( &oldctx );
+ ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
+ }
+ ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
+ ld->ld_defconn->lconn_sasl_sockctx = ctx;
+
+ if( flags != LDAP_SASL_QUIET ) {
+ fprintf( stderr, "SASL data security layer installed.\n" );
+ }
+ }
+ }
+ ld->ld_defconn->lconn_sasl_authctx = ctx;
+
+done:
+ return rc;
+}
+
+int
+ldap_int_sasl_external(
+ LDAP *ld,
+ LDAPConn *conn,
+ const char * authid,
+ ber_len_t ssf )
+{
+ int sc;
+ sasl_conn_t *ctx;
+#if SASL_VERSION_MAJOR < 2
+ sasl_external_properties_t extprops;
+#else
+ sasl_ssf_t sasl_ssf = ssf;
+#endif
+
+ ctx = conn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
+ if ( sc == SASL_OK )
+ sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
+#else
+ memset( &extprops, '\0', sizeof(extprops) );
+ extprops.ssf = ssf;
+ extprops.auth_id = (char *) authid;
+
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
+ (void *) &extprops );
+#endif
+
+ if ( sc != SASL_OK ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+#define GOT_MINSSF 1
+#define GOT_MAXSSF 2
+#define GOT_MAXBUF 4
+
+static struct {
+ struct berval key;
+ int sflag;
+ int ival;
+ int idef;
+} sprops[] = {
+ { BER_BVC("none"), 0, 0, 0 },
+ { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
+ { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
+ { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
+ { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
+ { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
+ { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
+ { BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
+ { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
+ { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
+ { BER_BVNULL, 0, 0, 0 }
+};
+
+void ldap_pvt_sasl_secprops_unparse(
+ sasl_security_properties_t *secprops,
+ struct berval *out )
+{
+ int i, l = 0;
+ int comma;
+ char *ptr;
+
+ if ( secprops == NULL || out == NULL ) {
+ return;
+ }
+
+ comma = 0;
+ for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
+ if ( sprops[i].ival ) {
+ int v = 0;
+
+ switch( sprops[i].ival ) {
+ case GOT_MINSSF: v = secprops->min_ssf; break;
+ case GOT_MAXSSF: v = secprops->max_ssf; break;
+ case GOT_MAXBUF: v = secprops->maxbufsize; break;
+ }
+ /* It is the default, ignore it */
+ if ( v == sprops[i].idef ) continue;
+
+ l += sprops[i].key.bv_len + 24;
+ } else if ( sprops[i].sflag ) {
+ if ( sprops[i].sflag & secprops->security_flags ) {
+ l += sprops[i].key.bv_len;
+ }
+ } else if ( secprops->security_flags == 0 ) {
+ l += sprops[i].key.bv_len;
+ }
+ if ( comma ) l++;
+ comma = 1;
+ }
+ l++;
+
+ out->bv_val = LDAP_MALLOC( l );
+ if ( out->bv_val == NULL ) {
+ out->bv_len = 0;
+ return;
+ }
+
+ ptr = out->bv_val;
+ comma = 0;
+ for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
+ if ( sprops[i].ival ) {
+ int v = 0;
+
+ switch( sprops[i].ival ) {
+ case GOT_MINSSF: v = secprops->min_ssf; break;
+ case GOT_MAXSSF: v = secprops->max_ssf; break;
+ case GOT_MAXBUF: v = secprops->maxbufsize; break;
+ }
+ /* It is the default, ignore it */
+ if ( v == sprops[i].idef ) continue;
+
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
+ comma = 1;
+ } else if ( sprops[i].sflag ) {
+ if ( sprops[i].sflag & secprops->security_flags ) {
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
+ comma = 1;
+ }
+ } else if ( secprops->security_flags == 0 ) {
+ if ( comma ) *ptr++ = ',';
+ ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
+ comma = 1;
+ }
+ }
+ out->bv_len = ptr - out->bv_val;
+}
+
+int ldap_pvt_sasl_secprops(
+ const char *in,
+ sasl_security_properties_t *secprops )
+{
+ unsigned i, j, l;
+ char **props;
+ unsigned sflags = 0;
+ int got_sflags = 0;
+ sasl_ssf_t max_ssf = 0;
+ int got_max_ssf = 0;
+ sasl_ssf_t min_ssf = 0;
+ int got_min_ssf = 0;
+ unsigned maxbufsize = 0;
+ int got_maxbufsize = 0;
+
+ if( secprops == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+ props = ldap_str2charray( in, "," );
+ if( props == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ for( i=0; props[i]; i++ ) {
+ l = strlen( props[i] );
+ for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
+ if ( l < sprops[j].key.bv_len ) continue;
+ if ( strncasecmp( props[i], sprops[j].key.bv_val,
+ sprops[j].key.bv_len )) continue;
+ if ( sprops[j].ival ) {
+ unsigned v;
+ char *next = NULL;
+ if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
+ continue;
+ v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
+ if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
+ switch( sprops[j].ival ) {
+ case GOT_MINSSF:
+ min_ssf = v; got_min_ssf++; break;
+ case GOT_MAXSSF:
+ max_ssf = v; got_max_ssf++; break;
+ case GOT_MAXBUF:
+ maxbufsize = v; got_maxbufsize++; break;
+ }
+ } else {
+ if ( props[i][sprops[j].key.bv_len] ) continue;
+ if ( sprops[j].sflag )
+ sflags |= sprops[j].sflag;
+ else
+ sflags = 0;
+ got_sflags++;
+ }
+ break;
+ }
+ if ( BER_BVISNULL( &sprops[j].key )) {
+ ldap_charray_free( props );
+ return LDAP_NOT_SUPPORTED;
+ }
+ }
+
+ if(got_sflags) {
+ secprops->security_flags = sflags;
+ }
+ if(got_min_ssf) {
+ secprops->min_ssf = min_ssf;
+ }
+ if(got_max_ssf) {
+ secprops->max_ssf = max_ssf;
+ }
+ if(got_maxbufsize) {
+ secprops->maxbufsize = maxbufsize;
+ }
+
+ ldap_charray_free( props );
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
+{
+ int rc;
+
+ switch( option ) {
+ case LDAP_OPT_X_SASL_SECPROPS:
+ rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
+ if( rc == LDAP_SUCCESS ) return 0;
+ }
+
+ return -1;
+}
+
+int
+ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
+{
+ if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
+ *(char ***)arg = (char **)sasl_global_listmech();
+ return 0;
+ }
+
+ if ( ld == NULL )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_X_SASL_MECH: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_mech
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_REALM: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_realm
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_AUTHCID: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_authcid
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
+ } break;
+ case LDAP_OPT_X_SASL_AUTHZID: {
+ *(char **)arg = ld->ld_options.ldo_def_sasl_authzid
+ ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF: {
+ int sc;
+ sasl_ssf_t *ssf;
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_sockctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+ sc = sasl_getprop( ctx, SASL_SSF,
+ (SASL_CONST void **)(char *) &ssf );
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+
+ *(ber_len_t *)arg = *ssf;
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF_EXTERNAL:
+ /* this option is write only */
+ return -1;
+
+ case LDAP_OPT_X_SASL_SSF_MIN:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
+ break;
+ case LDAP_OPT_X_SASL_SSF_MAX:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
+ break;
+ case LDAP_OPT_X_SASL_MAXBUFSIZE:
+ *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
+ break;
+ case LDAP_OPT_X_SASL_NOCANON:
+ *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ break;
+
+ case LDAP_OPT_X_SASL_USERNAME: {
+ int sc;
+ char *username;
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+ sc = sasl_getprop( ctx, SASL_USERNAME,
+ (SASL_CONST void **)(char **) &username );
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+
+ *(char **)arg = username ? LDAP_STRDUP( username ) : NULL;
+ } break;
+
+ case LDAP_OPT_X_SASL_SECPROPS:
+ /* this option is write only */
+ return -1;
+
+#ifdef SASL_GSS_CREDS
+ case LDAP_OPT_X_SASL_GSS_CREDS: {
+ sasl_conn_t *ctx;
+ int sc;
+
+ if ( ld->ld_defconn == NULL )
+ return -1;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+ if ( ctx == NULL )
+ return -1;
+
+ sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg );
+ if ( sc != SASL_OK )
+ return -1;
+ }
+ break;
+#endif
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
+{
+ if ( ld == NULL )
+ return -1;
+
+ if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_X_SASL_SSF:
+ case LDAP_OPT_X_SASL_USERNAME:
+ /* This option is read-only */
+ return -1;
+
+ case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
+ int sc;
+#if SASL_VERSION_MAJOR < 2
+ sasl_external_properties_t extprops;
+#else
+ sasl_ssf_t sasl_ssf;
+#endif
+ sasl_conn_t *ctx;
+
+ if( ld->ld_defconn == NULL ) {
+ return -1;
+ }
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+
+ if ( ctx == NULL ) {
+ return -1;
+ }
+
+#if SASL_VERSION_MAJOR >= 2
+ sasl_ssf = * (ber_len_t *)arg;
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
+#else
+ memset(&extprops, 0L, sizeof(extprops));
+
+ extprops.ssf = * (ber_len_t *) arg;
+
+ sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
+ (void *) &extprops );
+#endif
+
+ if ( sc != SASL_OK ) {
+ return -1;
+ }
+ } break;
+
+ case LDAP_OPT_X_SASL_SSF_MIN:
+ ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_SSF_MAX:
+ ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_MAXBUFSIZE:
+ ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
+ break;
+ case LDAP_OPT_X_SASL_NOCANON:
+ if ( arg == LDAP_OPT_OFF ) {
+ LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ } else {
+ LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
+ }
+ break;
+
+ case LDAP_OPT_X_SASL_SECPROPS: {
+ int sc;
+ sc = ldap_pvt_sasl_secprops( (char *) arg,
+ &ld->ld_options.ldo_sasl_secprops );
+
+ return sc == LDAP_SUCCESS ? 0 : -1;
+ }
+
+#ifdef SASL_GSS_CREDS
+ case LDAP_OPT_X_SASL_GSS_CREDS: {
+ sasl_conn_t *ctx;
+ int sc;
+
+ if ( ld->ld_defconn == NULL )
+ return -1;
+
+ ctx = ld->ld_defconn->lconn_sasl_authctx;
+ if ( ctx == NULL )
+ return -1;
+
+ sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg );
+ if ( sc != SASL_OK )
+ return -1;
+ }
+ break;
+#endif
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef LDAP_R_COMPILE
+#define LDAP_DEBUG_R_SASL
+void *ldap_pvt_sasl_mutex_new(void)
+{
+ ldap_pvt_thread_mutex_t *mutex;
+
+ mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
+ sizeof(ldap_pvt_thread_mutex_t) );
+
+ if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
+ return mutex;
+ }
+ LDAP_FREE( mutex );
+#ifndef LDAP_DEBUG_R_SASL
+ assert( 0 );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return NULL;
+}
+
+int ldap_pvt_sasl_mutex_lock(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return SASL_OK;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
+ ? SASL_FAIL : SASL_OK;
+}
+
+int ldap_pvt_sasl_mutex_unlock(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return SASL_OK;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
+ ? SASL_FAIL : SASL_OK;
+}
+
+void ldap_pvt_sasl_mutex_dispose(void *mutex)
+{
+#ifdef LDAP_DEBUG_R_SASL
+ if ( mutex == NULL ) {
+ return;
+ }
+#else /* !LDAP_DEBUG_R_SASL */
+ assert( mutex != NULL );
+#endif /* !LDAP_DEBUG_R_SASL */
+ (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
+ LDAP_FREE( mutex );
+}
+#endif
+
+#else
+int ldap_int_sasl_init( void )
+{ return LDAP_SUCCESS; }
+
+int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
+{ return LDAP_SUCCESS; }
+
+int
+ldap_int_sasl_bind(
+ LDAP *ld,
+ const char *dn,
+ const char *mechs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{ return LDAP_NOT_SUPPORTED; }
+
+int
+ldap_int_sasl_external(
+ LDAP *ld,
+ LDAPConn *conn,
+ const char * authid,
+ ber_len_t ssf )
+{ return LDAP_SUCCESS; }
+
+#endif /* HAVE_CYRUS_SASL */
diff --git a/libraries/libldap/dds.c b/libraries/libldap/dds.c
new file mode 100644
index 0000000..1e908fb
--- /dev/null
+++ b/libraries/libldap/dds.c
@@ -0,0 +1,156 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2005-2006 SysNet s.n.c.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_parse_refresh( LDAP *ld, LDAPMessage *res, ber_int_t *newttl )
+{
+ int rc;
+ struct berval *retdata = NULL;
+ ber_tag_t tag;
+ BerElement *ber;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( newttl != NULL );
+
+ *newttl = 0;
+
+ rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+
+ if ( retdata == NULL ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ return rc;
+ }
+
+ ber = ber_init( retdata );
+ if ( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ /* check the tag */
+ tag = ber_scanf( ber, "{i}", newttl );
+ ber_free( ber, 1 );
+
+ if ( tag != LDAP_TAG_EXOP_REFRESH_RES_TTL ) {
+ *newttl = 0;
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+
+done:;
+ if ( retdata ) {
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+
+int
+ldap_refresh(
+ LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ struct berval bv = { 0, NULL };
+ BerElement *ber = NULL;
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ *msgidp = -1;
+
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ ber_printf( ber, "{tOtiN}",
+ LDAP_TAG_EXOP_REFRESH_REQ_DN, dn,
+ LDAP_TAG_EXOP_REFRESH_REQ_TTL, ttl );
+
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if ( rc < 0 ) {
+ rc = ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_REFRESH, &bv,
+ sctrls, cctrls, msgidp );
+
+done:;
+ ber_free( ber, 1 );
+
+ return rc;
+}
+
+int
+ldap_refresh_s(
+ LDAP *ld,
+ struct berval *dn,
+ ber_int_t ttl,
+ ber_int_t *newttl,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_refresh( ld, dn, ttl, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *)NULL, &res );
+ if( rc == -1 || !res ) return ld->ld_errno;
+
+ rc = ldap_parse_refresh( ld, res, newttl );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return ldap_result2error( ld, res, 1 );
+}
+
diff --git a/libraries/libldap/delete.c b/libraries/libldap/delete.c
new file mode 100644
index 0000000..256b6b5
--- /dev/null
+++ b/libraries/libldap/delete.c
@@ -0,0 +1,174 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * A delete request looks like this:
+ * DelRequet ::= DistinguishedName,
+ */
+
+BerElement *
+ldap_build_delete_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ rc = ber_printf( ber, "{its", /* '}' */
+ *msgidp, LDAP_REQ_DELETE, dn );
+ if ( rc == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_delete_ext - initiate an ldap extended delete operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to delete
+ * sctrls Server Controls
+ * cctrls Client Controls
+ * msgidp Message Id Pointer
+ *
+ * Example:
+ * rc = ldap_delete( ld, dn, sctrls, cctrls, msgidp );
+ */
+int
+ldap_delete_ext(
+ LDAP *ld,
+ LDAP_CONST char* dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_delete_ext\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( dn != NULL );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_delete_req( ld, dn, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_DELETE, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_delete_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int msgid;
+ int rc;
+ LDAPMessage *res;
+
+ rc = ldap_delete_ext( ld, dn, sctrls, cctrls, &msgid );
+
+ if( rc != LDAP_SUCCESS )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+/*
+ * ldap_delete - initiate an ldap (and X.500) delete operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to delete
+ *
+ * Example:
+ * msgid = ldap_delete( ld, dn );
+ */
+int
+ldap_delete( LDAP *ld, LDAP_CONST char *dn )
+{
+ int msgid;
+
+ /*
+ * A delete request looks like this:
+ * DelRequet ::= DistinguishedName,
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_delete\n", 0, 0, 0 );
+
+ return ldap_delete_ext( ld, dn, NULL, NULL, &msgid ) == LDAP_SUCCESS
+ ? msgid : -1 ;
+}
+
+
+int
+ldap_delete_s( LDAP *ld, LDAP_CONST char *dn )
+{
+ return ldap_delete_ext_s( ld, dn, NULL, NULL );
+}
diff --git a/libraries/libldap/deref.c b/libraries/libldap/deref.c
new file mode 100644
index 0000000..a5071c0
--- /dev/null
+++ b/libraries/libldap/deref.c
@@ -0,0 +1,282 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2008 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_create_deref_control_value(
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+ int i;
+
+ if ( ld == NULL || value == NULL || ds == NULL )
+ {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{" /*}*/ );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ for ( i = 0; ds[i].derefAttr != NULL; i++ ) {
+ int j;
+
+ tag = ber_printf( ber, "{s{" /*}}*/ , ds[i].derefAttr );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ for ( j = 0; ds[i].attributes[j] != NULL; j++ ) {
+ tag = ber_printf( ber, "s", ds[i].attributes[ j ] );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_printf( ber, /*{{*/ "}N}" );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "}" );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_create_deref_control(
+ LDAP *ld,
+ LDAPDerefSpec *ds,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_deref_control_value( ld, ds, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_DEREF,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+void
+ldap_derefresponse_free( LDAPDerefRes *dr )
+{
+ for ( ; dr; ) {
+ LDAPDerefRes *drnext = dr->next;
+ LDAPDerefVal *dv;
+
+ LDAP_FREE( dr->derefAttr );
+ LDAP_FREE( dr->derefVal.bv_val );
+
+ for ( dv = dr->attrVals; dv; ) {
+ LDAPDerefVal *dvnext = dv->next;
+ LDAP_FREE( dv->type );
+ ber_bvarray_free( dv->vals );
+ LDAP_FREE( dv );
+ dv = dvnext;
+ }
+
+ LDAP_FREE( dr );
+
+ dr = drnext;
+ }
+}
+
+int
+ldap_parse_derefresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ LDAPDerefRes **drp2 )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ char *last;
+ LDAPDerefRes *drhead = NULL, **drp;
+
+ if ( ld == NULL || ctrl == NULL || drp2 == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ /* Extract the count and cookie from the control. */
+ drp = &drhead;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ LDAPDerefRes *dr;
+ LDAPDerefVal **dvp;
+ char *last2;
+
+ dr = LDAP_CALLOC( 1, sizeof(LDAPDerefRes) );
+ dvp = &dr->attrVals;
+
+ tag = ber_scanf( ber, "{ao", &dr->derefAttr, &dr->derefVal );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == (LBER_CONSTRUCTED|LBER_CLASS_CONTEXT) ) {
+ for ( tag = ber_first_element( ber, &len, &last2 );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &len, last2 ) )
+ {
+ LDAPDerefVal *dv;
+
+ dv = LDAP_CALLOC( 1, sizeof(LDAPDerefVal) );
+
+ tag = ber_scanf( ber, "{a[W]}", &dv->type, &dv->vals );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ *dvp = dv;
+ dvp = &dv->next;
+ }
+ }
+
+ tag = ber_scanf( ber, "}" );
+ if ( tag == LBER_ERROR ) {
+ goto done;
+ }
+
+ *drp = dr;
+ drp = &dr->next;
+ }
+
+ tag = 0;
+
+done:;
+ ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ if ( drhead != NULL ) {
+ ldap_derefresponse_free( drhead );
+ }
+
+ *drp2 = NULL;
+ ld->ld_errno = LDAP_DECODING_ERROR;
+
+ } else {
+ *drp2 = drhead;
+ ld->ld_errno = LDAP_SUCCESS;
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_deref_control(
+ LDAP *ld,
+ LDAPControl **ctrls,
+ LDAPDerefRes **drp )
+{
+ LDAPControl *c;
+
+ if ( drp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ *drp = NULL;
+
+ if ( ctrls == NULL ) {
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ c = ldap_control_find( LDAP_CONTROL_X_DEREF, ctrls, NULL );
+ if ( c == NULL ) {
+ /* No deref control was found. */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_parse_derefresponse_control( ld, c, drp );
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/dnssrv.c b/libraries/libldap/dnssrv.c
new file mode 100644
index 0000000..e665c27
--- /dev/null
+++ b/libraries/libldap/dnssrv.c
@@ -0,0 +1,431 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * locate LDAP servers using DNS SRV records.
+ * Location code based on MIT Kerberos KDC location code.
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#ifdef HAVE_RESOLV_H
+#include <resolv.h>
+#endif
+
+int ldap_dn2domain(
+ LDAP_CONST char *dn_in,
+ char **domainp)
+{
+ int i, j;
+ char *ndomain;
+ LDAPDN dn = NULL;
+ LDAPRDN rdn = NULL;
+ LDAPAVA *ava = NULL;
+ struct berval domain = BER_BVNULL;
+ static const struct berval DC = BER_BVC("DC");
+ static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25");
+
+ assert( dn_in != NULL );
+ assert( domainp != NULL );
+
+ *domainp = NULL;
+
+ if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) {
+ return -2;
+ }
+
+ if( dn ) for( i=0; dn[i] != NULL; i++ ) {
+ rdn = dn[i];
+
+ for( j=0; rdn[j] != NULL; j++ ) {
+ ava = rdn[j];
+
+ if( rdn[j+1] == NULL &&
+ (ava->la_flags & LDAP_AVA_STRING) &&
+ ava->la_value.bv_len &&
+ ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0
+ || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) )
+ {
+ if( domain.bv_len == 0 ) {
+ ndomain = LDAP_REALLOC( domain.bv_val,
+ ava->la_value.bv_len + 1);
+
+ if( ndomain == NULL ) {
+ goto return_error;
+ }
+
+ domain.bv_val = ndomain;
+
+ AC_MEMCPY( domain.bv_val, ava->la_value.bv_val,
+ ava->la_value.bv_len );
+
+ domain.bv_len = ava->la_value.bv_len;
+ domain.bv_val[domain.bv_len] = '\0';
+
+ } else {
+ ndomain = LDAP_REALLOC( domain.bv_val,
+ ava->la_value.bv_len + sizeof(".") + domain.bv_len );
+
+ if( ndomain == NULL ) {
+ goto return_error;
+ }
+
+ domain.bv_val = ndomain;
+ domain.bv_val[domain.bv_len++] = '.';
+ AC_MEMCPY( &domain.bv_val[domain.bv_len],
+ ava->la_value.bv_val, ava->la_value.bv_len );
+ domain.bv_len += ava->la_value.bv_len;
+ domain.bv_val[domain.bv_len] = '\0';
+ }
+ } else {
+ domain.bv_len = 0;
+ }
+ }
+ }
+
+
+ if( domain.bv_len == 0 && domain.bv_val != NULL ) {
+ LDAP_FREE( domain.bv_val );
+ domain.bv_val = NULL;
+ }
+
+ ldap_dnfree( dn );
+ *domainp = domain.bv_val;
+ return 0;
+
+return_error:
+ ldap_dnfree( dn );
+ LDAP_FREE( domain.bv_val );
+ return -1;
+}
+
+int ldap_domain2dn(
+ LDAP_CONST char *domain_in,
+ char **dnp)
+{
+ char *domain, *s, *tok_r, *dn, *dntmp;
+ size_t loc;
+
+ assert( domain_in != NULL );
+ assert( dnp != NULL );
+
+ domain = LDAP_STRDUP(domain_in);
+ if (domain == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+ dn = NULL;
+ loc = 0;
+
+ for (s = ldap_pvt_strtok(domain, ".", &tok_r);
+ s != NULL;
+ s = ldap_pvt_strtok(NULL, ".", &tok_r))
+ {
+ size_t len = strlen(s);
+
+ dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len );
+ if (dntmp == NULL) {
+ if (dn != NULL)
+ LDAP_FREE(dn);
+ LDAP_FREE(domain);
+ return LDAP_NO_MEMORY;
+ }
+
+ dn = dntmp;
+
+ if (loc > 0) {
+ /* not first time. */
+ strcpy(dn + loc, ",");
+ loc++;
+ }
+ strcpy(dn + loc, "dc=");
+ loc += sizeof("dc=")-1;
+
+ strcpy(dn + loc, s);
+ loc += len;
+ }
+
+ LDAP_FREE(domain);
+ *dnp = dn;
+ return LDAP_SUCCESS;
+}
+
+#ifdef HAVE_RES_QUERY
+#define DNSBUFSIZ (64*1024)
+#define MAXHOST 254 /* RFC 1034, max length is 253 chars */
+typedef struct srv_record {
+ u_short priority;
+ u_short weight;
+ u_short port;
+ char hostname[MAXHOST];
+} srv_record;
+
+/* Linear Congruential Generator - we don't need
+ * high quality randomness, and we don't want to
+ * interfere with anyone else's use of srand().
+ *
+ * The PRNG here cycles thru 941,955 numbers.
+ */
+static float srv_seed;
+
+static void srv_srand(int seed) {
+ srv_seed = (float)seed / (float)RAND_MAX;
+}
+
+static float srv_rand() {
+ float val = 9821.0 * srv_seed + .211327;
+ srv_seed = val - (int)val;
+ return srv_seed;
+}
+
+static int srv_cmp(const void *aa, const void *bb){
+ srv_record *a=(srv_record *)aa;
+ srv_record *b=(srv_record *)bb;
+ int i = a->priority - b->priority;
+ if (i) return i;
+ return b->weight - a->weight;
+}
+
+static void srv_shuffle(srv_record *a, int n) {
+ int i, j, total = 0, r, p;
+
+ for (i=0; i<n; i++)
+ total += a[i].weight;
+
+ /* all weights are zero, do a straight Fisher-Yates shuffle */
+ if (!total) {
+ while (n) {
+ srv_record t;
+ i = srv_rand() * n--;
+ t = a[n];
+ a[n] = a[i];
+ a[i] = t;
+ }
+ return;
+ }
+
+ /* Do a shuffle per RFC2782 Page 4 */
+ p = n;
+ for (i=0; i<n-1; i++) {
+ r = srv_rand() * total;
+ for (j=0; j<p; j++) {
+ r -= a[j].weight;
+ if (r <= 0) {
+ if (j) {
+ srv_record t = a[0];
+ a[0] = a[j];
+ a[j] = t;
+ }
+ total -= a[0].weight;
+ a++;
+ p--;
+ break;
+ }
+ }
+ }
+}
+#endif /* HAVE_RES_QUERY */
+
+/*
+ * Lookup and return LDAP servers for domain (using the DNS
+ * SRV record _ldap._tcp.domain).
+ */
+int ldap_domain2hostlist(
+ LDAP_CONST char *domain,
+ char **list )
+{
+#ifdef HAVE_RES_QUERY
+ char *request;
+ char *hostlist = NULL;
+ srv_record *hostent_head=NULL;
+ int i, j;
+ int rc, len, cur = 0;
+ unsigned char reply[DNSBUFSIZ];
+ int hostent_count=0;
+
+ assert( domain != NULL );
+ assert( list != NULL );
+ if( *domain == '\0' ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp."));
+ if (request == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+ sprintf(request, "_ldap._tcp.%s", domain);
+
+ LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
+
+ rc = LDAP_UNAVAILABLE;
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply));
+# ifndef T_SRV
+# define T_SRV ns_t_srv
+# endif
+#else
+ /* Bind 4 interface */
+# ifndef T_SRV
+# define T_SRV 33
+# endif
+
+ len = res_query(request, C_IN, T_SRV, reply, sizeof(reply));
+#endif
+ if (len >= 0) {
+ unsigned char *p;
+ char host[DNSBUFSIZ];
+ int status;
+ u_short port, priority, weight;
+
+ /* Parse out query */
+ p = reply;
+
+#ifdef NS_HFIXEDSZ
+ /* Bind 8/9 interface */
+ p += NS_HFIXEDSZ;
+#elif defined(HFIXEDSZ)
+ /* Bind 4 interface w/ HFIXEDSZ */
+ p += HFIXEDSZ;
+#else
+ /* Bind 4 interface w/o HFIXEDSZ */
+ p += sizeof(HEADER);
+#endif
+
+ status = dn_expand(reply, reply + len, p, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+ p += status;
+ p += 4;
+
+ while (p < reply + len) {
+ int type, class, ttl, size;
+ status = dn_expand(reply, reply + len, p, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+ p += status;
+ type = (p[0] << 8) | p[1];
+ p += 2;
+ class = (p[0] << 8) | p[1];
+ p += 2;
+ ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ p += 4;
+ size = (p[0] << 8) | p[1];
+ p += 2;
+ if (type == T_SRV) {
+ status = dn_expand(reply, reply + len, p + 6, host, sizeof(host));
+ if (status < 0) {
+ goto out;
+ }
+
+ /* Get priority weight and port */
+ priority = (p[0] << 8) | p[1];
+ weight = (p[2] << 8) | p[3];
+ port = (p[4] << 8) | p[5];
+
+ if ( port == 0 || host[ 0 ] == '\0' ) {
+ goto add_size;
+ }
+
+ hostent_head = (srv_record *) LDAP_REALLOC(hostent_head, (hostent_count+1)*(sizeof(srv_record)));
+ if(hostent_head==NULL){
+ rc=LDAP_NO_MEMORY;
+ goto out;
+ }
+ hostent_head[hostent_count].priority=priority;
+ hostent_head[hostent_count].weight=weight;
+ hostent_head[hostent_count].port=port;
+ strncpy(hostent_head[hostent_count].hostname, host, MAXHOST-1);
+ hostent_head[hostent_count].hostname[MAXHOST-1] = '\0';
+ hostent_count++;
+ }
+add_size:;
+ p += size;
+ }
+ if (!hostent_head) goto out;
+ qsort(hostent_head, hostent_count, sizeof(srv_record), srv_cmp);
+
+ if (!srv_seed)
+ srv_srand(time(0L));
+
+ /* shuffle records of same priority */
+ j = 0;
+ priority = hostent_head[0].priority;
+ for (i=1; i<hostent_count; i++) {
+ if (hostent_head[i].priority != priority) {
+ priority = hostent_head[i].priority;
+ if (i-j > 1)
+ srv_shuffle(hostent_head+j, i-j);
+ j = i;
+ }
+ }
+ if (i-j > 1)
+ srv_shuffle(hostent_head+j, i-j);
+
+ for(i=0; i<hostent_count; i++){
+ int buflen;
+ buflen = strlen(hostent_head[i].hostname) + STRLENOF(":65535 ");
+ hostlist = (char *) LDAP_REALLOC(hostlist, cur+buflen+1);
+ if (hostlist == NULL) {
+ rc = LDAP_NO_MEMORY;
+ goto out;
+ }
+ if(cur>0){
+ hostlist[cur++]=' ';
+ }
+ cur += sprintf(&hostlist[cur], "%s:%hu", hostent_head[i].hostname, hostent_head[i].port);
+ }
+ }
+
+ if (hostlist == NULL) {
+ /* No LDAP servers found in DNS. */
+ rc = LDAP_UNAVAILABLE;
+ goto out;
+ }
+
+ rc = LDAP_SUCCESS;
+ *list = hostlist;
+
+ out:
+ LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
+
+ if (request != NULL) {
+ LDAP_FREE(request);
+ }
+ if (hostent_head != NULL) {
+ LDAP_FREE(hostent_head);
+ }
+ if (rc != LDAP_SUCCESS && hostlist != NULL) {
+ LDAP_FREE(hostlist);
+ }
+ return rc;
+#else
+ return LDAP_NOT_SUPPORTED;
+#endif /* HAVE_RES_QUERY */
+}
diff --git a/libraries/libldap/dntest.c b/libraries/libldap/dntest.c
new file mode 100644
index 0000000..49b4002
--- /dev/null
+++ b/libraries/libldap/dntest.c
@@ -0,0 +1,296 @@
+/* dntest.c -- OpenLDAP DN API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This program was initially developed by Pierangelo Masarati <ando@OpenLDAP.org>
+ * for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * This program is designed to test the ldap_str2dn/ldap_dn2str
+ * functions
+ */
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+
+#include "ldap-int.h"
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+int
+main( int argc, char *argv[] )
+{
+ int rc, i, debug = 0, f2 = 0;
+ unsigned flags[ 2 ] = { 0U, 0 };
+ char *strin, *str = NULL, buf[ 1024 ];
+ LDAPDN dn, dn2 = NULL;
+
+ while ( 1 ) {
+ int opt = getopt( argc, argv, "d:" );
+
+ if ( opt == EOF ) {
+ break;
+ }
+
+ switch ( opt ) {
+ case 'd':
+ debug = atoi( optarg );
+ break;
+ }
+ }
+
+ optind--;
+ argc -= optind;
+ argv += optind;
+
+ if ( argc < 2 ) {
+ fprintf( stderr, "usage: dntest <dn> [flags-in[,...]] [flags-out[,...]]\n\n" );
+ fprintf( stderr, "\tflags-in: V3,V2,DCE,<flags>\n" );
+ fprintf( stderr, "\tflags-out: V3,V2,UFN,DCE,AD,<flags>\n\n" );
+ fprintf( stderr, "\t<flags>: PRETTY,PEDANTIC,NOSPACES,NOONESPACE\n\n" );
+ return( 0 );
+ }
+
+ if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug ) != LBER_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+ if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug ) != LDAP_OPT_SUCCESS ) {
+ fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n", debug );
+ }
+
+ if ( strcmp( argv[ 1 ], "-" ) == 0 ) {
+ size_t len = fgets( buf, sizeof( buf ), stdin ) ? strlen( buf ) : 0;
+
+ if ( len == 0 || buf[ --len ] == '\n' ) {
+ buf[ len ] = '\0';
+ }
+ strin = buf;
+ } else {
+ strin = argv[ 1 ];
+ }
+
+ if ( argc >= 3 ) {
+ for ( i = 0; i < argc - 2; i++ ) {
+ char *s, *e;
+ for ( s = argv[ 2 + i ]; s; s = e ) {
+ e = strchr( s, ',' );
+ if ( e != NULL ) {
+ e[ 0 ] = '\0';
+ e++;
+ }
+
+ if ( !strcasecmp( s, "V3" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_LDAPV3;
+ } else if ( !strcasecmp( s, "V2" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_LDAPV2;
+ } else if ( !strcasecmp( s, "DCE" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_DCE;
+ } else if ( !strcasecmp( s, "UFN" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_UFN;
+ } else if ( !strcasecmp( s, "AD" ) ) {
+ flags[ i ] |= LDAP_DN_FORMAT_AD_CANONICAL;
+ } else if ( !strcasecmp( s, "PRETTY" ) ) {
+ flags[ i ] |= LDAP_DN_PRETTY;
+ } else if ( !strcasecmp( s, "PEDANTIC" ) ) {
+ flags[ i ] |= LDAP_DN_PEDANTIC;
+ } else if ( !strcasecmp( s, "NOSPACES" ) ) {
+ flags[ i ] |= LDAP_DN_P_NOLEADTRAILSPACES;
+ } else if ( !strcasecmp( s, "NOONESPACE" ) ) {
+ flags[ i ] |= LDAP_DN_P_NOSPACEAFTERRDN;
+ }
+ }
+ }
+ }
+
+ if ( flags[ 1 ] == 0 )
+ flags[ 1 ] = LDAP_DN_FORMAT_LDAPV3;
+
+ f2 = 1;
+
+ rc = ldap_str2dn( strin, &dn, flags[ 0 ] );
+
+ if ( rc == LDAP_SUCCESS ) {
+ int i;
+ if ( dn ) {
+ for ( i = 0; dn[ i ]; i++ ) {
+ LDAPRDN rdn = dn[ i ];
+ char *rstr = NULL;
+
+ if ( ldap_rdn2str( rdn, &rstr, flags[ f2 ] ) ) {
+ fprintf( stdout, "\tldap_rdn2str() failed\n" );
+ continue;
+ }
+
+ fprintf( stdout, "\tldap_rdn2str() = \"%s\"\n", rstr );
+ ldap_memfree( rstr );
+ }
+ } else {
+ fprintf( stdout, "\tempty DN\n" );
+ }
+ }
+
+ str = NULL;
+ if ( rc == LDAP_SUCCESS &&
+ ldap_dn2str( dn, &str, flags[ f2 ] ) == LDAP_SUCCESS )
+ {
+ char **values, *tmp, *tmp2, *str2 = NULL;
+ int n;
+
+ fprintf( stdout, "\nldap_dn2str(ldap_str2dn(\"%s\"))\n"
+ "\t= \"%s\"\n", strin, str );
+
+ switch ( flags[ f2 ] & LDAP_DN_FORMAT_MASK ) {
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return( 0 );
+
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ n = ldap_dn2domain( strin, &tmp );
+ if ( n ) {
+ fprintf( stdout, "\nldap_dn2domain(\"%s\") FAILED\n", strin );
+ } else {
+ fprintf( stdout, "\nldap_dn2domain(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ }
+ ldap_memfree( tmp );
+
+ tmp = ldap_dn2ufn( strin );
+ fprintf( stdout, "\nldap_dn2ufn(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ ldap_memfree( tmp );
+
+ tmp = ldap_dn2dcedn( strin );
+ fprintf( stdout, "\nldap_dn2dcedn(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ tmp2 = ldap_dcedn2dn( tmp );
+ fprintf( stdout, "\nldap_dcedn2dn(\"%s\")\n"
+ "\t= \"%s\"\n",
+ tmp ? tmp : "", tmp2 ? tmp2 : "" );
+ ldap_memfree( tmp );
+ ldap_memfree( tmp2 );
+
+ tmp = ldap_dn2ad_canonical( strin );
+ fprintf( stdout, "\nldap_dn2ad_canonical(\"%s\")\n"
+ "\t= \"%s\"\n", strin, tmp ? tmp : "" );
+ ldap_memfree( tmp );
+
+ fprintf( stdout, "\nldap_explode_dn(\"%s\"):\n", str );
+ values = ldap_explode_dn( str, 0 );
+ for ( n = 0; values && values[ n ]; n++ ) {
+ char **vv;
+ int nn;
+
+ fprintf( stdout, "\t\"%s\"\n", values[ n ] );
+
+ fprintf( stdout, "\tldap_explode_rdn(\"%s\")\n",
+ values[ n ] );
+ vv = ldap_explode_rdn( values[ n ], 0 );
+ for ( nn = 0; vv && vv[ nn ]; nn++ ) {
+ fprintf( stdout, "\t\t'%s'\n",
+ vv[ nn ] );
+ }
+ LDAP_VFREE( vv );
+
+ fprintf( stdout, "\tldap_explode_rdn(\"%s\")"
+ " (no types)\n", values[ n ] );
+ vv = ldap_explode_rdn( values[ n ], 1 );
+ for ( nn = 0; vv && vv[ nn ]; nn++ ) {
+ fprintf( stdout, "\t\t\t\"%s\"\n",
+ vv[ nn ] );
+ }
+ LDAP_VFREE( vv );
+
+ }
+ LDAP_VFREE( values );
+
+ fprintf( stdout, "\nldap_explode_dn(\"%s\")"
+ " (no types):\n", str );
+ values = ldap_explode_dn( str, 1 );
+ for ( n = 0; values && values[ n ]; n++ ) {
+ fprintf( stdout, "\t\"%s\"\n", values[ n ] );
+ }
+ LDAP_VFREE( values );
+
+ break;
+ }
+
+ dn2 = NULL;
+ rc = ldap_str2dn( str, &dn2, flags[ f2 ] );
+ str2 = NULL;
+ if ( rc == LDAP_SUCCESS &&
+ ldap_dn2str( dn2, &str2, flags[ f2 ] )
+ == LDAP_SUCCESS ) {
+ int iRDN;
+
+ fprintf( stdout, "\n\"%s\"\n\t == \"%s\" ? %s\n",
+ str, str2,
+ strcmp( str, str2 ) == 0 ? "yes" : "no" );
+
+ if( dn != NULL && dn2 == NULL ) {
+ fprintf( stdout, "dn mismatch\n" );
+ } else if (( dn != NULL ) && (dn2 != NULL))
+ for ( iRDN = 0; dn[ iRDN ] && dn2[ iRDN ]; iRDN++ )
+ {
+ LDAPRDN r = dn[ iRDN ];
+ LDAPRDN r2 = dn2[ iRDN ];
+ int iAVA;
+
+ for ( iAVA = 0; r[ iAVA ] && r2[ iAVA ]; iAVA++ ) {
+ LDAPAVA *a = r[ iAVA ];
+ LDAPAVA *a2 = r2[ iAVA ];
+
+ if ( a->la_attr.bv_len != a2->la_attr.bv_len ) {
+ fprintf( stdout, "ava(%d), rdn(%d) attr len mismatch (%ld->%ld)\n",
+ iAVA + 1, iRDN + 1,
+ a->la_attr.bv_len, a2->la_attr.bv_len );
+ } else if ( memcmp( a->la_attr.bv_val, a2->la_attr.bv_val, a->la_attr.bv_len ) ) {
+ fprintf( stdout, "ava(%d), rdn(%d) attr mismatch\n",
+ iAVA + 1, iRDN + 1 );
+ } else if ( a->la_flags != a2->la_flags ) {
+ fprintf( stdout, "ava(%d), rdn(%d) flag mismatch (%x->%x)\n",
+ iAVA + 1, iRDN + 1, a->la_flags, a2->la_flags );
+ } else if ( a->la_value.bv_len != a2->la_value.bv_len ) {
+ fprintf( stdout, "ava(%d), rdn(%d) value len mismatch (%ld->%ld)\n",
+ iAVA + 1, iRDN + 1,
+ a->la_value.bv_len, a2->la_value.bv_len );
+ } else if ( memcmp( a->la_value.bv_val, a2->la_value.bv_val, a->la_value.bv_len ) ) {
+ fprintf( stdout, "ava(%d), rdn(%d) value mismatch\n",
+ iAVA + 1, iRDN + 1 );
+ }
+ }
+ }
+
+ ldap_dnfree( dn2 );
+ ldap_memfree( str2 );
+ }
+ ldap_memfree( str );
+ }
+ ldap_dnfree( dn );
+
+ /* note: dn is not freed */
+
+ return( 0 );
+}
diff --git a/libraries/libldap/error.c b/libraries/libldap/error.c
new file mode 100644
index 0000000..c3dba31
--- /dev/null
+++ b/libraries/libldap/error.c
@@ -0,0 +1,398 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+void ldap_int_error_init( void ) {
+}
+
+char *
+ldap_err2string( int err )
+{
+ char *m;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_err2string\n", 0, 0, 0 );
+
+ switch ( err ) {
+# define C(code, message) case code: m = message; break
+
+ /* LDAPv3 (RFC 4511) codes */
+ C(LDAP_SUCCESS, N_("Success"));
+ C(LDAP_OPERATIONS_ERROR, N_("Operations error"));
+ C(LDAP_PROTOCOL_ERROR, N_("Protocol error"));
+ C(LDAP_TIMELIMIT_EXCEEDED, N_("Time limit exceeded"));
+ C(LDAP_SIZELIMIT_EXCEEDED, N_("Size limit exceeded"));
+ C(LDAP_COMPARE_FALSE, N_("Compare False"));
+ C(LDAP_COMPARE_TRUE, N_("Compare True"));
+ C(LDAP_STRONG_AUTH_NOT_SUPPORTED,N_("Authentication method not supported"));
+ C(LDAP_STRONG_AUTH_REQUIRED, N_("Strong(er) authentication required"));
+
+ C(LDAP_REFERRAL, N_("Referral"));
+ C(LDAP_ADMINLIMIT_EXCEEDED, N_("Administrative limit exceeded"));
+ C(LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+ N_("Critical extension is unavailable"));
+ C(LDAP_CONFIDENTIALITY_REQUIRED,N_("Confidentiality required"));
+ C(LDAP_SASL_BIND_IN_PROGRESS, N_("SASL bind in progress"));
+
+ C(LDAP_NO_SUCH_ATTRIBUTE, N_("No such attribute"));
+ C(LDAP_UNDEFINED_TYPE, N_("Undefined attribute type"));
+ C(LDAP_INAPPROPRIATE_MATCHING, N_("Inappropriate matching"));
+ C(LDAP_CONSTRAINT_VIOLATION, N_("Constraint violation"));
+ C(LDAP_TYPE_OR_VALUE_EXISTS, N_("Type or value exists"));
+ C(LDAP_INVALID_SYNTAX, N_("Invalid syntax"));
+
+ C(LDAP_NO_SUCH_OBJECT, N_("No such object"));
+ C(LDAP_ALIAS_PROBLEM, N_("Alias problem"));
+ C(LDAP_INVALID_DN_SYNTAX, N_("Invalid DN syntax"));
+
+ C(LDAP_ALIAS_DEREF_PROBLEM, N_("Alias dereferencing problem"));
+
+ C(LDAP_INAPPROPRIATE_AUTH, N_("Inappropriate authentication"));
+ C(LDAP_INVALID_CREDENTIALS, N_("Invalid credentials"));
+ C(LDAP_INSUFFICIENT_ACCESS, N_("Insufficient access"));
+ C(LDAP_BUSY, N_("Server is busy"));
+ C(LDAP_UNAVAILABLE, N_("Server is unavailable"));
+ C(LDAP_UNWILLING_TO_PERFORM, N_("Server is unwilling to perform"));
+ C(LDAP_LOOP_DETECT, N_("Loop detected"));
+
+ C(LDAP_NAMING_VIOLATION, N_("Naming violation"));
+ C(LDAP_OBJECT_CLASS_VIOLATION, N_("Object class violation"));
+ C(LDAP_NOT_ALLOWED_ON_NONLEAF, N_("Operation not allowed on non-leaf"));
+ C(LDAP_NOT_ALLOWED_ON_RDN, N_("Operation not allowed on RDN"));
+ C(LDAP_ALREADY_EXISTS, N_("Already exists"));
+ C(LDAP_NO_OBJECT_CLASS_MODS, N_("Cannot modify object class"));
+
+ C(LDAP_AFFECTS_MULTIPLE_DSAS, N_("Operation affects multiple DSAs"));
+
+ /* Virtual List View draft */
+ C(LDAP_VLV_ERROR, N_("Virtual List View error"));
+
+ C(LDAP_OTHER, N_("Other (e.g., implementation specific) error"));
+
+ /* LDAPv2 (RFC 1777) codes */
+ C(LDAP_PARTIAL_RESULTS, N_("Partial results and referral received"));
+ C(LDAP_IS_LEAF, N_("Entry is a leaf"));
+
+ /* Connection-less LDAP (CLDAP - RFC 1798) code */
+ C(LDAP_RESULTS_TOO_LARGE, N_("Results too large"));
+
+ /* Cancel Operation (RFC 3909) codes */
+ C(LDAP_CANCELLED, N_("Cancelled"));
+ C(LDAP_NO_SUCH_OPERATION, N_("No Operation to Cancel"));
+ C(LDAP_TOO_LATE, N_("Too Late to Cancel"));
+ C(LDAP_CANNOT_CANCEL, N_("Cannot Cancel"));
+
+ /* Assert Control (RFC 4528 and old internet-draft) codes */
+ C(LDAP_ASSERTION_FAILED, N_("Assertion Failed"));
+ C(LDAP_X_ASSERTION_FAILED, N_("Assertion Failed (X)"));
+
+ /* Proxied Authorization Control (RFC 4370 and I-D) codes */
+ C(LDAP_PROXIED_AUTHORIZATION_DENIED, N_("Proxied Authorization Denied"));
+ C(LDAP_X_PROXY_AUTHZ_FAILURE, N_("Proxy Authorization Failure (X)"));
+
+ /* Content Sync Operation (RFC 4533 and I-D) codes */
+ C(LDAP_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required"));
+ C(LDAP_X_SYNC_REFRESH_REQUIRED, N_("Content Sync Refresh Required (X)"));
+
+ /* No-Op Control (draft-zeilenga-ldap-noop) code */
+ C(LDAP_X_NO_OPERATION, N_("No Operation (X)"));
+
+ /* Client Update Protocol (RFC 3928) codes */
+ C(LDAP_CUP_RESOURCES_EXHAUSTED, N_("LCUP Resources Exhausted"));
+ C(LDAP_CUP_SECURITY_VIOLATION, N_("LCUP Security Violation"));
+ C(LDAP_CUP_INVALID_DATA, N_("LCUP Invalid Data"));
+ C(LDAP_CUP_UNSUPPORTED_SCHEME, N_("LCUP Unsupported Scheme"));
+ C(LDAP_CUP_RELOAD_REQUIRED, N_("LCUP Reload Required"));
+
+#ifdef LDAP_X_TXN
+ /* Codes related to LDAP Transactions (draft-zeilenga-ldap-txn) */
+ C(LDAP_X_TXN_SPECIFY_OKAY, N_("TXN specify okay"));
+ C(LDAP_X_TXN_ID_INVALID, N_("TXN ID is invalid"));
+#endif
+
+ /* API codes - renumbered since draft-ietf-ldapext-ldap-c-api */
+ C(LDAP_SERVER_DOWN, N_("Can't contact LDAP server"));
+ C(LDAP_LOCAL_ERROR, N_("Local error"));
+ C(LDAP_ENCODING_ERROR, N_("Encoding error"));
+ C(LDAP_DECODING_ERROR, N_("Decoding error"));
+ C(LDAP_TIMEOUT, N_("Timed out"));
+ C(LDAP_AUTH_UNKNOWN, N_("Unknown authentication method"));
+ C(LDAP_FILTER_ERROR, N_("Bad search filter"));
+ C(LDAP_USER_CANCELLED, N_("User cancelled operation"));
+ C(LDAP_PARAM_ERROR, N_("Bad parameter to an ldap routine"));
+ C(LDAP_NO_MEMORY, N_("Out of memory"));
+ C(LDAP_CONNECT_ERROR, N_("Connect error"));
+ C(LDAP_NOT_SUPPORTED, N_("Not Supported"));
+ C(LDAP_CONTROL_NOT_FOUND, N_("Control not found"));
+ C(LDAP_NO_RESULTS_RETURNED, N_("No results returned"));
+ C(LDAP_MORE_RESULTS_TO_RETURN, N_("More results to return"));
+ C(LDAP_CLIENT_LOOP, N_("Client Loop"));
+ C(LDAP_REFERRAL_LIMIT_EXCEEDED, N_("Referral Limit Exceeded"));
+ C(LDAP_X_CONNECTING, N_("Connecting (X)"));
+# undef C
+
+ default:
+ m = (LDAP_API_ERROR(err) ? N_("Unknown API error")
+ : LDAP_E_ERROR(err) ? N_("Unknown (extension) error")
+ : LDAP_X_ERROR(err) ? N_("Unknown (private extension) error")
+ : N_("Unknown error"));
+ break;
+ }
+
+ return _(m);
+}
+
+/* deprecated */
+void
+ldap_perror( LDAP *ld, LDAP_CONST char *str )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( str != NULL );
+
+ fprintf( stderr, "%s: %s (%d)\n",
+ str ? str : "ldap_perror",
+ ldap_err2string( ld->ld_errno ),
+ ld->ld_errno );
+
+ if ( ld->ld_matched != NULL && ld->ld_matched[0] != '\0' ) {
+ fprintf( stderr, _("\tmatched DN: %s\n"), ld->ld_matched );
+ }
+
+ if ( ld->ld_error != NULL && ld->ld_error[0] != '\0' ) {
+ fprintf( stderr, _("\tadditional info: %s\n"), ld->ld_error );
+ }
+
+ if ( ld->ld_referrals != NULL && ld->ld_referrals[0] != NULL) {
+ fprintf( stderr, _("\treferrals:\n") );
+ for (i=0; ld->ld_referrals[i]; i++) {
+ fprintf( stderr, _("\t\t%s\n"), ld->ld_referrals[i] );
+ }
+ }
+
+ fflush( stderr );
+}
+
+/* deprecated */
+int
+ldap_result2error( LDAP *ld, LDAPMessage *r, int freeit )
+{
+ int rc, err;
+
+ rc = ldap_parse_result( ld, r, &err,
+ NULL, NULL, NULL, NULL, freeit );
+
+ return err != LDAP_SUCCESS ? err : rc;
+}
+
+/*
+ * Parse LDAPResult Messages:
+ *
+ * LDAPResult ::= SEQUENCE {
+ * resultCode ENUMERATED,
+ * matchedDN LDAPDN,
+ * errorMessage LDAPString,
+ * referral [3] Referral OPTIONAL }
+ *
+ * including Bind results:
+ *
+ * BindResponse ::= [APPLICATION 1] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds [7] OCTET STRING OPTIONAL }
+ *
+ * and ExtendedOp results:
+ *
+ * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * responseName [10] LDAPOID OPTIONAL,
+ * response [11] OCTET STRING OPTIONAL }
+ *
+ */
+int
+ldap_parse_result(
+ LDAP *ld,
+ LDAPMessage *r,
+ int *errcodep,
+ char **matcheddnp,
+ char **errmsgp,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit )
+{
+ LDAPMessage *lm;
+ ber_int_t errcode = LDAP_SUCCESS;
+
+ ber_tag_t tag;
+ BerElement *ber;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_parse_result\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( r != NULL );
+
+ if(errcodep != NULL) *errcodep = LDAP_SUCCESS;
+ if(matcheddnp != NULL) *matcheddnp = NULL;
+ if(errmsgp != NULL) *errmsgp = NULL;
+ if(referralsp != NULL) *referralsp = NULL;
+ if(serverctrls != NULL) *serverctrls = NULL;
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ /* Find the result, last msg in chain... */
+ lm = r->lm_chain_tail;
+ /* FIXME: either this is not possible (assert?)
+ * or it should be handled */
+ if ( lm != NULL ) {
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ lm = NULL;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if( lm == NULL ) {
+ errcode = ld->ld_errno = LDAP_NO_RESULTS_RETURNED;
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+ goto done;
+ }
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+ if ( ld->ld_referrals ) {
+ LDAP_VFREE( ld->ld_referrals );
+ ld->ld_referrals = NULL;
+ }
+
+ /* parse results */
+
+ ber = ber_dup( lm->lm_ber );
+
+ if ( ld->ld_version < LDAP_VERSION2 ) {
+ tag = ber_scanf( ber, "{iA}",
+ &ld->ld_errno, &ld->ld_error );
+
+ } else {
+ ber_len_t len;
+
+ tag = ber_scanf( ber, "{iAA" /*}*/,
+ &ld->ld_errno, &ld->ld_matched, &ld->ld_error );
+
+ if( tag != LBER_ERROR ) {
+ /* peek for referrals */
+ if( ber_peek_tag(ber, &len) == LDAP_TAG_REFERRAL ) {
+ tag = ber_scanf( ber, "v", &ld->ld_referrals );
+ }
+ }
+
+ /* need to clean out misc items */
+ if( tag != LBER_ERROR ) {
+ if( lm->lm_msgtype == LDAP_RES_BIND ) {
+ /* look for sasl result creditials */
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS ) {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+
+ } else if( lm->lm_msgtype == LDAP_RES_EXTENDED ) {
+ /* look for exop result oid or value */
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_OID ) {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+
+ if ( tag != LBER_ERROR &&
+ ber_peek_tag( ber, &len ) == LDAP_TAG_EXOP_RES_VALUE )
+ {
+ /* skip 'em */
+ tag = ber_scanf( ber, "x" );
+ }
+ }
+ }
+
+ if( tag != LBER_ERROR ) {
+ int rc = ldap_pvt_get_controls( ber, serverctrls );
+
+ if( rc != LDAP_SUCCESS ) {
+ tag = LBER_ERROR;
+ }
+ }
+
+ if( tag != LBER_ERROR ) {
+ tag = ber_scanf( ber, /*{*/"}" );
+ }
+ }
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = errcode = LDAP_DECODING_ERROR;
+ }
+
+ if( ber != NULL ) {
+ ber_free( ber, 0 );
+ }
+
+ /* return */
+ if( errcodep != NULL ) {
+ *errcodep = ld->ld_errno;
+ }
+ if ( errcode == LDAP_SUCCESS ) {
+ if( matcheddnp != NULL ) {
+ if ( ld->ld_matched )
+ {
+ *matcheddnp = LDAP_STRDUP( ld->ld_matched );
+ }
+ }
+ if( errmsgp != NULL ) {
+ if ( ld->ld_error )
+ {
+ *errmsgp = LDAP_STRDUP( ld->ld_error );
+ }
+ }
+
+ if( referralsp != NULL) {
+ *referralsp = ldap_value_dup( ld->ld_referrals );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+done:
+ if ( freeit ) {
+ ldap_msgfree( r );
+ }
+
+ return errcode;
+}
diff --git a/libraries/libldap/extended.c b/libraries/libldap/extended.c
new file mode 100644
index 0000000..0bec086
--- /dev/null
+++ b/libraries/libldap/extended.c
@@ -0,0 +1,419 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+BerElement *
+ldap_build_extended_req(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if ( reqdata != NULL ) {
+ rc = ber_printf( ber, "{it{tstON}", /* '}' */
+ *msgidp, LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, reqoid,
+ LDAP_TAG_EXOP_REQ_VALUE, reqdata );
+
+ } else {
+ rc = ber_printf( ber, "{it{tsN}", /* '}' */
+ *msgidp, LDAP_REQ_EXTENDED,
+ LDAP_TAG_EXOP_REQ_OID, reqoid );
+ }
+
+ if( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * LDAPv3 Extended Operation Request
+ * ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
+ * requestName [0] LDAPOID,
+ * requestValue [1] OCTET STRING OPTIONAL
+ * }
+ *
+ * LDAPv3 Extended Operation Response
+ * ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * responseName [10] LDAPOID OPTIONAL,
+ * response [11] OCTET STRING OPTIONAL
+ * }
+ *
+ * (Source RFC 4511)
+ */
+
+int
+ldap_extended_operation(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( reqoid != NULL && *reqoid != '\0' );
+ assert( msgidp != NULL );
+
+ /* must be version 3 (or greater) */
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return( ld->ld_errno );
+ }
+
+ ber = ldap_build_extended_req( ld, reqoid, reqdata,
+ sctrls, cctrls, &id );
+ if ( !ber )
+ return( ld->ld_errno );
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_EXTENDED, NULL, ber, id );
+
+ return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+int
+ldap_extended_operation_s(
+ LDAP *ld,
+ LDAP_CONST char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ char **retoidp,
+ struct berval **retdatap )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_extended_operation_s\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( reqoid != NULL && *reqoid != '\0' );
+
+ rc = ldap_extended_operation( ld, reqoid, reqdata,
+ sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return( ld->ld_errno );
+ }
+
+ if ( retoidp != NULL ) *retoidp = NULL;
+ if ( retdatap != NULL ) *retdatap = NULL;
+
+ rc = ldap_parse_extended_result( ld, res, retoidp, retdatap, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+/* Parse an extended result */
+int
+ldap_parse_extended_result (
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ int freeit )
+{
+ BerElement *ber;
+ ber_tag_t rc;
+ ber_tag_t tag;
+ ber_len_t len;
+ struct berval *resdata;
+ ber_int_t errcode;
+ char *resoid;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_parse_extended_result\n", 0, 0, 0 );
+
+ if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_EXTENDED ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if( retoidp != NULL ) *retoidp = NULL;
+ if( retdatap != NULL ) *retdatap = NULL;
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ ber = ber_dup( res->lm_ber );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ rc = ber_scanf( ber, "{eAA" /*}*/, &errcode,
+ &ld->ld_matched, &ld->ld_error );
+
+ if( rc == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ resoid = NULL;
+ resdata = NULL;
+
+ tag = ber_peek_tag( ber, &len );
+
+ if( tag == LDAP_TAG_REFERRAL ) {
+ /* skip over referral */
+ if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_RES_OID ) {
+ /* we have a resoid */
+ if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ assert( resoid[ 0 ] != '\0' );
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ /* we have a resdata */
+ if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ if( resoid != NULL ) LDAP_FREE( resoid );
+ return ld->ld_errno;
+ }
+ }
+
+ ber_free( ber, 0 );
+
+ if( retoidp != NULL ) {
+ *retoidp = resoid;
+ } else {
+ LDAP_FREE( resoid );
+ }
+
+ if( retdatap != NULL ) {
+ *retdatap = resdata;
+ } else {
+ ber_bvfree( resdata );
+ }
+
+ ld->ld_errno = errcode;
+
+ if( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/* Parse an extended partial */
+int
+ldap_parse_intermediate (
+ LDAP *ld,
+ LDAPMessage *res,
+ char **retoidp,
+ struct berval **retdatap,
+ LDAPControl ***serverctrls,
+ int freeit )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+ struct berval *resdata;
+ char *resoid;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_parse_intermediate\n", 0, 0, 0 );
+
+ if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_INTERMEDIATE ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if( retoidp != NULL ) *retoidp = NULL;
+ if( retdatap != NULL ) *retdatap = NULL;
+ if( serverctrls != NULL ) *serverctrls = NULL;
+
+ ber = ber_dup( res->lm_ber );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_scanf( ber, "{" /*}*/ );
+
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ resoid = NULL;
+ resdata = NULL;
+
+ tag = ber_peek_tag( ber, &len );
+
+ /*
+ * NOTE: accept intermediate and extended response tag values
+ * as older versions of slapd(8) incorrectly used extended
+ * response tags.
+ * Should be removed when 2.2 is moved to Historic.
+ */
+ if( tag == LDAP_TAG_IM_RES_OID || tag == LDAP_TAG_EXOP_RES_OID ) {
+ /* we have a resoid */
+ if( ber_scanf( ber, "a", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ assert( resoid[ 0 ] != '\0' );
+
+ tag = ber_peek_tag( ber, &len );
+ }
+
+ if( tag == LDAP_TAG_IM_RES_VALUE || tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ /* we have a resdata */
+ if( ber_scanf( ber, "O", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ if( resoid != NULL ) LDAP_FREE( resoid );
+ return ld->ld_errno;
+ }
+ }
+
+ if ( serverctrls == NULL ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ goto free_and_return;
+ }
+
+ if ( ber_scanf( ber, /*{*/ "}" ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ ld->ld_errno = ldap_pvt_get_controls( ber, serverctrls );
+
+free_and_return:
+ ber_free( ber, 0 );
+
+ if( retoidp != NULL ) {
+ *retoidp = resoid;
+ } else {
+ LDAP_FREE( resoid );
+ }
+
+ if( retdatap != NULL ) {
+ *retdatap = resdata;
+ } else {
+ ber_bvfree( resdata );
+ }
+
+ if( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/fetch.c b/libraries/libldap/fetch.c
new file mode 100644
index 0000000..cdc69b8
--- /dev/null
+++ b/libraries/libldap/fetch.c
@@ -0,0 +1,146 @@
+/* fetch.c - routines for fetching data at URLs */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * Portions Copyright 1999-2003 Kurt D. Zeilenga.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for
+ * inclusion in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+#ifdef HAVE_FETCH
+#include <fetch.h>
+#endif
+
+#include "lber_pvt.h"
+#include "ldap_pvt.h"
+#include "ldap_config.h"
+#include "ldif.h"
+
+FILE *
+ldif_open_url(
+ LDAP_CONST char *urlstr )
+{
+ FILE *url;
+
+ if( strncasecmp( "file:", urlstr, sizeof("file:")-1 ) == 0 ) {
+ char *p;
+ urlstr += sizeof("file:")-1;
+
+ /* we don't check for LDAP_DIRSEP since URLs should contain '/' */
+ if ( urlstr[0] == '/' && urlstr[1] == '/' ) {
+ urlstr += 2;
+ /* path must be absolute if authority is present
+ * technically, file://hostname/path is also legal but we don't
+ * accept a non-empty hostname
+ */
+ if ( urlstr[0] != '/' ) {
+#ifdef _WIN32
+ /* An absolute path in improper file://C:/foo/bar format */
+ if ( urlstr[1] != ':' )
+#endif
+ return NULL;
+ }
+#ifdef _WIN32
+ /* An absolute path in proper file:///C:/foo/bar format */
+ if ( urlstr[2] == ':' )
+ urlstr++;
+#endif
+ }
+
+ p = ber_strdup( urlstr );
+
+ /* But we should convert to LDAP_DIRSEP before use */
+ if ( LDAP_DIRSEP[0] != '/' ) {
+ char *s = p;
+ while (( s = strchr( s, '/' )))
+ *s++ = LDAP_DIRSEP[0];
+ }
+
+ ldap_pvt_hex_unescape( p );
+
+ url = fopen( p, "rb" );
+
+ ber_memfree( p );
+ } else {
+#ifdef HAVE_FETCH
+ url = fetchGetURL( (char*) urlstr, "" );
+#else
+ url = NULL;
+#endif
+ }
+ return url;
+}
+
+int
+ldif_fetch_url(
+ LDAP_CONST char *urlstr,
+ char **valuep,
+ ber_len_t *vlenp )
+{
+ FILE *url;
+ char buffer[1024];
+ char *p = NULL;
+ size_t total;
+ size_t bytes;
+
+ *valuep = NULL;
+ *vlenp = 0;
+
+ url = ldif_open_url( urlstr );
+
+ if( url == NULL ) {
+ return -1;
+ }
+
+ total = 0;
+
+ while( (bytes = fread( buffer, 1, sizeof(buffer), url )) != 0 ) {
+ char *newp = ber_memrealloc( p, total + bytes + 1 );
+ if( newp == NULL ) {
+ ber_memfree( p );
+ fclose( url );
+ return -1;
+ }
+ p = newp;
+ AC_MEMCPY( &p[total], buffer, bytes );
+ total += bytes;
+ }
+
+ fclose( url );
+
+ if( total == 0 ) {
+ char *newp = ber_memrealloc( p, 1 );
+ if( newp == NULL ) {
+ ber_memfree( p );
+ return -1;
+ }
+ p = newp;
+ }
+
+ p[total] = '\0';
+ *valuep = p;
+ *vlenp = total;
+
+ return 0;
+}
diff --git a/libraries/libldap/filter.c b/libraries/libldap/filter.c
new file mode 100644
index 0000000..c666298
--- /dev/null
+++ b/libraries/libldap/filter.c
@@ -0,0 +1,1124 @@
+/* search.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+static int put_simple_vrFilter LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static int put_vrFilter_list LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static char *put_complex_filter LDAP_P((
+ BerElement *ber,
+ char *str,
+ ber_tag_t tag,
+ int not ));
+
+static int put_simple_filter LDAP_P((
+ BerElement *ber,
+ char *str ));
+
+static int put_substring_filter LDAP_P((
+ BerElement *ber,
+ char *type,
+ char *str,
+ char *nextstar ));
+
+static int put_filter_list LDAP_P((
+ BerElement *ber,
+ char *str,
+ ber_tag_t tag ));
+
+static int ldap_is_oid ( const char *str )
+{
+ int i;
+
+ if( LDAP_ALPHA( str[0] )) {
+ for( i=1; str[i]; i++ ) {
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+
+ } else if LDAP_DIGIT( str[0] ) {
+ int dot=0;
+ for( i=1; str[i]; i++ ) {
+ if( LDAP_DIGIT( str[i] )) {
+ dot=0;
+
+ } else if ( str[i] == '.' ) {
+ if( ++dot > 1 ) return 0;
+
+ } else {
+ return 0;
+ }
+ }
+ return !dot;
+ }
+
+ return 0;
+}
+
+static int ldap_is_desc ( const char *str )
+{
+ int i;
+
+ if( LDAP_ALPHA( str[0] )) {
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ str = &str[i+1];
+ goto options;
+ }
+
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+
+ } else if LDAP_DIGIT( str[0] ) {
+ int dot=0;
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ if( dot ) return 0;
+ str = &str[i+1];
+ goto options;
+ }
+
+ if( LDAP_DIGIT( str[i] )) {
+ dot=0;
+
+ } else if ( str[i] == '.' ) {
+ if( ++dot > 1 ) return 0;
+
+ } else {
+ return 0;
+ }
+ }
+ return !dot;
+ }
+
+ return 0;
+
+options:
+ if( !LDAP_LDH( str[0] )) {
+ return 0;
+ }
+ for( i=1; str[i]; i++ ) {
+ if( str[i] == ';' ) {
+ str = &str[i+1];
+ goto options;
+ }
+ if( !LDAP_LDH( str[i] )) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static char *
+find_right_paren( char *s )
+{
+ int balance, escape;
+
+ balance = 1;
+ escape = 0;
+ while ( *s && balance ) {
+ if ( !escape ) {
+ if ( *s == '(' ) {
+ balance++;
+ } else if ( *s == ')' ) {
+ balance--;
+ }
+ }
+
+ escape = ( *s == '\\' && !escape );
+
+ if ( balance ) s++;
+ }
+
+ return *s ? s : NULL;
+}
+
+static int hex2value( int c )
+{
+ if( c >= '0' && c <= '9' ) {
+ return c - '0';
+ }
+
+ if( c >= 'A' && c <= 'F' ) {
+ return c + (10 - (int) 'A');
+ }
+
+ if( c >= 'a' && c <= 'f' ) {
+ return c + (10 - (int) 'a');
+ }
+
+ return -1;
+}
+
+char *
+ldap_pvt_find_wildcard( const char *s )
+{
+ for( ; *s; s++ ) {
+ switch( *s ) {
+ case '*': /* found wildcard */
+ return (char *) s;
+
+ case '(':
+ case ')':
+ return NULL;
+
+ case '\\':
+ if( s[1] == '\0' ) return NULL;
+
+ if( LDAP_HEX( s[1] ) && LDAP_HEX( s[2] ) ) {
+ s+=2;
+
+ } else switch( s[1] ) {
+ default:
+ return NULL;
+
+ /* allow RFC 1960 escapes */
+ case '*':
+ case '(':
+ case ')':
+ case '\\':
+ s++;
+ }
+ }
+ }
+
+ return (char *) s;
+}
+
+/* unescape filter value */
+/* support both LDAP v2 and v3 escapes */
+/* output can include nul characters! */
+ber_slen_t
+ldap_pvt_filter_value_unescape( char *fval )
+{
+ ber_slen_t r, v;
+ int v1, v2;
+
+ for( r=v=0; fval[v] != '\0'; v++ ) {
+ switch( fval[v] ) {
+ case '(':
+ case ')':
+ case '*':
+ return -1;
+
+ case '\\':
+ /* escape */
+ v++;
+
+ if ( fval[v] == '\0' ) {
+ /* escape at end of string */
+ return -1;
+ }
+
+ if (( v1 = hex2value( fval[v] )) >= 0 ) {
+ /* LDAPv3 escape */
+ if (( v2 = hex2value( fval[v+1] )) < 0 ) {
+ /* must be two digit code */
+ return -1;
+ }
+
+ fval[r++] = v1 * 16 + v2;
+ v++;
+
+ } else {
+ /* LDAPv2 escape */
+ switch( fval[v] ) {
+ case '(':
+ case ')':
+ case '*':
+ case '\\':
+ fval[r++] = fval[v];
+ break;
+ default:
+ /* illegal escape */
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ fval[r++] = fval[v];
+ }
+ }
+
+ fval[r] = '\0';
+ return r;
+}
+
+static char *
+put_complex_filter( BerElement *ber, char *str, ber_tag_t tag, int not )
+{
+ char *next;
+
+ /*
+ * We have (x(filter)...) with str sitting on
+ * the x. We have to find the paren matching
+ * the one before the x and put the intervening
+ * filters by calling put_filter_list().
+ */
+
+ /* put explicit tag */
+ if ( ber_printf( ber, "t{" /*"}"*/, tag ) == -1 ) {
+ return NULL;
+ }
+
+ str++;
+ if ( (next = find_right_paren( str )) == NULL ) {
+ return NULL;
+ }
+
+ *next = '\0';
+ if ( put_filter_list( ber, str, tag ) == -1 ) {
+ return NULL;
+ }
+
+ /* close the '(' */
+ *next++ = ')';
+
+ /* flush explicit tagged thang */
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ return NULL;
+ }
+
+ return next;
+}
+
+int
+ldap_pvt_put_filter( BerElement *ber, const char *str_in )
+{
+ int rc;
+ char *freeme;
+ char *str;
+ char *next;
+ int parens, balance, escape;
+
+ /*
+ * A Filter looks like this (RFC 4511 as extended by RFC 4526):
+ * Filter ::= CHOICE {
+ * and [0] SET SIZE (0..MAX) OF filter Filter,
+ * or [1] SET SIZE (0..MAX) OF filter Filter,
+ * not [2] Filter,
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeDescription,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] MatchingRuleAssertion,
+ * ... }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeDescription,
+ * substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE {
+ * initial [0] AssertionValue, -- only once
+ * any [1] AssertionValue,
+ * final [2] AssertionValue -- only once
+ * }
+ * }
+ *
+ * MatchingRuleAssertion ::= SEQUENCE {
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE }
+ *
+ * Note: tags in a CHOICE are always explicit
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "put_filter: \"%s\"\n", str_in, 0, 0 );
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
+
+ parens = 0;
+ while ( *str ) {
+ switch ( *str ) {
+ case '(': /*')'*/
+ str++;
+ parens++;
+
+ /* skip spaces */
+ while( LDAP_SPACE( *str ) ) str++;
+
+ switch ( *str ) {
+ case '&':
+ Debug( LDAP_DEBUG_TRACE, "put_filter: AND\n",
+ 0, 0, 0 );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_AND, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '|':
+ Debug( LDAP_DEBUG_TRACE, "put_filter: OR\n",
+ 0, 0, 0 );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_OR, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '!':
+ Debug( LDAP_DEBUG_TRACE, "put_filter: NOT\n",
+ 0, 0, 0 );
+
+ str = put_complex_filter( ber, str,
+ LDAP_FILTER_NOT, 0 );
+ if( str == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ parens--;
+ break;
+
+ case '(':
+ rc = -1;
+ goto done;
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "put_filter: simple\n",
+ 0, 0, 0 );
+
+ balance = 1;
+ escape = 0;
+ next = str;
+
+ while ( *next && balance ) {
+ if ( escape == 0 ) {
+ if ( *next == '(' ) {
+ balance++;
+ } else if ( *next == ')' ) {
+ balance--;
+ }
+ }
+
+ if ( *next == '\\' && ! escape ) {
+ escape = 1;
+ } else {
+ escape = 0;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_simple_filter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next++ = /*'('*/ ')';
+
+ str = next;
+ parens--;
+ break;
+ }
+ break;
+
+ case /*'('*/ ')':
+ Debug( LDAP_DEBUG_TRACE, "put_filter: end\n",
+ 0, 0, 0 );
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str++;
+ parens--;
+ break;
+
+ case ' ':
+ str++;
+ break;
+
+ default: /* assume it's a simple type=value filter */
+ Debug( LDAP_DEBUG_TRACE, "put_filter: default\n",
+ 0, 0, 0 );
+ next = strchr( str, '\0' );
+ if ( put_simple_filter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str = next;
+ break;
+ }
+ if ( !parens )
+ break;
+ }
+
+ rc = ( parens || *str ) ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
+}
+
+/*
+ * Put a list of filters like this "(filter1)(filter2)..."
+ */
+
+static int
+put_filter_list( BerElement *ber, char *str, ber_tag_t tag )
+{
+ char *next = NULL;
+ char save;
+
+ Debug( LDAP_DEBUG_TRACE, "put_filter_list \"%s\"\n",
+ str, 0, 0 );
+
+ while ( *str ) {
+ while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+ str++;
+ }
+ if ( *str == '\0' ) break;
+
+ if ( (next = find_right_paren( str + 1 )) == NULL ) {
+ return -1;
+ }
+ save = *++next;
+
+ /* now we have "(filter)" with str pointing to it */
+ *next = '\0';
+ if ( ldap_pvt_put_filter( ber, str ) == -1 ) return -1;
+ *next = save;
+ str = next;
+
+ if( tag == LDAP_FILTER_NOT ) break;
+ }
+
+ if( tag == LDAP_FILTER_NOT && ( next == NULL || *str )) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+put_simple_filter(
+ BerElement *ber,
+ char *str )
+{
+ char *s;
+ char *value;
+ ber_tag_t ftype;
+ int rc = -1;
+
+ Debug( LDAP_DEBUG_TRACE, "put_simple_filter: \"%s\"\n",
+ str, 0, 0 );
+
+ str = LDAP_STRDUP( str );
+ if( str == NULL ) return -1;
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ goto done;
+ }
+
+ value = s + 1;
+ *s-- = '\0';
+
+ switch ( *s ) {
+ case '<':
+ ftype = LDAP_FILTER_LE;
+ *s = '\0';
+ break;
+
+ case '>':
+ ftype = LDAP_FILTER_GE;
+ *s = '\0';
+ break;
+
+ case '~':
+ ftype = LDAP_FILTER_APPROX;
+ *s = '\0';
+ break;
+
+ case ':':
+ /* RFC 4515 extensible filters are off the form:
+ * type [:dn] [:rule] := value
+ * or [:dn]:rule := value
+ */
+ ftype = LDAP_FILTER_EXT;
+ *s = '\0';
+
+ {
+ char *dn = strchr( str, ':' );
+ char *rule = NULL;
+
+ if( dn != NULL ) {
+ *dn++ = '\0';
+ rule = strchr( dn, ':' );
+
+ if( rule == NULL ) {
+ /* one colon */
+ if ( strcasecmp(dn, "dn") == 0 ) {
+ /* must have attribute */
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ rule = "";
+
+ } else {
+ rule = dn;
+ dn = NULL;
+ }
+
+ } else {
+ /* two colons */
+ *rule++ = '\0';
+
+ if ( strcasecmp(dn, "dn") != 0 ) {
+ /* must have "dn" */
+ goto done;
+ }
+ }
+
+ }
+
+ if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+ /* must have either type or rule */
+ goto done;
+ }
+
+ if ( *str != '\0' && !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+ goto done;
+ }
+
+ rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+ if( rc != -1 && rule && *rule != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+ }
+
+ if( rc != -1 && *str != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+ }
+
+ if( rc != -1 ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
+ } else {
+ rc = -1;
+ }
+ }
+
+ if( rc != -1 && dn ) {
+ rc = ber_printf( ber, "tb",
+ LDAP_FILTER_EXT_DNATTRS, (ber_int_t) 1 );
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+ }
+ goto done;
+
+ default:
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
+ char *nextstar = ldap_pvt_find_wildcard( value );
+
+ if ( nextstar == NULL ) {
+ goto done;
+
+ } else if ( *nextstar == '\0' ) {
+ ftype = LDAP_FILTER_EQUALITY;
+
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ ftype = LDAP_FILTER_PRESENT;
+
+ } else {
+ rc = put_substring_filter( ber, str, value, nextstar );
+ goto done;
+ }
+ } break;
+ }
+
+ if( !ldap_is_desc( str ) ) goto done;
+
+ if ( ftype == LDAP_FILTER_PRESENT ) {
+ rc = ber_printf( ber, "ts", ftype, str );
+
+ } else {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "t{soN}",
+ ftype, str, value, len );
+ }
+ }
+
+done:
+ if( rc != -1 ) rc = 0;
+ LDAP_FREE( str );
+ return rc;
+}
+
+static int
+put_substring_filter( BerElement *ber, char *type, char *val, char *nextstar )
+{
+ int gotstar = 0;
+ ber_tag_t ftype = LDAP_FILTER_SUBSTRINGS;
+
+ Debug( LDAP_DEBUG_TRACE, "put_substring_filter \"%s=%s\"\n",
+ type, val, 0 );
+
+ if ( ber_printf( ber, "t{s{" /*"}}"*/, ftype, type ) == -1 ) {
+ return -1;
+ }
+
+ for( ; *val; val=nextstar ) {
+ if ( gotstar )
+ nextstar = ldap_pvt_find_wildcard( val );
+
+ if ( nextstar == NULL ) {
+ return -1;
+ }
+
+ if ( *nextstar == '\0' ) {
+ ftype = LDAP_SUBSTRING_FINAL;
+ } else {
+ *nextstar++ = '\0';
+ if ( gotstar++ == 0 ) {
+ ftype = LDAP_SUBSTRING_INITIAL;
+ } else {
+ ftype = LDAP_SUBSTRING_ANY;
+ }
+ }
+
+ if ( *val != '\0' || ftype == LDAP_SUBSTRING_ANY ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( val );
+
+ if ( len <= 0 ) {
+ return -1;
+ }
+
+ if ( ber_printf( ber, "to", ftype, val, len ) == -1 ) {
+ return -1;
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*"{{"*/ "N}N}" ) == -1 ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc;
+ char *freeme;
+ char *str;
+ char *next;
+ int parens, balance, escape;
+
+ /*
+ * A ValuesReturnFilter looks like this:
+ *
+ * ValuesReturnFilter ::= SEQUENCE OF SimpleFilterItem
+ * SimpleFilterItem ::= CHOICE {
+ * equalityMatch [3] AttributeValueAssertion,
+ * substrings [4] SubstringFilter,
+ * greaterOrEqual [5] AttributeValueAssertion,
+ * lessOrEqual [6] AttributeValueAssertion,
+ * present [7] AttributeType,
+ * approxMatch [8] AttributeValueAssertion,
+ * extensibleMatch [9] SimpleMatchingAssertion -- LDAPv3
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * SimpleMatchingAssertion ::= SEQUENCE { -- LDAPv3
+ * matchingRule [1] MatchingRuleId OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue }
+ *
+ * (Source: RFC 3876)
+ */
+
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: \"%s\"\n", str_in, 0, 0 );
+
+ freeme = LDAP_STRDUP( str_in );
+ if( freeme == NULL ) return LDAP_NO_MEMORY;
+ str = freeme;
+
+ parens = 0;
+ while ( *str ) {
+ switch ( *str ) {
+ case '(': /*')'*/
+ str++;
+ parens++;
+
+ /* skip spaces */
+ while( LDAP_SPACE( *str ) ) str++;
+
+ switch ( *str ) {
+ case '(':
+ if ( (next = find_right_paren( str )) == NULL ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_vrFilter_list( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ /* close the '(' */
+ *next++ = ')';
+
+ str = next;
+
+ parens--;
+ break;
+
+
+ default:
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: simple\n",
+ 0, 0, 0 );
+
+ balance = 1;
+ escape = 0;
+ next = str;
+
+ while ( *next && balance ) {
+ if ( escape == 0 ) {
+ if ( *next == '(' ) {
+ balance++;
+ } else if ( *next == ')' ) {
+ balance--;
+ }
+ }
+
+ if ( *next == '\\' && ! escape ) {
+ escape = 1;
+ } else {
+ escape = 0;
+ }
+
+ if ( balance ) next++;
+ }
+
+ if ( balance != 0 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next = '\0';
+
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+
+ *next++ = /*'('*/ ')';
+
+ str = next;
+ parens--;
+ break;
+ }
+ break;
+
+ case /*'('*/ ')':
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: end\n",
+ 0, 0, 0 );
+ if ( ber_printf( ber, /*"["*/ "]" ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str++;
+ parens--;
+ break;
+
+ case ' ':
+ str++;
+ break;
+
+ default: /* assume it's a simple type=value filter */
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter: default\n",
+ 0, 0, 0 );
+ next = strchr( str, '\0' );
+ if ( put_simple_vrFilter( ber, str ) == -1 ) {
+ rc = -1;
+ goto done;
+ }
+ str = next;
+ break;
+ }
+ }
+
+ rc = parens ? -1 : 0;
+
+done:
+ LDAP_FREE( freeme );
+ return rc;
+}
+
+int
+ldap_put_vrFilter( BerElement *ber, const char *str_in )
+{
+ int rc =0;
+
+ if ( ber_printf( ber, "{" /*"}"*/ ) == -1 ) {
+ return -1;
+ }
+
+ rc = put_vrFilter( ber, str_in );
+
+ if ( ber_printf( ber, /*"{"*/ "N}" ) == -1 ) {
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static int
+put_vrFilter_list( BerElement *ber, char *str )
+{
+ char *next = NULL;
+ char save;
+
+ Debug( LDAP_DEBUG_TRACE, "put_vrFilter_list \"%s\"\n",
+ str, 0, 0 );
+
+ while ( *str ) {
+ while ( *str && LDAP_SPACE( (unsigned char) *str ) ) {
+ str++;
+ }
+ if ( *str == '\0' ) break;
+
+ if ( (next = find_right_paren( str + 1 )) == NULL ) {
+ return -1;
+ }
+ save = *++next;
+
+ /* now we have "(filter)" with str pointing to it */
+ *next = '\0';
+ if ( put_vrFilter( ber, str ) == -1 ) return -1;
+ *next = save;
+ str = next;
+ }
+
+ return 0;
+}
+
+static int
+put_simple_vrFilter(
+ BerElement *ber,
+ char *str )
+{
+ char *s;
+ char *value;
+ ber_tag_t ftype;
+ int rc = -1;
+
+ Debug( LDAP_DEBUG_TRACE, "put_simple_vrFilter: \"%s\"\n",
+ str, 0, 0 );
+
+ str = LDAP_STRDUP( str );
+ if( str == NULL ) return -1;
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ goto done;
+ }
+
+ value = s + 1;
+ *s-- = '\0';
+
+ switch ( *s ) {
+ case '<':
+ ftype = LDAP_FILTER_LE;
+ *s = '\0';
+ break;
+
+ case '>':
+ ftype = LDAP_FILTER_GE;
+ *s = '\0';
+ break;
+
+ case '~':
+ ftype = LDAP_FILTER_APPROX;
+ *s = '\0';
+ break;
+
+ case ':':
+ /* According to ValuesReturnFilter control definition
+ * extensible filters are off the form:
+ * type [:rule] := value
+ * or :rule := value
+ */
+ ftype = LDAP_FILTER_EXT;
+ *s = '\0';
+
+ {
+ char *rule = strchr( str, ':' );
+
+ if( rule == NULL ) {
+ /* must have attribute */
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+ }
+ rule = "";
+ } else {
+ *rule++ = '\0';
+ }
+
+ if ( *str == '\0' && ( !rule || *rule == '\0' ) ) {
+ /* must have either type or rule */
+ goto done;
+ }
+
+ if ( *str != '\0' && !ldap_is_desc( str ) ) {
+ goto done;
+ }
+
+ if ( rule && *rule != '\0' && !ldap_is_oid( rule ) ) {
+ goto done;
+ }
+
+ rc = ber_printf( ber, "t{" /*"}"*/, ftype );
+
+ if( rc != -1 && rule && *rule != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_OID, rule );
+ }
+
+ if( rc != -1 && *str != '\0' ) {
+ rc = ber_printf( ber, "ts", LDAP_FILTER_EXT_TYPE, str );
+ }
+
+ if( rc != -1 ) {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "to",
+ LDAP_FILTER_EXT_VALUE, value, len );
+ } else {
+ rc = -1;
+ }
+ }
+
+ if( rc != -1 ) {
+ rc = ber_printf( ber, /*"{"*/ "N}" );
+ }
+ }
+ goto done;
+
+ default:
+ if( !ldap_is_desc( str ) ) {
+ goto done;
+
+ } else {
+ char *nextstar = ldap_pvt_find_wildcard( value );
+
+ if ( nextstar == NULL ) {
+ goto done;
+
+ } else if ( *nextstar == '\0' ) {
+ ftype = LDAP_FILTER_EQUALITY;
+
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ ftype = LDAP_FILTER_PRESENT;
+
+ } else {
+ rc = put_substring_filter( ber, str, value, nextstar );
+ goto done;
+ }
+ } break;
+ }
+
+ if( !ldap_is_desc( str ) ) goto done;
+
+ if ( ftype == LDAP_FILTER_PRESENT ) {
+ rc = ber_printf( ber, "ts", ftype, str );
+
+ } else {
+ ber_slen_t len = ldap_pvt_filter_value_unescape( value );
+
+ if( len >= 0 ) {
+ rc = ber_printf( ber, "t{soN}",
+ ftype, str, value, len );
+ }
+ }
+
+done:
+ if( rc != -1 ) rc = 0;
+ LDAP_FREE( str );
+ return rc;
+}
+
diff --git a/libraries/libldap/free.c b/libraries/libldap/free.c
new file mode 100644
index 0000000..6595e29
--- /dev/null
+++ b/libraries/libldap/free.c
@@ -0,0 +1,107 @@
+/* free.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 The Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+/*
+ * free.c - some free routines are included here to avoid having to
+ * link in lots of extra code when not using certain features
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * C-API deallocator
+ */
+void
+ldap_memfree( void *p )
+{
+ LDAP_FREE( p );
+}
+
+void
+ldap_memvfree( void **v )
+{
+ LDAP_VFREE( v );
+}
+
+void *
+ldap_memalloc( ber_len_t s )
+{
+ return LDAP_MALLOC( s );
+}
+
+void *
+ldap_memcalloc( ber_len_t n, ber_len_t s )
+{
+ return LDAP_CALLOC( n, s );
+}
+
+void *
+ldap_memrealloc( void* p, ber_len_t s )
+{
+ return LDAP_REALLOC( p, s );
+}
+
+char *
+ldap_strdup( LDAP_CONST char *p )
+{
+ return LDAP_STRDUP( p );
+}
+
+/*
+ * free a null-terminated array of pointers to mod structures. the
+ * structures are freed, not the array itself, unless the freemods
+ * flag is set.
+ */
+
+void
+ldap_mods_free( LDAPMod **mods, int freemods )
+{
+ int i;
+
+ if ( mods == NULL )
+ return;
+
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if ( mods[i]->mod_op & LDAP_MOD_BVALUES ) {
+ if( mods[i]->mod_bvalues != NULL )
+ ber_bvecfree( mods[i]->mod_bvalues );
+
+ } else if( mods[i]->mod_values != NULL ) {
+ LDAP_VFREE( mods[i]->mod_values );
+ }
+
+ if ( mods[i]->mod_type != NULL ) {
+ LDAP_FREE( mods[i]->mod_type );
+ }
+
+ LDAP_FREE( (char *) mods[i] );
+ }
+
+ if ( freemods ) {
+ LDAP_FREE( (char *) mods );
+ }
+}
diff --git a/libraries/libldap/ftest.c b/libraries/libldap/ftest.c
new file mode 100644
index 0000000..c0952b5
--- /dev/null
+++ b/libraries/libldap/ftest.c
@@ -0,0 +1,119 @@
+/* ftest.c -- OpenLDAP Filter API Test */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <stdio.h>
+
+#include <ldap.h>
+
+#include "ldap_pvt.h"
+#include "lber_pvt.h"
+
+#include "ldif.h"
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+static int filter2ber( char *filter );
+
+int usage()
+{
+ fprintf( stderr, "usage:\n"
+ " ftest [-d n] filter\n"
+ " filter - RFC 4515 string representation of an "
+ "LDAP search filter\n" );
+ return EXIT_FAILURE;
+}
+
+int
+main( int argc, char *argv[] )
+{
+ int c;
+ int debug=0;
+
+ while( (c = getopt( argc, argv, "d:" )) != EOF ) {
+ switch ( c ) {
+ case 'd':
+ debug = atoi( optarg );
+ break;
+ default:
+ fprintf( stderr, "ftest: unrecognized option -%c\n",
+ optopt );
+ return usage();
+ }
+ }
+
+ if ( debug ) {
+ if ( ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &debug )
+ != LBER_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LBER_OPT_DEBUG_LEVEL %d\n",
+ debug );
+ }
+ if ( ldap_set_option( NULL, LDAP_OPT_DEBUG_LEVEL, &debug )
+ != LDAP_OPT_SUCCESS )
+ {
+ fprintf( stderr, "Could not set LDAP_OPT_DEBUG_LEVEL %d\n",
+ debug );
+ }
+ }
+
+ if ( argc - optind != 1 ) {
+ return usage();
+ }
+
+ return filter2ber( strdup( argv[optind] ) );
+}
+
+static int filter2ber( char *filter )
+{
+ int rc;
+ struct berval bv = BER_BVNULL;
+ BerElement *ber;
+
+ printf( "Filter: %s\n", filter );
+
+ ber = ber_alloc_t( LBER_USE_DER );
+ if( ber == NULL ) {
+ perror( "ber_alloc_t" );
+ return EXIT_FAILURE;
+ }
+
+ rc = ldap_pvt_put_filter( ber, filter );
+ if( rc < 0 ) {
+ fprintf( stderr, "Filter error!\n");
+ return EXIT_FAILURE;
+ }
+
+ rc = ber_flatten2( ber, &bv, 0 );
+ if( rc < 0 ) {
+ perror( "ber_flatten2" );
+ return EXIT_FAILURE;
+ }
+
+ printf( "BER encoding (len=%ld):\n", (long) bv.bv_len );
+ ber_bprint( bv.bv_val, bv.bv_len );
+
+ ber_free( ber, 1 );
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/libraries/libldap/getattr.c b/libraries/libldap/getattr.c
new file mode 100644
index 0000000..0ee21f7
--- /dev/null
+++ b/libraries/libldap/getattr.c
@@ -0,0 +1,157 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+char *
+ldap_first_attribute( LDAP *ld, LDAPMessage *entry, BerElement **berout )
+{
+ int rc;
+ ber_tag_t tag;
+ ber_len_t len = 0;
+ char *attr;
+ BerElement *ber;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_first_attribute\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( berout != NULL );
+
+ *berout = NULL;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if( ber == NULL ) {
+ return NULL;
+ }
+
+ *ber = *entry->lm_ber;
+
+ /*
+ * Skip past the sequence, dn, sequence of sequence leaving
+ * us at the first attribute.
+ */
+
+ tag = ber_scanf( ber, "{xl{" /*}}*/, &len );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ /* set the length to avoid overrun */
+ rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
+ if( rc != LBER_OPT_SUCCESS ) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ if ( ber_pvt_ber_remaining( ber ) == 0 ) {
+ assert( len == 0 );
+ ber_free( ber, 0 );
+ return NULL;
+ }
+ assert( len != 0 );
+
+ /* snatch the first attribute */
+ tag = ber_scanf( ber, "{ax}", &attr );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return NULL;
+ }
+
+ *berout = ber;
+ return attr;
+}
+
+/* ARGSUSED */
+char *
+ldap_next_attribute( LDAP *ld, LDAPMessage *entry, BerElement *ber )
+{
+ ber_tag_t tag;
+ char *attr;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_next_attribute\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( ber != NULL );
+
+ if ( ber_pvt_ber_remaining( ber ) == 0 ) {
+ return NULL;
+ }
+
+ /* skip sequence, snarf attribute type, skip values */
+ tag = ber_scanf( ber, "{ax}", &attr );
+ if( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return NULL;
+ }
+
+ return attr;
+}
+
+/* Fetch attribute type and optionally fetch values. The type
+ * and values are referenced in-place from the BerElement, they are
+ * not dup'd into malloc'd memory.
+ */
+/* ARGSUSED */
+int
+ldap_get_attribute_ber( LDAP *ld, LDAPMessage *entry, BerElement *ber,
+ BerValue *attr, BerVarray *vals )
+{
+ ber_tag_t tag;
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_get_attribute_ber\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( ber != NULL );
+ assert( attr != NULL );
+
+ attr->bv_val = NULL;
+ attr->bv_len = 0;
+
+ if ( ber_pvt_ber_remaining( ber ) ) {
+ ber_len_t siz = sizeof( BerValue );
+
+ /* skip sequence, snarf attribute type */
+ tag = ber_scanf( ber, vals ? "{mM}" : "{mx}", attr, vals,
+ &siz, (ber_len_t)0 );
+ if( tag == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/getdn.c b/libraries/libldap/getdn.c
new file mode 100644
index 0000000..5d43e69
--- /dev/null
+++ b/libraries/libldap/getdn.c
@@ -0,0 +1,3305 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_schema.h"
+#include "ldif.h"
+
+/* extension to UFN that turns trailing "dc=value" rdns in DNS style,
+ * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
+#define DC_IN_UFN
+
+/* parsing/printing routines */
+static int str2strval( const char *str, ber_len_t stoplen, struct berval *val,
+ const char **next, unsigned flags, int *retFlags, void *ctx );
+static int DCE2strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int IA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int quotedIA52strval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int hexstr2binval( const char *str, struct berval *val,
+ const char **next, unsigned flags, void *ctx );
+static int hexstr2bin( const char *str, char *c );
+static int byte2hexpair( const char *val, char *pair );
+static int binval2hexstr( struct berval *val, char *str );
+static int strval2strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5strlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2IA5str( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2DCEstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstrlen( struct berval *val, unsigned flags,
+ ber_len_t *len );
+static int strval2ADstr( struct berval *val, char *str, unsigned flags,
+ ber_len_t *len );
+static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN );
+
+/* AVA helpers */
+static LDAPAVA * ldapava_new(
+ const struct berval *attr, const struct berval *val, unsigned flags, void *ctx );
+
+/* Higher level helpers */
+static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
+static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
+static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len );
+static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first );
+static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
+static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first );
+
+/*
+ * RFC 1823 ldap_get_dn
+ */
+char *
+ldap_get_dn( LDAP *ld, LDAPMessage *entry )
+{
+ char *dn;
+ BerElement tmp;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID(ld) );
+ assert( entry != NULL );
+
+ tmp = *entry->lm_ber; /* struct copy */
+ if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( dn );
+}
+
+int
+ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout,
+ BerValue *dn )
+{
+ BerElement tmp, *ber;
+ ber_len_t len = 0;
+ int rc = LDAP_SUCCESS;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID(ld) );
+ assert( entry != NULL );
+ assert( dn != NULL );
+
+ dn->bv_val = NULL;
+ dn->bv_len = 0;
+
+ if ( berout ) {
+ *berout = NULL;
+ ber = ldap_alloc_ber_with_options( ld );
+ if( ber == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ *berout = ber;
+ } else {
+ ber = &tmp;
+ }
+
+ *ber = *entry->lm_ber; /* struct copy */
+ if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ if ( rc == LDAP_SUCCESS ) {
+ /* set the length to avoid overrun */
+ rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
+ if( rc != LBER_OPT_SUCCESS ) {
+ rc = ld->ld_errno = LDAP_LOCAL_ERROR;
+ }
+ }
+ if ( rc != LDAP_SUCCESS && berout ) {
+ ber_free( ber, 0 );
+ *berout = NULL;
+ }
+ return rc;
+}
+
+/*
+ * RFC 1823 ldap_dn2ufn
+ */
+char *
+ldap_dn2ufn( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_UFN );
+
+ return( out );
+}
+
+/*
+ * RFC 1823 ldap_explode_dn
+ */
+char **
+ldap_explode_dn( LDAP_CONST char *dn, int notypes )
+{
+ LDAPDN tmpDN;
+ char **values = NULL;
+ int iRDN;
+ unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+
+ if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
+ return NULL;
+ }
+
+ if( tmpDN == NULL ) {
+ values = LDAP_MALLOC( sizeof( char * ) );
+ if( values == NULL ) return NULL;
+
+ values[0] = NULL;
+ return values;
+ }
+
+ for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ );
+
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
+ if ( values == NULL ) {
+ ldap_dnfree( tmpDN );
+ return NULL;
+ }
+
+ for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) {
+ ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag );
+ }
+ ldap_dnfree( tmpDN );
+ values[ iRDN ] = NULL;
+
+ return values;
+}
+
+char **
+ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
+{
+ LDAPRDN tmpRDN;
+ char **values = NULL;
+ const char *p;
+ int iAVA;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
+
+ /*
+ * we only parse the first rdn
+ * FIXME: we prefer efficiency over checking if the _ENTIRE_
+ * dn can be parsed
+ */
+ if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP )
+ != LDAP_SUCCESS ) {
+ return( NULL );
+ }
+
+ for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ;
+ values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
+ if ( values == NULL ) {
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
+ }
+
+ for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) {
+ ber_len_t l = 0, vl, al = 0;
+ char *str;
+ LDAPAVA *ava = tmpRDN[ iAVA ];
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ vl = 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ if ( strval2strlen( &ava->la_value,
+ ava->la_flags, &vl ) ) {
+ goto error_return;
+ }
+ }
+
+ if ( !notypes ) {
+ al = ava->la_attr.bv_len;
+ l = vl + ava->la_attr.bv_len + 1;
+
+ str = LDAP_MALLOC( l + 1 );
+ AC_MEMCPY( str, ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ str[ al++ ] = '=';
+
+ } else {
+ l = vl;
+ str = LDAP_MALLOC( l + 1 );
+ }
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ al++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
+ goto error_return;
+ }
+
+ } else {
+ if ( strval2str( &ava->la_value, &str[ al ],
+ ava->la_flags, &vl ) ) {
+ goto error_return;
+ }
+ }
+
+ str[ l ] = '\0';
+ values[ iAVA ] = str;
+ }
+ values[ iAVA ] = NULL;
+
+ ldap_rdnfree( tmpRDN );
+
+ return( values );
+
+error_return:;
+ LBER_VFREE( values );
+ ldap_rdnfree( tmpRDN );
+ return( NULL );
+}
+
+char *
+ldap_dn2dcedn( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_DCE );
+
+ return( out );
+}
+
+char *
+ldap_dcedn2dn( LDAP_CONST char *dce )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
+
+ return( out );
+}
+
+char *
+ldap_dn2ad_canonical( LDAP_CONST char *dn )
+{
+ char *out = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
+
+ ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP,
+ &out, LDAP_DN_FORMAT_AD_CANONICAL );
+
+ return( out );
+}
+
+/*
+ * function that changes the string representation of dnin
+ * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
+ *
+ * fin can be one of:
+ * LDAP_DN_FORMAT_LDAP (RFC 4514 liberal, plus some RFC 1779)
+ * LDAP_DN_FORMAT_LDAPV3 (RFC 4514)
+ * LDAP_DN_FORMAT_LDAPV2 (RFC 1779)
+ * LDAP_DN_FORMAT_DCE (?)
+ *
+ * fout can be any of the above except
+ * LDAP_DN_FORMAT_LDAP
+ * plus:
+ * LDAP_DN_FORMAT_UFN (RFC 1781, partial and with extensions)
+ * LDAP_DN_FORMAT_AD_CANONICAL (?)
+ */
+int
+ldap_dn_normalize( LDAP_CONST char *dnin,
+ unsigned fin, char **dnout, unsigned fout )
+{
+ int rc;
+ LDAPDN tmpDN = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 );
+
+ assert( dnout != NULL );
+
+ *dnout = NULL;
+
+ if ( dnin == NULL ) {
+ return( LDAP_SUCCESS );
+ }
+
+ rc = ldap_str2dn( dnin , &tmpDN, fin );
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ rc = ldap_dn2str( tmpDN, dnout, fout );
+
+ ldap_dnfree( tmpDN );
+
+ return( rc );
+}
+
+/* States */
+#define B4AVA 0x0000
+
+/* #define B4ATTRTYPE 0x0001 */
+#define B4OIDATTRTYPE 0x0002
+#define B4STRINGATTRTYPE 0x0003
+
+#define B4AVAEQUALS 0x0100
+#define B4AVASEP 0x0200
+#define B4RDNSEP 0x0300
+#define GOTAVA 0x0400
+
+#define B4ATTRVALUE 0x0010
+#define B4STRINGVALUE 0x0020
+#define B4IA5VALUEQUOTED 0x0030
+#define B4IA5VALUE 0x0040
+#define B4BINARYVALUE 0x0050
+
+/*
+ * Helpers (mostly from slap.h)
+ * c is assumed to Unicode in an ASCII compatible format (UTF-8)
+ * Macros assume "C" Locale (ASCII)
+ */
+#define LDAP_DN_ASCII_SPACE(c) \
+ ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
+#define LDAP_DN_ASCII_LOWER(c) LDAP_LOWER(c)
+#define LDAP_DN_ASCII_UPPER(c) LDAP_UPPER(c)
+#define LDAP_DN_ASCII_ALPHA(c) LDAP_ALPHA(c)
+
+#define LDAP_DN_ASCII_DIGIT(c) LDAP_DIGIT(c)
+#define LDAP_DN_ASCII_LCASE_HEXALPHA(c) LDAP_HEXLOWER(c)
+#define LDAP_DN_ASCII_UCASE_HEXALPHA(c) LDAP_HEXUPPER(c)
+#define LDAP_DN_ASCII_HEXDIGIT(c) LDAP_HEX(c)
+#define LDAP_DN_ASCII_ALNUM(c) LDAP_ALNUM(c)
+#define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' )
+
+/* attribute type */
+#define LDAP_DN_OID_LEADCHAR(c) LDAP_DIGIT(c)
+#define LDAP_DN_DESC_LEADCHAR(c) LDAP_ALPHA(c)
+#define LDAP_DN_DESC_CHAR(c) LDAP_LDH(c)
+#define LDAP_DN_LANG_SEP(c) ( (c) == ';' )
+#define LDAP_DN_ATTRDESC_CHAR(c) \
+ ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
+
+/* special symbols */
+#define LDAP_DN_AVA_EQUALS(c) ( (c) == '=' )
+#define LDAP_DN_AVA_SEP(c) ( (c) == '+' )
+#define LDAP_DN_RDN_SEP(c) ( (c) == ',' )
+#define LDAP_DN_RDN_SEP_V2(c) ( LDAP_DN_RDN_SEP(c) || (c) == ';' )
+#define LDAP_DN_OCTOTHORPE(c) ( (c) == '#' )
+#define LDAP_DN_QUOTES(c) ( (c) == '\"' )
+#define LDAP_DN_ESCAPE(c) ( (c) == '\\' )
+#define LDAP_DN_VALUE_END(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
+
+/* NOTE: according to RFC 4514, '=' can be escaped and treated as special,
+ * i.e. escaped both as "\<hexpair>" and * as "\=", but it is treated as
+ * a regular char, i.e. it can also appear as '='.
+ *
+ * As such, in 2.2 we used to allow reading unescaped '=', but we always
+ * produced escaped '\3D'; this changes since 2.3, if compatibility issues
+ * do not arise
+ */
+#define LDAP_DN_NE(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
+ || LDAP_DN_QUOTES(c) \
+ || (c) == '<' || (c) == '>' )
+#define LDAP_DN_MAYESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
+ || LDAP_DN_AVA_EQUALS(c) \
+ || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_SHOULDESCAPE(c) ( LDAP_DN_AVA_EQUALS(c) )
+
+#define LDAP_DN_NEEDESCAPE(c) \
+ ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
+#define LDAP_DN_NEEDESCAPE_LEAD(c) LDAP_DN_MAYESCAPE(c)
+#define LDAP_DN_NEEDESCAPE_TRAIL(c) \
+ ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
+#define LDAP_DN_WILLESCAPE_CHAR(c) \
+ ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_IS_PRETTY(f) ( (f) & LDAP_DN_PRETTY )
+#define LDAP_DN_WILLESCAPE_HEX(f, c) \
+ ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
+
+/* LDAPv2 */
+#define LDAP_DN_VALUE_END_V2(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
+/* RFC 1779 */
+#define LDAP_DN_V2_SPECIAL(c) \
+ ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
+ || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
+ || LDAP_DN_OCTOTHORPE(c) )
+#define LDAP_DN_V2_PAIR(c) \
+ ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
+
+/*
+ * DCE (mostly from Luke Howard and IBM implementation for AIX)
+ *
+ * From: "Application Development Guide - Directory Services" (FIXME: add link?)
+ * Here escapes and valid chars for GDS are considered; as soon as more
+ * specific info is found, the macros will be updated.
+ *
+ * Chars: 'a'-'z', 'A'-'Z', '0'-'9',
+ * '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
+ *
+ * Metachars: '/', ',', '=', '\'.
+ *
+ * the '\' is used to escape other metachars.
+ *
+ * Assertion: '='
+ * RDN separator: '/'
+ * AVA separator: ','
+ *
+ * Attribute types must start with alphabetic chars and can contain
+ * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
+ */
+#define LDAP_DN_RDN_SEP_DCE(c) ( (c) == '/' )
+#define LDAP_DN_AVA_SEP_DCE(c) ( (c) == ',' )
+#define LDAP_DN_ESCAPE_DCE(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_VALUE_END_DCE(c) \
+ ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
+#define LDAP_DN_NEEDESCAPE_DCE(c) \
+ ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* AD Canonical */
+#define LDAP_DN_RDN_SEP_AD(c) ( (c) == '/' )
+#define LDAP_DN_ESCAPE_AD(c) ( LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_AVA_SEP_AD(c) ( (c) == ',' ) /* assume same as DCE */
+#define LDAP_DN_VALUE_END_AD(c) \
+ ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
+#define LDAP_DN_NEEDESCAPE_AD(c) \
+ ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
+
+/* generics */
+#define LDAP_DN_HEXPAIR(s) \
+ ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
+/* better look at the AttributeDescription? */
+
+/* FIXME: no composite rdn or non-"dc" types, right?
+ * (what about "dc" in OID form?) */
+/* FIXME: we do not allow binary values in domain, right? */
+/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
+/* NOTE: don't use strcasecmp() as it is locale specific! */
+#define LDAP_DC_ATTR "dc"
+#define LDAP_DC_ATTRU "DC"
+#define LDAP_DN_IS_RDN_DC( r ) \
+ ( (r) && (r)[0] && !(r)[1] \
+ && ((r)[0]->la_flags & LDAP_AVA_STRING) \
+ && ((r)[0]->la_attr.bv_len == 2) \
+ && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
+ || ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
+ && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
+ || ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
+
+/* Composite rules */
+#define LDAP_DN_ALLOW_ONE_SPACE(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
+#define LDAP_DN_ALLOW_SPACES(f) \
+ ( LDAP_DN_LDAPV2(f) \
+ || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
+#define LDAP_DN_LDAP(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
+#define LDAP_DN_LDAPV3(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
+#define LDAP_DN_LDAPV2(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
+#define LDAP_DN_DCE(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
+#define LDAP_DN_UFN(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
+#define LDAP_DN_ADC(f) \
+ ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
+#define LDAP_DN_FORMAT(f) ( (f) & LDAP_DN_FORMAT_MASK )
+
+/*
+ * LDAPAVA helpers (will become part of the API for operations
+ * on structural representations of DNs).
+ */
+static LDAPAVA *
+ldapava_new( const struct berval *attr, const struct berval *val,
+ unsigned flags, void *ctx )
+{
+ LDAPAVA *ava;
+
+ assert( attr != NULL );
+ assert( val != NULL );
+
+ ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx );
+
+ if ( ava ) {
+ ava->la_attr.bv_len = attr->bv_len;
+ ava->la_attr.bv_val = (char *)(ava+1);
+ AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
+ ava->la_attr.bv_val[attr->bv_len] = '\0';
+
+ ava->la_value = *val;
+ ava->la_flags = flags | LDAP_AVA_FREE_VALUE;
+
+ ava->la_private = NULL;
+ }
+
+ return( ava );
+}
+
+static void
+ldapava_free( LDAPAVA *ava, void *ctx )
+{
+ assert( ava != NULL );
+
+#if 0
+ /* ava's private must be freed by caller
+ * (at present let's skip this check because la_private
+ * basically holds static data) */
+ assert( ava->la_private == NULL );
+#endif
+
+ if (ava->la_flags & LDAP_AVA_FREE_VALUE)
+ LDAP_FREEX( ava->la_value.bv_val, ctx );
+
+ LDAP_FREEX( ava, ctx );
+}
+
+void
+ldap_rdnfree( LDAPRDN rdn )
+{
+ ldap_rdnfree_x( rdn, NULL );
+}
+
+void
+ldap_rdnfree_x( LDAPRDN rdn, void *ctx )
+{
+ int iAVA;
+
+ if ( rdn == NULL ) {
+ return;
+ }
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ ldapava_free( rdn[ iAVA ], ctx );
+ }
+
+ LDAP_FREEX( rdn, ctx );
+}
+
+void
+ldap_dnfree( LDAPDN dn )
+{
+ ldap_dnfree_x( dn, NULL );
+}
+
+void
+ldap_dnfree_x( LDAPDN dn, void *ctx )
+{
+ int iRDN;
+
+ if ( dn == NULL ) {
+ return;
+ }
+
+ for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ldap_rdnfree_x( dn[ iRDN ], ctx );
+ }
+
+ LDAP_FREEX( dn, ctx );
+}
+
+/*
+ * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
+ * into a structural representation of the DN, by separating attribute
+ * types and values encoded in the more appropriate form, which is
+ * string or OID for attribute types and binary form of the BER encoded
+ * value or Unicode string. Formats different from LDAPv3 are parsed
+ * according to their own rules and turned into the more appropriate
+ * form according to LDAPv3.
+ *
+ * NOTE: I realize the code is getting spaghettish; it is rather
+ * experimental and will hopefully turn into something more simple
+ * and readable as soon as it works as expected.
+ */
+
+/*
+ * Default sizes of AVA and RDN static working arrays; if required
+ * the are dynamically resized. The values can be tuned in case
+ * of special requirements (e.g. very deep DN trees or high number
+ * of AVAs per RDN).
+ */
+#define TMP_AVA_SLOTS 8
+#define TMP_RDN_SLOTS 32
+
+int
+ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags )
+{
+ struct berval bv;
+
+ assert( str != NULL );
+
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
+
+ return ldap_bv2dn_x( &bv, dn, flags, NULL );
+}
+
+int
+ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags )
+{
+ return ldap_bv2dn_x( bv, dn, flags, NULL );
+}
+
+int
+ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx )
+{
+ const char *p;
+ int rc = LDAP_DECODING_ERROR;
+ int nrdns = 0;
+
+ LDAPDN newDN = NULL;
+ LDAPRDN newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_;
+ int num_slots = TMP_RDN_SLOTS;
+ char *str, *end;
+ struct berval bvtmp, *bv = &bvtmp;
+
+ assert( bvin != NULL );
+ assert( bvin->bv_val != NULL );
+ assert( dn != NULL );
+
+ *bv = *bvin;
+ str = bv->bv_val;
+ end = str + bv->bv_len;
+
+ Debug( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags, 0 );
+
+ *dn = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* allow DN enclosed in brackets */
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( str[0] == '<' ) {
+ if ( bv->bv_len < 2 || end[ -1 ] != '>' ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ bv->bv_val++;
+ bv->bv_len -= 2;
+ str++;
+ end--;
+ }
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ if ( LDAP_DN_DCE( flags ) ) {
+
+ /*
+ * (from Luke Howard: thnx) A RDN separator is required
+ * at the beginning of an (absolute) DN.
+ */
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /*
+ * actually we do not want to accept by default the DCE form,
+ * we do not want to auto-detect it
+ */
+#if 0
+ } else if ( LDAP_DN_LDAP( flags ) ) {
+ /*
+ * if dn starts with '/' let's make it a DCE dn
+ */
+ if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ flags |= LDAP_DN_FORMAT_DCE;
+ p++;
+ }
+#endif
+ }
+
+ for ( ; p < end; p++ ) {
+ int err;
+ struct berval tmpbv;
+ tmpbv.bv_len = bv->bv_len - ( p - str );
+ tmpbv.bv_val = (char *)p;
+
+ err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx);
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+
+ /*
+ * We expect a rdn separator
+ */
+ if ( p < end && p[ 0 ] ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+ rc = LDAP_DECODING_ERROR;
+ goto parsing_error;
+ }
+ break;
+ }
+ }
+
+
+ tmpDN[nrdns++] = newRDN;
+ newRDN = NULL;
+
+ /*
+ * make the static RDN array dynamically rescalable
+ */
+ if ( nrdns == num_slots ) {
+ LDAPRDN *tmp;
+
+ if ( tmpDN == tmpDN_ ) {
+ tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
+
+ } else {
+ tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpDN = tmp;
+ num_slots *= 2;
+ }
+
+ if ( p >= end || p[ 0 ] == '\0' ) {
+ /*
+ * the DN is over, phew
+ */
+ newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx );
+ if ( newDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ int i;
+
+ if ( LDAP_DN_DCE( flags ) ) {
+ /* add in reversed order */
+ for ( i=0; i<nrdns; i++ )
+ newDN[i] = tmpDN[nrdns-1-i];
+ } else {
+ for ( i=0; i<nrdns; i++ )
+ newDN[i] = tmpDN[i];
+ }
+ newDN[nrdns] = NULL;
+ rc = LDAP_SUCCESS;
+ }
+ goto return_result;
+ }
+ }
+
+parsing_error:;
+ if ( newRDN ) {
+ ldap_rdnfree_x( newRDN, ctx );
+ }
+
+ for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
+ ldap_rdnfree_x( tmpDN[nrdns], ctx );
+ }
+
+return_result:;
+
+ if ( tmpDN != tmpDN_ ) {
+ LDAP_FREEX( tmpDN, ctx );
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc,
+ rc ? ldap_err2string( rc ) : "" );
+ *dn = newDN;
+
+ return( rc );
+}
+
+/*
+ * ldap_str2rdn
+ *
+ * Parses a relative DN according to flags up to a rdn separator
+ * or to the end of str.
+ * Returns the rdn and a pointer to the string continuation, which
+ * corresponds to the rdn separator or to '\0' in case the string is over.
+ */
+int
+ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn,
+ char **n_in, unsigned flags )
+{
+ struct berval bv;
+
+ assert( str != NULL );
+ assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */
+
+ bv.bv_len = strlen( str );
+ bv.bv_val = (char *) str;
+
+ return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL );
+}
+
+int
+ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn,
+ char **n_in, unsigned flags )
+{
+ return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL );
+}
+
+int
+ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn,
+ char **n_in, unsigned flags, void *ctx )
+{
+ const char **n = (const char **) n_in;
+ const char *p;
+ int navas = 0;
+ int state = B4AVA;
+ int rc = LDAP_DECODING_ERROR;
+ int attrTypeEncoding = LDAP_AVA_STRING,
+ attrValueEncoding = LDAP_AVA_STRING;
+
+ struct berval attrType = BER_BVNULL;
+ struct berval attrValue = BER_BVNULL;
+
+ LDAPRDN newRDN = NULL;
+ LDAPAVA *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
+ int num_slots = TMP_AVA_SLOTS;
+
+ char *str;
+ ber_len_t stoplen;
+
+ assert( bv != NULL );
+ assert( bv->bv_len != 0 );
+ assert( bv->bv_val != NULL );
+ assert( rdn || flags & LDAP_DN_SKIP );
+ assert( n != NULL );
+
+ str = bv->bv_val;
+ stoplen = bv->bv_len;
+
+ if ( rdn ) {
+ *rdn = NULL;
+ }
+ *n = NULL;
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ case LDAP_DN_FORMAT_DCE:
+ break;
+
+ /* unsupported in str2dn */
+ case LDAP_DN_FORMAT_UFN:
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ return LDAP_PARAM_ERROR;
+
+ case LDAP_DN_FORMAT_LBER:
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( bv->bv_len == 0 ) {
+ return LDAP_SUCCESS;
+
+ }
+
+ if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+ /* value must have embedded NULs */
+ return LDAP_DECODING_ERROR;
+ }
+
+ p = str;
+ for ( ; p[ 0 ] || state == GOTAVA; ) {
+
+ /*
+ * The parser in principle advances one token a time,
+ * or toggles state if preferable.
+ */
+ switch (state) {
+
+ /*
+ * an AttributeType can be encoded as:
+ * - its string representation; in detail, implementations
+ * MUST recognize AttributeType string type names listed
+ * in Section 3 of RFC 4514, and MAY recognize other names.
+ * - its numeric OID (a dotted decimal string)
+ */
+ case B4AVA:
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+ p++;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ /* error */
+ goto parsing_error;
+ }
+
+ /* whitespace is allowed (and trimmed) */
+ p++;
+ while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ p++;
+ }
+
+ if ( !p[ 0 ] ) {
+ /* error: we expected an AVA */
+ goto parsing_error;
+ }
+ }
+
+ /* oid */
+ if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
+ state = B4OIDATTRTYPE;
+ break;
+ }
+
+ /* else must be alpha */
+ if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+
+ /* LDAPv2 "oid." prefix */
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ /*
+ * to be overly pedantic, we only accept
+ * "OID." or "oid."
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ if ( !strncmp( p, "OID.", 4 )
+ || !strncmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ } else {
+ if ( !strncasecmp( p, "oid.", 4 ) ) {
+ p += 4;
+ state = B4OIDATTRTYPE;
+ break;
+ }
+ }
+ }
+
+ state = B4STRINGATTRTYPE;
+ break;
+
+ case B4OIDATTRTYPE: {
+ int err = LDAP_SUCCESS;
+
+ attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
+ LDAP_SCHEMA_SKIP);
+
+ if ( err != LDAP_SUCCESS ) {
+ goto parsing_error;
+ }
+ attrType.bv_len = p - attrType.bv_val;
+
+ attrTypeEncoding = LDAP_AVA_BINARY;
+
+ state = B4AVAEQUALS;
+ break;
+ }
+
+ case B4STRINGATTRTYPE: {
+ const char *startPos, *endPos = NULL;
+ ber_len_t len;
+
+ /*
+ * the starting char has been found to be
+ * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
+ * FIXME: DCE attr types seem to have a more
+ * restrictive syntax (no '-' ...)
+ */
+ for ( startPos = p++; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
+ continue;
+ }
+
+ if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
+
+ /*
+ * RFC 4514 explicitly does not allow attribute
+ * description options, such as language tags.
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ goto parsing_error;
+ }
+
+ /*
+ * we trim ';' and following lang
+ * and so from attribute types
+ */
+ endPos = p;
+ for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
+ || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
+ /* no op */ ;
+ }
+ break;
+ }
+ break;
+ }
+
+ len = ( endPos ? endPos : p ) - startPos;
+ if ( len == 0 ) {
+ goto parsing_error;
+ }
+
+ attrTypeEncoding = LDAP_AVA_STRING;
+
+ /*
+ * here we need to decide whether to use it as is
+ * or turn it in OID form; as a consequence, we
+ * need to decide whether to binary encode the value
+ */
+
+ state = B4AVAEQUALS;
+
+ if ( flags & LDAP_DN_SKIP ) {
+ break;
+ }
+
+ attrType.bv_val = (char *)startPos;
+ attrType.bv_len = len;
+
+ break;
+ }
+
+ case B4AVAEQUALS:
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /* need equal sign */
+ if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
+ goto parsing_error;
+ }
+ p++;
+
+ /* spaces may not be allowed */
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+ goto parsing_error;
+ }
+
+ /* trim spaces */
+ for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+ }
+
+ /*
+ * octothorpe means a BER encoded value will follow
+ * FIXME: I don't think DCE will allow it
+ */
+ if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
+ p++;
+ attrValueEncoding = LDAP_AVA_BINARY;
+ state = B4BINARYVALUE;
+ break;
+ }
+
+ /* STRING value expected */
+
+ /*
+ * if we're pedantic, an attribute type in OID form
+ * SHOULD imply a BER encoded attribute value; we
+ * should at least issue a warning
+ */
+ if ( ( flags & LDAP_DN_PEDANTIC )
+ && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
+ /* OID attrType SHOULD use binary encoding */
+ goto parsing_error;
+ }
+
+ attrValueEncoding = LDAP_AVA_STRING;
+
+ /*
+ * LDAPv2 allows the attribute value to be quoted;
+ * also, IA5 values are expected, in principle
+ */
+ if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
+ if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ p++;
+ state = B4IA5VALUEQUOTED;
+ break;
+ }
+
+ if ( LDAP_DN_LDAPV2( flags ) ) {
+ state = B4IA5VALUE;
+ break;
+ }
+ }
+
+ /*
+ * here STRING means RFC 4514 string
+ * FIXME: what about DCE strings?
+ */
+ if ( !p[ 0 ] ) {
+ /* empty value */
+ state = GOTAVA;
+ } else {
+ state = B4STRINGVALUE;
+ }
+ break;
+
+ case B4BINARYVALUE:
+ if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4STRINGVALUE:
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( str2strval( p, stoplen - ( p - str ),
+ &attrValue, &p, flags,
+ &attrValueEncoding, ctx ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+ break;
+
+ default:
+ assert( 0 );
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUE:
+ if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case B4IA5VALUEQUOTED:
+
+ /* lead quote already stripped */
+ if ( quotedIA52strval( p, &attrValue,
+ &p, flags, ctx ) ) {
+ goto parsing_error;
+ }
+
+ state = GOTAVA;
+ break;
+
+ case GOTAVA: {
+ int rdnsep = 0;
+
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ LDAPAVA *ava;
+
+ /*
+ * we accept empty values
+ */
+ ava = ldapava_new( &attrType, &attrValue,
+ attrValueEncoding, ctx );
+ if ( ava == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ tmpRDN[navas++] = ava;
+
+ attrValue.bv_val = NULL;
+ attrValue.bv_len = 0;
+
+ /*
+ * prepare room for new AVAs if needed
+ */
+ if (navas == num_slots) {
+ LDAPAVA **tmp;
+
+ if ( tmpRDN == tmpRDN_ ) {
+ tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
+
+ } else {
+ tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx );
+ if ( tmp == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ }
+ }
+
+ tmpRDN = tmp;
+ num_slots *= 2;
+ }
+ }
+
+ /*
+ * if we got an AVA separator ('+', or ',' for DCE )
+ * we expect a new AVA for this RDN; otherwise
+ * we add the RDN to the DN
+ */
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV3:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
+ rdnsep = 1;
+ }
+ break;
+ }
+
+ if ( rdnsep ) {
+ /*
+ * the RDN is over, phew
+ */
+ *n = p;
+ if ( !( flags & LDAP_DN_SKIP ) ) {
+ newRDN = (LDAPRDN)LDAP_MALLOCX(
+ sizeof(LDAPAVA) * (navas+1), ctx );
+ if ( newRDN == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto parsing_error;
+ } else {
+ AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas);
+ newRDN[navas] = NULL;
+ }
+
+ }
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+
+ /* they should have been used in an AVA */
+ attrType.bv_val = NULL;
+ attrValue.bv_val = NULL;
+
+ p++;
+ state = B4AVA;
+ break;
+ }
+
+ default:
+ assert( 0 );
+ goto parsing_error;
+ }
+ }
+ *n = p;
+
+parsing_error:;
+ /* They are set to NULL after they're used in an AVA */
+
+ if ( attrValue.bv_val ) {
+ LDAP_FREEX( attrValue.bv_val, ctx );
+ }
+
+ for ( navas-- ; navas >= 0; navas-- ) {
+ ldapava_free( tmpRDN[navas], ctx );
+ }
+
+return_result:;
+
+ if ( tmpRDN != tmpRDN_ ) {
+ LDAP_FREEX( tmpRDN, ctx );
+ }
+
+ if ( rdn ) {
+ *rdn = newRDN;
+ }
+
+ return( rc );
+}
+
+/*
+ * reads in a UTF-8 string value, unescaping stuff:
+ * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
+ * '\' + HEXPAIR(p) -> unhex(p)
+ */
+static int
+str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx )
+{
+ const char *p, *end, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+ end = str + stoplen;
+ for ( startPos = p = str, escapes = 0; p < end; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
+ escapes++;
+ continue;
+ }
+
+ if ( LDAP_DN_HEXPAIR( p ) ) {
+ char c;
+
+ hexstr2bin( p, &c );
+ escapes += 2;
+
+ if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
+
+ /*
+ * we assume the string is UTF-8
+ */
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+ }
+ p++;
+
+ continue;
+ }
+
+ if ( LDAP_DN_PEDANTIC & flags ) {
+ return( 1 );
+ }
+ /*
+ * we do not allow escaping
+ * of chars that don't need
+ * to and do not belong to
+ * HEXDIGITS
+ */
+ return( 1 );
+
+ } else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+ *retFlags = LDAP_AVA_NONPRINTABLE;
+
+ } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) )
+ || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
+ break;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+ /*
+ * FIXME: maybe we can add
+ * escapes if not pedantic?
+ */
+ return( 1 );
+ }
+ }
+
+ /*
+ * we do allow unescaped spaces at the end
+ * of the value only in non-pedantic mode
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ /*
+ * FIXME: test memory?
+ */
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+
+ if ( escapes == 0 ) {
+ if ( *retFlags & LDAP_AVA_NONPRINTABLE ) {
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ AC_MEMCPY( val->bv_val, startPos, len );
+ val->bv_val[ len ] = '\0';
+ } else {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+ }
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
+ val->bv_val[ d++ ] =
+ startPos[ s++ ];
+
+ } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+ val->bv_val[ d++ ] = c;
+ s += 2;
+
+ } else {
+ /* we should never get here */
+ assert( 0 );
+ }
+
+ } else {
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ }
+
+ val->bv_val[ d ] = '\0';
+ assert( d == len );
+ }
+
+ return( 0 );
+}
+
+static int
+DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
+ p++;
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ escapes++;
+
+ } else {
+ return( 1 );
+ }
+
+ } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /*
+ * (unescaped) trailing spaces are trimmed must be silently ignored;
+ * so we eat them
+ */
+ if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p - 1;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ){
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ for ( s = 0, d = 0; d < len; ) {
+ /*
+ * This point is reached only if escapes
+ * are properly used, so all we need to
+ * do is eat them
+ */
+ if ( LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
+ s++;
+
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len, escapes;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ /*
+ * LDAPv2 (RFC 1779)
+ */
+
+ for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ p++;
+ if ( p[ 0 ] == '\0' ) {
+ return( 1 );
+ }
+
+ if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ /* strip trailing (unescaped) spaces */
+ for ( endPos = p;
+ endPos > startPos + 1 &&
+ LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
+ !LDAP_DN_ESCAPE( endPos[ -2 ] );
+ endPos-- ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( endPos ? endPos : p ) - startPos - escapes;
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ for ( s = 0, d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = startPos[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ unsigned escapes = 0;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ /* initial quote already eaten */
+ for ( startPos = p = str; p[ 0 ]; p++ ) {
+ /*
+ * According to RFC 1779, the quoted value can
+ * contain escaped as well as unescaped special values;
+ * as a consequence we tolerate escaped values
+ * (e.g. '"\,"' -> '\,') and escape unescaped specials
+ * (e.g. '","' -> '\,').
+ */
+ if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
+ if ( p[ 1 ] == '\0' ) {
+ return( 1 );
+ }
+ p++;
+
+ if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
+ && ( LDAP_DN_PEDANTIC & flags ) ) {
+ /*
+ * do we allow to escape normal chars?
+ * LDAPv2 does not allow any mechanism
+ * for escaping chars with '\' and hex
+ * pair
+ */
+ return( 1 );
+ }
+ escapes++;
+
+ } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
+ endPos = p;
+ /* eat closing quotes */
+ p++;
+ break;
+ }
+
+ /*
+ * FIXME: can we accept anything else? I guess we need
+ * to stop if a value is not legal
+ */
+ }
+
+ if ( endPos == NULL ) {
+ return( 1 );
+ }
+
+ /* Strip trailing (unescaped) spaces */
+ for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
+ /* no op */
+ }
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = endPos - startPos - escapes;
+ assert( endPos >= startPos + escapes );
+ val->bv_len = len;
+ if ( escapes == 0 ) {
+ val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
+
+ } else {
+ ber_len_t s, d;
+
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ val->bv_len = len;
+
+ for ( s = d = 0; d < len; ) {
+ if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
+ s++;
+ }
+ val->bv_val[ d++ ] = str[ s++ ];
+ }
+ val->bv_val[ d ] = '\0';
+ assert( strlen( val->bv_val ) == len );
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2bin( const char *str, char *c )
+{
+ char c1, c2;
+
+ assert( str != NULL );
+ assert( c != NULL );
+
+ c1 = str[ 0 ];
+ c2 = str[ 1 ];
+
+ if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
+ *c = c1 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
+ *c = c1 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
+ *c = c1 - 'a' + 10;
+ }
+ }
+
+ *c <<= 4;
+
+ if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
+ *c += c2 - '0';
+
+ } else {
+ if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
+ *c += c2 - 'A' + 10;
+ } else {
+ assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
+ *c += c2 - 'a' + 10;
+ }
+ }
+
+ return( 0 );
+}
+
+static int
+hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
+{
+ const char *p, *startPos, *endPos = NULL;
+ ber_len_t len;
+ ber_len_t s, d;
+
+ assert( str != NULL );
+ assert( val != NULL );
+ assert( next != NULL );
+
+ *next = NULL;
+
+ for ( startPos = p = str; p[ 0 ]; p += 2 ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+
+ if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ return( 1 );
+ }
+ endPos = p;
+
+ for ( ; p[ 0 ]; p++ ) {
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAP:
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
+ goto end_of_value;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ if ( !LDAP_DN_HEXPAIR( p ) ) {
+ return( 1 );
+ }
+ }
+
+end_of_value:;
+
+ *next = p;
+ if ( flags & LDAP_DN_SKIP ) {
+ return( 0 );
+ }
+
+ len = ( ( endPos ? endPos : p ) - startPos ) / 2;
+ /* must be even! */
+ assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
+
+ val->bv_len = len;
+ val->bv_val = LDAP_MALLOCX( len + 1, ctx );
+ if ( val->bv_val == NULL ) {
+ return( LDAP_NO_MEMORY );
+ }
+
+ for ( s = 0, d = 0; d < len; s += 2, d++ ) {
+ char c;
+
+ hexstr2bin( &startPos[ s ], &c );
+
+ val->bv_val[ d ] = c;
+ }
+
+ val->bv_val[ d ] = '\0';
+
+ return( 0 );
+}
+
+/*
+ * convert a byte in a hexadecimal pair
+ */
+static int
+byte2hexpair( const char *val, char *pair )
+{
+ static const char hexdig[] = "0123456789ABCDEF";
+
+ assert( val != NULL );
+ assert( pair != NULL );
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
+ pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
+
+ return( 0 );
+}
+
+/*
+ * convert a binary value in hexadecimal pairs
+ */
+static int
+binval2hexstr( struct berval *val, char *str )
+{
+ ber_len_t s, d;
+
+ assert( val != NULL );
+ assert( str != NULL );
+
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Length of the string representation, accounting for escaped hex
+ * of UTF-8 chars
+ */
+static int
+strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l, cl = 1;
+ char *p, *end;
+ int escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
+#ifdef PRETTY_ESCAPE
+ int escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
+#endif /* PRETTY_ESCAPE */
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ end = val->bv_val + val->bv_len - 1;
+ for ( l = 0, p = val->bv_val; p <= end; p += cl ) {
+
+ /*
+ * escape '%x00'
+ */
+ if ( p[ 0 ] == '\0' ) {
+ cl = 1;
+ l += 3;
+ continue;
+ }
+
+ cl = LDAP_UTF8_CHARLEN2( p, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char! */
+ return( -1 );
+
+ } else if ( cl > 1 ) {
+ ber_len_t cnt;
+
+ for ( cnt = 1; cnt < cl; cnt++ ) {
+ if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
+ return( -1 );
+ }
+ }
+ l += escaped_byte_len * cl;
+
+ } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+#ifdef PRETTY_ESCAPE
+#if 0
+ if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
+#else
+ if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
+#endif
+
+ /*
+ * there might be some chars we want
+ * to escape in form of a couple
+ * of hexdigits for optimization purposes
+ */
+ l += 3;
+
+ } else {
+ l += escaped_ascii_len;
+ }
+#else /* ! PRETTY_ESCAPE */
+ l += 3;
+#endif /* ! PRETTY_ESCAPE */
+
+ } else {
+ l++;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation, escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ ber_len_t cl;
+
+ /*
+ * escape '%x00'
+ */
+ if ( val->bv_val[ s ] == '\0' ) {
+ cl = 1;
+ str[ d++ ] = '\\';
+ str[ d++ ] = '0';
+ str[ d++ ] = '0';
+ s++;
+ continue;
+ }
+
+ /*
+ * The length was checked in strval2strlen();
+ */
+ cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] );
+
+ /*
+ * there might be some chars we want to escape in form
+ * of a couple of hexdigits for optimization purposes
+ */
+ if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) )
+#ifdef PRETTY_ESCAPE
+#if 0
+ || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] )
+#else
+ || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] )
+#endif
+#else /* ! PRETTY_ESCAPE */
+ || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
+
+#endif /* ! PRETTY_ESCAPE */
+ ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = '\\';
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ }
+
+ } else if ( cl > 1 ) {
+ for ( ; cl--; ) {
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+
+ } else {
+#ifdef PRETTY_ESCAPE
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ if ( !LDAP_DN_IS_PRETTY( flags ) ) {
+ byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+ s++;
+ d += 2;
+ continue;
+ }
+ }
+#endif /* PRETTY_ESCAPE */
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the IA5 string representation (no UTF-8 allowed)
+ */
+static int
+strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+ || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
+ || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+ || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to string representation (np UTF-8)
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, end;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * Turn value into a binary encoded BER
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+ || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
+ || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+ || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) DCE string representation,
+ * accounting for escaped hex of UTF-8 chars
+ */
+static int
+strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ return( -1 );
+
+ } else {
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
+ l += 2;
+
+ } else {
+ l++;
+ }
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) DCE string representation,
+ * escaping with hex the UTF-8 stuff;
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ if ( flags & LDAP_AVA_NONPRINTABLE ) {
+ /*
+ * FIXME: Turn the value into a binary encoded BER?
+ */
+ *len = 0;
+ return( -1 );
+
+ } else {
+
+ /*
+ * we assume the string has enough room for the hex encoding
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
+ str[ d++ ] = '\\';
+ }
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * Length of the (supposedly) AD canonical string representation,
+ * accounting for chars that need to be escaped
+ */
+static int
+strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
+{
+ ber_len_t l, cl;
+ char *p;
+
+ assert( val != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+ if ( val->bv_len == 0 ) {
+ return( 0 );
+ }
+
+ for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) {
+ cl = LDAP_UTF8_CHARLEN2( p, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char */
+ return -1;
+ } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
+ l += 2;
+ } else {
+ l += cl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * convert to (supposedly) AD string representation,
+ * assume the destination has enough room for escaping
+ */
+static int
+strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
+{
+ ber_len_t s, d, cl;
+
+ assert( val != NULL );
+ assert( str != NULL );
+ assert( len != NULL );
+
+ if ( val->bv_len == 0 ) {
+ *len = 0;
+ return( 0 );
+ }
+
+ /*
+ * we assume the string has enough room for the escaping
+ * of the value
+ */
+
+ for ( s = 0, d = 0; s < val->bv_len; ) {
+ cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl );
+ if ( cl == 0 ) {
+ /* illegal utf-8 char */
+ return -1;
+ } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) {
+ str[ d++ ] = '\\';
+ }
+ for (; cl--;) {
+ str[ d++ ] = val->bv_val[ s++ ];
+ }
+ }
+
+ *len = d;
+
+ return( 0 );
+}
+
+/*
+ * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
+ * the first part of the AD representation of the DN is written in DNS
+ * form, i.e. dot separated domain name components (as suggested
+ * by Luke Howard, http://www.padl.com/~lukeh)
+ */
+static int
+dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN )
+{
+ int i;
+ int domain = 0, first = 1;
+ ber_len_t l = 1; /* we move the null also */
+ char *str;
+
+ /* we are guaranteed there's enough memory in str */
+
+ /* sanity */
+ assert( dn != NULL );
+ assert( bv != NULL );
+ assert( iRDN != NULL );
+ assert( *iRDN >= 0 );
+
+ str = bv->bv_val + pos;
+
+ for ( i = *iRDN; i >= 0; i-- ) {
+ LDAPRDN rdn;
+ LDAPAVA *ava;
+
+ assert( dn[ i ] != NULL );
+ rdn = dn[ i ];
+
+ assert( rdn[ 0 ] != NULL );
+ ava = rdn[ 0 ];
+
+ if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
+ break;
+ }
+
+ if ( ldif_is_not_printable( ava->la_value.bv_val, ava->la_value.bv_len ) ) {
+ domain = 0;
+ break;
+ }
+
+ domain = 1;
+
+ if ( first ) {
+ first = 0;
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len + 1);
+ l += ava->la_value.bv_len;
+
+ } else {
+ AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
+ AC_MEMCPY( str, ava->la_value.bv_val,
+ ava->la_value.bv_len );
+ str[ ava->la_value.bv_len ] = '.';
+ l += ava->la_value.bv_len + 1;
+ }
+ }
+
+ *iRDN = i;
+ bv->bv_len = pos + l - 1;
+
+ return( domain );
+}
+
+static int
+rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
+ int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* len(type) + '=' + '+' | ',' */
+ l += ava->la_attr.bv_len + 2;
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
+ int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' );
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* len(type) + '=' + ',' | '/' */
+ l += ava->la_attr.bv_len + 2;
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the length */
+ l += 1 + 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ AC_MEMCPY( &str[ l ], ava->la_attr.bv_val,
+ ava->la_attr.bv_len );
+ l += ava->la_attr.bv_len;
+
+ str[ l++ ] = '=';
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* ' + ' | ', ' */
+ l += ( rdn[ iAVA + 1 ] ? 3 : 2 );
+
+ /* FIXME: are binary values allowed in UFN? */
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2strlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+
+ if ( rdn[ iAVA + 1 ] ) {
+ AC_MEMCPY( &str[ l ], " + ", 3 );
+ l += 3;
+
+ } else {
+ AC_MEMCPY( &str[ l ], ", ", 2 );
+ l += 2;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ assert( rdn != NULL );
+ assert( len != NULL );
+
+ *len = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ /* ',' | '/' */
+ l++;
+
+ /* FIXME: are binary values allowed in UFN? */
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ /* octothorpe + twice the value */
+ l += 1 + 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+static int
+rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+ int iAVA;
+ ber_len_t l = 0;
+
+ for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
+ LDAPAVA *ava = rdn[ iAVA ];
+
+ if ( first ) {
+ first = 0;
+ } else {
+ str[ l++ ] = ( iAVA ? ',' : '/' );
+ }
+
+ if ( ava->la_flags & LDAP_AVA_BINARY ) {
+ str[ l++ ] = '#';
+ if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+ return( -1 );
+ }
+ l += 2 * ava->la_value.bv_len;
+ } else {
+ ber_len_t vl;
+ unsigned f = flags | ava->la_flags;
+
+ if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+ return( -1 );
+ }
+ l += vl;
+ }
+ }
+
+ *len = l;
+
+ return( 0 );
+}
+
+/*
+ * ldap_rdn2str
+ *
+ * Returns in str a string representation of rdn based on flags.
+ * There is some duplication of code between this and ldap_dn2str;
+ * this is wanted to reduce the allocation of temporary buffers.
+ */
+int
+ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str != NULL );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int
+ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags )
+{
+ return ldap_rdn2bv_x( rdn, bv, flags, NULL );
+}
+
+int
+ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx )
+{
+ int rc, back;
+ ber_len_t l;
+
+ assert( bv != NULL );
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ if ( rdn == NULL ) {
+ bv->bv_val = LDAP_STRDUPX( "", ctx );
+ return( LDAP_SUCCESS );
+ }
+
+ /*
+ * This routine wastes "back" bytes at the end of the string
+ */
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ if ( rdn2ADstrlen( rdn, flags, &l ) ) {
+ return LDAP_DECODING_ERROR;
+ }
+ break;
+
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ bv->bv_val = LDAP_MALLOCX( l + 1, ctx );
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_LDAPV2:
+ rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
+ back = 1;
+ break;
+
+ case LDAP_DN_FORMAT_UFN:
+ rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
+ back = 2;
+ break;
+
+ case LDAP_DN_FORMAT_DCE:
+ rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL:
+ rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
+ back = 0;
+ break;
+
+ default:
+ /* need at least one of the previous */
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( rc ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ return rc;
+ }
+
+ bv->bv_len = l - back;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Very bulk implementation; many optimizations can be performed
+ * - a NULL dn results in an empty string ""
+ *
+ * FIXME: doubts
+ * a) what do we do if a UTF-8 string must be converted in LDAPv2?
+ * we must encode it in binary form ('#' + HEXPAIRs)
+ * b) does DCE/AD support UTF-8?
+ * no clue; don't think so.
+ * c) what do we do when binary values must be converted in UTF/DCE/AD?
+ * use binary encoded BER
+ */
+int ldap_dn2str( LDAPDN dn, char **str, unsigned flags )
+{
+ struct berval bv;
+ int rc;
+
+ assert( str != NULL );
+
+ if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = ldap_dn2bv_x( dn, &bv, flags, NULL );
+ *str = bv.bv_val;
+ return rc;
+}
+
+int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags )
+{
+ return ldap_dn2bv_x( dn, bv, flags, NULL );
+}
+
+int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx )
+{
+ int iRDN;
+ int rc = LDAP_ENCODING_ERROR;
+ ber_len_t len, l;
+
+ /* stringifying helpers for LDAPv3/LDAPv2 */
+ int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
+ int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
+
+ assert( bv != NULL );
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ Debug( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags, 0, 0 );
+
+ /*
+ * a null dn means an empty dn string
+ * FIXME: better raise an error?
+ */
+ if ( dn == NULL || dn[0] == NULL ) {
+ bv->bv_val = LDAP_STRDUPX( "", ctx );
+ return( LDAP_SUCCESS );
+ }
+
+ switch ( LDAP_DN_FORMAT( flags ) ) {
+ case LDAP_DN_FORMAT_LDAPV3:
+ sv2l = strval2strlen;
+ sv2s = strval2str;
+
+ if( 0 ) {
+ case LDAP_DN_FORMAT_LDAPV2:
+ sv2l = strval2IA5strlen;
+ sv2s = strval2IA5str;
+ }
+
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags,
+ &rdnl, sv2s ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ /*
+ * trim the last ',' (the allocated memory
+ * is one byte longer than required)
+ */
+ bv->bv_len = len - 1;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_UFN: {
+ /*
+ * FIXME: quoting from RFC 1781:
+ *
+ To take a distinguished name, and generate a name of this format with
+ attribute types omitted, the following steps are followed.
+
+ 1. If the first attribute is of type CommonName, the type may be
+ omitted.
+
+ 2. If the last attribute is of type Country, the type may be
+ omitted.
+
+ 3. If the last attribute is of type Country, the last
+ Organisation attribute may have the type omitted.
+
+ 4. All attributes of type OrganisationalUnit may have the type
+ omitted, unless they are after an Organisation attribute or
+ the first attribute is of type OrganisationalUnit.
+
+ * this should be the pedantic implementation.
+ *
+ * Here the standard implementation reflects
+ * the one historically provided by OpenLDAP
+ * (and UMIch, I presume), with the variant
+ * of spaces and plusses (' + ') separating
+ * rdn components.
+ *
+ * A non-standard but nice implementation could
+ * be to turn the final "dc" attributes into a
+ * dot-separated domain.
+ *
+ * Other improvements could involve the use of
+ * friendly country names and so.
+ */
+#ifdef DC_IN_UFN
+ int leftmost_dc = -1;
+ int last_iRDN = -1;
+#endif /* DC_IN_UFN */
+
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+ len += rdnl;
+
+#ifdef DC_IN_UFN
+ if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) {
+ if ( leftmost_dc == -1 ) {
+ leftmost_dc = iRDN;
+ }
+ } else {
+ leftmost_dc = -1;
+ }
+#endif /* DC_IN_UFN */
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+#ifdef DC_IN_UFN
+ if ( leftmost_dc == -1 ) {
+#endif /* DC_IN_UFN */
+ for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t vl;
+
+ if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ /*
+ * trim the last ', ' (the allocated memory
+ * is two bytes longer than required)
+ */
+ bv->bv_len = len - 2;
+ bv->bv_val[ bv->bv_len ] = '\0';
+#ifdef DC_IN_UFN
+ } else {
+ last_iRDN = iRDN - 1;
+
+ for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
+ ber_len_t vl;
+
+ if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &vl ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += vl;
+ }
+
+ if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+
+ /* the string is correctly terminated by dn2domain */
+ }
+#endif /* DC_IN_UFN */
+
+ rc = LDAP_SUCCESS;
+
+ } break;
+
+ case LDAP_DN_FORMAT_DCE:
+ for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+ if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ for ( l = 0; iRDN--; ) {
+ ber_len_t rdnl;
+
+ if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags,
+ &rdnl, 0 ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ assert( l == len );
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_DN_FORMAT_AD_CANONICAL: {
+ int trailing_slash = 1;
+
+ /*
+ * Sort of UFN for DCE DNs: a slash ('/') separated
+ * global->local DN with no types; strictly speaking,
+ * the naming context should be a domain, which is
+ * written in DNS-style, e.g. dot-deparated.
+ *
+ * Example:
+ *
+ * "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
+ *
+ * will read
+ *
+ * "microsoft.com/People/Bill,Gates"
+ */
+ for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) {
+ ber_len_t rdnl;
+
+ if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) {
+ goto return_results;
+ }
+
+ len += rdnl;
+ }
+
+ /* reserve room for trailing '/' in case the DN
+ * is exactly a domain */
+ if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ iRDN--;
+ if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) {
+ for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+
+ trailing_slash = 0;
+
+ if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &rdnl, 0 ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ l += rdnl;
+ }
+
+ } else {
+ int first = 1;
+
+ /*
+ * Strictly speaking, AD canonical requires
+ * a DN to be in the form "..., dc=smtg",
+ * i.e. terminated by a domain component
+ */
+ if ( flags & LDAP_DN_PEDANTIC ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ rc = LDAP_ENCODING_ERROR;
+ break;
+ }
+
+ for ( l = 0; iRDN >= 0 ; iRDN-- ) {
+ ber_len_t rdnl;
+
+ if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ],
+ flags, &rdnl, first ) ) {
+ LDAP_FREEX( bv->bv_val, ctx );
+ bv->bv_val = NULL;
+ goto return_results;
+ }
+ if ( first ) {
+ first = 0;
+ }
+ l += rdnl;
+ }
+ }
+
+ if ( trailing_slash ) {
+ /* the DN is exactly a domain -- need a trailing
+ * slash; room was reserved in advance */
+ bv->bv_val[ len ] = '/';
+ len++;
+ }
+
+ bv->bv_len = len;
+ bv->bv_val[ bv->bv_len ] = '\0';
+
+ rc = LDAP_SUCCESS;
+ } break;
+
+ default:
+ return LDAP_PARAM_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n",
+ bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" );
+
+return_results:;
+ return( rc );
+}
+
diff --git a/libraries/libldap/getentry.c b/libraries/libldap/getentry.c
new file mode 100644
index 0000000..372cb40
--- /dev/null
+++ b/libraries/libldap/getentry.c
@@ -0,0 +1,124 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* ARGSUSED */
+LDAPMessage *
+ldap_first_entry( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY
+ ? chain
+ : ldap_next_entry( ld, chain );
+}
+
+LDAPMessage *
+ldap_next_entry( LDAP *ld, LDAPMessage *entry )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+
+ for(
+ entry = entry->lm_chain;
+ entry != NULL;
+ entry = entry->lm_chain )
+ {
+ if( entry->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ return( entry );
+ }
+ }
+
+ return( NULL );
+}
+
+int
+ldap_count_entries( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ if( chain->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ i++;
+ }
+ }
+
+ return( i );
+}
+
+int
+ldap_get_entry_controls(
+ LDAP *ld,
+ LDAPMessage *entry,
+ LDAPControl ***sctrls )
+{
+ int rc;
+ BerElement be;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( sctrls != NULL );
+
+ if ( entry->lm_msgtype != LDAP_RES_SEARCH_ENTRY ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* make a local copy of the BerElement */
+ AC_MEMCPY(&be, entry->lm_ber, sizeof(be));
+
+ if ( ber_scanf( &be, "{xx" /*}*/ ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto cleanup_and_return;
+ }
+
+ rc = ldap_pvt_get_controls( &be, sctrls );
+
+cleanup_and_return:
+ if( rc != LDAP_SUCCESS ) {
+ ld->ld_errno = rc;
+
+ if( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/getvalues.c b/libraries/libldap/getvalues.c
new file mode 100644
index 0000000..b4fef9f
--- /dev/null
+++ b/libraries/libldap/getvalues.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+char **
+ldap_get_values( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target )
+{
+ BerElement ber;
+ char *attr;
+ int found = 0;
+ char **vals;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( target != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_get_values\n", 0, 0, 0 );
+
+ ber = *entry->lm_ber;
+
+ /* skip sequence, dn, sequence of, and snag the first attr */
+ if ( ber_scanf( &ber, "{x{{a" /*}}}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ found = 1;
+
+ /* break out on success, return out on error */
+ while ( ! found ) {
+ LDAP_FREE(attr);
+ attr = NULL;
+
+ if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ break;
+
+ }
+
+ LDAP_FREE(attr);
+ attr = NULL;
+
+ /*
+ * if we get this far, we've found the attribute and are sitting
+ * just before the set of values.
+ */
+
+ if ( ber_scanf( &ber, "[v]", &vals ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( vals );
+}
+
+struct berval **
+ldap_get_values_len( LDAP *ld, LDAPMessage *entry, LDAP_CONST char *target )
+{
+ BerElement ber;
+ char *attr;
+ int found = 0;
+ struct berval **vals;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( entry != NULL );
+ assert( target != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_get_values_len\n", 0, 0, 0 );
+
+ ber = *entry->lm_ber;
+
+ /* skip sequence, dn, sequence of, and snag the first attr */
+ if ( ber_scanf( &ber, "{x{{a" /* }}} */, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ found = 1;
+
+ /* break out on success, return out on error */
+ while ( ! found ) {
+ LDAP_FREE( attr );
+ attr = NULL;
+
+ if ( ber_scanf( &ber, /*{*/ "x}{a" /*}*/, &attr ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ if ( strcasecmp( target, attr ) == 0 )
+ break;
+ }
+
+ LDAP_FREE( attr );
+ attr = NULL;
+
+ /*
+ * if we get this far, we've found the attribute and are sitting
+ * just before the set of values.
+ */
+
+ if ( ber_scanf( &ber, "[V]", &vals ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ return( vals );
+}
+
+int
+ldap_count_values( char **vals )
+{
+ int i;
+
+ if ( vals == NULL )
+ return( 0 );
+
+ for ( i = 0; vals[i] != NULL; i++ )
+ ; /* NULL */
+
+ return( i );
+}
+
+int
+ldap_count_values_len( struct berval **vals )
+{
+ return( ldap_count_values( (char **) vals ) );
+}
+
+void
+ldap_value_free( char **vals )
+{
+ LDAP_VFREE( vals );
+}
+
+void
+ldap_value_free_len( struct berval **vals )
+{
+ ber_bvecfree( vals );
+}
+
+char **
+ldap_value_dup( char *const *vals )
+{
+ char **new;
+ int i;
+
+ if( vals == NULL ) {
+ return NULL;
+ }
+
+ for( i=0; vals[i]; i++ ) {
+ ; /* Count the number of values */
+ }
+
+ if( i == 0 ) {
+ return NULL;
+ }
+
+ new = LDAP_MALLOC( (i+1)*sizeof(char *) ); /* Alloc array of pointers */
+ if( new == NULL ) {
+ return NULL;
+ }
+
+ for( i=0; vals[i]; i++ ) {
+ new[i] = LDAP_STRDUP( vals[i] ); /* Dup each value */
+ if( new[i] == NULL ) {
+ LDAP_VFREE( new );
+ return NULL;
+ }
+ }
+ new[i] = NULL;
+
+ return new;
+}
+
diff --git a/libraries/libldap/gssapi.c b/libraries/libldap/gssapi.c
new file mode 100644
index 0000000..8251ea3
--- /dev/null
+++ b/libraries/libldap/gssapi.c
@@ -0,0 +1,1011 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Author: Stefan Metzmacher <metze@sernet.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+#include <ac/ctype.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "ldap-int.h"
+
+#ifdef HAVE_GSSAPI
+
+#ifdef HAVE_GSSAPI_GSSAPI_H
+#include <gssapi/gssapi.h>
+#else
+#include <gssapi.h>
+#endif
+
+static char *
+gsserrstr(
+ char *buf,
+ ber_len_t buf_len,
+ gss_OID mech,
+ int gss_rc,
+ OM_uint32 minor_status )
+{
+ OM_uint32 min2;
+ gss_buffer_desc mech_msg = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc gss_msg = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc minor_msg = GSS_C_EMPTY_BUFFER;
+ OM_uint32 msg_ctx = 0;
+
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ if (buf_len == 0) {
+ return NULL;
+ }
+
+#ifdef HAVE_GSS_OID_TO_STR
+ gss_oid_to_str(&min2, mech, &mech_msg);
+#endif
+ gss_display_status(&min2, gss_rc, GSS_C_GSS_CODE,
+ mech, &msg_ctx, &gss_msg);
+ gss_display_status(&min2, minor_status, GSS_C_MECH_CODE,
+ mech, &msg_ctx, &minor_msg);
+
+ snprintf(buf, buf_len, "gss_rc[%d:%*s] mech[%*s] minor[%u:%*s]",
+ gss_rc, (int)gss_msg.length,
+ (const char *)(gss_msg.value?gss_msg.value:""),
+ (int)mech_msg.length,
+ (const char *)(mech_msg.value?mech_msg.value:""),
+ minor_status, (int)minor_msg.length,
+ (const char *)(minor_msg.value?minor_msg.value:""));
+
+ gss_release_buffer(&min2, &mech_msg);
+ gss_release_buffer(&min2, &gss_msg);
+ gss_release_buffer(&min2, &minor_msg);
+
+ buf[buf_len-1] = '\0';
+
+ return buf;
+}
+
+static void
+sb_sasl_gssapi_init(
+ struct sb_sasl_generic_data *p,
+ ber_len_t *min_send,
+ ber_len_t *max_send,
+ ber_len_t *max_recv )
+{
+ gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
+ int gss_rc;
+ OM_uint32 minor_status;
+ gss_OID ctx_mech = GSS_C_NO_OID;
+ OM_uint32 ctx_flags = 0;
+ int conf_req_flag = 0;
+ OM_uint32 max_input_size;
+
+ gss_inquire_context(&minor_status,
+ gss_ctx,
+ NULL,
+ NULL,
+ NULL,
+ &ctx_mech,
+ &ctx_flags,
+ NULL,
+ NULL);
+
+ if (ctx_flags & (GSS_C_CONF_FLAG)) {
+ conf_req_flag = 1;
+ }
+
+#if defined(HAVE_CYRUS_SASL)
+#define SEND_PREALLOC_SIZE SASL_MIN_BUFF_SIZE
+#else
+#define SEND_PREALLOC_SIZE 4096
+#endif
+#define SEND_MAX_WIRE_SIZE 0x00A00000
+#define RECV_MAX_WIRE_SIZE 0x0FFFFFFF
+#define FALLBACK_SEND_MAX_SIZE 0x009FFFB8 /* from MIT 1.5.x */
+
+ gss_rc = gss_wrap_size_limit(&minor_status, gss_ctx,
+ conf_req_flag, GSS_C_QOP_DEFAULT,
+ SEND_MAX_WIRE_SIZE, &max_input_size);
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ char msg[256];
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_init: failed to wrap size limit: %s\n",
+ gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_init: fallback to default wrap size limit\n");
+ /*
+ * some libgssglue/libgssapi versions
+ * have a broken gss_wrap_size_limit()
+ * implementation
+ */
+ max_input_size = FALLBACK_SEND_MAX_SIZE;
+ }
+
+ *min_send = SEND_PREALLOC_SIZE;
+ *max_send = max_input_size;
+ *max_recv = RECV_MAX_WIRE_SIZE;
+}
+
+static ber_int_t
+sb_sasl_gssapi_encode(
+ struct sb_sasl_generic_data *p,
+ unsigned char *buf,
+ ber_len_t len,
+ Sockbuf_Buf *dst )
+{
+ gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
+ int gss_rc;
+ OM_uint32 minor_status;
+ gss_buffer_desc unwrapped, wrapped;
+ gss_OID ctx_mech = GSS_C_NO_OID;
+ OM_uint32 ctx_flags = 0;
+ int conf_req_flag = 0;
+ int conf_state;
+ unsigned char *b;
+ ber_len_t pkt_len;
+
+ unwrapped.value = buf;
+ unwrapped.length = len;
+
+ gss_inquire_context(&minor_status,
+ gss_ctx,
+ NULL,
+ NULL,
+ NULL,
+ &ctx_mech,
+ &ctx_flags,
+ NULL,
+ NULL);
+
+ if (ctx_flags & (GSS_C_CONF_FLAG)) {
+ conf_req_flag = 1;
+ }
+
+ gss_rc = gss_wrap(&minor_status, gss_ctx,
+ conf_req_flag, GSS_C_QOP_DEFAULT,
+ &unwrapped, &conf_state,
+ &wrapped);
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ char msg[256];
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_encode: failed to encode packet: %s\n",
+ gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
+ return -1;
+ }
+
+ if ( conf_req_flag && conf_state == 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our gss_wrap()\n" );
+ return -1;
+ }
+
+ pkt_len = 4 + wrapped.length;
+
+ /* Grow the packet buffer if neccessary */
+ if ( dst->buf_size < pkt_len &&
+ ber_pvt_sb_grow_buffer( dst, pkt_len ) < 0 )
+ {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_encode: failed to grow the buffer to %lu bytes\n",
+ pkt_len );
+ return -1;
+ }
+
+ dst->buf_end = pkt_len;
+
+ b = (unsigned char *)dst->buf_base;
+
+ b[0] = (unsigned char)(wrapped.length >> 24);
+ b[1] = (unsigned char)(wrapped.length >> 16);
+ b[2] = (unsigned char)(wrapped.length >> 8);
+ b[3] = (unsigned char)(wrapped.length >> 0);
+
+ /* copy the wrapped blob to the right location */
+ memcpy(b + 4, wrapped.value, wrapped.length);
+
+ gss_release_buffer(&minor_status, &wrapped);
+
+ return 0;
+}
+
+static ber_int_t
+sb_sasl_gssapi_decode(
+ struct sb_sasl_generic_data *p,
+ const Sockbuf_Buf *src,
+ Sockbuf_Buf *dst )
+{
+ gss_ctx_id_t gss_ctx = (gss_ctx_id_t)p->ops_private;
+ int gss_rc;
+ OM_uint32 minor_status;
+ gss_buffer_desc unwrapped, wrapped;
+ gss_OID ctx_mech = GSS_C_NO_OID;
+ OM_uint32 ctx_flags = 0;
+ int conf_req_flag = 0;
+ int conf_state;
+ unsigned char *b;
+
+ wrapped.value = src->buf_base + 4;
+ wrapped.length = src->buf_end - 4;
+
+ gss_inquire_context(&minor_status,
+ gss_ctx,
+ NULL,
+ NULL,
+ NULL,
+ &ctx_mech,
+ &ctx_flags,
+ NULL,
+ NULL);
+
+ if (ctx_flags & (GSS_C_CONF_FLAG)) {
+ conf_req_flag = 1;
+ }
+
+ gss_rc = gss_unwrap(&minor_status, gss_ctx,
+ &wrapped, &unwrapped,
+ &conf_state, GSS_C_QOP_DEFAULT);
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ char msg[256];
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_decode: failed to decode packet: %s\n",
+ gsserrstr( msg, sizeof(msg), ctx_mech, gss_rc, minor_status ) );
+ return -1;
+ }
+
+ if ( conf_req_flag && conf_state == 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_encode: GSS_C_CONF_FLAG was ignored by our peer\n" );
+ return -1;
+ }
+
+ /* Grow the packet buffer if neccessary */
+ if ( dst->buf_size < unwrapped.length &&
+ ber_pvt_sb_grow_buffer( dst, unwrapped.length ) < 0 )
+ {
+ ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_gssapi_decode: failed to grow the buffer to %lu bytes\n",
+ unwrapped.length );
+ return -1;
+ }
+
+ dst->buf_end = unwrapped.length;
+
+ b = (unsigned char *)dst->buf_base;
+
+ /* copy the wrapped blob to the right location */
+ memcpy(b, unwrapped.value, unwrapped.length);
+
+ gss_release_buffer(&minor_status, &unwrapped);
+
+ return 0;
+}
+
+static void
+sb_sasl_gssapi_reset_buf(
+ struct sb_sasl_generic_data *p,
+ Sockbuf_Buf *buf )
+{
+ ber_pvt_sb_buf_destroy( buf );
+}
+
+static void
+sb_sasl_gssapi_fini( struct sb_sasl_generic_data *p )
+{
+}
+
+static const struct sb_sasl_generic_ops sb_sasl_gssapi_ops = {
+ sb_sasl_gssapi_init,
+ sb_sasl_gssapi_encode,
+ sb_sasl_gssapi_decode,
+ sb_sasl_gssapi_reset_buf,
+ sb_sasl_gssapi_fini
+};
+
+static int
+sb_sasl_gssapi_install(
+ Sockbuf *sb,
+ gss_ctx_id_t gss_ctx )
+{
+ struct sb_sasl_generic_install install_arg;
+
+ install_arg.ops = &sb_sasl_gssapi_ops;
+ install_arg.ops_private = gss_ctx;
+
+ return ldap_pvt_sasl_generic_install( sb, &install_arg );
+}
+
+static void
+sb_sasl_gssapi_remove( Sockbuf *sb )
+{
+ ldap_pvt_sasl_generic_remove( sb );
+}
+
+static int
+map_gsserr2ldap(
+ LDAP *ld,
+ gss_OID mech,
+ int gss_rc,
+ OM_uint32 minor_status )
+{
+ char msg[256];
+
+ Debug( LDAP_DEBUG_ANY, "%s\n",
+ gsserrstr( msg, sizeof(msg), mech, gss_rc, minor_status ),
+ NULL, NULL );
+
+ if (gss_rc == GSS_S_COMPLETE) {
+ ld->ld_errno = LDAP_SUCCESS;
+ } else if (GSS_CALLING_ERROR(gss_rc)) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ } else if (GSS_ROUTINE_ERROR(gss_rc)) {
+ ld->ld_errno = LDAP_INAPPROPRIATE_AUTH;
+ } else if (gss_rc == GSS_S_CONTINUE_NEEDED) {
+ ld->ld_errno = LDAP_SASL_BIND_IN_PROGRESS;
+ } else if (GSS_SUPPLEMENTARY_INFO(gss_rc)) {
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ } else if (GSS_ERROR(gss_rc)) {
+ ld->ld_errno = LDAP_AUTH_UNKNOWN;
+ } else {
+ ld->ld_errno = LDAP_OTHER;
+ }
+
+ return ld->ld_errno;
+}
+
+
+static int
+ldap_gssapi_get_rootdse_infos (
+ LDAP *ld,
+ char **pmechlist,
+ char **pldapServiceName,
+ char **pdnsHostName )
+{
+ /* we need to query the server for supported mechs anyway */
+ LDAPMessage *res, *e;
+ char *attrs[] = {
+ "supportedSASLMechanisms",
+ "ldapServiceName",
+ "dnsHostName",
+ NULL
+ };
+ char **values, *mechlist;
+ char *ldapServiceName = NULL;
+ char *dnsHostName = NULL;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_gssapi_get_rootdse_infos\n", 0, 0, 0 );
+
+ rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
+ NULL, attrs, 0, &res );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+
+ e = ldap_first_entry( ld, res );
+ if ( e == NULL ) {
+ ldap_msgfree( res );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = LDAP_NO_SUCH_OBJECT;
+ }
+ return ld->ld_errno;
+ }
+
+ values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
+ if ( values == NULL ) {
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
+ return ld->ld_errno;
+ }
+
+ mechlist = ldap_charray2str( values, " " );
+ if ( mechlist == NULL ) {
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ LDAP_VFREE( values );
+
+ values = ldap_get_values( ld, e, "ldapServiceName" );
+ if ( values == NULL ) {
+ goto get_dns_host_name;
+ }
+
+ ldapServiceName = ldap_charray2str( values, " " );
+ if ( ldapServiceName == NULL ) {
+ LDAP_FREE( mechlist );
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+ LDAP_VFREE( values );
+
+get_dns_host_name:
+
+ values = ldap_get_values( ld, e, "dnsHostName" );
+ if ( values == NULL ) {
+ goto done;
+ }
+
+ dnsHostName = ldap_charray2str( values, " " );
+ if ( dnsHostName == NULL ) {
+ LDAP_FREE( mechlist );
+ LDAP_FREE( ldapServiceName );
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+ LDAP_VFREE( values );
+
+done:
+ ldap_msgfree( res );
+
+ *pmechlist = mechlist;
+ *pldapServiceName = ldapServiceName;
+ *pdnsHostName = dnsHostName;
+
+ return LDAP_SUCCESS;
+}
+
+
+static int check_for_gss_spnego_support( LDAP *ld, const char *mechs_str )
+{
+ int rc;
+ char **mechs_list = NULL;
+
+ mechs_list = ldap_str2charray( mechs_str, " " );
+ if ( mechs_list == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ rc = ldap_charray_inlist( mechs_list, "GSS-SPNEGO" );
+ ldap_charray_free( mechs_list );
+ if ( rc != 1) {
+ ld->ld_errno = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+guess_service_principal(
+ LDAP *ld,
+ const char *ldapServiceName,
+ const char *dnsHostName,
+ gss_name_t *principal )
+{
+ gss_buffer_desc input_name;
+ /* GSS_KRB5_NT_PRINCIPAL_NAME */
+ gss_OID_desc nt_principal =
+ {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x01"};
+ const char *host = ld->ld_defconn->lconn_server->lud_host;
+ OM_uint32 minor_status;
+ int gss_rc;
+ int ret;
+ size_t svc_principal_size;
+ char *svc_principal = NULL;
+ const char *principal_fmt = NULL;
+ const char *str = NULL;
+ const char *givenstr = NULL;
+ const char *ignore = "not_defined_in_RFC4178@please_ignore";
+ int allow_remote = 0;
+
+ if (ldapServiceName) {
+ givenstr = strchr(ldapServiceName, ':');
+ if (givenstr && givenstr[1]) {
+ givenstr++;
+ if (strcmp(givenstr, ignore) == 0) {
+ givenstr = NULL;
+ }
+ } else {
+ givenstr = NULL;
+ }
+ }
+
+ if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
+ allow_remote = 1;
+ }
+
+ if (allow_remote && givenstr) {
+ principal_fmt = "%s";
+ svc_principal_size = strlen(givenstr) + 1;
+ str = givenstr;
+
+ } else if (allow_remote && dnsHostName) {
+ principal_fmt = "ldap/%s";
+ svc_principal_size = STRLENOF("ldap/") + strlen(dnsHostName) + 1;
+ str = dnsHostName;
+
+ } else {
+ principal_fmt = "ldap/%s";
+ svc_principal_size = STRLENOF("ldap/") + strlen(host) + 1;
+ str = host;
+ }
+
+ svc_principal = (char*) ldap_memalloc(svc_principal_size * sizeof(char));
+ if ( svc_principal == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ ret = snprintf( svc_principal, svc_principal_size, principal_fmt, str );
+ if (ret < 0 || (size_t)ret >= svc_principal_size) {
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ return ld->ld_errno;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "principal for host[%s]: '%s'\n",
+ host, svc_principal, 0 );
+
+ input_name.value = svc_principal;
+ input_name.length = (size_t)ret;
+
+ gss_rc = gss_import_name( &minor_status, &input_name, &nt_principal, principal );
+ ldap_memfree( svc_principal );
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ return map_gsserr2ldap( ld, GSS_C_NO_OID, gss_rc, minor_status );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void ldap_int_gssapi_close( LDAP *ld, LDAPConn *lc )
+{
+ if ( lc && lc->lconn_gss_ctx ) {
+ OM_uint32 minor_status;
+ OM_uint32 ctx_flags = 0;
+ gss_ctx_id_t old_gss_ctx = GSS_C_NO_CONTEXT;
+ old_gss_ctx = (gss_ctx_id_t)lc->lconn_gss_ctx;
+
+ gss_inquire_context(&minor_status,
+ old_gss_ctx,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &ctx_flags,
+ NULL,
+ NULL);
+
+ if (!( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT )) {
+ gss_delete_sec_context( &minor_status, &old_gss_ctx, GSS_C_NO_BUFFER );
+ }
+ lc->lconn_gss_ctx = GSS_C_NO_CONTEXT;
+
+ if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
+ /* remove wrapping layer */
+ sb_sasl_gssapi_remove( lc->lconn_sb );
+ }
+ }
+}
+
+static void
+ldap_int_gssapi_setup(
+ LDAP *ld,
+ LDAPConn *lc,
+ gss_ctx_id_t gss_ctx)
+{
+ OM_uint32 minor_status;
+ OM_uint32 ctx_flags = 0;
+
+ ldap_int_gssapi_close( ld, lc );
+
+ gss_inquire_context(&minor_status,
+ gss_ctx,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &ctx_flags,
+ NULL,
+ NULL);
+
+ lc->lconn_gss_ctx = gss_ctx;
+
+ if (ctx_flags & (GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG)) {
+ /* setup wrapping layer */
+ sb_sasl_gssapi_install( lc->lconn_sb, gss_ctx );
+ }
+}
+
+#ifdef LDAP_R_COMPILE
+ldap_pvt_thread_mutex_t ldap_int_gssapi_mutex;
+#endif
+
+static int
+ldap_int_gss_spnego_bind_s( LDAP *ld )
+{
+ int rc;
+ int gss_rc;
+ OM_uint32 minor_status;
+ char *mechlist = NULL;
+ char *ldapServiceName = NULL;
+ char *dnsHostName = NULL;
+ gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
+ int spnego_support = 0;
+#define __SPNEGO_OID_LENGTH 6
+#define __SPNEGO_OID "\053\006\001\005\005\002"
+ gss_OID_desc spnego_oid = {__SPNEGO_OID_LENGTH, __SPNEGO_OID};
+ gss_OID req_mech = GSS_C_NO_OID;
+ gss_OID ret_mech = GSS_C_NO_OID;
+ gss_ctx_id_t gss_ctx = GSS_C_NO_CONTEXT;
+ gss_name_t principal = GSS_C_NO_NAME;
+ OM_uint32 req_flags;
+ OM_uint32 ret_flags;
+ gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
+ struct berval cred, *scred = NULL;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gssapi_mutex );
+
+ /* get information from RootDSE entry */
+ rc = ldap_gssapi_get_rootdse_infos ( ld, &mechlist,
+ &ldapServiceName, &dnsHostName);
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ /* check that the server supports GSS-SPNEGO */
+ rc = check_for_gss_spnego_support( ld, mechlist );
+ if ( rc != LDAP_SUCCESS ) {
+ goto rc_error;
+ }
+
+ /* prepare new gss_ctx_id_t */
+ rc = guess_service_principal( ld, ldapServiceName, dnsHostName, &principal );
+ if ( rc != LDAP_SUCCESS ) {
+ goto rc_error;
+ }
+
+ /* see if our gssapi library supports spnego */
+ gss_rc = gss_indicate_mechs( &minor_status, &supported_mechs );
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ goto gss_error;
+ }
+ gss_rc = gss_test_oid_set_member( &minor_status,
+ &spnego_oid, supported_mechs, &spnego_support);
+ gss_release_oid_set( &minor_status, &supported_mechs);
+ if ( gss_rc != GSS_S_COMPLETE ) {
+ goto gss_error;
+ }
+ if ( spnego_support != 0 ) {
+ req_mech = &spnego_oid;
+ }
+
+ req_flags = ld->ld_options.ldo_gssapi_flags;
+ req_flags |= GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG;
+
+ /*
+ * loop around gss_init_sec_context() and ldap_sasl_bind_s()
+ */
+ input_token.value = NULL;
+ input_token.length = 0;
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &gss_ctx,
+ principal,
+ req_mech,
+ req_flags,
+ 0,
+ NULL,
+ &input_token,
+ &ret_mech,
+ &output_token,
+ &ret_flags,
+ NULL);
+ if ( gss_rc == GSS_S_COMPLETE ) {
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ goto rc_error;
+ }
+ if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
+ goto gss_error;
+ }
+ while (1) {
+ cred.bv_val = (char *)output_token.value;
+ cred.bv_len = output_token.length;
+ rc = ldap_sasl_bind_s( ld, NULL, "GSS-SPNEGO", &cred, NULL, NULL, &scred );
+ gss_release_buffer( &minor_status, &output_token );
+ if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
+ goto rc_error;
+ }
+
+ if ( scred ) {
+ input_token.value = scred->bv_val;
+ input_token.length = scred->bv_len;
+ } else {
+ input_token.value = NULL;
+ input_token.length = 0;
+ }
+
+ gss_rc = gss_init_sec_context(&minor_status,
+ GSS_C_NO_CREDENTIAL,
+ &gss_ctx,
+ principal,
+ req_mech,
+ req_flags,
+ 0,
+ NULL,
+ &input_token,
+ &ret_mech,
+ &output_token,
+ &ret_flags,
+ NULL);
+ if ( scred ) {
+ ber_bvfree( scred );
+ }
+ if ( gss_rc == GSS_S_COMPLETE ) {
+ gss_release_buffer( &minor_status, &output_token );
+ break;
+ }
+
+ if ( gss_rc != GSS_S_CONTINUE_NEEDED ) {
+ goto gss_error;
+ }
+ }
+
+ ldap_int_gssapi_setup( ld, ld->ld_defconn, gss_ctx);
+ gss_ctx = GSS_C_NO_CONTEXT;
+
+ rc = LDAP_SUCCESS;
+ goto rc_error;
+
+gss_error:
+ rc = map_gsserr2ldap( ld,
+ (ret_mech != GSS_C_NO_OID ? ret_mech : req_mech ),
+ gss_rc, minor_status );
+rc_error:
+ LDAP_MUTEX_UNLOCK( &ldap_int_gssapi_mutex );
+ LDAP_FREE( mechlist );
+ LDAP_FREE( ldapServiceName );
+ LDAP_FREE( dnsHostName );
+ gss_release_buffer( &minor_status, &output_token );
+ if ( gss_ctx != GSS_C_NO_CONTEXT ) {
+ gss_delete_sec_context( &minor_status, &gss_ctx, GSS_C_NO_BUFFER );
+ }
+ if ( principal != GSS_C_NO_NAME ) {
+ gss_release_name( &minor_status, &principal );
+ }
+ return rc;
+}
+
+int
+ldap_int_gssapi_config( struct ldapoptions *lo, int option, const char *arg )
+{
+ int ok = 0;
+
+ switch( option ) {
+ case LDAP_OPT_SIGN:
+
+ if (!arg) {
+ } else if (strcasecmp(arg, "on") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "yes") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "true") == 0) {
+ ok = 1;
+
+ }
+ if (ok) {
+ lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
+ }
+
+ return 0;
+
+ case LDAP_OPT_ENCRYPT:
+
+ if (!arg) {
+ } else if (strcasecmp(arg, "on") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "yes") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "true") == 0) {
+ ok = 1;
+ }
+
+ if (ok) {
+ lo->ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
+ }
+
+ return 0;
+
+ case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
+
+ if (!arg) {
+ } else if (strcasecmp(arg, "on") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "yes") == 0) {
+ ok = 1;
+ } else if (strcasecmp(arg, "true") == 0) {
+ ok = 1;
+ }
+
+ if (ok) {
+ lo->ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+ldap_int_gssapi_get_option( LDAP *ld, int option, void *arg )
+{
+ if ( ld == NULL )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_SSPI_FLAGS:
+ * (unsigned *) arg = (unsigned) ld->ld_options.ldo_gssapi_flags;
+ break;
+
+ case LDAP_OPT_SIGN:
+ if ( ld->ld_options.ldo_gssapi_flags & GSS_C_INTEG_FLAG ) {
+ * (int *) arg = (int)-1;
+ } else {
+ * (int *) arg = (int)0;
+ }
+ break;
+
+ case LDAP_OPT_ENCRYPT:
+ if ( ld->ld_options.ldo_gssapi_flags & GSS_C_CONF_FLAG ) {
+ * (int *) arg = (int)-1;
+ } else {
+ * (int *) arg = (int)0;
+ }
+ break;
+
+ case LDAP_OPT_SASL_METHOD:
+ * (char **) arg = LDAP_STRDUP("GSS-SPNEGO");
+ break;
+
+ case LDAP_OPT_SECURITY_CONTEXT:
+ if ( ld->ld_defconn && ld->ld_defconn->lconn_gss_ctx ) {
+ * (gss_ctx_id_t *) arg = (gss_ctx_id_t)ld->ld_defconn->lconn_gss_ctx;
+ } else {
+ * (gss_ctx_id_t *) arg = GSS_C_NO_CONTEXT;
+ }
+ break;
+
+ case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
+ if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT ) {
+ * (int *) arg = (int)-1;
+ } else {
+ * (int *) arg = (int)0;
+ }
+ break;
+
+ case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
+ if ( ld->ld_options.ldo_gssapi_options & LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL ) {
+ * (int *) arg = (int)-1;
+ } else {
+ * (int *) arg = (int)0;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+ldap_int_gssapi_set_option( LDAP *ld, int option, void *arg )
+{
+ if ( ld == NULL )
+ return -1;
+
+ switch ( option ) {
+ case LDAP_OPT_SSPI_FLAGS:
+ if ( arg != LDAP_OPT_OFF ) {
+ ld->ld_options.ldo_gssapi_flags = * (unsigned *)arg;
+ }
+ break;
+
+ case LDAP_OPT_SIGN:
+ if ( arg != LDAP_OPT_OFF ) {
+ ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG;
+ }
+ break;
+
+ case LDAP_OPT_ENCRYPT:
+ if ( arg != LDAP_OPT_OFF ) {
+ ld->ld_options.ldo_gssapi_flags |= GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG;
+ }
+ break;
+
+ case LDAP_OPT_SASL_METHOD:
+ if ( arg != LDAP_OPT_OFF ) {
+ const char *m = (const char *)arg;
+ if ( strcmp( "GSS-SPNEGO", m ) != 0 ) {
+ /* we currently only support GSS-SPNEGO */
+ return -1;
+ }
+ }
+ break;
+
+ case LDAP_OPT_SECURITY_CONTEXT:
+ if ( arg != LDAP_OPT_OFF && ld->ld_defconn) {
+ ldap_int_gssapi_setup( ld, ld->ld_defconn,
+ (gss_ctx_id_t) arg);
+ }
+ break;
+
+ case LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT:
+ if ( arg != LDAP_OPT_OFF ) {
+ ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT;
+ }
+ break;
+
+ case LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL:
+ if ( arg != LDAP_OPT_OFF ) {
+ ld->ld_options.ldo_gssapi_options |= LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL;
+ }
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+#else /* HAVE_GSSAPI */
+#define ldap_int_gss_spnego_bind_s(ld) LDAP_NOT_SUPPORTED
+#endif /* HAVE_GSSAPI */
+
+int
+ldap_gssapi_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *creds )
+{
+ return LDAP_NOT_SUPPORTED;
+}
+
+int
+ldap_gssapi_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *creds )
+{
+ if ( dn != NULL ) {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ if ( creds != NULL ) {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ return ldap_int_gss_spnego_bind_s(ld);
+}
diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
new file mode 100644
index 0000000..182ef7d
--- /dev/null
+++ b/libraries/libldap/init.c
@@ -0,0 +1,728 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#ifdef HAVE_GETEUID
+#include <ac/unistd.h>
+#endif
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+#include "lutil.h"
+
+struct ldapoptions ldap_int_global_options =
+ { LDAP_UNINITIALIZED, LDAP_DEBUG_NONE
+ LDAP_LDO_NULLARG
+ LDAP_LDO_CONNECTIONLESS_NULLARG
+ LDAP_LDO_TLS_NULLARG
+ LDAP_LDO_SASL_NULLARG
+ LDAP_LDO_GSSAPI_NULLARG
+ LDAP_LDO_MUTEX_NULLARG };
+
+#define ATTR_NONE 0
+#define ATTR_BOOL 1
+#define ATTR_INT 2
+#define ATTR_KV 3
+#define ATTR_STRING 4
+#define ATTR_OPTION 5
+
+#define ATTR_SASL 6
+#define ATTR_TLS 7
+
+#define ATTR_OPT_TV 8
+#define ATTR_OPT_INT 9
+
+#define ATTR_GSSAPI 10
+
+struct ol_keyvalue {
+ const char * key;
+ int value;
+};
+
+static const struct ol_keyvalue deref_kv[] = {
+ {"never", LDAP_DEREF_NEVER},
+ {"searching", LDAP_DEREF_SEARCHING},
+ {"finding", LDAP_DEREF_FINDING},
+ {"always", LDAP_DEREF_ALWAYS},
+ {NULL, 0}
+};
+
+static const struct ol_attribute {
+ int useronly;
+ int type;
+ const char * name;
+ const void * data;
+ size_t offset;
+} attrs[] = {
+ {0, ATTR_OPT_TV, "TIMEOUT", NULL, LDAP_OPT_TIMEOUT},
+ {0, ATTR_OPT_TV, "NETWORK_TIMEOUT", NULL, LDAP_OPT_NETWORK_TIMEOUT},
+ {0, ATTR_OPT_INT, "VERSION", NULL, LDAP_OPT_PROTOCOL_VERSION},
+ {0, ATTR_KV, "DEREF", deref_kv, /* or &deref_kv[0] */
+ offsetof(struct ldapoptions, ldo_deref)},
+ {0, ATTR_INT, "SIZELIMIT", NULL,
+ offsetof(struct ldapoptions, ldo_sizelimit)},
+ {0, ATTR_INT, "TIMELIMIT", NULL,
+ offsetof(struct ldapoptions, ldo_timelimit)},
+ {1, ATTR_STRING, "BINDDN", NULL,
+ offsetof(struct ldapoptions, ldo_defbinddn)},
+ {0, ATTR_STRING, "BASE", NULL,
+ offsetof(struct ldapoptions, ldo_defbase)},
+ {0, ATTR_INT, "PORT", NULL, /* deprecated */
+ offsetof(struct ldapoptions, ldo_defport)},
+ {0, ATTR_OPTION, "HOST", NULL, LDAP_OPT_HOST_NAME}, /* deprecated */
+ {0, ATTR_OPTION, "URI", NULL, LDAP_OPT_URI}, /* replaces HOST/PORT */
+ {0, ATTR_BOOL, "REFERRALS", NULL, LDAP_BOOL_REFERRALS},
+#if 0
+ /* This should only be allowed via ldap_set_option(3) */
+ {0, ATTR_BOOL, "RESTART", NULL, LDAP_BOOL_RESTART},
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ {0, ATTR_STRING, "SASL_MECH", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_mech)},
+ {0, ATTR_STRING, "SASL_REALM", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_realm)},
+ {1, ATTR_STRING, "SASL_AUTHCID", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_authcid)},
+ {1, ATTR_STRING, "SASL_AUTHZID", NULL,
+ offsetof(struct ldapoptions, ldo_def_sasl_authzid)},
+ {0, ATTR_SASL, "SASL_SECPROPS", NULL, LDAP_OPT_X_SASL_SECPROPS},
+ {0, ATTR_BOOL, "SASL_NOCANON", NULL, LDAP_BOOL_SASL_NOCANON},
+#endif
+
+#ifdef HAVE_GSSAPI
+ {0, ATTR_GSSAPI,"GSSAPI_SIGN", NULL, LDAP_OPT_SIGN},
+ {0, ATTR_GSSAPI,"GSSAPI_ENCRYPT", NULL, LDAP_OPT_ENCRYPT},
+ {0, ATTR_GSSAPI,"GSSAPI_ALLOW_REMOTE_PRINCIPAL",NULL, LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL},
+#endif
+
+#ifdef HAVE_TLS
+ {1, ATTR_TLS, "TLS_CERT", NULL, LDAP_OPT_X_TLS_CERTFILE},
+ {1, ATTR_TLS, "TLS_KEY", NULL, LDAP_OPT_X_TLS_KEYFILE},
+ {0, ATTR_TLS, "TLS_CACERT", NULL, LDAP_OPT_X_TLS_CACERTFILE},
+ {0, ATTR_TLS, "TLS_CACERTDIR", NULL, LDAP_OPT_X_TLS_CACERTDIR},
+ {0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT},
+ {0, ATTR_TLS, "TLS_REQSAN", NULL, LDAP_OPT_X_TLS_REQUIRE_SAN},
+ {0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE},
+ {0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE},
+ {0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN},
+ {0, ATTR_TLS, "TLS_ECNAME", NULL, LDAP_OPT_X_TLS_ECNAME},
+
+#ifdef HAVE_OPENSSL_CRL
+ {0, ATTR_TLS, "TLS_CRLCHECK", NULL, LDAP_OPT_X_TLS_CRLCHECK},
+#endif
+#ifdef HAVE_GNUTLS
+ {0, ATTR_TLS, "TLS_CRLFILE", NULL, LDAP_OPT_X_TLS_CRLFILE},
+#endif
+
+#endif
+
+ {0, ATTR_NONE, NULL, NULL, 0}
+};
+
+#define MAX_LDAP_ATTR_LEN sizeof("GSSAPI_ALLOW_REMOTE_PRINCIPAL")
+#define MAX_LDAP_ENV_PREFIX_LEN 8
+
+static void openldap_ldap_init_w_conf(
+ const char *file, int userconf )
+{
+ char linebuf[ AC_LINE_MAX ];
+ FILE *fp;
+ int i;
+ char *cmd, *opt;
+ char *start, *end;
+ struct ldapoptions *gopts;
+
+ if ((gopts = LDAP_INT_GLOBAL_OPT()) == NULL) {
+ return; /* Could not allocate mem for global options */
+ }
+
+ if (file == NULL) {
+ /* no file name */
+ return;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: trying %s\n", file, 0, 0);
+
+ fp = fopen(file, "r");
+ if(fp == NULL) {
+ /* could not open file */
+ return;
+ }
+
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: using %s\n", file, 0, 0);
+
+ while((start = fgets(linebuf, sizeof(linebuf), fp)) != NULL) {
+ /* skip lines starting with '#' */
+ if(*start == '#') continue;
+
+ /* trim leading white space */
+ while((*start != '\0') && isspace((unsigned char) *start))
+ start++;
+
+ /* anything left? */
+ if(*start == '\0') continue;
+
+ /* trim trailing white space */
+ end = &start[strlen(start)-1];
+ while(isspace((unsigned char)*end)) end--;
+ end[1] = '\0';
+
+ /* anything left? */
+ if(*start == '\0') continue;
+
+
+ /* parse the command */
+ cmd=start;
+ while((*start != '\0') && !isspace((unsigned char)*start)) {
+ start++;
+ }
+ if(*start == '\0') {
+ /* command has no argument */
+ continue;
+ }
+
+ *start++ = '\0';
+
+ /* we must have some whitespace to skip */
+ while(isspace((unsigned char)*start)) start++;
+ opt = start;
+
+ for(i=0; attrs[i].type != ATTR_NONE; i++) {
+ void *p;
+
+ if( !userconf && attrs[i].useronly ) {
+ continue;
+ }
+
+ if(strcasecmp(cmd, attrs[i].name) != 0) {
+ continue;
+ }
+
+ switch(attrs[i].type) {
+ case ATTR_BOOL:
+ if((strcasecmp(opt, "on") == 0)
+ || (strcasecmp(opt, "yes") == 0)
+ || (strcasecmp(opt, "true") == 0))
+ {
+ LDAP_BOOL_SET(gopts, attrs[i].offset);
+
+ } else {
+ LDAP_BOOL_CLR(gopts, attrs[i].offset);
+ }
+
+ break;
+
+ case ATTR_INT: {
+ char *next;
+ long l;
+ p = &((char *) gopts)[attrs[i].offset];
+ l = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' ) {
+ * (int*) p = l;
+ }
+ } break;
+
+ case ATTR_KV: {
+ const struct ol_keyvalue *kv;
+
+ for(kv = attrs[i].data;
+ kv->key != NULL;
+ kv++) {
+
+ if(strcasecmp(opt, kv->key) == 0) {
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = kv->value;
+ break;
+ }
+ }
+ } break;
+
+ case ATTR_STRING:
+ p = &((char *) gopts)[attrs[i].offset];
+ if (* (char**) p != NULL) LDAP_FREE(* (char**) p);
+ * (char**) p = LDAP_STRDUP(opt);
+ break;
+ case ATTR_OPTION:
+ ldap_set_option( NULL, attrs[i].offset, opt );
+ break;
+ case ATTR_SASL:
+#ifdef HAVE_CYRUS_SASL
+ ldap_int_sasl_config( gopts, attrs[i].offset, opt );
+#endif
+ break;
+ case ATTR_GSSAPI:
+#ifdef HAVE_GSSAPI
+ ldap_int_gssapi_config( gopts, attrs[i].offset, opt );
+#endif
+ break;
+ case ATTR_TLS:
+#ifdef HAVE_TLS
+ ldap_int_tls_config( NULL, attrs[i].offset, opt );
+#endif
+ break;
+ case ATTR_OPT_TV: {
+ struct timeval tv;
+ char *next;
+ tv.tv_usec = 0;
+ tv.tv_sec = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' && tv.tv_sec > 0 ) {
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv );
+ }
+ } break;
+ case ATTR_OPT_INT: {
+ long l;
+ char *next;
+ l = strtol( opt, &next, 10 );
+ if ( next != opt && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) {
+ int v = (int)l;
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v );
+ }
+ } break;
+ }
+
+ break;
+ }
+ }
+
+ fclose(fp);
+}
+
+static void openldap_ldap_init_w_sysconf(const char *file)
+{
+ openldap_ldap_init_w_conf( file, 0 );
+}
+
+static void openldap_ldap_init_w_userconf(const char *file)
+{
+ char *home;
+ char *path = NULL;
+
+ if (file == NULL) {
+ /* no file name */
+ return;
+ }
+
+ home = getenv("HOME");
+
+ if (home != NULL) {
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: HOME env is %s\n",
+ home, 0, 0);
+ path = LDAP_MALLOC(strlen(home) + strlen(file) + sizeof( LDAP_DIRSEP "."));
+ } else {
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: HOME env is NULL\n",
+ 0, 0, 0);
+ }
+
+ if(home != NULL && path != NULL) {
+ /* we assume UNIX path syntax is used... */
+
+ /* try ~/file */
+ sprintf(path, "%s" LDAP_DIRSEP "%s", home, file);
+ openldap_ldap_init_w_conf(path, 1);
+
+ /* try ~/.file */
+ sprintf(path, "%s" LDAP_DIRSEP ".%s", home, file);
+ openldap_ldap_init_w_conf(path, 1);
+ }
+
+ if(path != NULL) {
+ LDAP_FREE(path);
+ }
+
+ /* try file */
+ openldap_ldap_init_w_conf(file, 1);
+}
+
+static void openldap_ldap_init_w_env(
+ struct ldapoptions *gopts,
+ const char *prefix)
+{
+ char buf[MAX_LDAP_ATTR_LEN+MAX_LDAP_ENV_PREFIX_LEN];
+ int len;
+ int i;
+ void *p;
+ char *value;
+
+ if (prefix == NULL) {
+ prefix = LDAP_ENV_PREFIX;
+ }
+
+ strncpy(buf, prefix, MAX_LDAP_ENV_PREFIX_LEN);
+ buf[MAX_LDAP_ENV_PREFIX_LEN] = '\0';
+ len = strlen(buf);
+
+ for(i=0; attrs[i].type != ATTR_NONE; i++) {
+ strcpy(&buf[len], attrs[i].name);
+ value = getenv(buf);
+
+ if(value == NULL) {
+ continue;
+ }
+
+ switch(attrs[i].type) {
+ case ATTR_BOOL:
+ if((strcasecmp(value, "on") == 0)
+ || (strcasecmp(value, "yes") == 0)
+ || (strcasecmp(value, "true") == 0))
+ {
+ LDAP_BOOL_SET(gopts, attrs[i].offset);
+
+ } else {
+ LDAP_BOOL_CLR(gopts, attrs[i].offset);
+ }
+ break;
+
+ case ATTR_INT:
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = atoi(value);
+ break;
+
+ case ATTR_KV: {
+ const struct ol_keyvalue *kv;
+
+ for(kv = attrs[i].data;
+ kv->key != NULL;
+ kv++) {
+
+ if(strcasecmp(value, kv->key) == 0) {
+ p = &((char *) gopts)[attrs[i].offset];
+ * (int*) p = kv->value;
+ break;
+ }
+ }
+ } break;
+
+ case ATTR_STRING:
+ p = &((char *) gopts)[attrs[i].offset];
+ if (* (char**) p != NULL) LDAP_FREE(* (char**) p);
+ if (*value == '\0') {
+ * (char**) p = NULL;
+ } else {
+ * (char**) p = LDAP_STRDUP(value);
+ }
+ break;
+ case ATTR_OPTION:
+ ldap_set_option( NULL, attrs[i].offset, value );
+ break;
+ case ATTR_SASL:
+#ifdef HAVE_CYRUS_SASL
+ ldap_int_sasl_config( gopts, attrs[i].offset, value );
+#endif
+ break;
+ case ATTR_GSSAPI:
+#ifdef HAVE_GSSAPI
+ ldap_int_gssapi_config( gopts, attrs[i].offset, value );
+#endif
+ break;
+ case ATTR_TLS:
+#ifdef HAVE_TLS
+ ldap_int_tls_config( NULL, attrs[i].offset, value );
+#endif
+ break;
+ case ATTR_OPT_TV: {
+ struct timeval tv;
+ char *next;
+ tv.tv_usec = 0;
+ tv.tv_sec = strtol( value, &next, 10 );
+ if ( next != value && next[ 0 ] == '\0' && tv.tv_sec > 0 ) {
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&tv );
+ }
+ } break;
+ case ATTR_OPT_INT: {
+ long l;
+ char *next;
+ l = strtol( value, &next, 10 );
+ if ( next != value && next[ 0 ] == '\0' && l > 0 && (long)((int)l) == l ) {
+ int v = (int)l;
+ (void)ldap_set_option( NULL, attrs[i].offset, (const void *)&v );
+ }
+ } break;
+ }
+ }
+}
+
+#if defined(__GNUC__)
+/* Declare this function as a destructor so that it will automatically be
+ * invoked either at program exit (if libldap is a static library) or
+ * at unload time (if libldap is a dynamic library).
+ *
+ * Sorry, don't know how to handle this for non-GCC environments.
+ */
+static void ldap_int_destroy_global_options(void)
+ __attribute__ ((destructor));
+#endif
+
+static void
+ldap_int_destroy_global_options(void)
+{
+ struct ldapoptions *gopts = LDAP_INT_GLOBAL_OPT();
+
+ if ( gopts == NULL )
+ return;
+
+ gopts->ldo_valid = LDAP_UNINITIALIZED;
+
+ if ( gopts->ldo_defludp ) {
+ ldap_free_urllist( gopts->ldo_defludp );
+ gopts->ldo_defludp = NULL;
+ }
+#if defined(HAVE_WINSOCK) || defined(HAVE_WINSOCK2)
+ WSACleanup( );
+#endif
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+ if ( ldap_int_hostname ) {
+ LDAP_FREE( ldap_int_hostname );
+ ldap_int_hostname = NULL;
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( gopts->ldo_def_sasl_authcid ) {
+ LDAP_FREE( gopts->ldo_def_sasl_authcid );
+ gopts->ldo_def_sasl_authcid = NULL;
+ }
+#endif
+#ifdef HAVE_TLS
+ ldap_int_tls_destroy( gopts );
+#endif
+}
+
+/*
+ * Initialize the global options structure with default values.
+ */
+void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl )
+{
+ if (dbglvl)
+ gopts->ldo_debug = *dbglvl;
+ else
+ gopts->ldo_debug = 0;
+
+ gopts->ldo_version = LDAP_VERSION2;
+ gopts->ldo_deref = LDAP_DEREF_NEVER;
+ gopts->ldo_timelimit = LDAP_NO_LIMIT;
+ gopts->ldo_sizelimit = LDAP_NO_LIMIT;
+
+ gopts->ldo_tm_api.tv_sec = -1;
+ gopts->ldo_tm_net.tv_sec = -1;
+
+ /* ldo_defludp will be freed by the termination handler
+ */
+ ldap_url_parselist(&gopts->ldo_defludp, "ldap://localhost/");
+ gopts->ldo_defport = LDAP_PORT;
+#if !defined(__GNUC__) && !defined(PIC)
+ /* Do this only for a static library, and only if we can't
+ * arrange for it to be executed as a library destructor
+ */
+ atexit(ldap_int_destroy_global_options);
+#endif
+
+ gopts->ldo_refhoplimit = LDAP_DEFAULT_REFHOPLIMIT;
+ gopts->ldo_rebind_proc = NULL;
+ gopts->ldo_rebind_params = NULL;
+
+ LDAP_BOOL_ZERO(gopts);
+
+ LDAP_BOOL_SET(gopts, LDAP_BOOL_REFERRALS);
+
+#ifdef LDAP_CONNECTIONLESS
+ gopts->ldo_peer = NULL;
+ gopts->ldo_cldapdn = NULL;
+ gopts->ldo_is_udp = 0;
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ gopts->ldo_def_sasl_mech = NULL;
+ gopts->ldo_def_sasl_realm = NULL;
+ gopts->ldo_def_sasl_authcid = NULL;
+ gopts->ldo_def_sasl_authzid = NULL;
+
+ memset( &gopts->ldo_sasl_secprops,
+ '\0', sizeof(gopts->ldo_sasl_secprops) );
+
+ gopts->ldo_sasl_secprops.max_ssf = INT_MAX;
+ gopts->ldo_sasl_secprops.maxbufsize = SASL_MAX_BUFF_SIZE;
+ gopts->ldo_sasl_secprops.security_flags =
+ SASL_SEC_NOPLAINTEXT | SASL_SEC_NOANONYMOUS;
+#endif
+
+#ifdef HAVE_TLS
+ gopts->ldo_tls_connect_cb = NULL;
+ gopts->ldo_tls_connect_arg = NULL;
+ gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
+ gopts->ldo_tls_require_san = LDAP_OPT_X_TLS_ALLOW;
+#endif
+ gopts->ldo_keepalive_probes = 0;
+ gopts->ldo_keepalive_interval = 0;
+ gopts->ldo_keepalive_idle = 0;
+
+ gopts->ldo_valid = LDAP_INITIALIZED;
+ return;
+}
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+char * ldap_int_hostname = NULL;
+#endif
+
+void ldap_int_initialize( struct ldapoptions *gopts, int *dbglvl )
+{
+#ifdef LDAP_R_COMPILE
+ static ldap_pvt_thread_mutex_t init_mutex;
+ LDAP_PVT_MUTEX_FIRSTCREATE( init_mutex );
+
+ LDAP_MUTEX_LOCK( &init_mutex );
+#endif
+ if ( gopts->ldo_valid == LDAP_INITIALIZED ) {
+ /* someone else got here first */
+ goto done;
+ }
+
+ ldap_int_error_init();
+
+ ldap_int_utils_init();
+
+#ifdef HAVE_WINSOCK2
+{ WORD wVersionRequested;
+ WSADATA wsaData;
+
+ wVersionRequested = MAKEWORD( 2, 0 );
+ if ( WSAStartup( wVersionRequested, &wsaData ) != 0 ) {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ goto done;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.0.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 2.0 in addition to 2.0, it will still return */
+ /* 2.0 in wVersion since that is the version we */
+ /* requested. */
+
+ if ( LOBYTE( wsaData.wVersion ) != 2 ||
+ HIBYTE( wsaData.wVersion ) != 0 )
+ {
+ /* Tell the user that we couldn't find a usable */
+ /* WinSock DLL. */
+ WSACleanup( );
+ goto done;
+ }
+} /* The WinSock DLL is acceptable. Proceed. */
+#elif defined(HAVE_WINSOCK)
+{ WSADATA wsaData;
+ if ( WSAStartup( 0x0101, &wsaData ) != 0 ) {
+ goto done;
+ }
+}
+#endif
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+ LDAP_MUTEX_LOCK( &ldap_int_hostname_mutex );
+ {
+ char *name = ldap_int_hostname;
+
+ ldap_int_hostname = ldap_pvt_get_fqdn( name );
+
+ if ( name != NULL && name != ldap_int_hostname ) {
+ LDAP_FREE( name );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_hostname_mutex );
+#endif
+
+#ifndef HAVE_POLL
+ if ( ldap_int_tblsize == 0 ) ldap_int_ip_init();
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_init() != 0 ) {
+ goto done;
+ }
+#endif
+
+ ldap_int_initialize_global_options(gopts, dbglvl);
+
+ if( getenv("LDAPNOINIT") != NULL ) {
+ goto done;
+ }
+
+#ifdef HAVE_CYRUS_SASL
+ {
+ /* set authentication identity to current user name */
+ char *user = getenv("USER");
+
+ if( user == NULL ) user = getenv("USERNAME");
+ if( user == NULL ) user = getenv("LOGNAME");
+
+ if( user != NULL ) {
+ gopts->ldo_def_sasl_authcid = LDAP_STRDUP( user );
+ }
+ }
+#endif
+
+ openldap_ldap_init_w_sysconf(LDAP_CONF_FILE);
+
+#ifdef HAVE_GETEUID
+ if ( geteuid() != getuid() )
+ goto done;
+#endif
+
+ openldap_ldap_init_w_userconf(LDAP_USERRC_FILE);
+
+ {
+ char *altfile = getenv(LDAP_ENV_PREFIX "CONF");
+
+ if( altfile != NULL ) {
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n",
+ LDAP_ENV_PREFIX "CONF", altfile, 0);
+ openldap_ldap_init_w_sysconf( altfile );
+ }
+ else
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n",
+ LDAP_ENV_PREFIX "CONF", 0, 0);
+ }
+
+ {
+ char *altfile = getenv(LDAP_ENV_PREFIX "RC");
+
+ if( altfile != NULL ) {
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is %s\n",
+ LDAP_ENV_PREFIX "RC", altfile, 0);
+ openldap_ldap_init_w_userconf( altfile );
+ }
+ else
+ Debug(LDAP_DEBUG_TRACE, "ldap_init: %s env is NULL\n",
+ LDAP_ENV_PREFIX "RC", 0, 0);
+ }
+
+ openldap_ldap_init_w_env(gopts, NULL);
+
+done:;
+#ifdef LDAP_R_COMPILE
+ LDAP_MUTEX_UNLOCK( &init_mutex );
+#endif
+}
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
new file mode 100644
index 0000000..98ad4dc
--- /dev/null
+++ b/libraries/libldap/ldap-int.h
@@ -0,0 +1,889 @@
+/* ldap-int.h - defines & prototypes internal to the LDAP library */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#ifndef _LDAP_INT_H
+#define _LDAP_INT_H 1
+
+#ifdef LDAP_R_COMPILE
+#define LDAP_THREAD_SAFE 1
+#endif
+
+#include "../liblber/lber-int.h"
+#include "lutil.h"
+
+#ifdef LDAP_R_COMPILE
+#include <ldap_pvt_thread.h>
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ /* the need for this should be removed */
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#define SASL_MAX_BUFF_SIZE (0xffffff)
+#define SASL_MIN_BUFF_SIZE 4096
+#endif
+
+/* for struct timeval */
+#include <ac/time.h>
+#ifdef _WIN32
+#include <ac/socket.h>
+#endif
+
+#undef TV2MILLISEC
+#define TV2MILLISEC(tv) (((tv)->tv_sec * 1000) + ((tv)->tv_usec/1000))
+
+/*
+ * Support needed if the library is running in the kernel
+ */
+#if LDAP_INT_IN_KERNEL
+ /*
+ * Platform specific function to return a pointer to the
+ * process-specific global options.
+ *
+ * This function should perform the following functions:
+ * Allocate and initialize a global options struct on a per process basis
+ * Use callers process identifier to return its global options struct
+ * Note: Deallocate structure when the process exits
+ */
+# define LDAP_INT_GLOBAL_OPT() ldap_int_global_opt()
+ struct ldapoptions *ldap_int_global_opt(void);
+#else
+# define LDAP_INT_GLOBAL_OPT() (&ldap_int_global_options)
+#endif
+
+#define ldap_debug ((LDAP_INT_GLOBAL_OPT())->ldo_debug)
+
+#include "ldap_log.h"
+
+#undef Debug
+
+#ifdef LDAP_DEBUG
+
+#define DebugTest( level ) \
+ ( ldap_debug & level )
+
+#define Debug( level, fmt, arg1, arg2, arg3 ) \
+ do { if ( ldap_debug & level ) \
+ ldap_log_printf( NULL, (level), (fmt), (arg1), (arg2), (arg3) ); \
+ } while ( 0 )
+
+#define LDAP_Debug( subsystem, level, fmt, arg1, arg2, arg3 )\
+ ldap_log_printf( NULL, (level), (fmt), (arg1), (arg2), (arg3) )
+
+#else
+
+#define DebugTest( level ) (0 == 1)
+#define Debug( level, fmt, arg1, arg2, arg3 ) ((void)0)
+#define LDAP_Debug( subsystem, level, fmt, arg1, arg2, arg3 ) ((void)0)
+
+#endif /* LDAP_DEBUG */
+
+#define LDAP_DEPRECATED 1
+#include "ldap.h"
+
+#include "ldap_pvt.h"
+
+LDAP_BEGIN_DECL
+
+#define LDAP_URL_PREFIX "ldap://"
+#define LDAP_URL_PREFIX_LEN STRLENOF(LDAP_URL_PREFIX)
+#define LDAPS_URL_PREFIX "ldaps://"
+#define LDAPS_URL_PREFIX_LEN STRLENOF(LDAPS_URL_PREFIX)
+#define LDAPI_URL_PREFIX "ldapi://"
+#define LDAPI_URL_PREFIX_LEN STRLENOF(LDAPI_URL_PREFIX)
+#ifdef LDAP_CONNECTIONLESS
+#define LDAPC_URL_PREFIX "cldap://"
+#define LDAPC_URL_PREFIX_LEN STRLENOF(LDAPC_URL_PREFIX)
+#endif
+#define LDAP_URL_URLCOLON "URL:"
+#define LDAP_URL_URLCOLON_LEN STRLENOF(LDAP_URL_URLCOLON)
+
+#define LDAP_REF_STR "Referral:\n"
+#define LDAP_REF_STR_LEN STRLENOF(LDAP_REF_STR)
+#define LDAP_LDAP_REF_STR LDAP_URL_PREFIX
+#define LDAP_LDAP_REF_STR_LEN LDAP_URL_PREFIX_LEN
+
+#define LDAP_DEFAULT_REFHOPLIMIT 5
+
+#define LDAP_BOOL_REFERRALS 0
+#define LDAP_BOOL_RESTART 1
+#define LDAP_BOOL_TLS 3
+#define LDAP_BOOL_CONNECT_ASYNC 4
+#define LDAP_BOOL_SASL_NOCANON 5
+
+#define LDAP_BOOLEANS unsigned long
+#define LDAP_BOOL(n) ((LDAP_BOOLEANS)1 << (n))
+#define LDAP_BOOL_GET(lo, bool) \
+ ((lo)->ldo_booleans & LDAP_BOOL(bool) ? -1 : 0)
+#define LDAP_BOOL_SET(lo, bool) ((lo)->ldo_booleans |= LDAP_BOOL(bool))
+#define LDAP_BOOL_CLR(lo, bool) ((lo)->ldo_booleans &= ~LDAP_BOOL(bool))
+#define LDAP_BOOL_ZERO(lo) ((lo)->ldo_booleans = 0)
+
+/*
+ * This structure represents both ldap messages and ldap responses.
+ * These are really the same, except in the case of search responses,
+ * where a response has multiple messages.
+ */
+
+struct ldapmsg {
+ ber_int_t lm_msgid; /* the message id */
+ ber_tag_t lm_msgtype; /* the message type */
+ BerElement *lm_ber; /* the ber encoded message contents */
+ struct ldapmsg *lm_chain; /* for search - next msg in the resp */
+ struct ldapmsg *lm_chain_tail;
+ struct ldapmsg *lm_next; /* next response */
+ time_t lm_time; /* used to maintain cache */
+};
+
+#ifdef HAVE_TLS
+struct ldaptls {
+ char *lt_certfile;
+ char *lt_keyfile;
+ char *lt_dhfile;
+ char *lt_cacertfile;
+ char *lt_cacertdir;
+ char *lt_ciphersuite;
+ char *lt_crlfile;
+ char *lt_randfile; /* OpenSSL only */
+ char *lt_ecname; /* OpenSSL only */
+ int lt_protocol_min;
+};
+#endif
+
+typedef struct ldaplist {
+ struct ldaplist *ll_next;
+ void *ll_data;
+} ldaplist;
+
+/*
+ * structure representing get/set'able options
+ * which have global defaults.
+ * Protect access to this struct with ldo_mutex
+ * ldap_log.h:ldapoptions_prefix must match the head of this struct.
+ */
+struct ldapoptions {
+ short ldo_valid;
+#define LDAP_UNINITIALIZED 0x0
+#define LDAP_INITIALIZED 0x1
+#define LDAP_VALID_SESSION 0x2
+#define LDAP_TRASHED_SESSION 0xFF
+ int ldo_debug;
+
+ ber_int_t ldo_version;
+ ber_int_t ldo_deref;
+ ber_int_t ldo_timelimit;
+ ber_int_t ldo_sizelimit;
+
+ /* per API call timeout */
+ struct timeval ldo_tm_api;
+ struct timeval ldo_tm_net;
+
+ LDAPURLDesc *ldo_defludp;
+ int ldo_defport;
+ char* ldo_defbase;
+ char* ldo_defbinddn; /* bind dn */
+
+ /*
+ * Per connection tcp-keepalive settings (Linux only,
+ * ignored where unsupported)
+ */
+ ber_int_t ldo_keepalive_idle;
+ ber_int_t ldo_keepalive_probes;
+ ber_int_t ldo_keepalive_interval;
+
+ int ldo_refhoplimit; /* limit on referral nesting */
+
+ /* LDAPv3 server and client controls */
+ LDAPControl **ldo_sctrls;
+ LDAPControl **ldo_cctrls;
+
+ /* LDAP rebind callback function */
+ LDAP_REBIND_PROC *ldo_rebind_proc;
+ void *ldo_rebind_params;
+ LDAP_NEXTREF_PROC *ldo_nextref_proc;
+ void *ldo_nextref_params;
+ LDAP_URLLIST_PROC *ldo_urllist_proc;
+ void *ldo_urllist_params;
+
+ /* LDAP connection callback stack */
+ ldaplist *ldo_conn_cbs;
+
+ LDAP_BOOLEANS ldo_booleans; /* boolean options */
+
+#define LDAP_LDO_NULLARG ,0,0,0,0 ,{0},{0} ,0,0,0,0, 0,0,0,0, 0,0, 0,0,0,0,0,0, 0, 0
+
+#ifdef LDAP_CONNECTIONLESS
+#define LDAP_IS_UDP(ld) ((ld)->ld_options.ldo_is_udp)
+ void* ldo_peer; /* struct sockaddr* */
+ char* ldo_cldapdn;
+ int ldo_is_udp;
+#define LDAP_LDO_CONNECTIONLESS_NULLARG ,0,0,0
+#else
+#define LDAP_LDO_CONNECTIONLESS_NULLARG
+#endif
+
+#ifdef HAVE_TLS
+ /* tls context */
+ void *ldo_tls_ctx;
+ LDAP_TLS_CONNECT_CB *ldo_tls_connect_cb;
+ void* ldo_tls_connect_arg;
+ struct ldaptls ldo_tls_info;
+#define ldo_tls_certfile ldo_tls_info.lt_certfile
+#define ldo_tls_keyfile ldo_tls_info.lt_keyfile
+#define ldo_tls_dhfile ldo_tls_info.lt_dhfile
+#define ldo_tls_ecname ldo_tls_info.lt_ecname
+#define ldo_tls_cacertfile ldo_tls_info.lt_cacertfile
+#define ldo_tls_cacertdir ldo_tls_info.lt_cacertdir
+#define ldo_tls_ciphersuite ldo_tls_info.lt_ciphersuite
+#define ldo_tls_protocol_min ldo_tls_info.lt_protocol_min
+#define ldo_tls_crlfile ldo_tls_info.lt_crlfile
+#define ldo_tls_randfile ldo_tls_info.lt_randfile
+ int ldo_tls_mode;
+ int ldo_tls_require_cert;
+ int ldo_tls_impl;
+ int ldo_tls_crlcheck;
+ int ldo_tls_require_san;
+#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0
+#else
+#define LDAP_LDO_TLS_NULLARG
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ char* ldo_def_sasl_mech; /* SASL Mechanism(s) */
+ char* ldo_def_sasl_realm; /* SASL realm */
+ char* ldo_def_sasl_authcid; /* SASL authentication identity */
+ char* ldo_def_sasl_authzid; /* SASL authorization identity */
+
+ /* SASL Security Properties */
+ struct sasl_security_properties ldo_sasl_secprops;
+#define LDAP_LDO_SASL_NULLARG ,0,0,0,0,{0}
+#else
+#define LDAP_LDO_SASL_NULLARG
+#endif
+
+#ifdef HAVE_GSSAPI
+ unsigned ldo_gssapi_flags;
+#define LDAP_GSSAPI_OPT_DO_NOT_FREE_GSS_CONTEXT 0x0001
+#define LDAP_GSSAPI_OPT_ALLOW_REMOTE_PRINCIPAL 0x0002
+ unsigned ldo_gssapi_options;
+#define LDAP_LDO_GSSAPI_NULLARG ,0,0
+#else
+#define LDAP_LDO_GSSAPI_NULLARG
+#endif
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ldo_mutex;
+#define LDAP_LDO_MUTEX_NULLARG , LDAP_PVT_MUTEX_NULL
+#else
+#define LDAP_LDO_MUTEX_NULLARG
+#endif
+};
+
+
+/*
+ * structure for representing an LDAP server connection
+ */
+typedef struct ldap_conn {
+ Sockbuf *lconn_sb;
+#ifdef HAVE_CYRUS_SASL
+ void *lconn_sasl_authctx; /* context for bind */
+ void *lconn_sasl_sockctx; /* for security layer */
+#endif
+#ifdef HAVE_GSSAPI
+ void *lconn_gss_ctx; /* gss_ctx_id_t */
+#endif
+ int lconn_refcnt;
+ time_t lconn_created; /* time */
+ time_t lconn_lastused; /* time */
+ int lconn_rebind_inprogress; /* set if rebind in progress */
+ char ***lconn_rebind_queue; /* used if rebind in progress */
+ int lconn_status;
+#define LDAP_CONNST_NEEDSOCKET 1
+#define LDAP_CONNST_CONNECTING 2
+#define LDAP_CONNST_CONNECTED 3
+ LDAPURLDesc *lconn_server;
+ BerElement *lconn_ber; /* ber receiving on this conn. */
+
+ struct ldap_conn *lconn_next;
+} LDAPConn;
+
+
+/*
+ * structure used to track outstanding requests
+ */
+typedef struct ldapreq {
+ ber_int_t lr_msgid; /* the message id */
+ int lr_status; /* status of request */
+#define LDAP_REQST_COMPLETED 0
+#define LDAP_REQST_INPROGRESS 1
+#define LDAP_REQST_CHASINGREFS 2
+#define LDAP_REQST_NOTCONNECTED 3
+#define LDAP_REQST_WRITING 4
+ int lr_refcnt; /* count of references */
+ int lr_outrefcnt; /* count of outstanding referrals */
+ int lr_abandoned; /* the request has been abandoned */
+ ber_int_t lr_origid; /* original request's message id */
+ int lr_parentcnt; /* count of parent requests */
+ ber_tag_t lr_res_msgtype; /* result message type */
+ ber_int_t lr_res_errno; /* result LDAP errno */
+ char *lr_res_error; /* result error string */
+ char *lr_res_matched;/* result matched DN string */
+ BerElement *lr_ber; /* ber encoded request contents */
+ LDAPConn *lr_conn; /* connection used to send request */
+ struct berval lr_dn; /* DN of request, in lr_ber */
+ struct ldapreq *lr_parent; /* request that spawned this referral */
+ struct ldapreq *lr_child; /* first child request */
+ struct ldapreq *lr_refnext; /* next referral spawned */
+ struct ldapreq *lr_prev; /* previous request */
+ struct ldapreq *lr_next; /* next request */
+} LDAPRequest;
+
+/*
+ * structure for client cache
+ */
+#define LDAP_CACHE_BUCKETS 31 /* cache hash table size */
+typedef struct ldapcache {
+ LDAPMessage *lc_buckets[LDAP_CACHE_BUCKETS];/* hash table */
+ LDAPMessage *lc_requests; /* unfulfilled reqs */
+ long lc_timeout; /* request timeout */
+ ber_len_t lc_maxmem; /* memory to use */
+ ber_len_t lc_memused; /* memory in use */
+ int lc_enabled; /* enabled? */
+ unsigned long lc_options; /* options */
+#define LDAP_CACHE_OPT_CACHENOERRS 0x00000001
+#define LDAP_CACHE_OPT_CACHEALLERRS 0x00000002
+} LDAPCache;
+
+/*
+ * structure containing referral request info for rebind procedure
+ */
+typedef struct ldapreqinfo {
+ ber_len_t ri_msgid;
+ int ri_request;
+ char *ri_url;
+} LDAPreqinfo;
+
+/*
+ * structure representing an ldap connection
+ */
+
+struct ldap_common {
+ Sockbuf *ldc_sb; /* socket descriptor & buffer */
+#define ld_sb ldc->ldc_sb
+
+ unsigned short ldc_lberoptions;
+#define ld_lberoptions ldc->ldc_lberoptions
+
+ /* protected by msgid_mutex */
+ ber_len_t ldc_msgid;
+#define ld_msgid ldc->ldc_msgid
+
+ /* do not mess with these */
+ /* protected by req_mutex */
+ LDAPRequest *ldc_requests; /* list of outstanding requests */
+ /* protected by res_mutex */
+ LDAPMessage *ldc_responses; /* list of outstanding responses */
+#define ld_requests ldc->ldc_requests
+#define ld_responses ldc->ldc_responses
+
+ /* protected by abandon_mutex */
+ ber_len_t ldc_nabandoned;
+ ber_int_t *ldc_abandoned; /* array of abandoned requests */
+#define ld_nabandoned ldc->ldc_nabandoned
+#define ld_abandoned ldc->ldc_abandoned
+
+ /* unused by libldap */
+ LDAPCache *ldc_cache; /* non-null if cache is initialized */
+#define ld_cache ldc->ldc_cache
+
+ /* do not mess with the rest though */
+
+ /* protected by conn_mutex */
+ LDAPConn *ldc_defconn; /* default connection */
+#define ld_defconn ldc->ldc_defconn
+ LDAPConn *ldc_conns; /* list of server connections */
+#define ld_conns ldc->ldc_conns
+ void *ldc_selectinfo;/* platform specifics for select */
+#define ld_selectinfo ldc->ldc_selectinfo
+
+ /* ldap_common refcnt - free only if 0 */
+ /* protected by ldc_mutex */
+ unsigned int ldc_refcnt;
+#define ld_ldcrefcnt ldc->ldc_refcnt
+
+ /* protected by ldo_mutex */
+ struct ldapoptions ldc_options;
+#define ld_options ldc->ldc_options
+
+#define ld_valid ld_options.ldo_valid
+#define ld_debug ld_options.ldo_debug
+
+#define ld_deref ld_options.ldo_deref
+#define ld_timelimit ld_options.ldo_timelimit
+#define ld_sizelimit ld_options.ldo_sizelimit
+
+#define ld_defbinddn ld_options.ldo_defbinddn
+#define ld_defbase ld_options.ldo_defbase
+#define ld_defhost ld_options.ldo_defhost
+#define ld_defport ld_options.ldo_defport
+
+#define ld_refhoplimit ld_options.ldo_refhoplimit
+
+#define ld_sctrls ld_options.ldo_sctrls
+#define ld_cctrls ld_options.ldo_cctrls
+#define ld_rebind_proc ld_options.ldo_rebind_proc
+#define ld_rebind_params ld_options.ldo_rebind_params
+#define ld_nextref_proc ld_options.ldo_nextref_proc
+#define ld_nextref_params ld_options.ldo_nextref_params
+#define ld_urllist_proc ld_options.ldo_urllist_proc
+#define ld_urllist_params ld_options.ldo_urllist_params
+
+#define ld_version ld_options.ldo_version
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ldc_mutex;
+ ldap_pvt_thread_mutex_t ldc_msgid_mutex;
+ ldap_pvt_thread_mutex_t ldc_conn_mutex;
+ ldap_pvt_thread_mutex_t ldc_req_mutex;
+ ldap_pvt_thread_mutex_t ldc_res_mutex;
+ ldap_pvt_thread_mutex_t ldc_abandon_mutex;
+#define ld_ldopts_mutex ld_options.ldo_mutex
+#define ld_ldcmutex ldc->ldc_mutex
+#define ld_msgid_mutex ldc->ldc_msgid_mutex
+#define ld_conn_mutex ldc->ldc_conn_mutex
+#define ld_req_mutex ldc->ldc_req_mutex
+#define ld_res_mutex ldc->ldc_res_mutex
+#define ld_abandon_mutex ldc->ldc_abandon_mutex
+#endif
+};
+
+struct ldap {
+ /* thread shared */
+ struct ldap_common *ldc;
+
+ /* thread specific */
+ ber_int_t ld_errno;
+ char *ld_error;
+ char *ld_matched;
+ char **ld_referrals;
+};
+
+#define LDAP_VALID(ld) ( (ld)->ld_valid == LDAP_VALID_SESSION )
+#define LDAP_TRASHED(ld) ( (ld)->ld_valid == LDAP_TRASHED_SESSION )
+#define LDAP_TRASH(ld) ( (ld)->ld_valid = LDAP_TRASHED_SESSION )
+
+#ifdef LDAP_R_COMPILE
+LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_resolv_mutex;
+LDAP_V ( ldap_pvt_thread_mutex_t ) ldap_int_hostname_mutex;
+
+#ifdef HAVE_GSSAPI
+LDAP_V( ldap_pvt_thread_mutex_t ) ldap_int_gssapi_mutex;
+#endif
+#endif
+
+#ifdef LDAP_R_COMPILE
+#define LDAP_MUTEX_LOCK(mutex) ldap_pvt_thread_mutex_lock( mutex )
+#define LDAP_MUTEX_UNLOCK(mutex) ldap_pvt_thread_mutex_unlock( mutex )
+#define LDAP_ASSERT_MUTEX_OWNER(mutex) \
+ LDAP_PVT_THREAD_ASSERT_MUTEX_OWNER(mutex)
+#else
+#define LDAP_MUTEX_LOCK(mutex) ((void) 0)
+#define LDAP_MUTEX_UNLOCK(mutex) ((void) 0)
+#define LDAP_ASSERT_MUTEX_OWNER(mutex) ((void) 0)
+#endif
+
+#define LDAP_NEXT_MSGID(ld, id) do { \
+ LDAP_MUTEX_LOCK( &(ld)->ld_msgid_mutex ); \
+ (id) = ++(ld)->ld_msgid; \
+ LDAP_MUTEX_UNLOCK( &(ld)->ld_msgid_mutex ); \
+} while (0)
+
+/*
+ * in abandon.c
+ */
+
+LDAP_F (int)
+ldap_int_bisect_find( ber_int_t *v, ber_len_t n, ber_int_t id, int *idxp );
+LDAP_F (int)
+ldap_int_bisect_insert( ber_int_t **vp, ber_len_t *np, int id, int idx );
+LDAP_F (int)
+ldap_int_bisect_delete( ber_int_t **vp, ber_len_t *np, int id, int idx );
+
+/*
+ * in add.c
+ */
+
+LDAP_F (BerElement *) ldap_build_add_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **attrs,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in compare.c
+ */
+
+LDAP_F (BerElement *) ldap_build_compare_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *attr,
+ struct berval *bvalue,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in delete.c
+ */
+
+LDAP_F (BerElement *) ldap_build_delete_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in extended.c
+ */
+
+LDAP_F (BerElement *) ldap_build_extended_req LDAP_P((
+ LDAP *ld,
+ const char *reqoid,
+ struct berval *reqdata,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in init.c
+ */
+
+LDAP_V ( struct ldapoptions ) ldap_int_global_options;
+
+LDAP_F ( void ) ldap_int_initialize LDAP_P((struct ldapoptions *, int *));
+LDAP_F ( void ) ldap_int_initialize_global_options LDAP_P((
+ struct ldapoptions *, int *));
+
+/* memory.c */
+ /* simple macros to realloc for now */
+#define LDAP_MALLOC(s) (ber_memalloc_x((s),NULL))
+#define LDAP_CALLOC(n,s) (ber_memcalloc_x((n),(s),NULL))
+#define LDAP_REALLOC(p,s) (ber_memrealloc_x((p),(s),NULL))
+#define LDAP_FREE(p) (ber_memfree_x((p),NULL))
+#define LDAP_VFREE(v) (ber_memvfree_x((void **)(v),NULL))
+#define LDAP_STRDUP(s) (ber_strdup_x((s),NULL))
+#define LDAP_STRNDUP(s,l) (ber_strndup_x((s),(l),NULL))
+
+#define LDAP_MALLOCX(s,x) (ber_memalloc_x((s),(x)))
+#define LDAP_CALLOCX(n,s,x) (ber_memcalloc_x((n),(s),(x)))
+#define LDAP_REALLOCX(p,s,x) (ber_memrealloc_x((p),(s),(x)))
+#define LDAP_FREEX(p,x) (ber_memfree_x((p),(x)))
+#define LDAP_VFREEX(v,x) (ber_memvfree_x((void **)(v),(x)))
+#define LDAP_STRDUPX(s,x) (ber_strdup_x((s),(x)))
+#define LDAP_STRNDUPX(s,l,x) (ber_strndup_x((s),(l),(x)))
+
+/*
+ * in error.c
+ */
+LDAP_F (void) ldap_int_error_init( void );
+
+/*
+ * in modify.c
+ */
+
+LDAP_F (BerElement *) ldap_build_modify_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in modrdn.c
+ */
+
+LDAP_F (BerElement *) ldap_build_moddn_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *newrdn,
+ const char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/*
+ * in unit-int.c
+ */
+LDAP_F (void) ldap_int_utils_init LDAP_P(( void ));
+
+
+/*
+ * in print.c
+ */
+LDAP_F (int) ldap_log_printf LDAP_P((LDAP *ld, int level, const char *fmt, ...)) LDAP_GCCATTR((format(printf, 3, 4)));
+
+/*
+ * in cache.c
+ */
+LDAP_F (void) ldap_add_request_to_cache LDAP_P(( LDAP *ld, ber_tag_t msgtype,
+ BerElement *request ));
+LDAP_F (void) ldap_add_result_to_cache LDAP_P(( LDAP *ld, LDAPMessage *result ));
+LDAP_F (int) ldap_check_cache LDAP_P(( LDAP *ld, ber_tag_t msgtype, BerElement *request ));
+
+/*
+ * in controls.c
+ */
+LDAP_F (int) ldap_int_put_controls LDAP_P((
+ LDAP *ld,
+ LDAPControl *const *ctrls,
+ BerElement *ber ));
+
+LDAP_F (int) ldap_int_client_controls LDAP_P((
+ LDAP *ld,
+ LDAPControl **ctrlp ));
+
+/*
+ * in dsparse.c
+ */
+LDAP_F (int) ldap_int_next_line_tokens LDAP_P(( char **bufp, ber_len_t *blenp, char ***toksp ));
+
+
+/*
+ * in open.c
+ */
+LDAP_F (int) ldap_open_defconn( LDAP *ld );
+LDAP_F (int) ldap_int_open_connection( LDAP *ld,
+ LDAPConn *conn, LDAPURLDesc *srvlist, int async );
+LDAP_F (int) ldap_int_check_async_open( LDAP *ld, ber_socket_t sd );
+
+/*
+ * in os-ip.c
+ */
+#ifndef HAVE_POLL
+LDAP_V (int) ldap_int_tblsize;
+LDAP_F (void) ldap_int_ip_init( void );
+#endif
+
+LDAP_F (int) ldap_int_timeval_dup( struct timeval **dest,
+ const struct timeval *tm );
+LDAP_F (int) ldap_connect_to_host( LDAP *ld, Sockbuf *sb,
+ int proto, LDAPURLDesc *srv, int async );
+LDAP_F (int) ldap_int_poll( LDAP *ld, ber_socket_t s,
+ struct timeval *tvp, int wr );
+
+#if defined(HAVE_TLS) || defined(HAVE_CYRUS_SASL)
+LDAP_V (char *) ldap_int_hostname;
+LDAP_F (char *) ldap_host_connected_to( Sockbuf *sb,
+ const char *host );
+#endif
+
+LDAP_F (int) ldap_int_select( LDAP *ld, struct timeval *timeout );
+LDAP_F (void *) ldap_new_select_info( void );
+LDAP_F (void) ldap_free_select_info( void *sip );
+LDAP_F (void) ldap_mark_select_write( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_mark_select_read( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_mark_select_clear( LDAP *ld, Sockbuf *sb );
+LDAP_F (void) ldap_clear_select_write( LDAP *ld, Sockbuf *sb );
+LDAP_F (int) ldap_is_read_ready( LDAP *ld, Sockbuf *sb );
+LDAP_F (int) ldap_is_write_ready( LDAP *ld, Sockbuf *sb );
+
+LDAP_F (int) ldap_int_connect_cbs( LDAP *ld, Sockbuf *sb,
+ ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr );
+
+/*
+ * in os-local.c
+ */
+#ifdef LDAP_PF_LOCAL
+LDAP_F (int) ldap_connect_to_path( LDAP *ld, Sockbuf *sb,
+ LDAPURLDesc *srv, int async );
+#endif /* LDAP_PF_LOCAL */
+
+/*
+ * in request.c
+ */
+LDAP_F (ber_int_t) ldap_send_initial_request( LDAP *ld, ber_tag_t msgtype,
+ const char *dn, BerElement *ber, ber_int_t msgid );
+LDAP_F (BerElement *) ldap_alloc_ber_with_options( LDAP *ld );
+LDAP_F (void) ldap_set_ber_options( LDAP *ld, BerElement *ber );
+
+LDAP_F (int) ldap_send_server_request( LDAP *ld, BerElement *ber,
+ ber_int_t msgid, LDAPRequest *parentreq, LDAPURLDesc **srvlist,
+ LDAPConn *lc, LDAPreqinfo *bind, int noconn, int m_res );
+LDAP_F (LDAPConn *) ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist,
+ int use_ldsb, int connect, LDAPreqinfo *bind, int m_req, int m_res );
+LDAP_F (LDAPRequest *) ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid );
+LDAP_F (void) ldap_return_request( LDAP *ld, LDAPRequest *lr, int freeit );
+LDAP_F (void) ldap_free_request( LDAP *ld, LDAPRequest *lr );
+LDAP_F (void) ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind );
+LDAP_F (void) ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all );
+LDAP_F (void) ldap_dump_requests_and_responses( LDAP *ld );
+LDAP_F (int) ldap_chase_referrals( LDAP *ld, LDAPRequest *lr,
+ char **errstrp, int sref, int *hadrefp );
+LDAP_F (int) ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr,
+ char **refs, int sref, char **referralsp, int *hadrefp );
+LDAP_F (int) ldap_append_referral( LDAP *ld, char **referralsp, char *s );
+LDAP_F (int) ldap_int_flush_request( LDAP *ld, LDAPRequest *lr );
+
+/*
+ * in result.c:
+ */
+LDAP_F (const char *) ldap_int_msgtype2str( ber_tag_t tag );
+
+/*
+ * in search.c
+ */
+LDAP_F (BerElement *) ldap_build_search_req LDAP_P((
+ LDAP *ld,
+ const char *base,
+ ber_int_t scope,
+ const char *filter,
+ char **attrs,
+ ber_int_t attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t timelimit,
+ ber_int_t sizelimit,
+ ber_int_t deref,
+ ber_int_t *msgidp));
+
+
+/*
+ * in unbind.c
+ */
+LDAP_F (int) ldap_ld_free LDAP_P((
+ LDAP *ld,
+ int close,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+LDAP_F (int) ldap_send_unbind LDAP_P((
+ LDAP *ld,
+ Sockbuf *sb,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls ));
+
+/*
+ * in url.c
+ */
+LDAP_F (LDAPURLDesc *) ldap_url_dup LDAP_P((
+ LDAPURLDesc *ludp ));
+
+LDAP_F (LDAPURLDesc *) ldap_url_duplist LDAP_P((
+ LDAPURLDesc *ludlist ));
+
+LDAP_F (int) ldap_url_parsehosts LDAP_P((
+ LDAPURLDesc **ludlist,
+ const char *hosts,
+ int port ));
+
+LDAP_F (char *) ldap_url_list2hosts LDAP_P((
+ LDAPURLDesc *ludlist ));
+
+/*
+ * in cyrus.c
+ */
+
+LDAP_F (int) ldap_int_sasl_init LDAP_P(( void ));
+
+LDAP_F (int) ldap_int_sasl_open LDAP_P((
+ LDAP *ld, LDAPConn *conn,
+ const char* host ));
+LDAP_F (int) ldap_int_sasl_close LDAP_P(( LDAP *ld, LDAPConn *conn ));
+
+LDAP_F (int) ldap_int_sasl_external LDAP_P((
+ LDAP *ld, LDAPConn *conn,
+ const char* authid, ber_len_t ssf ));
+
+LDAP_F (int) ldap_int_sasl_get_option LDAP_P(( LDAP *ld,
+ int option, void *arg ));
+LDAP_F (int) ldap_int_sasl_set_option LDAP_P(( LDAP *ld,
+ int option, void *arg ));
+LDAP_F (int) ldap_int_sasl_config LDAP_P(( struct ldapoptions *lo,
+ int option, const char *arg ));
+
+LDAP_F (int) ldap_int_sasl_bind LDAP_P((
+ LDAP *ld,
+ const char *,
+ const char *,
+ LDAPControl **, LDAPControl **,
+
+ /* should be passed in client controls */
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid ));
+
+/* in sasl.c */
+
+LDAP_F (BerElement *) ldap_build_bind_req LDAP_P((
+ LDAP *ld,
+ const char *dn,
+ const char *mech,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp ));
+
+/* in schema.c */
+LDAP_F (char *) ldap_int_parse_numericoid LDAP_P((
+ const char **sp,
+ int *code,
+ const int flags ));
+
+/*
+ * in tls.c
+ */
+LDAP_F (int) ldap_int_tls_config LDAP_P(( LDAP *ld,
+ int option, const char *arg ));
+
+LDAP_F (int) ldap_int_tls_start LDAP_P(( LDAP *ld,
+ LDAPConn *conn, LDAPURLDesc *srv ));
+
+LDAP_F (void) ldap_int_tls_destroy LDAP_P(( struct ldapoptions *lo ));
+
+/*
+ * in getvalues.c
+ */
+LDAP_F (char **) ldap_value_dup LDAP_P((
+ char *const *vals ));
+
+/*
+ * in gssapi.c
+ */
+#ifdef HAVE_GSSAPI
+LDAP_F(int) ldap_int_gssapi_get_option LDAP_P(( LDAP *ld, int option, void *arg ));
+LDAP_F(int) ldap_int_gssapi_set_option LDAP_P(( LDAP *ld, int option, void *arg ));
+LDAP_F(int) ldap_int_gssapi_config LDAP_P(( struct ldapoptions *lo, int option, const char *arg ));
+LDAP_F(void) ldap_int_gssapi_close LDAP_P(( LDAP *ld, LDAPConn *lc ));
+#endif
+
+LDAP_END_DECL
+
+#endif /* _LDAP_INT_H */
diff --git a/libraries/libldap/ldap-tls.h b/libraries/libldap/ldap-tls.h
new file mode 100644
index 0000000..c8a2711
--- /dev/null
+++ b/libraries/libldap/ldap-tls.h
@@ -0,0 +1,77 @@
+/* ldap-tls.h - TLS defines & prototypes internal to the LDAP library */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifndef _LDAP_TLS_H
+#define _LDAP_TLS_H 1
+
+struct tls_impl;
+
+struct tls_ctx;
+struct tls_session;
+
+typedef struct tls_ctx tls_ctx;
+typedef struct tls_session tls_session;
+
+typedef int (TI_tls_init)(void);
+typedef void (TI_tls_destroy)(void);
+
+typedef tls_ctx *(TI_ctx_new)(struct ldapoptions *lo);
+typedef void (TI_ctx_ref)(tls_ctx *ctx);
+typedef void (TI_ctx_free)(tls_ctx *ctx);
+typedef int (TI_ctx_init)(struct ldapoptions *lo, struct ldaptls *lt, int is_server);
+
+typedef tls_session *(TI_session_new)(tls_ctx *ctx, int is_server);
+typedef int (TI_session_connect)(LDAP *ld, tls_session *s);
+typedef int (TI_session_accept)(tls_session *s);
+typedef int (TI_session_upflags)(Sockbuf *sb, tls_session *s, int rc);
+typedef char *(TI_session_errmsg)(tls_session *s, int rc, char *buf, size_t len );
+typedef int (TI_session_dn)(tls_session *sess, struct berval *dn);
+typedef int (TI_session_chkhost)(LDAP *ld, tls_session *s, const char *name_in);
+typedef int (TI_session_strength)(tls_session *sess);
+
+typedef void (TI_thr_init)(void);
+
+typedef struct tls_impl {
+ const char *ti_name;
+
+ TI_tls_init *ti_tls_init; /* library initialization */
+ TI_tls_destroy *ti_tls_destroy;
+
+ TI_ctx_new *ti_ctx_new;
+ TI_ctx_ref *ti_ctx_ref;
+ TI_ctx_free *ti_ctx_free;
+ TI_ctx_init *ti_ctx_init;
+
+ TI_session_new *ti_session_new;
+ TI_session_connect *ti_session_connect;
+ TI_session_accept *ti_session_accept;
+ TI_session_upflags *ti_session_upflags;
+ TI_session_errmsg *ti_session_errmsg;
+ TI_session_dn *ti_session_my_dn;
+ TI_session_dn *ti_session_peer_dn;
+ TI_session_chkhost *ti_session_chkhost;
+ TI_session_strength *ti_session_strength;
+
+ Sockbuf_IO *ti_sbio;
+
+ TI_thr_init *ti_thr_init;
+
+ int ti_inited;
+} tls_impl;
+
+extern tls_impl ldap_int_tls_impl;
+
+#endif /* _LDAP_TLS_H */
diff --git a/libraries/libldap/ldap.conf b/libraries/libldap/ldap.conf
new file mode 100644
index 0000000..af738ad
--- /dev/null
+++ b/libraries/libldap/ldap.conf
@@ -0,0 +1,13 @@
+#
+# LDAP Defaults
+#
+
+# See ldap.conf(5) for details
+# This file should be world readable but not world writable.
+
+#BASE dc=example,dc=com
+#URI ldap://ldap.example.com ldap://ldap-provider.example.com:666
+
+#SIZELIMIT 12
+#TIMELIMIT 15
+#DEREF never
diff --git a/libraries/libldap/ldap_sync.c b/libraries/libldap/ldap_sync.c
new file mode 100644
index 0000000..b5ac207
--- /dev/null
+++ b/libraries/libldap/ldap_sync.c
@@ -0,0 +1,928 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was originally developed by Pierangelo Masarati
+ * for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * Proof-of-concept API that implement the client-side
+ * of the "LDAP Content Sync Operation" (RFC 4533)
+ */
+
+#include "portable.h"
+
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_SYNC_TRACE
+static const char *
+ldap_sync_state2str( int state )
+{
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ return "LDAP_SYNC_PRESENT";
+
+ case LDAP_SYNC_ADD:
+ return "LDAP_SYNC_ADD";
+
+ case LDAP_SYNC_MODIFY:
+ return "LDAP_SYNC_MODIFY";
+
+ case LDAP_SYNC_DELETE:
+ return "LDAP_SYNC_DELETE";
+
+ default:
+ return "(unknown)";
+ }
+}
+#endif
+
+/*
+ * initialize the persistent search structure
+ */
+ldap_sync_t *
+ldap_sync_initialize( ldap_sync_t *ls_in )
+{
+ ldap_sync_t *ls = ls_in;
+
+ if ( ls == NULL ) {
+ ls = ldap_memalloc( sizeof( ldap_sync_t ) );
+ if ( ls == NULL ) {
+ return NULL;
+ }
+ }
+ memset( ls, 0, sizeof( ldap_sync_t ) );
+
+ ls->ls_scope = LDAP_SCOPE_SUBTREE;
+ ls->ls_timeout = -1;
+
+ return ls;
+}
+
+/*
+ * destroy the persistent search structure
+ */
+void
+ldap_sync_destroy( ldap_sync_t *ls, int freeit )
+{
+ assert( ls != NULL );
+
+ if ( ls->ls_base != NULL ) {
+ ldap_memfree( ls->ls_base );
+ ls->ls_base = NULL;
+ }
+
+ if ( ls->ls_filter != NULL ) {
+ ldap_memfree( ls->ls_filter );
+ ls->ls_filter = NULL;
+ }
+
+ if ( ls->ls_attrs != NULL ) {
+ int i;
+
+ for ( i = 0; ls->ls_attrs[ i ] != NULL; i++ ) {
+ ldap_memfree( ls->ls_attrs[ i ] );
+ }
+ ldap_memfree( ls->ls_attrs );
+ ls->ls_attrs = NULL;
+ }
+
+ if ( ls->ls_ld != NULL ) {
+ (void)ldap_unbind_ext( ls->ls_ld, NULL, NULL );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_unbind_ext()\n" );
+#endif /* LDAP_SYNC_TRACE */
+ ls->ls_ld = NULL;
+ }
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ldap_memfree( ls->ls_cookie.bv_val );
+ ls->ls_cookie.bv_val = NULL;
+ }
+
+ if ( freeit ) {
+ ldap_memfree( ls );
+ }
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_ENTRY response
+ */
+static int
+ldap_sync_search_entry( ldap_sync_t *ls, LDAPMessage *res )
+{
+ LDAPControl **ctrls = NULL;
+ int rc = LDAP_OTHER,
+ i;
+ BerElement *ber = NULL;
+ struct berval entryUUID = { 0 },
+ cookie = { 0 };
+ int state = -1;
+ ber_len_t len;
+ ldap_sync_refresh_t phase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_ENTRY\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ phase = ls->ls_refreshPhase;
+
+ /* OK */
+
+ /* extract:
+ * - data
+ * - entryUUID
+ *
+ * check that:
+ * - Sync State Control is "add"
+ */
+
+ /* the control MUST be present */
+
+ /* extract controls */
+ ldap_get_entry_controls( ls->ls_ld, res, &ctrls );
+ if ( ctrls == NULL ) {
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_SYNC_STATE ) == 0 ) {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+ if ( ber == NULL ) {
+ goto done;
+ }
+ /* scan entryUUID in-place ("m") */
+ if ( ber_scanf( ber, "{em" /*"}"*/, &state, &entryUUID ) == LBER_ERROR
+ || entryUUID.bv_len == 0 )
+ {
+ goto done;
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ /* scan cookie in-place ("m") */
+ if ( ber_scanf( ber, /*"{"*/ "m}", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ switch ( state ) {
+ case LDAP_SYNC_PRESENT:
+ case LDAP_SYNC_DELETE:
+ case LDAP_SYNC_ADD:
+ case LDAP_SYNC_MODIFY:
+ /* NOTE: ldap_sync_refresh_t is defined
+ * as the corresponding LDAP_SYNC_*
+ * for the 4 above cases */
+ phase = state;
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncState=%s\n", ldap_sync_state2str( state ) );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot unknown syncState=%d\n", state );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+ rc = ls->ls_search_entry
+ ? ls->ls_search_entry( ls, res, &entryUUID, phase )
+ : LDAP_SUCCESS;
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_REFERENCE response
+ * (to be implemented yet)
+ */
+static int
+ldap_sync_search_reference( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_REFERENCE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ if ( ls->ls_search_reference ) {
+ rc = ls->ls_search_reference( ls, res );
+ }
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_SEARCH_RESULT response
+ */
+static int
+ldap_sync_search_result( ldap_sync_t *ls, LDAPMessage *res )
+{
+ int err;
+ char *matched = NULL,
+ *msg = NULL;
+ LDAPControl **ctrls = NULL;
+ int rc;
+ int refreshDeletes = -1;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_SEARCH_RESULT\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+
+ /* should not happen in refreshAndPersist... */
+ rc = ldap_parse_result( ls->ls_ld,
+ res, &err, &matched, &msg, NULL, &ctrls, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\tldap_parse_result(%d, \"%s\", \"%s\") == %d\n",
+ err,
+ matched ? matched : "",
+ msg ? msg : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc == LDAP_SUCCESS ) {
+ rc = err;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ switch ( rc ) {
+ case LDAP_SUCCESS: {
+ int i;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ struct berval cookie = { 0 };
+
+ rc = LDAP_OTHER;
+
+ /* deal with control; then fallthru to handler */
+ if ( ctrls == NULL ) {
+ goto done;
+ }
+
+ /* lookup the sync state control */
+ for ( i = 0; ctrls[ i ] != NULL; i++ ) {
+ if ( strcmp( ctrls[ i ]->ldctl_oid,
+ LDAP_CONTROL_SYNC_DONE ) == 0 )
+ {
+ break;
+ }
+ }
+
+ /* control must be present; there might be other... */
+ if ( ctrls[ i ] == NULL ) {
+ goto done;
+ }
+
+ /* extract data */
+ ber = ber_init( &ctrls[ i ]->ldctl_value );
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ if ( ber_scanf( ber, "{" /*"}"*/) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ refreshDeletes = 0;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
+ goto ber_done;
+ }
+ if ( refreshDeletes ) {
+ refreshDeletes = 1;
+ }
+ }
+
+ if ( ber_scanf( ber, /*"{"*/ "}" ) != LBER_ERROR ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ ber_done:;
+ ber_free( ber, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ break;
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ /* FIXME: what should we do with the refreshDelete? */
+ switch ( refreshDeletes ) {
+ case 0:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+ }
+
+ } /* fallthru */
+
+ case LDAP_SYNC_REFRESH_REQUIRED:
+ /* TODO: check for Sync Done Control */
+ /* FIXME: perhaps the handler should be called
+ * also in case of failure; we'll deal with this
+ * later when implementing refreshOnly */
+ if ( ls->ls_search_result ) {
+ err = ls->ls_search_result( ls, res, refreshDeletes );
+ }
+ break;
+ }
+
+done:;
+ if ( matched != NULL ) {
+ ldap_memfree( matched );
+ }
+
+ if ( msg != NULL ) {
+ ldap_memfree( msg );
+ }
+
+ if ( ctrls != NULL ) {
+ ldap_controls_free( ctrls );
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+
+ return rc;
+}
+
+/*
+ * handle the LDAP_RES_INTERMEDIATE response
+ */
+static int
+ldap_sync_search_intermediate( ldap_sync_t *ls, LDAPMessage *res, int *refreshDone )
+{
+ int rc;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ BerElement *ber = NULL;
+ ber_len_t len;
+ ber_tag_t syncinfo_tag;
+ struct berval cookie;
+ int refreshDeletes = 0;
+ BerVarray syncUUIDs = NULL;
+ ldap_sync_refresh_t phase;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot LDAP_RES_INTERMEDIATE\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( res != NULL );
+ assert( refreshDone != NULL );
+
+ *refreshDone = 0;
+
+ rc = ldap_parse_intermediate( ls->ls_ld, res,
+ &retoid, &retdata, NULL, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t%sldap_parse_intermediate(%s) == %d\n",
+ rc != LDAP_SUCCESS ? "!!! " : "",
+ retoid == NULL ? "\"\"" : retoid,
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ /* parsing must be successful, and yield the OID
+ * of the sync info intermediate response */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ rc = LDAP_OTHER;
+
+ if ( retoid == NULL || strcmp( retoid, LDAP_SYNC_INFO ) != 0 ) {
+ goto done;
+ }
+
+ /* init ber using the value in the response */
+ ber = ber_init( retdata );
+ if ( ber == NULL ) {
+ goto done;
+ }
+
+ syncinfo_tag = ber_peek_tag( ber, &len );
+ switch ( syncinfo_tag ) {
+ case LDAP_TAG_SYNC_NEW_COOKIE:
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ break;
+
+ case LDAP_TAG_SYNC_REFRESH_DELETE:
+ case LDAP_TAG_SYNC_REFRESH_PRESENT:
+ if ( syncinfo_tag == LDAP_TAG_SYNC_REFRESH_DELETE ) {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDelete\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ case LDAP_SYNC_CAPI_PRESENTS:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DELETES;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ goto done;
+ }
+
+ } else {
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshPresent\n" );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( ls->ls_refreshPhase ) {
+ case LDAP_SYNC_CAPI_NONE:
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_PRESENTS;
+ break;
+
+ default:
+ /* TODO: impossible; handle */
+ goto done;
+ }
+ }
+
+ if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ *refreshDone = 1;
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDONE ) {
+ if ( ber_scanf( ber, "b", refreshDone ) == LBER_ERROR ) {
+ goto done;
+ }
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot refreshDone=%s\n",
+ *refreshDone ? "TRUE" : "FALSE" );
+#endif /* LDAP_SYNC_TRACE */
+
+ if ( ber_scanf( ber, /*"{"*/ "}" ) == LBER_ERROR ) {
+ goto done;
+ }
+
+ if ( *refreshDone ) {
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_DONE;
+ }
+
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, NULL, ls->ls_refreshPhase );
+ }
+
+ break;
+
+ case LDAP_TAG_SYNC_ID_SET:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot syncIdSet\n" );
+#endif /* LDAP_SYNC_TRACE */
+ if ( ber_scanf( ber, "{" /*"}"*/ ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SYNC_COOKIE ) {
+ if ( ber_scanf( ber, "m", &cookie ) == LBER_ERROR ) {
+ goto done;
+ }
+ if ( cookie.bv_val != NULL ) {
+ ber_bvreplace( &ls->ls_cookie, &cookie );
+ }
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tgot cookie=%s\n",
+ cookie.bv_val ? cookie.bv_val : "(null)" );
+#endif /* LDAP_SYNC_TRACE */
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_REFRESHDELETES ) {
+ if ( ber_scanf( ber, "b", &refreshDeletes ) == LBER_ERROR ) {
+ goto done;
+ }
+ }
+
+ if ( ber_scanf( ber, /*"{"*/ "[W]}", &syncUUIDs ) == LBER_ERROR
+ || syncUUIDs == NULL )
+ {
+ goto done;
+ }
+
+#ifdef LDAP_SYNC_TRACE
+ {
+ int i;
+
+ fprintf( stderr, "\t\tgot refreshDeletes=%s\n",
+ refreshDeletes ? "TRUE" : "FALSE" );
+ for ( i = 0; syncUUIDs[ i ].bv_val != NULL; i++ ) {
+ char buf[ BUFSIZ ];
+ fprintf( stderr, "\t\t%s\n",
+ lutil_uuidstr_from_normalized(
+ syncUUIDs[ i ].bv_val, syncUUIDs[ i ].bv_len,
+ buf, sizeof( buf ) ) );
+ }
+ }
+#endif /* LDAP_SYNC_TRACE */
+
+ if ( refreshDeletes ) {
+ phase = LDAP_SYNC_CAPI_DELETES_IDSET;
+
+ } else {
+ phase = LDAP_SYNC_CAPI_PRESENTS_IDSET;
+ }
+
+ /* FIXME: should touch ls->ls_refreshPhase? */
+ if ( ls->ls_intermediate ) {
+ ls->ls_intermediate( ls, res, syncUUIDs, phase );
+ }
+
+ ber_bvarray_free( syncUUIDs );
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\t\tunknown tag!\n" );
+#endif /* LDAP_SYNC_TRACE */
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ if ( retoid != NULL ) {
+ ldap_memfree( retoid );
+ }
+
+ if ( retdata != NULL ) {
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the sync
+ */
+int
+ldap_sync_init( ldap_sync_t *ls, int mode )
+{
+ LDAPControl ctrl = { 0 },
+ *ctrls[ 2 ];
+ BerElement *ber = NULL;
+ int rc;
+ struct timeval tv = { 0 },
+ *tvp = NULL;
+ LDAPMessage *res = NULL;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_init(%s)...\n",
+ mode == LDAP_SYNC_REFRESH_AND_PERSIST ?
+ "LDAP_SYNC_REFRESH_AND_PERSIST" :
+ ( mode == LDAP_SYNC_REFRESH_ONLY ?
+ "LDAP_SYNC_REFRESH_ONLY" : "unknown" ) );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ /* support both refreshOnly and refreshAndPersist */
+ switch ( mode ) {
+ case LDAP_SYNC_REFRESH_AND_PERSIST:
+ case LDAP_SYNC_REFRESH_ONLY:
+ break;
+
+ default:
+ fprintf( stderr, "ldap_sync_init: unknown mode=%d\n", mode );
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* check consistency of cookie and reloadHint at initial refresh */
+ if ( ls->ls_cookie.bv_val == NULL && ls->ls_reloadHint != 0 ) {
+ fprintf( stderr, "ldap_sync_init: inconsistent cookie/rhint\n" );
+ return LDAP_PARAM_ERROR;
+ }
+
+ ctrls[ 0 ] = &ctrl;
+ ctrls[ 1 ] = NULL;
+
+ /* prepare the Sync Request control */
+ ber = ber_alloc_t( LBER_USE_DER );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "%sber_alloc_t() %s= NULL\n",
+ ber == NULL ? "!!! " : "",
+ ber == NULL ? "=" : "!" );
+#endif /* LDAP_SYNC_TRACE */
+ if ( ber == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ ls->ls_refreshPhase = LDAP_SYNC_CAPI_NONE;
+
+ if ( ls->ls_cookie.bv_val != NULL ) {
+ ber_printf( ber, "{eOb}", mode,
+ &ls->ls_cookie, ls->ls_reloadHint );
+
+ } else {
+ ber_printf( ber, "{eb}", mode, ls->ls_reloadHint );
+ }
+
+ rc = ber_flatten2( ber, &ctrl.ldctl_value, 0 );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sber_flatten2() == %d\n",
+ rc ? "!!! " : "",
+ rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc < 0 ) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+
+ /* make the control critical, as we cannot proceed without */
+ ctrl.ldctl_oid = LDAP_CONTROL_SYNC;
+ ctrl.ldctl_iscritical = 1;
+
+ /* timelimit? */
+ if ( ls->ls_timelimit ) {
+ tv.tv_sec = ls->ls_timelimit;
+ tvp = &tv;
+ }
+
+ /* actually run the search */
+ rc = ldap_search_ext( ls->ls_ld,
+ ls->ls_base, ls->ls_scope, ls->ls_filter,
+ ls->ls_attrs, 0, ctrls, NULL,
+ tvp, ls->ls_sizelimit, &ls->ls_msgid );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "%sldap_search_ext(\"%s\", %d, \"%s\") == %d\n",
+ rc ? "!!! " : "",
+ ls->ls_base, ls->ls_scope, ls->ls_filter, rc );
+#endif /* LDAP_SYNC_TRACE */
+ if ( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ /* initial content/content update phase */
+ for ( ; ; ) {
+ LDAPMessage *msg = NULL;
+
+ /* NOTE: this very short timeout is just to let
+ * ldap_result() yield long enough to get something */
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, &tv, &res );
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr,
+ "\t%sldap_result(%d) == %d\n",
+ rc == -1 ? "!!! " : "",
+ ls->ls_msgid, rc );
+#endif /* LDAP_SYNC_TRACE */
+ switch ( rc ) {
+ case 0:
+ /*
+ * timeout
+ *
+ * TODO: can do something else in the meanwhile)
+ */
+ break;
+
+ case -1:
+ /* smtg bad! */
+ goto done;
+
+ default:
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg != NULL;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ ldap_msgfree( res );
+ res = NULL;
+ break;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return rc;
+}
+
+/*
+ * initialize the refreshOnly sync
+ */
+int
+ldap_sync_init_refresh_only( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_ONLY );
+}
+
+/*
+ * initialize the refreshAndPersist sync
+ */
+int
+ldap_sync_init_refresh_and_persist( ldap_sync_t *ls )
+{
+ return ldap_sync_init( ls, LDAP_SYNC_REFRESH_AND_PERSIST );
+}
+
+/*
+ * poll for new responses
+ */
+int
+ldap_sync_poll( ldap_sync_t *ls )
+{
+ struct timeval tv,
+ *tvp = NULL;
+ LDAPMessage *res = NULL,
+ *msg;
+ int rc = 0;
+
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "ldap_sync_poll...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ assert( ls != NULL );
+ assert( ls->ls_ld != NULL );
+
+ if ( ls->ls_timeout != -1 ) {
+ tv.tv_sec = ls->ls_timeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ }
+
+ rc = ldap_result( ls->ls_ld, ls->ls_msgid,
+ LDAP_MSG_RECEIVED, tvp, &res );
+ if ( rc <= 0 ) {
+ return rc;
+ }
+
+ for ( msg = ldap_first_message( ls->ls_ld, res );
+ msg;
+ msg = ldap_next_message( ls->ls_ld, msg ) )
+ {
+ int refreshDone;
+
+ switch ( ldap_msgtype( msg ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ rc = ldap_sync_search_entry( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+ rc = ldap_sync_search_reference( ls, res );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ rc = ldap_sync_search_result( ls, res );
+ goto done_search;
+
+ case LDAP_RES_INTERMEDIATE:
+ rc = ldap_sync_search_intermediate( ls, res, &refreshDone );
+ if ( rc != LDAP_SUCCESS || refreshDone ) {
+ goto done_search;
+ }
+ break;
+
+ default:
+#ifdef LDAP_SYNC_TRACE
+ fprintf( stderr, "\tgot something unexpected...\n" );
+#endif /* LDAP_SYNC_TRACE */
+
+ ldap_msgfree( res );
+
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+
+done_search:;
+ ldap_msgfree( res );
+
+done:;
+ return rc;
+}
diff --git a/libraries/libldap/ldif.c b/libraries/libldap/ldif.c
new file mode 100644
index 0000000..55d9852
--- /dev/null
+++ b/libraries/libldap/ldif.c
@@ -0,0 +1,944 @@
+/* ldif.c - routines for dealing with LDIF files */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1992-1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission. This
+ * software is provided ``as is'' without express or implied warranty.
+ */
+/* This work was originally developed by the University of Michigan
+ * and distributed as part of U-MICH LDAP.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+
+#include <ac/string.h>
+#include <ac/socket.h>
+#include <ac/time.h>
+
+int ldif_debug = 0;
+
+#include "ldap_log.h"
+#include "lber_pvt.h"
+#include "ldif.h"
+
+#define RIGHT2 0x03
+#define RIGHT4 0x0f
+#define CONTINUED_LINE_MARKER '\r'
+
+#ifdef CSRIMALLOC
+#define ber_memalloc malloc
+#define ber_memcalloc calloc
+#define ber_memrealloc realloc
+#define ber_strdup strdup
+#endif
+
+static const char nib2b64[0x40] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const unsigned char b642nib[0x80] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/*
+ * ldif_parse_line - takes a line of the form "type:[:] value" and splits it
+ * into components "type" and "value". if a double colon separates type from
+ * value, then value is encoded in base 64, and parse_line un-decodes it
+ * (in place) before returning. The type and value are stored in malloc'd
+ * memory which must be freed by the caller.
+ *
+ * ldif_parse_line2 - operates in-place on input buffer, returning type
+ * in-place. Will return value in-place if possible, (must malloc for
+ * fetched URLs). If freeval is NULL, all return data will be malloc'd
+ * and the input line will be unmodified. Otherwise freeval is set to
+ * True if the value was malloc'd.
+ */
+
+int
+ldif_parse_line(
+ LDAP_CONST char *line,
+ char **typep,
+ char **valuep,
+ ber_len_t *vlenp
+)
+{
+ struct berval type, value;
+ int rc = ldif_parse_line2( (char *)line, &type, &value, NULL );
+
+ *typep = type.bv_val;
+ *valuep = value.bv_val;
+ *vlenp = value.bv_len;
+ return rc;
+}
+
+int
+ldif_parse_line2(
+ char *line,
+ struct berval *type,
+ struct berval *value,
+ int *freeval
+)
+{
+ char *s, *p, *d;
+ char nib;
+ int b64, url;
+
+ BER_BVZERO( type );
+ BER_BVZERO( value );
+
+ /* skip any leading space */
+ while ( isspace( (unsigned char) *line ) ) {
+ line++;
+ }
+
+ if ( freeval ) {
+ *freeval = 0;
+ } else {
+ line = ber_strdup( line );
+
+ if( line == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: line malloc failed\n"));
+ return( -1 );
+ }
+ }
+
+ type->bv_val = line;
+
+ s = strchr( type->bv_val, ':' );
+
+ if ( s == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: missing ':' after %s\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ /* trim any space between type and : */
+ for ( p = &s[-1]; p > type->bv_val && isspace( * (unsigned char *) p ); p-- ) {
+ *p = '\0';
+ }
+ *s++ = '\0';
+ type->bv_len = s - type->bv_val - 1;
+
+ url = 0;
+ b64 = 0;
+
+ if ( *s == '<' ) {
+ s++;
+ url = 1;
+
+ } else if ( *s == ':' ) {
+ /* base 64 encoded value */
+ s++;
+ b64 = 1;
+ }
+
+ /* skip space between : and value */
+ while ( isspace( (unsigned char) *s ) ) {
+ s++;
+ }
+
+ /* check for continued line markers that should be deleted */
+ for ( p = s, d = s; *p; p++ ) {
+ if ( *p != CONTINUED_LINE_MARKER )
+ *d++ = *p;
+ }
+ *d = '\0';
+
+ if ( b64 ) {
+ char *byte = s;
+
+ if ( *s == '\0' ) {
+ /* no value is present, error out */
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: %s missing base64 value\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ byte = value->bv_val = s;
+
+ for ( p = s, value->bv_len = 0; p < d; p += 4, value->bv_len += 3 ) {
+ int i;
+ for ( i = 0; i < 4; i++ ) {
+ if ( p[i] != '=' && (p[i] & 0x80 ||
+ b642nib[ p[i] & 0x7f ] > 0x3f) ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: %s: invalid base64 encoding"
+ " char (%c) 0x%x\n"),
+ type->bv_val, p[i], p[i] );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+ }
+
+ /* first digit */
+ nib = b642nib[ p[0] & 0x7f ];
+ byte[0] = nib << 2;
+ /* second digit */
+ nib = b642nib[ p[1] & 0x7f ];
+ byte[0] |= nib >> 4;
+ byte[1] = (nib & RIGHT4) << 4;
+ /* third digit */
+ if ( p[2] == '=' ) {
+ value->bv_len += 1;
+ break;
+ }
+ nib = b642nib[ p[2] & 0x7f ];
+ byte[1] |= nib >> 2;
+ byte[2] = (nib & RIGHT2) << 6;
+ /* fourth digit */
+ if ( p[3] == '=' ) {
+ value->bv_len += 2;
+ break;
+ }
+ nib = b642nib[ p[3] & 0x7f ];
+ byte[2] |= nib;
+
+ byte += 3;
+ }
+ s[ value->bv_len ] = '\0';
+
+ } else if ( url ) {
+ if ( *s == '\0' ) {
+ /* no value is present, error out */
+ ber_pvt_log_printf( LDAP_DEBUG_PARSE, ldif_debug,
+ _("ldif_parse_line: %s missing URL value\n"),
+ type->bv_val );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+
+ if( ldif_fetch_url( s, &value->bv_val, &value->bv_len ) ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: %s: URL \"%s\" fetch failed\n"),
+ type->bv_val, s );
+ if ( !freeval ) ber_memfree( line );
+ return( -1 );
+ }
+ if ( freeval ) *freeval = 1;
+
+ } else {
+ value->bv_val = s;
+ value->bv_len = (int) (d - s);
+ }
+
+ if ( !freeval ) {
+ struct berval bv = *type;
+
+ ber_dupbv( type, &bv );
+
+ if( BER_BVISNULL( type )) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: type malloc failed\n"));
+ if( url ) ber_memfree( value->bv_val );
+ ber_memfree( line );
+ return( -1 );
+ }
+
+ if( !url ) {
+ bv = *value;
+ ber_dupbv( value, &bv );
+ if( BER_BVISNULL( value )) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_parse_line: value malloc failed\n"));
+ ber_memfree( type->bv_val );
+ ber_memfree( line );
+ return( -1 );
+ }
+ }
+
+ ber_memfree( line );
+ }
+
+ return( 0 );
+}
+
+/*
+ * ldif_getline - return the next "line" (minus newline) of input from a
+ * string buffer of lines separated by newlines, terminated by \n\n
+ * or \0. this routine handles continued lines, bundling them into
+ * a single big line before returning. if a line begins with a white
+ * space character, it is a continuation of the previous line. the white
+ * space character (nb: only one char), and preceeding newline are changed
+ * into CONTINUED_LINE_MARKER chars, to be deleted later by the
+ * ldif_parse_line() routine above.
+ *
+ * ldif_getline will skip over any line which starts '#'.
+ *
+ * ldif_getline takes a pointer to a pointer to the buffer on the first call,
+ * which it updates and must be supplied on subsequent calls.
+ */
+
+int
+ldif_countlines( LDAP_CONST char *buf )
+{
+ char *nl;
+ int ret = 0;
+
+ if ( !buf ) return ret;
+
+ for ( nl = strchr(buf, '\n'); nl; nl = strchr(nl, '\n') ) {
+ nl++;
+ if ( *nl != ' ' ) ret++;
+ }
+ return ret;
+}
+
+char *
+ldif_getline( char **next )
+{
+ char *line;
+
+ do {
+ if ( *next == NULL || **next == '\n' || **next == '\0' ) {
+ return( NULL );
+ }
+
+ line = *next;
+
+ while ( (*next = strchr( *next, '\n' )) != NULL ) {
+#if CONTINUED_LINE_MARKER != '\r'
+ if ( (*next)[-1] == '\r' ) {
+ (*next)[-1] = CONTINUED_LINE_MARKER;
+ }
+#endif
+
+ if ( (*next)[1] != ' ' ) {
+ if ( (*next)[1] == '\r' && (*next)[2] == '\n' ) {
+ *(*next)++ = '\0';
+ }
+ *(*next)++ = '\0';
+ break;
+ }
+
+ **next = CONTINUED_LINE_MARKER;
+ (*next)[1] = CONTINUED_LINE_MARKER;
+ (*next)++;
+ }
+ } while( *line == '#' );
+
+ return( line );
+}
+
+/*
+ * name and OID of attributeTypes that must be base64 encoded in any case
+ */
+typedef struct must_b64_encode_s {
+ struct berval name;
+ struct berval oid;
+} must_b64_encode_s;
+
+static must_b64_encode_s default_must_b64_encode[] = {
+ { BER_BVC( "userPassword" ), BER_BVC( "2.5.4.35" ) },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+static must_b64_encode_s *must_b64_encode = default_must_b64_encode;
+
+/*
+ * register name and OID of attributeTypes that must always be base64
+ * encoded
+ *
+ * NOTE: this routine mallocs memory in a static struct which must
+ * be explicitly freed when no longer required
+ */
+int
+ldif_must_b64_encode_register( LDAP_CONST char *name, LDAP_CONST char *oid )
+{
+ int i;
+ ber_len_t len;
+
+ assert( must_b64_encode != NULL );
+ assert( name != NULL );
+ assert( oid != NULL );
+
+ len = strlen( name );
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ if ( len != must_b64_encode[i].name.bv_len ) {
+ continue;
+ }
+
+ if ( strcasecmp( name, must_b64_encode[i].name.bv_val ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( !BER_BVISNULL( &must_b64_encode[i].name ) ) {
+ return 1;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ )
+ /* just count */ ;
+
+ if ( must_b64_encode == default_must_b64_encode ) {
+ must_b64_encode = ber_memalloc( sizeof( must_b64_encode_s ) * ( i + 2 ) );
+
+ for ( i = 0; !BER_BVISNULL( &default_must_b64_encode[i].name ); i++ ) {
+ ber_dupbv( &must_b64_encode[i].name, &default_must_b64_encode[i].name );
+ ber_dupbv( &must_b64_encode[i].oid, &default_must_b64_encode[i].oid );
+ }
+
+ } else {
+ must_b64_encode_s *tmp;
+
+ tmp = ber_memrealloc( must_b64_encode,
+ sizeof( must_b64_encode_s ) * ( i + 2 ) );
+ if ( tmp == NULL ) {
+ return 1;
+ }
+ must_b64_encode = tmp;
+ }
+
+ ber_str2bv( name, len, 1, &must_b64_encode[i].name );
+ ber_str2bv( oid, 0, 1, &must_b64_encode[i].oid );
+
+ BER_BVZERO( &must_b64_encode[i + 1].name );
+
+ return 0;
+}
+
+void
+ldif_must_b64_encode_release( void )
+{
+ int i;
+
+ assert( must_b64_encode != NULL );
+
+ if ( must_b64_encode == default_must_b64_encode ) {
+ return;
+ }
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ ber_memfree( must_b64_encode[i].name.bv_val );
+ ber_memfree( must_b64_encode[i].oid.bv_val );
+ }
+
+ ber_memfree( must_b64_encode );
+
+ must_b64_encode = default_must_b64_encode;
+}
+
+/*
+ * returns 1 iff the string corresponds to the name or the OID of any
+ * of the attributeTypes listed in must_b64_encode
+ */
+static int
+ldif_must_b64_encode( LDAP_CONST char *s )
+{
+ int i;
+ struct berval bv;
+
+ assert( must_b64_encode != NULL );
+ assert( s != NULL );
+
+ ber_str2bv( s, 0, 0, &bv );
+
+ for ( i = 0; !BER_BVISNULL( &must_b64_encode[i].name ); i++ ) {
+ if ( ber_bvstrcasecmp( &must_b64_encode[i].name, &bv ) == 0
+ || ber_bvcmp( &must_b64_encode[i].oid, &bv ) == 0 )
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* compatibility with U-Mich off by two bug */
+#define LDIF_KLUDGE 2
+
+/* NOTE: only preserved for binary compatibility */
+void
+ldif_sput(
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ ldif_sput_wrap( out, type, name, val, vlen, LDIF_LINE_WIDTH+LDIF_KLUDGE );
+}
+
+void
+ldif_sput_wrap(
+ char **out,
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap )
+{
+ const unsigned char *byte, *stop;
+ unsigned char buf[3];
+ unsigned long bits;
+ char *save;
+ int pad;
+ int namelen = 0;
+
+ ber_len_t savelen;
+ ber_len_t len=0;
+ ber_len_t i;
+
+ if ( !wrap )
+ wrap = LDIF_LINE_WIDTH+LDIF_KLUDGE;
+
+ /* prefix */
+ switch( type ) {
+ case LDIF_PUT_COMMENT:
+ *(*out)++ = '#';
+ len++;
+
+ if( vlen ) {
+ *(*out)++ = ' ';
+ len++;
+ }
+
+ break;
+
+ case LDIF_PUT_SEP:
+ *(*out)++ = '\n';
+ return;
+ }
+
+ /* name (attribute type) */
+ if( name != NULL ) {
+ /* put the name + ":" */
+ namelen = strlen(name);
+ strcpy(*out, name);
+ *out += namelen;
+ len += namelen;
+
+ if( type != LDIF_PUT_COMMENT ) {
+ *(*out)++ = ':';
+ len++;
+ }
+
+ }
+#ifdef LDAP_DEBUG
+ else {
+ assert( type == LDIF_PUT_COMMENT );
+ }
+#endif
+
+ if( vlen == 0 ) {
+ *(*out)++ = '\n';
+ return;
+ }
+
+ switch( type ) {
+ case LDIF_PUT_NOVALUE:
+ *(*out)++ = '\n';
+ return;
+
+ case LDIF_PUT_URL: /* url value */
+ *(*out)++ = '<';
+ len++;
+ break;
+
+ case LDIF_PUT_B64: /* base64 value */
+ *(*out)++ = ':';
+ len++;
+ break;
+ }
+
+ switch( type ) {
+ case LDIF_PUT_TEXT:
+ case LDIF_PUT_URL:
+ case LDIF_PUT_B64:
+ *(*out)++ = ' ';
+ len++;
+ /* fall-thru */
+
+ case LDIF_PUT_COMMENT:
+ /* pre-encoded names */
+ for ( i=0; i < vlen; i++ ) {
+ if ( len > wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ *(*out)++ = val[i];
+ len++;
+ }
+ *(*out)++ = '\n';
+ return;
+ }
+
+ save = *out;
+ savelen = len;
+
+ *(*out)++ = ' ';
+ len++;
+
+ stop = (const unsigned char *) (val + vlen);
+
+ if ( type == LDIF_PUT_VALUE
+ && isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<'
+ && isgraph( (unsigned char) val[vlen-1] )
+#ifndef LDAP_BINARY_DEBUG
+ && strstr( name, ";binary" ) == NULL
+#endif
+#ifndef LDAP_PASSWD_DEBUG
+ && !ldif_must_b64_encode( name )
+#endif
+ ) {
+ int b64 = 0;
+
+ for ( byte = (const unsigned char *) val; byte < stop;
+ byte++, len++ )
+ {
+ if ( !isascii( *byte ) || !isprint( *byte ) ) {
+ b64 = 1;
+ break;
+ }
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+ *(*out)++ = *byte;
+ }
+
+ if( !b64 ) {
+ *(*out)++ = '\n';
+ return;
+ }
+ }
+
+ *out = save;
+ *(*out)++ = ':';
+ *(*out)++ = ' ';
+ len = savelen + 2;
+
+ /* convert to base 64 (3 bytes => 4 base 64 digits) */
+ for ( byte = (const unsigned char *) val;
+ byte < stop - 2;
+ byte += 3 )
+ {
+ bits = (byte[0] & 0xff) << 16;
+ bits |= (byte[1] & 0xff) << 8;
+ bits |= (byte[2] & 0xff);
+
+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ /* get b64 digit from high order 6 bits */
+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
+ }
+ }
+
+ /* add padding if necessary */
+ if ( byte < stop ) {
+ for ( i = 0; byte + i < stop; i++ ) {
+ buf[i] = byte[i];
+ }
+ for ( pad = 0; i < 3; i++, pad++ ) {
+ buf[i] = '\0';
+ }
+ byte = buf;
+ bits = (byte[0] & 0xff) << 16;
+ bits |= (byte[1] & 0xff) << 8;
+ bits |= (byte[2] & 0xff);
+
+ for ( i = 0; i < 4; i++, len++, bits <<= 6 ) {
+ if ( len >= wrap ) {
+ *(*out)++ = '\n';
+ *(*out)++ = ' ';
+ len = 1;
+ }
+
+ if( i + pad < 4 ) {
+ /* get b64 digit from low order 6 bits */
+ *(*out)++ = nib2b64[ (bits & 0xfc0000L) >> 18 ];
+ } else {
+ *(*out)++ = '=';
+ }
+ }
+ }
+ *(*out)++ = '\n';
+}
+
+
+/*
+ * ldif_type_and_value return BER malloc'd, zero-terminated LDIF line
+ */
+
+/* NOTE: only preserved for binary compatibility */
+char *
+ldif_put(
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ return ldif_put_wrap( type, name, val, vlen, LDIF_LINE_WIDTH );
+}
+
+char *
+ldif_put_wrap(
+ int type,
+ LDAP_CONST char *name,
+ LDAP_CONST char *val,
+ ber_len_t vlen,
+ ber_len_t wrap )
+{
+ char *buf, *p;
+ ber_len_t nlen;
+
+ nlen = ( name != NULL ) ? strlen( name ) : 0;
+
+ buf = (char *) ber_memalloc( LDIF_SIZE_NEEDED_WRAP( nlen, vlen, wrap ) + 1 );
+
+ if ( buf == NULL ) {
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_type_and_value: malloc failed!"));
+ return NULL;
+ }
+
+ p = buf;
+ ldif_sput_wrap( &p, type, name, val, vlen, wrap );
+ *p = '\0';
+
+ return( buf );
+}
+
+int ldif_is_not_printable(
+ LDAP_CONST char *val,
+ ber_len_t vlen )
+{
+ if( vlen == 0 || val == NULL ) {
+ return -1;
+ }
+
+ if( isgraph( (unsigned char) val[0] ) && val[0] != ':' && val[0] != '<' &&
+ isgraph( (unsigned char) val[vlen-1] ) )
+ {
+ ber_len_t i;
+
+ for ( i = 0; val[i]; i++ ) {
+ if ( !isascii( val[i] ) || !isprint( (unsigned char) val[i] ) ) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+LDIFFP *
+ldif_open(
+ LDAP_CONST char *file,
+ LDAP_CONST char *mode
+)
+{
+ FILE *fp = fopen( file, mode );
+ LDIFFP *lfp = NULL;
+
+ if ( fp ) {
+ lfp = ber_memalloc( sizeof( LDIFFP ));
+ lfp->fp = fp;
+ lfp->prev = NULL;
+ }
+ return lfp;
+}
+
+void
+ldif_close(
+ LDIFFP *lfp
+)
+{
+ LDIFFP *prev;
+
+ while ( lfp ) {
+ fclose( lfp->fp );
+ prev = lfp->prev;
+ ber_memfree( lfp );
+ lfp = prev;
+ }
+}
+
+#define LDIF_MAXLINE 4096
+
+/*
+ * ldif_read_record - read an ldif record. Return 1 for success, 0 for EOF,
+ * -1 for error.
+ */
+int
+ldif_read_record(
+ LDIFFP *lfp,
+ unsigned long *lno, /* ptr to line number counter */
+ char **bufp, /* ptr to malloced output buffer */
+ int *buflenp ) /* ptr to length of *bufp */
+{
+ char line[LDIF_MAXLINE], *nbufp;
+ ber_len_t lcur = 0, len;
+ int last_ch = '\n', found_entry = 0, stop, top_comment = 0;
+
+ for ( stop = 0; !stop; last_ch = line[len-1] ) {
+ /* If we're at the end of this file, see if we should pop
+ * back to a previous file. (return from an include)
+ */
+ while ( feof( lfp->fp )) {
+ if ( lfp->prev ) {
+ LDIFFP *tmp = lfp->prev;
+ fclose( lfp->fp );
+ *lfp = *tmp;
+ ber_memfree( tmp );
+ } else {
+ stop = 1;
+ break;
+ }
+ }
+ if ( !stop ) {
+ if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) {
+ stop = 1;
+ len = 0;
+ } else {
+ len = strlen( line );
+ }
+ }
+
+ if ( stop ) {
+ /* Add \n in case the file does not end with newline */
+ if (last_ch != '\n') {
+ len = 1;
+ line[0] = '\n';
+ line[1] = '\0';
+ goto last;
+ }
+ break;
+ }
+
+ /* Squash \r\n to \n */
+ if ( len > 1 && line[len-2] == '\r' ) {
+ len--;
+ line[len-1] = '\n';
+ }
+
+ if ( last_ch == '\n' ) {
+ (*lno)++;
+
+ if ( line[0] == '\n' ) {
+ if ( !found_entry ) {
+ lcur = 0;
+ top_comment = 0;
+ continue;
+ }
+ break;
+ }
+
+ if ( !found_entry ) {
+ if ( line[0] == '#' ) {
+ top_comment = 1;
+ } else if ( ! ( top_comment && line[0] == ' ' ) ) {
+ /* Found a new entry */
+ found_entry = 1;
+
+ if ( isdigit( (unsigned char) line[0] ) ) {
+ /* skip index */
+ continue;
+ }
+ if ( !strncasecmp( line, "include:",
+ STRLENOF("include:"))) {
+ FILE *fp2;
+ char *ptr;
+ found_entry = 0;
+
+ if ( line[len-1] == '\n' ) {
+ len--;
+ line[len] = '\0';
+ }
+
+ ptr = line + STRLENOF("include:");
+ while (isspace((unsigned char) *ptr)) ptr++;
+ fp2 = ldif_open_url( ptr );
+ if ( fp2 ) {
+ LDIFFP *lnew = ber_memalloc( sizeof( LDIFFP ));
+ if ( lnew == NULL ) {
+ fclose( fp2 );
+ return 0;
+ }
+ lnew->prev = lfp->prev;
+ lnew->fp = lfp->fp;
+ lfp->prev = lnew;
+ lfp->fp = fp2;
+ line[len] = '\n';
+ len++;
+ continue;
+ } else {
+ /* We failed to open the file, this should
+ * be reported as an error somehow.
+ */
+ ber_pvt_log_printf( LDAP_DEBUG_ANY, ldif_debug,
+ _("ldif_read_record: include %s failed\n"), ptr );
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+last:
+ if ( *buflenp - lcur <= len ) {
+ *buflenp += len + LDIF_MAXLINE;
+ nbufp = ber_memrealloc( *bufp, *buflenp );
+ if( nbufp == NULL ) {
+ return 0;
+ }
+ *bufp = nbufp;
+ }
+ strcpy( *bufp + lcur, line );
+ lcur += len;
+ }
+
+ return( found_entry );
+}
diff --git a/libraries/libldap/messages.c b/libraries/libldap/messages.c
new file mode 100644
index 0000000..80a5ea4
--- /dev/null
+++ b/libraries/libldap/messages.c
@@ -0,0 +1,68 @@
+/* messages.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_first_message( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain;
+}
+
+LDAPMessage *
+ldap_next_message( LDAP *ld, LDAPMessage *msg )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msg != NULL );
+
+ return msg->lm_chain;
+}
+
+int
+ldap_count_messages( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ i++;
+ }
+
+ return( i );
+}
+
+BerElement*
+ldap_get_message_ber( LDAPMessage *ld )
+{
+ return ld->lm_ber;
+}
diff --git a/libraries/libldap/modify.c b/libraries/libldap/modify.c
new file mode 100644
index 0000000..69be8c4
--- /dev/null
+++ b/libraries/libldap/modify.c
@@ -0,0 +1,233 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* A modify request/response looks like this:
+ * ModifyRequest ::= [APPLICATION 6] SEQUENCE {
+ * object LDAPDN,
+ * changes SEQUENCE OF change SEQUENCE {
+ * operation ENUMERATED {
+ * add (0),
+ * delete (1),
+ * replace (2),
+ * ... },
+ * modification PartialAttribute } }
+ *
+ * PartialAttribute ::= SEQUENCE {
+ * type AttributeDescription,
+ * vals SET OF value AttributeValue }
+ *
+ * AttributeDescription ::= LDAPString
+ * -- Constrained to <attributedescription> [RFC4512]
+ *
+ * AttributeValue ::= OCTET STRING
+ *
+ * ModifyResponse ::= [APPLICATION 7] LDAPResult
+ *
+ * (Source: RFC 4511)
+ */
+
+BerElement *
+ldap_build_modify_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int i, rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ rc = ber_printf( ber, "{it{s{" /*}}}*/, *msgidp, LDAP_REQ_MODIFY, dn );
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* allow mods to be NULL ("touch") */
+ if ( mods ) {
+ /* for each modification to be performed... */
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if (( mods[i]->mod_op & LDAP_MOD_BVALUES) != 0 ) {
+ rc = ber_printf( ber, "{e{s[V]N}N}",
+ (ber_int_t) ( mods[i]->mod_op & ~LDAP_MOD_BVALUES ),
+ mods[i]->mod_type, mods[i]->mod_bvalues );
+ } else {
+ rc = ber_printf( ber, "{e{s[v]N}N}",
+ (ber_int_t) mods[i]->mod_op,
+ mods[i]->mod_type, mods[i]->mod_values );
+ }
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ }
+ }
+
+ if ( ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_modify_ext - initiate an ldap extended modify operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * mods List of modifications to make. This is null-terminated
+ * array of struct ldapmod's, specifying the modifications
+ * to perform.
+ * sctrls Server Controls
+ * cctrls Client Controls
+ * msgidp Message ID pointer
+ *
+ * Example:
+ * LDAPMod *mods[] = {
+ * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_DELETE, "ou", 0 },
+ * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } }
+ * 0
+ * }
+ * rc= ldap_modify_ext( ld, dn, mods, sctrls, cctrls, &msgid );
+ */
+int
+ldap_modify_ext( LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPMod **mods,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_modify_ext\n", 0, 0, 0 );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_modify_req( ld, dn, mods, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODIFY, dn, ber, id );
+ return( *msgidp < 0 ? ld->ld_errno : LDAP_SUCCESS );
+}
+
+/*
+ * ldap_modify - initiate an ldap modify operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * mods List of modifications to make. This is null-terminated
+ * array of struct ldapmod's, specifying the modifications
+ * to perform.
+ *
+ * Example:
+ * LDAPMod *mods[] = {
+ * { LDAP_MOD_ADD, "cn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_REPLACE, "sn", { "babs jensen", "babs", 0 } },
+ * { LDAP_MOD_DELETE, "ou", 0 },
+ * { LDAP_MOD_INCREMENT, "uidNumber, { "1", 0 } }
+ * 0
+ * }
+ * msgid = ldap_modify( ld, dn, mods );
+ */
+int
+ldap_modify( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
+{
+ int rc, msgid;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_modify\n", 0, 0, 0 );
+
+ rc = ldap_modify_ext( ld, dn, mods, NULL, NULL, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return -1;
+
+ return msgid;
+}
+
+int
+ldap_modify_ext_s( LDAP *ld, LDAP_CONST char *dn,
+ LDAPMod **mods, LDAPControl **sctrl, LDAPControl **cctrl )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_modify_ext( ld, dn, mods, sctrl, cctrl, &msgid );
+
+ if ( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
+
+int
+ldap_modify_s( LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods )
+{
+ return ldap_modify_ext_s( ld, dn, mods, NULL, NULL );
+}
+
diff --git a/libraries/libldap/modrdn.c b/libraries/libldap/modrdn.c
new file mode 100644
index 0000000..bd210c0
--- /dev/null
+++ b/libraries/libldap/modrdn.c
@@ -0,0 +1,273 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Copyright 1999, Juan C. Gomez, All rights reserved.
+ * This software is not subject to any license of Silicon Graphics
+ * Inc. or Purdue University.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * without restriction or fee of any kind as long as this notice
+ * is preserved.
+ */
+
+/* ACKNOWLEDGEMENTS:
+ * Juan C. Gomez
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * A modify rdn request looks like this:
+ * ModifyRDNRequest ::= SEQUENCE {
+ * entry DistinguishedName,
+ * newrdn RelativeDistinguishedName,
+ * deleteoldrdn BOOLEAN
+ * newSuperior [0] DistinguishedName [v3 only]
+ * }
+ */
+
+BerElement *
+ldap_build_moddn_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if( newSuperior != NULL ) {
+ /* must be version 3 (or greater) */
+ if ( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+ rc = ber_printf( ber, "{it{ssbtsN}", /* '}' */
+ *msgidp, LDAP_REQ_MODDN,
+ dn, newrdn, (ber_int_t) deleteoldrdn,
+ LDAP_TAG_NEWSUPERIOR, newSuperior );
+
+ } else {
+ rc = ber_printf( ber, "{it{ssbN}", /* '}' */
+ *msgidp, LDAP_REQ_MODDN,
+ dn, newrdn, (ber_int_t) deleteoldrdn );
+ }
+
+ if ( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ rc = ber_printf( ber, /*{*/ "N}" );
+ if ( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_rename - initiate an ldap extended modifyDN operation.
+ *
+ * Parameters:
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ * newSuperior DN of the new parent if applicable
+ *
+ * Returns the LDAP error code.
+ */
+
+int
+ldap_rename(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_rename\n", 0, 0, 0 );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_moddn_req( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_MODRDN, dn, ber, id );
+
+ if( *msgidp < 0 ) {
+ return( ld->ld_errno );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * ldap_rename2 - initiate an ldap (and X.500) modifyDN operation. Parameters:
+ * (LDAP V3 MODIFYDN REQUEST)
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ * newSuperior DN of the new parent if applicable
+ *
+ * ldap_rename2 uses a U-Mich Style API. It returns the msgid.
+ */
+
+int
+ldap_rename2(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn )
+{
+ int msgid;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_rename2\n", 0, 0, 0 );
+
+ rc = ldap_rename( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, NULL, NULL, &msgid );
+
+ return rc == LDAP_SUCCESS ? msgid : -1;
+}
+
+
+/*
+ * ldap_modrdn2 - initiate an ldap modifyRDN operation. Parameters:
+ *
+ * ld LDAP descriptor
+ * dn DN of the object to modify
+ * newrdn RDN to give the object
+ * deleteoldrdn nonzero means to delete old rdn values from the entry
+ *
+ * Example:
+ * msgid = ldap_modrdn( ld, dn, newrdn );
+ */
+int
+ldap_modrdn2( LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ int deleteoldrdn )
+{
+ return ldap_rename2( ld, dn, newrdn, NULL, deleteoldrdn );
+}
+
+int
+ldap_modrdn( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn )
+{
+ return( ldap_rename2( ld, dn, newrdn, NULL, 1 ) );
+}
+
+
+int
+ldap_rename_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_rename( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, sctrls, cctrls, &msgid );
+
+ if( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &res );
+
+ if( rc == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ return ldap_result2error( ld, res, 1 );
+}
+
+int
+ldap_rename2_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *newrdn,
+ LDAP_CONST char *newSuperior,
+ int deleteoldrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, newSuperior,
+ deleteoldrdn, NULL, NULL );
+}
+
+int
+ldap_modrdn2_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn, int deleteoldrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, NULL, deleteoldrdn, NULL, NULL );
+}
+
+int
+ldap_modrdn_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *newrdn )
+{
+ return ldap_rename_s( ld, dn, newrdn, NULL, 1, NULL, NULL );
+}
+
diff --git a/libraries/libldap/open.c b/libraries/libldap/open.c
new file mode 100644
index 0000000..5882b63
--- /dev/null
+++ b/libraries/libldap/open.c
@@ -0,0 +1,611 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#include <ac/stdlib.h>
+
+#include <ac/param.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "ldap.h"
+#include "ldap_log.h"
+
+/* Caller must hold the conn_mutex since simultaneous accesses are possible */
+int ldap_open_defconn( LDAP *ld )
+{
+ ld->ld_defconn = ldap_new_connection( ld,
+ &ld->ld_options.ldo_defludp, 1, 1, NULL, 0, 0 );
+
+ if( ld->ld_defconn == NULL ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ return -1;
+ }
+
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+ return 0;
+}
+
+/*
+ * ldap_open - initialize and connect to an ldap server. A magic cookie to
+ * be used for future communication is returned on success, NULL on failure.
+ * "host" may be a space-separated list of hosts or IP addresses
+ *
+ * Example:
+ * LDAP *ld;
+ * ld = ldap_open( hostname, port );
+ */
+
+LDAP *
+ldap_open( LDAP_CONST char *host, int port )
+{
+ int rc;
+ LDAP *ld;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_open(%s, %d)\n",
+ host, port, 0 );
+
+ ld = ldap_init( host, port );
+ if ( ld == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ rc = ldap_open_defconn( ld );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ if( rc < 0 ) {
+ ldap_ld_free( ld, 0, NULL, NULL );
+ ld = NULL;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_open: %s\n",
+ ld != NULL ? "succeeded" : "failed", 0, 0 );
+
+ return ld;
+}
+
+
+
+int
+ldap_create( LDAP **ldp )
+{
+ LDAP *ld;
+ struct ldapoptions *gopts;
+
+ *ldp = NULL;
+ /* Get pointer to global option structure */
+ if ( (gopts = LDAP_INT_GLOBAL_OPT()) == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /* Initialize the global options, if not already done. */
+ if( gopts->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(gopts, NULL);
+ if ( gopts->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_create\n", 0, 0, 0 );
+
+ if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
+ return( LDAP_NO_MEMORY );
+ }
+
+ if ( (ld->ldc = (struct ldap_common *) LDAP_CALLOC( 1,
+ sizeof(struct ldap_common) )) == NULL ) {
+ LDAP_FREE( (char *)ld );
+ return( LDAP_NO_MEMORY );
+ }
+ /* copy the global options */
+ LDAP_MUTEX_LOCK( &gopts->ldo_mutex );
+ AC_MEMCPY(&ld->ld_options, gopts, sizeof(ld->ld_options));
+#ifdef LDAP_R_COMPILE
+ /* Properly initialize the structs mutex */
+ ldap_pvt_thread_mutex_init( &(ld->ld_ldopts_mutex) );
+#endif
+ LDAP_MUTEX_UNLOCK( &gopts->ldo_mutex );
+
+ ld->ld_valid = LDAP_VALID_SESSION;
+
+ /* but not pointers to malloc'ed items */
+ ld->ld_options.ldo_sctrls = NULL;
+ ld->ld_options.ldo_cctrls = NULL;
+ ld->ld_options.ldo_defludp = NULL;
+ ld->ld_options.ldo_conn_cbs = NULL;
+
+#ifdef HAVE_CYRUS_SASL
+ ld->ld_options.ldo_def_sasl_mech = gopts->ldo_def_sasl_mech
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_mech ) : NULL;
+ ld->ld_options.ldo_def_sasl_realm = gopts->ldo_def_sasl_realm
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_realm ) : NULL;
+ ld->ld_options.ldo_def_sasl_authcid = gopts->ldo_def_sasl_authcid
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_authcid ) : NULL;
+ ld->ld_options.ldo_def_sasl_authzid = gopts->ldo_def_sasl_authzid
+ ? LDAP_STRDUP( gopts->ldo_def_sasl_authzid ) : NULL;
+#endif
+
+#ifdef HAVE_TLS
+ /* We explicitly inherit the SSL_CTX, don't need the names/paths. Leave
+ * them empty to allow new SSL_CTX's to be created from scratch.
+ */
+ memset( &ld->ld_options.ldo_tls_info, 0,
+ sizeof( ld->ld_options.ldo_tls_info ));
+ ld->ld_options.ldo_tls_ctx = NULL;
+#endif
+
+ if ( gopts->ldo_defludp ) {
+ ld->ld_options.ldo_defludp = ldap_url_duplist(gopts->ldo_defludp);
+
+ if ( ld->ld_options.ldo_defludp == NULL ) goto nomem;
+ }
+
+ if (( ld->ld_selectinfo = ldap_new_select_info()) == NULL ) goto nomem;
+
+ ld->ld_lberoptions = LBER_USE_DER;
+
+ ld->ld_sb = ber_sockbuf_alloc( );
+ if ( ld->ld_sb == NULL ) goto nomem;
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &ld->ld_msgid_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_conn_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_req_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_res_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_abandon_mutex );
+ ldap_pvt_thread_mutex_init( &ld->ld_ldcmutex );
+#endif
+ ld->ld_ldcrefcnt = 1;
+ *ldp = ld;
+ return LDAP_SUCCESS;
+
+nomem:
+ ldap_free_select_info( ld->ld_selectinfo );
+ ldap_free_urllist( ld->ld_options.ldo_defludp );
+#ifdef HAVE_CYRUS_SASL
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_realm );
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_mech );
+#endif
+ LDAP_FREE( (char *)ld );
+ return LDAP_NO_MEMORY;
+}
+
+/*
+ * ldap_init - initialize the LDAP library. A magic cookie to be used for
+ * future communication is returned on success, NULL on failure.
+ * "host" may be a space-separated list of hosts or IP addresses
+ *
+ * Example:
+ * LDAP *ld;
+ * ld = ldap_init( host, port );
+ */
+LDAP *
+ldap_init( LDAP_CONST char *defhost, int defport )
+{
+ LDAP *ld;
+ int rc;
+
+ rc = ldap_create(&ld);
+ if ( rc != LDAP_SUCCESS )
+ return NULL;
+
+ if (defport != 0)
+ ld->ld_options.ldo_defport = defport;
+
+ if (defhost != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_HOST_NAME, defhost);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return NULL;
+ }
+ }
+
+ return( ld );
+}
+
+
+int
+ldap_initialize( LDAP **ldp, LDAP_CONST char *url )
+{
+ int rc;
+ LDAP *ld;
+
+ *ldp = NULL;
+ rc = ldap_create(&ld);
+ if ( rc != LDAP_SUCCESS )
+ return rc;
+
+ if (url != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_URI, url);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return rc;
+ }
+#ifdef LDAP_CONNECTIONLESS
+ if (ldap_is_ldapc_url(url))
+ LDAP_IS_UDP(ld) = 1;
+#endif
+ }
+
+ *ldp = ld;
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_init_fd(
+ ber_socket_t fd,
+ int proto,
+ LDAP_CONST char *url,
+ LDAP **ldp
+)
+{
+ int rc;
+ LDAP *ld;
+ LDAPConn *conn;
+#ifdef LDAP_CONNECTIONLESS
+ ber_socklen_t len;
+#endif
+
+ *ldp = NULL;
+ rc = ldap_create( &ld );
+ if( rc != LDAP_SUCCESS )
+ return( rc );
+
+ if (url != NULL) {
+ rc = ldap_set_option(ld, LDAP_OPT_URI, url);
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_ld_free(ld, 1, NULL, NULL);
+ return rc;
+ }
+ }
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ /* Attach the passed socket as the LDAP's connection */
+ conn = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
+ if( conn == NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ return( LDAP_NO_MEMORY );
+ }
+ if( url )
+ conn->lconn_server = ldap_url_dup( ld->ld_options.ldo_defludp );
+ ber_sockbuf_ctrl( conn->lconn_sb, LBER_SB_OPT_SET_FD, &fd );
+ ld->ld_defconn = conn;
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ switch( proto ) {
+ case LDAP_PROTO_TCP:
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ case LDAP_PROTO_UDP:
+ LDAP_IS_UDP(ld) = 1;
+ if( ld->ld_options.ldo_peer )
+ ldap_memfree( ld->ld_options.ldo_peer );
+ ld->ld_options.ldo_peer = ldap_memcalloc( 1, sizeof( struct sockaddr_storage ) );
+ len = sizeof( struct sockaddr_storage );
+ if( getpeername ( fd, ld->ld_options.ldo_peer, &len ) < 0) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ return( AC_SOCKET_ERROR );
+ }
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+#endif /* LDAP_CONNECTIONLESS */
+
+ case LDAP_PROTO_IPC:
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ break;
+
+ case LDAP_PROTO_EXT:
+ /* caller must supply sockbuf handlers */
+ break;
+
+ default:
+ ldap_unbind_ext( ld, NULL, NULL );
+ return LDAP_PARAM_ERROR;
+ }
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ INT_MAX, (void *)"ldap_" );
+#endif
+
+ /* Add the connection to the *LDAP's select pool */
+ ldap_mark_select_read( ld, conn->lconn_sb );
+
+ *ldp = ld;
+ return LDAP_SUCCESS;
+}
+
+/* Protected by ld_conn_mutex */
+int
+ldap_int_open_connection(
+ LDAP *ld,
+ LDAPConn *conn,
+ LDAPURLDesc *srv,
+ int async )
+{
+ int rc = -1;
+ int proto;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_open_connection\n", 0, 0, 0 );
+
+ switch ( proto = ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
+ case LDAP_PROTO_TCP:
+ rc = ldap_connect_to_host( ld, conn->lconn_sb,
+ proto, srv, async );
+
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"tcp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ case LDAP_PROTO_UDP:
+ LDAP_IS_UDP(ld) = 1;
+ rc = ldap_connect_to_host( ld, conn->lconn_sb,
+ proto, srv, async );
+
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"udp_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_udp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_readahead,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+#endif
+ case LDAP_PROTO_IPC:
+#ifdef LDAP_PF_LOCAL
+ /* only IPC mechanism supported is PF_LOCAL (PF_UNIX) */
+ rc = ldap_connect_to_path( ld, conn->lconn_sb,
+ srv, async );
+ if ( rc == -1 ) return rc;
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"ipc_" );
+#endif
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_fd,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+
+ break;
+#endif /* LDAP_PF_LOCAL */
+ default:
+ return -1;
+ break;
+ }
+
+ conn->lconn_created = time( NULL );
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( conn->lconn_sb, &ber_sockbuf_io_debug,
+ INT_MAX, (void *)"ldap_" );
+#endif
+
+#ifdef LDAP_CONNECTIONLESS
+ if( proto == LDAP_PROTO_UDP ) return 0;
+#endif
+
+#ifdef HAVE_TLS
+ if ((rc == 0 || rc == -2) && ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
+ strcmp( srv->lud_scheme, "ldaps" ) == 0 ))
+ {
+ ++conn->lconn_refcnt; /* avoid premature free */
+
+ rc = ldap_int_tls_start( ld, conn, srv );
+
+ --conn->lconn_refcnt;
+
+ if (rc != LDAP_SUCCESS) {
+ /* process connection callbacks */
+ {
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+
+ lo = &ld->ld_options;
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, conn->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ lo = LDAP_INT_GLOBAL_OPT();
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, conn->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ }
+ ber_int_sb_close( conn->lconn_sb );
+ return -1;
+ }
+ }
+#endif
+
+ return( 0 );
+}
+
+/*
+ * ldap_open_internal_connection - open connection and set file descriptor
+ *
+ * note: ldap_init_fd() may be preferable
+ */
+
+int
+ldap_open_internal_connection( LDAP **ldp, ber_socket_t *fdp )
+{
+ int rc;
+ LDAPConn *c;
+ LDAPRequest *lr;
+ LDAP *ld;
+
+ rc = ldap_create( &ld );
+ if( rc != LDAP_SUCCESS ) {
+ *ldp = NULL;
+ return( rc );
+ }
+
+ /* Make it appear that a search request, msgid 0, was sent */
+ lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ));
+ if( lr == NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ *ldp = NULL;
+ return( LDAP_NO_MEMORY );
+ }
+ memset(lr, 0, sizeof( LDAPRequest ));
+ lr->lr_msgid = 0;
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+ lr->lr_res_errno = LDAP_SUCCESS;
+ /* no mutex lock needed, we just created this ld here */
+ ld->ld_requests = lr;
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ /* Attach the passed socket as the *LDAP's connection */
+ c = ldap_new_connection( ld, NULL, 1, 0, NULL, 0, 0 );
+ if( c == NULL ) {
+ ldap_unbind_ext( ld, NULL, NULL );
+ *ldp = NULL;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( LDAP_NO_MEMORY );
+ }
+ ber_sockbuf_ctrl( c->lconn_sb, LBER_SB_OPT_SET_FD, fdp );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_PROVIDER, (void *)"int_" );
+#endif
+ ber_sockbuf_add_io( c->lconn_sb, &ber_sockbuf_io_tcp,
+ LBER_SBIOD_LEVEL_PROVIDER, NULL );
+ ld->ld_defconn = c;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+
+ /* Add the connection to the *LDAP's select pool */
+ ldap_mark_select_read( ld, c->lconn_sb );
+
+ /* Make this connection an LDAP V3 protocol connection */
+ rc = LDAP_VERSION3;
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &rc );
+ *ldp = ld;
+
+ ++ld->ld_defconn->lconn_refcnt; /* so it never gets closed/freed */
+
+ return( LDAP_SUCCESS );
+}
+
+LDAP *
+ldap_dup( LDAP *old )
+{
+ LDAP *ld;
+
+ if ( old == NULL ) {
+ return( NULL );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_dup\n", 0, 0, 0 );
+
+ if ( (ld = (LDAP *) LDAP_CALLOC( 1, sizeof(LDAP) )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_MUTEX_LOCK( &old->ld_ldcmutex );
+ ld->ldc = old->ldc;
+ old->ld_ldcrefcnt++;
+ LDAP_MUTEX_UNLOCK( &old->ld_ldcmutex );
+ return ( ld );
+}
+
+int
+ldap_int_check_async_open( LDAP *ld, ber_socket_t sd )
+{
+ struct timeval tv = { 0 };
+ int rc;
+
+ rc = ldap_int_poll( ld, sd, &tv, 1 );
+ switch ( rc ) {
+ case 0:
+ /* now ready to start tls */
+ ld->ld_defconn->lconn_status = LDAP_CONNST_CONNECTED;
+ break;
+
+ default:
+ ld->ld_errno = LDAP_CONNECT_ERROR;
+ return -1;
+
+ case -2:
+ /* connect not completed yet */
+ ld->ld_errno = LDAP_X_CONNECTING;
+ return rc;
+ }
+
+#ifdef HAVE_TLS
+ if ( ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
+ !strcmp( ld->ld_defconn->lconn_server->lud_scheme, "ldaps" )) {
+
+ ++ld->ld_defconn->lconn_refcnt; /* avoid premature free */
+
+ rc = ldap_int_tls_start( ld, ld->ld_defconn, ld->ld_defconn->lconn_server );
+
+ --ld->ld_defconn->lconn_refcnt;
+ }
+#endif
+ return rc;
+}
diff --git a/libraries/libldap/options.c b/libraries/libldap/options.c
new file mode 100644
index 0000000..c571d9d
--- /dev/null
+++ b/libraries/libldap/options.c
@@ -0,0 +1,940 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_OPT_REBIND_PROC 0x4e814d
+#define LDAP_OPT_REBIND_PARAMS 0x4e814e
+
+#define LDAP_OPT_NEXTREF_PROC 0x4e815d
+#define LDAP_OPT_NEXTREF_PARAMS 0x4e815e
+
+#define LDAP_OPT_URLLIST_PROC 0x4e816d
+#define LDAP_OPT_URLLIST_PARAMS 0x4e816e
+
+static const LDAPAPIFeatureInfo features[] = {
+#ifdef LDAP_API_FEATURE_X_OPENLDAP
+ { /* OpenLDAP Extensions API Feature */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP",
+ LDAP_API_FEATURE_X_OPENLDAP
+ },
+#endif
+
+#ifdef LDAP_API_FEATURE_THREAD_SAFE
+ { /* Basic Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "THREAD_SAFE",
+ LDAP_API_FEATURE_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_SESSION_THREAD_SAFE
+ { /* Session Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "SESSION_THREAD_SAFE",
+ LDAP_API_FEATURE_SESSION_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_OPERATION_THREAD_SAFE
+ { /* Operation Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "OPERATION_THREAD_SAFE",
+ LDAP_API_FEATURE_OPERATION_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+ { /* OpenLDAP Reentrant */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_REENTRANT",
+ LDAP_API_FEATURE_X_OPENLDAP_REENTRANT
+ },
+#endif
+#if defined( LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE ) && \
+ defined( LDAP_THREAD_SAFE )
+ { /* OpenLDAP Thread Safe */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_THREAD_SAFE",
+ LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE
+ },
+#endif
+#ifdef LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+ { /* V2 Referrals */
+ LDAP_FEATURE_INFO_VERSION,
+ "X_OPENLDAP_V2_REFERRALS",
+ LDAP_API_FEATURE_X_OPENLDAP_V2_REFERRALS
+ },
+#endif
+ {0, NULL, 0}
+};
+
+int
+ldap_get_option(
+ LDAP *ld,
+ int option,
+ void *outvalue)
+{
+ struct ldapoptions *lo;
+ int rc = LDAP_OPT_ERROR;
+
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if (NULL == lo) {
+ return LDAP_NO_MEMORY;
+ }
+
+ if( lo->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(lo, NULL);
+ if ( lo->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ if(ld != NULL) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+ }
+
+ if(outvalue == NULL) {
+ /* no place to get to */
+ return LDAP_OPT_ERROR;
+ }
+
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
+ switch(option) {
+ case LDAP_OPT_API_INFO: {
+ struct ldapapiinfo *info = (struct ldapapiinfo *) outvalue;
+
+ if(info == NULL) {
+ /* outvalue must point to an apiinfo structure */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if(info->ldapai_info_version != LDAP_API_INFO_VERSION) {
+ /* api info version mismatch */
+ info->ldapai_info_version = LDAP_API_INFO_VERSION;
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ info->ldapai_api_version = LDAP_API_VERSION;
+ info->ldapai_protocol_version = LDAP_VERSION_MAX;
+
+ if(features[0].ldapaif_name == NULL) {
+ info->ldapai_extensions = NULL;
+ } else {
+ int i;
+ info->ldapai_extensions = LDAP_MALLOC(sizeof(char *) *
+ sizeof(features)/sizeof(LDAPAPIFeatureInfo));
+
+ for(i=0; features[i].ldapaif_name != NULL; i++) {
+ info->ldapai_extensions[i] =
+ LDAP_STRDUP(features[i].ldapaif_name);
+ }
+
+ info->ldapai_extensions[i] = NULL;
+ }
+
+ info->ldapai_vendor_name = LDAP_STRDUP(LDAP_VENDOR_NAME);
+ info->ldapai_vendor_version = LDAP_VENDOR_VERSION;
+
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ } break;
+
+ case LDAP_OPT_DESC:
+ if( ld == NULL || ld->ld_sb == NULL ) {
+ /* bad param */
+ break;
+ }
+
+ ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, outvalue );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SOCKBUF:
+ if( ld == NULL ) break;
+ *(Sockbuf **)outvalue = ld->ld_sb;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMEOUT:
+ /* the caller has to free outvalue ! */
+ if ( lo->ldo_tm_api.tv_sec < 0 ) {
+ *(void **)outvalue = NULL;
+ } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_api ) != 0 ) {
+ break; /* LDAP_OPT_ERROR */
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_NETWORK_TIMEOUT:
+ /* the caller has to free outvalue ! */
+ if ( lo->ldo_tm_net.tv_sec < 0 ) {
+ *(void **)outvalue = NULL;
+ } else if ( ldap_int_timeval_dup( outvalue, &lo->ldo_tm_net ) != 0 ) {
+ break; /* LDAP_OPT_ERROR */
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEREF:
+ * (int *) outvalue = lo->ldo_deref;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SIZELIMIT:
+ * (int *) outvalue = lo->ldo_sizelimit;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMELIMIT:
+ * (int *) outvalue = lo->ldo_timelimit;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRALS:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_REFERRALS);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESTART:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_RESTART);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_PROTOCOL_VERSION:
+ * (int *) outvalue = lo->ldo_version;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SERVER_CONTROLS:
+ * (LDAPControl ***) outvalue =
+ ldap_controls_dup( lo->ldo_sctrls );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CLIENT_CONTROLS:
+ * (LDAPControl ***) outvalue =
+ ldap_controls_dup( lo->ldo_cctrls );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_HOST_NAME:
+ * (char **) outvalue = ldap_url_list2hosts(lo->ldo_defludp);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_URI:
+ * (char **) outvalue = ldap_url_list2urls(lo->ldo_defludp);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEFBASE:
+ if( lo->ldo_defbase == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP(lo->ldo_defbase);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_ASYNC:
+ * (int *) outvalue = (int) LDAP_BOOL_GET(lo, LDAP_BOOL_CONNECT_ASYNC);
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* Getting deletes the specified callback */
+ ldaplist **ll = &lo->ldo_conn_cbs;
+ for (;*ll;ll = &(*ll)->ll_next) {
+ if ((*ll)->ll_data == outvalue) {
+ ldaplist *lc = *ll;
+ *ll = lc->ll_next;
+ LDAP_FREE(lc);
+ break;
+ }
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESULT_CODE:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+ * (int *) outvalue = ld->ld_errno;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DIAGNOSTIC_MESSAGE:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_error == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP(ld->ld_error);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_MATCHED_DN:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_matched == NULL ) {
+ * (char **) outvalue = NULL;
+ } else {
+ * (char **) outvalue = LDAP_STRDUP( ld->ld_matched );
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRAL_URLS:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+
+ if( ld->ld_referrals == NULL ) {
+ * (char ***) outvalue = NULL;
+ } else {
+ * (char ***) outvalue = ldap_value_dup(ld->ld_referrals);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_API_FEATURE_INFO: {
+ LDAPAPIFeatureInfo *info = (LDAPAPIFeatureInfo *) outvalue;
+ int i;
+
+ if(info == NULL)
+ break; /* LDAP_OPT_ERROR */
+
+ if(info->ldapaif_info_version != LDAP_FEATURE_INFO_VERSION) {
+ /* api info version mismatch */
+ info->ldapaif_info_version = LDAP_FEATURE_INFO_VERSION;
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if(info->ldapaif_name == NULL)
+ break; /* LDAP_OPT_ERROR */
+
+ for(i=0; features[i].ldapaif_name != NULL; i++) {
+ if(!strcmp(info->ldapaif_name, features[i].ldapaif_name)) {
+ info->ldapaif_version =
+ features[i].ldapaif_version;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+ }
+ }
+ break;
+
+ case LDAP_OPT_DEBUG_LEVEL:
+ * (int *) outvalue = lo->ldo_debug;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SESSION_REFCNT:
+ if(ld == NULL) {
+ /* bad param */
+ break;
+ }
+ LDAP_MUTEX_LOCK( &ld->ld_ldcmutex );
+ * (int *) outvalue = ld->ld_ldcrefcnt;
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ * (int *) outvalue = lo->ldo_keepalive_idle;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_PROBES:
+ * (int *) outvalue = lo->ldo_keepalive_probes;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL:
+ * (int *) outvalue = lo->ldo_keepalive_interval;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ default:
+#ifdef HAVE_TLS
+ if ( ldap_pvt_tls_get_option( ld, option, outvalue ) == 0 ) {
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_get_option( ld, option, outvalue ) == 0 ) {
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+#endif
+#ifdef HAVE_GSSAPI
+ if ( ldap_int_gssapi_get_option( ld, option, outvalue ) == 0 ) {
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+#endif
+ /* bad param */
+ break;
+ }
+
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( rc );
+}
+
+int
+ldap_set_option(
+ LDAP *ld,
+ int option,
+ LDAP_CONST void *invalue)
+{
+ struct ldapoptions *lo;
+ int *dbglvl = NULL;
+ int rc = LDAP_OPT_ERROR;
+
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if (lo == NULL) {
+ return LDAP_NO_MEMORY;
+ }
+
+ /*
+ * The architecture to turn on debugging has a chicken and egg
+ * problem. Thus, we introduce a fix here.
+ */
+
+ if (option == LDAP_OPT_DEBUG_LEVEL) {
+ dbglvl = (int *) invalue;
+ }
+
+ if( lo->ldo_valid != LDAP_INITIALIZED ) {
+ ldap_int_initialize(lo, dbglvl);
+ if ( lo->ldo_valid != LDAP_INITIALIZED )
+ return LDAP_LOCAL_ERROR;
+ }
+
+ if(ld != NULL) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+ }
+
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+
+ switch ( option ) {
+
+ /* options with boolean values */
+ case LDAP_OPT_REFERRALS:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_REFERRALS);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_REFERRALS);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESTART:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_RESTART);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_RESTART);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_ASYNC:
+ if(invalue == LDAP_OPT_OFF) {
+ LDAP_BOOL_CLR(lo, LDAP_BOOL_CONNECT_ASYNC);
+ } else {
+ LDAP_BOOL_SET(lo, LDAP_BOOL_CONNECT_ASYNC);
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* options which can withstand invalue == NULL */
+ case LDAP_OPT_SERVER_CONTROLS: {
+ LDAPControl *const *controls =
+ (LDAPControl *const *) invalue;
+
+ if( lo->ldo_sctrls )
+ ldap_controls_free( lo->ldo_sctrls );
+
+ if( controls == NULL || *controls == NULL ) {
+ lo->ldo_sctrls = NULL;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+
+ lo->ldo_sctrls = ldap_controls_dup( controls );
+
+ if(lo->ldo_sctrls == NULL) {
+ /* memory allocation error ? */
+ break; /* LDAP_OPT_ERROR */
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CLIENT_CONTROLS: {
+ LDAPControl *const *controls =
+ (LDAPControl *const *) invalue;
+
+ if( lo->ldo_cctrls )
+ ldap_controls_free( lo->ldo_cctrls );
+
+ if( controls == NULL || *controls == NULL ) {
+ lo->ldo_cctrls = NULL;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ }
+
+ lo->ldo_cctrls = ldap_controls_dup( controls );
+
+ if(lo->ldo_cctrls == NULL) {
+ /* memory allocation error ? */
+ break; /* LDAP_OPT_ERROR */
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+
+ case LDAP_OPT_HOST_NAME: {
+ const char *host = (const char *) invalue;
+ LDAPURLDesc *ludlist = NULL;
+ rc = LDAP_OPT_SUCCESS;
+
+ if(host != NULL) {
+ rc = ldap_url_parsehosts( &ludlist, host,
+ lo->ldo_defport ? lo->ldo_defport : LDAP_PORT );
+
+ } else if(ld == NULL) {
+ /*
+ * must want global default returned
+ * to initial condition.
+ */
+ rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+
+ } else {
+ /*
+ * must want the session default
+ * updated to the current global default
+ */
+ ludlist = ldap_url_duplist(
+ ldap_int_global_options.ldo_defludp);
+ if (ludlist == NULL)
+ rc = LDAP_NO_MEMORY;
+ }
+
+ if (rc == LDAP_OPT_SUCCESS) {
+ if (lo->ldo_defludp != NULL)
+ ldap_free_urllist(lo->ldo_defludp);
+ lo->ldo_defludp = ludlist;
+ }
+ break;
+ }
+
+ case LDAP_OPT_URI: {
+ const char *urls = (const char *) invalue;
+ LDAPURLDesc *ludlist = NULL;
+ rc = LDAP_OPT_SUCCESS;
+
+ if(urls != NULL) {
+ rc = ldap_url_parselist_ext(&ludlist, urls, NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+ } else if(ld == NULL) {
+ /*
+ * must want global default returned
+ * to initial condition.
+ */
+ rc = ldap_url_parselist_ext(&ludlist, "ldap://localhost/", NULL,
+ LDAP_PVT_URL_PARSE_NOEMPTY_HOST
+ | LDAP_PVT_URL_PARSE_DEF_PORT );
+
+ } else {
+ /*
+ * must want the session default
+ * updated to the current global default
+ */
+ ludlist = ldap_url_duplist(
+ ldap_int_global_options.ldo_defludp);
+ if (ludlist == NULL)
+ rc = LDAP_URL_ERR_MEM;
+ }
+
+ switch (rc) {
+ case LDAP_URL_SUCCESS: /* Success */
+ rc = LDAP_SUCCESS;
+ break;
+
+ case LDAP_URL_ERR_MEM: /* can't allocate memory space */
+ rc = LDAP_NO_MEMORY;
+ break;
+
+ case LDAP_URL_ERR_PARAM: /* parameter is bad */
+ case LDAP_URL_ERR_BADSCHEME: /* URL doesn't begin with "ldap[si]://" */
+ case LDAP_URL_ERR_BADENCLOSURE: /* URL is missing trailing ">" */
+ case LDAP_URL_ERR_BADURL: /* URL is bad */
+ case LDAP_URL_ERR_BADHOST: /* host port is bad */
+ case LDAP_URL_ERR_BADATTRS: /* bad (or missing) attributes */
+ case LDAP_URL_ERR_BADSCOPE: /* scope string is invalid (or missing) */
+ case LDAP_URL_ERR_BADFILTER: /* bad or missing filter */
+ case LDAP_URL_ERR_BADEXTS: /* bad or missing extensions */
+ rc = LDAP_PARAM_ERROR;
+ break;
+ }
+
+ if (rc == LDAP_SUCCESS) {
+ if (lo->ldo_defludp != NULL)
+ ldap_free_urllist(lo->ldo_defludp);
+ lo->ldo_defludp = ludlist;
+ }
+ break;
+ }
+
+ case LDAP_OPT_DEFBASE: {
+ const char *newbase = (const char *) invalue;
+ char *defbase = NULL;
+
+ if ( newbase != NULL ) {
+ defbase = LDAP_STRDUP( newbase );
+ if ( defbase == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+
+ } else if ( ld != NULL ) {
+ defbase = LDAP_STRDUP( ldap_int_global_options.ldo_defbase );
+ if ( defbase == NULL ) {
+ rc = LDAP_NO_MEMORY;
+ break;
+ }
+ }
+
+ if ( lo->ldo_defbase != NULL )
+ LDAP_FREE( lo->ldo_defbase );
+ lo->ldo_defbase = defbase;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DIAGNOSTIC_MESSAGE: {
+ const char *err = (const char *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_error ) {
+ LDAP_FREE(ld->ld_error);
+ ld->ld_error = NULL;
+ }
+
+ if ( err ) {
+ ld->ld_error = LDAP_STRDUP(err);
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_MATCHED_DN: {
+ const char *matched = (const char *) invalue;
+
+ if (ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_matched ) {
+ LDAP_FREE(ld->ld_matched);
+ ld->ld_matched = NULL;
+ }
+
+ if ( matched ) {
+ ld->ld_matched = LDAP_STRDUP( matched );
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_REFERRAL_URLS: {
+ char *const *referrals = (char *const *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ if( ld->ld_referrals ) {
+ LDAP_VFREE(ld->ld_referrals);
+ }
+
+ if ( referrals ) {
+ ld->ld_referrals = ldap_value_dup(referrals);
+ }
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_rebind_proc() */
+ case LDAP_OPT_REBIND_PROC: {
+ lo->ldo_rebind_proc = (LDAP_REBIND_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_REBIND_PARAMS: {
+ lo->ldo_rebind_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_nextref_proc() */
+ case LDAP_OPT_NEXTREF_PROC: {
+ lo->ldo_nextref_proc = (LDAP_NEXTREF_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_NEXTREF_PARAMS: {
+ lo->ldo_nextref_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* Only accessed from inside this function by ldap_set_urllist_proc() */
+ case LDAP_OPT_URLLIST_PROC: {
+ lo->ldo_urllist_proc = (LDAP_URLLIST_PROC *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_URLLIST_PARAMS: {
+ lo->ldo_urllist_params = (void *)invalue;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ /* read-only options */
+ case LDAP_OPT_API_INFO:
+ case LDAP_OPT_DESC:
+ case LDAP_OPT_SOCKBUF:
+ case LDAP_OPT_API_FEATURE_INFO:
+ break; /* LDAP_OPT_ERROR */
+
+ /* options which cannot withstand invalue == NULL */
+ case LDAP_OPT_DEREF:
+ case LDAP_OPT_SIZELIMIT:
+ case LDAP_OPT_TIMELIMIT:
+ case LDAP_OPT_PROTOCOL_VERSION:
+ case LDAP_OPT_RESULT_CODE:
+ case LDAP_OPT_DEBUG_LEVEL:
+ case LDAP_OPT_TIMEOUT:
+ case LDAP_OPT_NETWORK_TIMEOUT:
+ case LDAP_OPT_CONNECT_CB:
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ case LDAP_OPT_X_KEEPALIVE_PROBES :
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL :
+ if(invalue == NULL) {
+ /* no place to set from */
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_ERROR );
+ }
+ break;
+
+ default:
+#ifdef HAVE_TLS
+ if ( ldap_pvt_tls_set_option( ld, option, (void *)invalue ) == 0 ) {
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_SUCCESS );
+ }
+#endif
+#ifdef HAVE_CYRUS_SASL
+ if ( ldap_int_sasl_set_option( ld, option, (void *)invalue ) == 0 ) {
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_SUCCESS );
+ }
+#endif
+#ifdef HAVE_GSSAPI
+ if ( ldap_int_gssapi_set_option( ld, option, (void *)invalue ) == 0 ) {
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( LDAP_OPT_SUCCESS );
+ }
+#endif
+ /* bad param */
+ break; /* LDAP_OPT_ERROR */
+ }
+
+ /* options which cannot withstand invalue == NULL */
+
+ switch(option) {
+ case LDAP_OPT_DEREF:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_deref = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_SIZELIMIT:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_sizelimit = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMELIMIT:
+ /* FIXME: check value for protocol compliance? */
+ lo->ldo_timelimit = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_TIMEOUT: {
+ const struct timeval *tv =
+ (const struct timeval *) invalue;
+
+ lo->ldo_tm_api = *tv;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_NETWORK_TIMEOUT: {
+ const struct timeval *tv =
+ (const struct timeval *) invalue;
+
+ lo->ldo_tm_net = *tv;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_PROTOCOL_VERSION: {
+ int vers = * (const int *) invalue;
+ if (vers < LDAP_VERSION_MIN || vers > LDAP_VERSION_MAX) {
+ /* not supported */
+ break;
+ }
+ lo->ldo_version = vers;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_RESULT_CODE: {
+ int err = * (const int *) invalue;
+
+ if(ld == NULL) {
+ /* need a struct ldap */
+ break;
+ }
+
+ ld->ld_errno = err;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_DEBUG_LEVEL:
+ lo->ldo_debug = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ case LDAP_OPT_CONNECT_CB:
+ {
+ /* setting pushes the callback */
+ ldaplist *ll;
+ ll = LDAP_MALLOC( sizeof( *ll ));
+ ll->ll_data = (void *)invalue;
+ ll->ll_next = lo->ldo_conn_cbs;
+ lo->ldo_conn_cbs = ll;
+ }
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_IDLE:
+ lo->ldo_keepalive_idle = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_PROBES :
+ lo->ldo_keepalive_probes = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+ case LDAP_OPT_X_KEEPALIVE_INTERVAL :
+ lo->ldo_keepalive_interval = * (const int *) invalue;
+ rc = LDAP_OPT_SUCCESS;
+ break;
+
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ return ( rc );
+}
+
+int
+ldap_set_rebind_proc( LDAP *ld, LDAP_REBIND_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_REBIND_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_REBIND_PARAMS, (void *)params );
+ return rc;
+}
+
+int
+ldap_set_nextref_proc( LDAP *ld, LDAP_NEXTREF_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_NEXTREF_PARAMS, (void *)params );
+ return rc;
+}
+
+int
+ldap_set_urllist_proc( LDAP *ld, LDAP_URLLIST_PROC *proc, void *params )
+{
+ int rc;
+ rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PROC, (void *)proc );
+ if( rc != LDAP_OPT_SUCCESS ) return rc;
+
+ rc = ldap_set_option( ld, LDAP_OPT_URLLIST_PARAMS, (void *)params );
+ return rc;
+}
diff --git a/libraries/libldap/os-ip.c b/libraries/libldap/os-ip.c
new file mode 100644
index 0000000..9d9242a
--- /dev/null
+++ b/libraries/libldap/os-ip.c
@@ -0,0 +1,1154 @@
+/* os-ip.c -- platform-specific TCP & UDP related code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Portions Copyright 1999 Lars Uffmann.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Significant additional contributors include:
+ * Lars Uffman
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif /* HAVE_IO_H */
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "ldap-int.h"
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+# ifdef LDAP_PF_INET6
+int ldap_int_inet4or6 = AF_UNSPEC;
+# else
+int ldap_int_inet4or6 = AF_INET;
+# endif
+#endif
+
+#ifdef LDAP_DEBUG
+
+#define osip_debug(ld,fmt,arg1,arg2,arg3) \
+do { \
+ ldap_log_printf(NULL, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \
+} while(0)
+
+#else
+
+#define osip_debug(ld,fmt,arg1,arg2,arg3) ((void)0)
+
+#endif /* LDAP_DEBUG */
+
+static void
+ldap_pvt_set_errno(int err)
+{
+ sock_errset(err);
+}
+
+int
+ldap_int_timeval_dup( struct timeval **dest, const struct timeval *src )
+{
+ struct timeval *new;
+
+ assert( dest != NULL );
+
+ if (src == NULL) {
+ *dest = NULL;
+ return 0;
+ }
+
+ new = (struct timeval *) LDAP_MALLOC(sizeof(struct timeval));
+
+ if( new == NULL ) {
+ *dest = NULL;
+ return 1;
+ }
+
+ AC_MEMCPY( (char *) new, (const char *) src, sizeof(struct timeval));
+
+ *dest = new;
+ return 0;
+}
+
+static int
+ldap_pvt_ndelay_on(LDAP *ld, int fd)
+{
+ osip_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0);
+ return ber_pvt_socket_set_nonblock( fd, 1 );
+}
+
+static int
+ldap_pvt_ndelay_off(LDAP *ld, int fd)
+{
+ osip_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0);
+ return ber_pvt_socket_set_nonblock( fd, 0 );
+}
+
+static ber_socket_t
+ldap_int_socket(LDAP *ld, int family, int type )
+{
+ ber_socket_t s = socket(family, type, 0);
+ osip_debug(ld, "ldap_new_socket: %d\n",s,0,0);
+#ifdef FD_CLOEXEC
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+ return ( s );
+}
+
+static int
+ldap_pvt_close_socket(LDAP *ld, int s)
+{
+ osip_debug(ld, "ldap_close_socket: %d\n",s,0,0);
+ return tcp_close(s);
+}
+
+static int
+ldap_int_prepare_socket(LDAP *ld, int s, int proto )
+{
+ osip_debug( ld, "ldap_prepare_socket: %d\n", s, 0, 0 );
+
+#if defined( SO_KEEPALIVE ) || defined( TCP_NODELAY )
+ if ( proto == LDAP_PROTO_TCP ) {
+ int dummy = 1;
+#ifdef SO_KEEPALIVE
+ if ( setsockopt( s, SOL_SOCKET, SO_KEEPALIVE,
+ (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
+ {
+ osip_debug( ld, "ldap_prepare_socket: "
+ "setsockopt(%d, SO_KEEPALIVE) failed (ignored).\n",
+ s, 0, 0 );
+ }
+ if ( ld->ld_options.ldo_keepalive_idle > 0 )
+ {
+#ifdef TCP_KEEPIDLE
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPIDLE,
+ (void*) &ld->ld_options.ldo_keepalive_idle,
+ sizeof(ld->ld_options.ldo_keepalive_idle) ) == AC_SOCKET_ERROR )
+ {
+ osip_debug( ld, "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPIDLE) failed (ignored).\n",
+ s, 0, 0 );
+ }
+#else
+ osip_debug( ld, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPIDLE not supported on this system.\n",
+ 0, 0, 0 );
+#endif /* TCP_KEEPIDLE */
+ }
+ if ( ld->ld_options.ldo_keepalive_probes > 0 )
+ {
+#ifdef TCP_KEEPCNT
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPCNT,
+ (void*) &ld->ld_options.ldo_keepalive_probes,
+ sizeof(ld->ld_options.ldo_keepalive_probes) ) == AC_SOCKET_ERROR )
+ {
+ osip_debug( ld, "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPCNT) failed (ignored).\n",
+ s, 0, 0 );
+ }
+#else
+ osip_debug( ld, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPCNT not supported on this system.\n",
+ 0, 0, 0 );
+#endif /* TCP_KEEPCNT */
+ }
+ if ( ld->ld_options.ldo_keepalive_interval > 0 )
+ {
+#ifdef TCP_KEEPINTVL
+ if ( setsockopt( s, IPPROTO_TCP, TCP_KEEPINTVL,
+ (void*) &ld->ld_options.ldo_keepalive_interval,
+ sizeof(ld->ld_options.ldo_keepalive_interval) ) == AC_SOCKET_ERROR )
+ {
+ osip_debug( ld, "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_KEEPINTVL) failed (ignored).\n",
+ s, 0, 0 );
+ }
+#else
+ osip_debug( ld, "ldap_prepare_socket: "
+ "sockopt TCP_KEEPINTVL not supported on this system.\n",
+ 0, 0, 0 );
+#endif /* TCP_KEEPINTVL */
+ }
+#endif /* SO_KEEPALIVE */
+#ifdef TCP_NODELAY
+ if ( setsockopt( s, IPPROTO_TCP, TCP_NODELAY,
+ (char*) &dummy, sizeof(dummy) ) == AC_SOCKET_ERROR )
+ {
+ osip_debug( ld, "ldap_prepare_socket: "
+ "setsockopt(%d, TCP_NODELAY) failed (ignored).\n",
+ s, 0, 0 );
+ }
+#endif /* TCP_NODELAY */
+ }
+#endif /* SO_KEEPALIVE || TCP_NODELAY */
+
+ return 0;
+}
+
+#ifndef HAVE_WINSOCK
+
+#undef TRACE
+#define TRACE do { \
+ osip_debug(ld, \
+ "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \
+ s, \
+ errno, \
+ sock_errstr(errno) ); \
+} while( 0 )
+
+/*
+ * check the socket for errors after select returned.
+ */
+static int
+ldap_pvt_is_socket_ready(LDAP *ld, int s)
+{
+ osip_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0);
+
+#if defined( notyet ) /* && defined( SO_ERROR ) */
+{
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ return -1;
+ }
+ if ( so_errno ) {
+ ldap_pvt_set_errno(so_errno);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#else
+{
+ /* error slippery */
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sin;
+#else
+ struct sockaddr_in sin;
+#endif
+ char ch;
+ ber_socklen_t dummy = sizeof(sin);
+ if ( getpeername( s, (struct sockaddr *) &sin, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ /* XXX: needs to be replace with ber_stream_read() */
+ (void)read(s, &ch, 1);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#endif
+ return -1;
+}
+#undef TRACE
+
+#endif /* HAVE_WINSOCK */
+
+/* NOTE: this is identical to analogous code in os-local.c */
+int
+ldap_int_poll(
+ LDAP *ld,
+ ber_socket_t s,
+ struct timeval *tvp,
+ int wr )
+{
+ int rc;
+
+
+ osip_debug(ld, "ldap_int_poll: fd: %d tm: %ld\n",
+ s, tvp ? tvp->tv_sec : -1L, 0);
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd fd;
+ int timeout = INFTIM;
+ short event = wr ? POLL_WRITE : POLL_READ;
+
+ fd.fd = s;
+ fd.events = event;
+
+ if ( tvp != NULL ) {
+ timeout = TV2MILLISEC( tvp );
+ }
+ do {
+ fd.revents = 0;
+ rc = poll( &fd, 1, timeout );
+
+ } while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
+
+ if ( rc == AC_SOCKET_ERROR ) {
+ return rc;
+ }
+
+ if ( timeout == 0 && rc == 0 ) {
+ return -2;
+ }
+
+ if ( fd.revents & event ) {
+ if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
+ return -1;
+ }
+
+ if ( ldap_pvt_ndelay_off( ld, s ) == -1 ) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+#else
+ {
+ fd_set wfds, *z = NULL;
+#ifdef HAVE_WINSOCK
+ fd_set efds;
+#endif
+ struct timeval tv = { 0 };
+
+#if defined( FD_SETSIZE ) && !defined( HAVE_WINSOCK )
+ if ( s >= FD_SETSIZE ) {
+ rc = AC_SOCKET_ERROR;
+ tcp_close( s );
+ ldap_pvt_set_errno( EMFILE );
+ return rc;
+ }
+#endif
+
+ if ( tvp != NULL ) {
+ tv = *tvp;
+ }
+
+ do {
+ FD_ZERO(&wfds);
+ FD_SET(s, &wfds );
+
+#ifdef HAVE_WINSOCK
+ FD_ZERO(&efds);
+ FD_SET(s, &efds );
+#endif
+
+ rc = select( ldap_int_tblsize, z, &wfds,
+#ifdef HAVE_WINSOCK
+ &efds,
+#else
+ z,
+#endif
+ tvp ? &tv : NULL );
+ } while ( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ) );
+
+ if ( rc == AC_SOCKET_ERROR ) {
+ return rc;
+ }
+
+ if ( rc == 0 && tvp && tvp->tv_sec == 0 && tvp->tv_usec == 0 ) {
+ return -2;
+ }
+
+#ifdef HAVE_WINSOCK
+ /* This means the connection failed */
+ if ( FD_ISSET(s, &efds) ) {
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR,
+ (char *) &so_errno, &dummy ) == AC_SOCKET_ERROR || !so_errno )
+ {
+ /* impossible */
+ so_errno = WSAGetLastError();
+ }
+ ldap_pvt_set_errno( so_errno );
+ osip_debug(ld, "ldap_int_poll: error on socket %d: "
+ "errno: %d (%s)\n", s, errno, sock_errstr( errno ));
+ return -1;
+ }
+#endif
+ if ( FD_ISSET(s, &wfds) ) {
+#ifndef HAVE_WINSOCK
+ if ( ldap_pvt_is_socket_ready( ld, s ) == -1 ) {
+ return -1;
+ }
+#endif
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) {
+ return -1;
+ }
+ return 0;
+ }
+ }
+#endif
+
+ osip_debug(ld, "ldap_int_poll: timed out\n",0,0,0);
+ ldap_pvt_set_errno( ETIMEDOUT );
+ return -1;
+}
+
+static int
+ldap_pvt_connect(LDAP *ld, ber_socket_t s,
+ struct sockaddr *sin, ber_socklen_t addrlen,
+ int async)
+{
+ int rc, err;
+ struct timeval tv, *opt_tv = NULL;
+
+#ifdef LDAP_CONNECTIONLESS
+ /* We could do a connect() but that would interfere with
+ * attempts to poll a broadcast address
+ */
+ if (LDAP_IS_UDP(ld)) {
+ if (ld->ld_options.ldo_peer)
+ ldap_memfree(ld->ld_options.ldo_peer);
+ ld->ld_options.ldo_peer=ldap_memcalloc(1, sizeof(struct sockaddr_storage));
+ AC_MEMCPY(ld->ld_options.ldo_peer,sin,addrlen);
+ return ( 0 );
+ }
+#endif
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_net;
+ opt_tv = &tv;
+ }
+
+ osip_debug(ld, "ldap_pvt_connect: fd: %d tm: %ld async: %d\n",
+ s, opt_tv ? tv.tv_sec : -1L, async);
+
+ if ( opt_tv && ldap_pvt_ndelay_on(ld, s) == -1 )
+ return ( -1 );
+
+ do{
+ osip_debug(ld, "attempting to connect: \n", 0, 0, 0);
+ if ( connect(s, sin, addrlen) != AC_SOCKET_ERROR ) {
+ osip_debug(ld, "connect success\n", 0, 0, 0);
+
+ if ( !async && opt_tv && ldap_pvt_ndelay_off(ld, s) == -1 )
+ return ( -1 );
+ return ( 0 );
+ }
+ err = sock_errno();
+ osip_debug(ld, "connect errno: %d\n", err, 0, 0);
+
+ } while(err == EINTR &&
+ LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if ( err != EINPROGRESS && err != EWOULDBLOCK ) {
+ return ( -1 );
+ }
+
+ if ( async ) {
+ /* caller will call ldap_int_poll() as appropriate? */
+ return ( -2 );
+ }
+
+ rc = ldap_int_poll( ld, s, opt_tv, 1 );
+
+ osip_debug(ld, "ldap_pvt_connect: %d\n", rc, 0, 0);
+
+ return rc;
+}
+
+#ifndef HAVE_INET_ATON
+int
+ldap_pvt_inet_aton( const char *host, struct in_addr *in)
+{
+ unsigned long u = inet_addr( host );
+
+#ifdef INADDR_NONE
+ if ( u == INADDR_NONE ) return 0;
+#endif
+ if ( u == 0xffffffffUL || u == (unsigned long) -1L ) return 0;
+
+ in->s_addr = u;
+ return 1;
+}
+#endif
+
+int
+ldap_int_connect_cbs(LDAP *ld, Sockbuf *sb, ber_socket_t *s, LDAPURLDesc *srv, struct sockaddr *addr)
+{
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+ int rc;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_FD, s );
+
+ /* Invoke all handle-specific callbacks first */
+ lo = &ld->ld_options;
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ /* on any failure, call the teardown functions for anything
+ * that previously succeeded
+ */
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ /* a failure might have implicitly closed the fd */
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ lo = LDAP_INT_GLOBAL_OPT();
+ for (ll = lo->ldo_conn_cbs; ll; ll = ll->ll_next) {
+ cb = ll->ll_data;
+ rc = cb->lc_add( ld, sb, srv, addr, cb );
+ if ( rc ) {
+ ldaplist *l2;
+ for (l2 = lo->ldo_conn_cbs; l2 != ll; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ lo = &ld->ld_options;
+ for (l2 = lo->ldo_conn_cbs; l2; l2 = l2->ll_next) {
+ cb = l2->ll_data;
+ cb->lc_del( ld, sb, cb );
+ }
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, s );
+ return rc;
+ }
+ }
+ return 0;
+}
+
+int
+ldap_connect_to_host(LDAP *ld, Sockbuf *sb,
+ int proto, LDAPURLDesc *srv,
+ int async )
+{
+ int rc;
+ int socktype, port;
+ ber_socket_t s = AC_SOCKET_INVALID;
+ char *host;
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ char serv[7];
+ int err;
+ struct addrinfo hints, *res, *sai;
+#else
+ int i;
+ int use_hp = 0;
+ struct hostent *hp = NULL;
+ struct hostent he_buf;
+ struct in_addr in;
+ char *ha_buf=NULL;
+#endif
+
+ if ( srv->lud_host == NULL || *srv->lud_host == 0 ) {
+ host = "localhost";
+ } else {
+ host = srv->lud_host;
+ }
+
+ port = srv->lud_port;
+
+ if( !port ) {
+ if( strcmp(srv->lud_scheme, "ldaps") == 0 ) {
+ port = LDAPS_PORT;
+ } else {
+ port = LDAP_PORT;
+ }
+ }
+
+ switch(proto) {
+ case LDAP_PROTO_TCP: socktype = SOCK_STREAM;
+ osip_debug( ld,
+ "ldap_connect_to_host: TCP %s:%d\n",
+ host, port, 0);
+ break;
+ case LDAP_PROTO_UDP: socktype = SOCK_DGRAM;
+ osip_debug( ld,
+ "ldap_connect_to_host: UDP %s:%d\n",
+ host, port, 0);
+ break;
+ default:
+ osip_debug( ld, "ldap_connect_to_host: unknown proto: %d\n",
+ proto, 0, 0 );
+ return -1;
+ }
+
+#if defined( HAVE_GETADDRINFO ) && defined( HAVE_INET_NTOP )
+ memset( &hints, '\0', sizeof(hints) );
+#ifdef USE_AI_ADDRCONFIG /* FIXME: configure test needed */
+ /* Use AI_ADDRCONFIG only on systems where its known to be needed. */
+ hints.ai_flags = AI_ADDRCONFIG;
+#endif
+ hints.ai_family = ldap_int_inet4or6;
+ hints.ai_socktype = socktype;
+ snprintf(serv, sizeof serv, "%d", port );
+
+ /* most getaddrinfo(3) use non-threadsafe resolver libraries */
+ LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex);
+
+ err = getaddrinfo( host, serv, &hints, &res );
+
+ LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex);
+
+ if ( err != 0 ) {
+ osip_debug(ld, "ldap_connect_to_host: getaddrinfo failed: %s\n",
+ AC_GAI_STRERROR(err), 0, 0);
+ return -1;
+ }
+ rc = -1;
+
+ for( sai=res; sai != NULL; sai=sai->ai_next) {
+ if( sai->ai_addr == NULL ) {
+ osip_debug(ld, "ldap_connect_to_host: getaddrinfo "
+ "ai_addr is NULL?\n", 0, 0, 0);
+ continue;
+ }
+
+#ifndef LDAP_PF_INET6
+ if ( sai->ai_family == AF_INET6 ) continue;
+#endif
+ /* we assume AF_x and PF_x are equal for all x */
+ s = ldap_int_socket( ld, sai->ai_family, socktype );
+ if ( s == AC_SOCKET_INVALID ) {
+ continue;
+ }
+
+ if ( ldap_int_prepare_socket(ld, s, proto ) == -1 ) {
+ ldap_pvt_close_socket(ld, s);
+ break;
+ }
+
+ switch (sai->ai_family) {
+#ifdef LDAP_PF_INET6
+ case AF_INET6: {
+ char addr[INET6_ADDRSTRLEN];
+ inet_ntop( AF_INET6,
+ &((struct sockaddr_in6 *)sai->ai_addr)->sin6_addr,
+ addr, sizeof addr);
+ osip_debug(ld, "ldap_connect_to_host: Trying %s %s\n",
+ addr, serv, 0);
+ } break;
+#endif
+ case AF_INET: {
+ char addr[INET_ADDRSTRLEN];
+ inet_ntop( AF_INET,
+ &((struct sockaddr_in *)sai->ai_addr)->sin_addr,
+ addr, sizeof addr);
+ osip_debug(ld, "ldap_connect_to_host: Trying %s:%s\n",
+ addr, serv, 0);
+ } break;
+ }
+
+ rc = ldap_pvt_connect( ld, s,
+ sai->ai_addr, sai->ai_addrlen, async );
+ if ( rc == 0 || rc == -2 ) {
+ err = ldap_int_connect_cbs( ld, sb, &s, srv, sai->ai_addr );
+ if ( err )
+ rc = err;
+ else
+ break;
+ }
+ ldap_pvt_close_socket(ld, s);
+ }
+ freeaddrinfo(res);
+
+#else
+ if (! inet_aton( host, &in ) ) {
+ int local_h_errno;
+ rc = ldap_pvt_gethostbyname_a( host, &he_buf, &ha_buf,
+ &hp, &local_h_errno );
+
+ if ( (rc < 0) || (hp == NULL) ) {
+#ifdef HAVE_WINSOCK
+ ldap_pvt_set_errno( WSAGetLastError() );
+#else
+ /* not exactly right, but... */
+ ldap_pvt_set_errno( EHOSTUNREACH );
+#endif
+ if (ha_buf) LDAP_FREE(ha_buf);
+ return -1;
+ }
+
+ use_hp = 1;
+ }
+
+ rc = s = -1;
+ for ( i = 0; !use_hp || (hp->h_addr_list[i] != 0); ++i, rc = -1 ) {
+ struct sockaddr_in sin;
+
+ s = ldap_int_socket( ld, PF_INET, socktype );
+ if ( s == AC_SOCKET_INVALID ) {
+ /* use_hp ? continue : break; */
+ break;
+ }
+
+ if ( ldap_int_prepare_socket( ld, s, proto ) == -1 ) {
+ ldap_pvt_close_socket(ld, s);
+ break;
+ }
+
+ (void)memset((char *)&sin, '\0', sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons((unsigned short) port);
+
+ if( use_hp ) {
+ AC_MEMCPY( &sin.sin_addr, hp->h_addr_list[i],
+ sizeof(sin.sin_addr) );
+ } else {
+ AC_MEMCPY( &sin.sin_addr, &in.s_addr,
+ sizeof(sin.sin_addr) );
+ }
+
+#ifdef HAVE_INET_NTOA_B
+ {
+ /* for VxWorks */
+ char address[INET_ADDR_LEN];
+ inet_ntoa_b(sin.sin_address, address);
+ osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n",
+ address, port, 0);
+ }
+#else
+ osip_debug(ld, "ldap_connect_to_host: Trying %s:%d\n",
+ inet_ntoa(sin.sin_addr), port, 0);
+#endif
+
+ rc = ldap_pvt_connect(ld, s,
+ (struct sockaddr *)&sin, sizeof(sin),
+ async);
+
+ if ( (rc == 0) || (rc == -2) ) {
+ int err = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&sin );
+ if ( err )
+ rc = err;
+ else
+ break;
+ }
+
+ ldap_pvt_close_socket(ld, s);
+
+ if (!use_hp) break;
+ }
+ if (ha_buf) LDAP_FREE(ha_buf);
+#endif
+
+ return rc;
+}
+
+#if defined( HAVE_CYRUS_SASL )
+char *
+ldap_host_connected_to( Sockbuf *sb, const char *host )
+{
+ ber_socklen_t len;
+#ifdef LDAP_PF_INET6
+ struct sockaddr_storage sabuf;
+#else
+ struct sockaddr sabuf;
+#endif
+ struct sockaddr *sa = (struct sockaddr *) &sabuf;
+ ber_socket_t sd;
+
+ (void)memset( (char *)sa, '\0', sizeof sabuf );
+ len = sizeof sabuf;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+ if ( getpeername( sd, sa, &len ) == -1 ) {
+ return( NULL );
+ }
+
+ /*
+ * do a reverse lookup on the addr to get the official hostname.
+ * this is necessary for kerberos to work right, since the official
+ * hostname is used as the kerberos instance.
+ */
+
+ switch (sa->sa_family) {
+#ifdef LDAP_PF_LOCAL
+ case AF_LOCAL:
+ return LDAP_STRDUP( ldap_int_hostname );
+#endif
+#ifdef LDAP_PF_INET6
+ case AF_INET6:
+ {
+ struct in6_addr localhost = IN6ADDR_LOOPBACK_INIT;
+ if( memcmp ( &((struct sockaddr_in6 *)sa)->sin6_addr,
+ &localhost, sizeof(localhost)) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+ }
+ break;
+#endif
+ case AF_INET:
+ {
+ struct in_addr localhost;
+ localhost.s_addr = htonl( INADDR_ANY );
+
+ if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
+ &localhost, sizeof(localhost) ) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+
+#ifdef INADDR_LOOPBACK
+ localhost.s_addr = htonl( INADDR_LOOPBACK );
+
+ if( memcmp ( &((struct sockaddr_in *)sa)->sin_addr,
+ &localhost, sizeof(localhost) ) == 0 )
+ {
+ return LDAP_STRDUP( ldap_int_hostname );
+ }
+#endif
+ }
+ break;
+
+ default:
+ return( NULL );
+ break;
+ }
+
+ {
+ char *herr;
+#ifdef NI_MAXHOST
+ char hbuf[NI_MAXHOST];
+#elif defined( MAXHOSTNAMELEN )
+ char hbuf[MAXHOSTNAMELEN];
+#else
+ char hbuf[256];
+#endif
+ hbuf[0] = 0;
+
+ if (ldap_pvt_get_hname( sa, len, hbuf, sizeof(hbuf), &herr ) == 0
+ && hbuf[0] )
+ {
+ return LDAP_STRDUP( hbuf );
+ }
+ }
+
+ return host ? LDAP_STRDUP( host ) : NULL;
+}
+#endif
+
+
+struct selectinfo {
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ int si_maxfd;
+ struct pollfd si_fds[FD_SETSIZE];
+#else
+ /* for UNIX select(2) */
+ fd_set si_readfds;
+ fd_set si_writefds;
+ fd_set si_use_readfds;
+ fd_set si_use_writefds;
+#endif
+};
+
+void
+ldap_mark_select_write( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int empty=-1;
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events |= POLL_WRITE;
+ return;
+ }
+ if( empty==-1 && sip->si_fds[i].fd == -1 ) {
+ empty=i;
+ }
+ }
+
+ if( empty == -1 ) {
+ if( sip->si_maxfd >= FD_SETSIZE ) {
+ /* FIXME */
+ return;
+ }
+ empty = sip->si_maxfd++;
+ }
+
+ sip->si_fds[empty].fd = sd;
+ sip->si_fds[empty].events = POLL_WRITE;
+ }
+#else
+ /* for UNIX select(2) */
+ if ( !FD_ISSET( sd, &sip->si_writefds )) {
+ FD_SET( sd, &sip->si_writefds );
+ }
+#endif
+}
+
+
+void
+ldap_mark_select_read( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int empty=-1;
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events |= POLL_READ;
+ return;
+ }
+ if( empty==-1 && sip->si_fds[i].fd == -1 ) {
+ empty=i;
+ }
+ }
+
+ if( empty == -1 ) {
+ if( sip->si_maxfd >= FD_SETSIZE ) {
+ /* FIXME */
+ return;
+ }
+ empty = sip->si_maxfd++;
+ }
+
+ sip->si_fds[empty].fd = sd;
+ sip->si_fds[empty].events = POLL_READ;
+ }
+#else
+ /* for UNIX select(2) */
+ if ( !FD_ISSET( sd, &sip->si_readfds )) {
+ FD_SET( sd, &sip->si_readfds );
+ }
+#endif
+}
+
+
+void
+ldap_mark_select_clear( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].fd = -1;
+ }
+ }
+ }
+#else
+ /* for UNIX select(2) */
+ FD_CLR( sd, &sip->si_writefds );
+ FD_CLR( sd, &sip->si_readfds );
+#endif
+}
+
+void
+ldap_clear_select_write( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ sip->si_fds[i].events &= ~POLL_WRITE;
+ }
+ }
+ }
+#else
+ /* for UNIX select(2) */
+ FD_CLR( sd, &sip->si_writefds );
+#endif
+}
+
+
+int
+ldap_is_write_ready( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ return sip->si_fds[i].revents & POLL_WRITE;
+ }
+ }
+
+ return 0;
+ }
+#else
+ /* for UNIX select(2) */
+ return( FD_ISSET( sd, &sip->si_use_writefds ));
+#endif
+}
+
+
+int
+ldap_is_read_ready( LDAP *ld, Sockbuf *sb )
+{
+ struct selectinfo *sip;
+ ber_socket_t sd;
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+
+ if (ber_sockbuf_ctrl( sb, LBER_SB_OPT_DATA_READY, NULL ))
+ return 1;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ {
+ int i;
+ for(i=0; i < sip->si_maxfd; i++) {
+ if( sip->si_fds[i].fd == sd ) {
+ return sip->si_fds[i].revents & POLL_READ;
+ }
+ }
+
+ return 0;
+ }
+#else
+ /* for UNIX select(2) */
+ return( FD_ISSET( sd, &sip->si_use_readfds ));
+#endif
+}
+
+
+void *
+ldap_new_select_info( void )
+{
+ struct selectinfo *sip;
+
+ sip = (struct selectinfo *)LDAP_CALLOC( 1, sizeof( struct selectinfo ));
+
+ if ( sip == NULL ) return NULL;
+
+#ifdef HAVE_POLL
+ /* for UNIX poll(2) */
+ /* sip->si_maxfd=0 */
+#else
+ /* for UNIX select(2) */
+ FD_ZERO( &sip->si_readfds );
+ FD_ZERO( &sip->si_writefds );
+#endif
+
+ return( (void *)sip );
+}
+
+
+void
+ldap_free_select_info( void *sip )
+{
+ LDAP_FREE( sip );
+}
+
+
+#ifndef HAVE_POLL
+int ldap_int_tblsize = 0;
+
+void
+ldap_int_ip_init( void )
+{
+#if defined( HAVE_SYSCONF )
+ long tblsize = sysconf( _SC_OPEN_MAX );
+ if( tblsize > INT_MAX ) tblsize = INT_MAX;
+
+#elif defined( HAVE_GETDTABLESIZE )
+ int tblsize = getdtablesize();
+#else
+ int tblsize = FD_SETSIZE;
+#endif /* !USE_SYSCONF */
+
+#ifdef FD_SETSIZE
+ if( tblsize > FD_SETSIZE ) tblsize = FD_SETSIZE;
+#endif /* FD_SETSIZE */
+
+ ldap_int_tblsize = tblsize;
+}
+#endif
+
+
+int
+ldap_int_select( LDAP *ld, struct timeval *timeout )
+{
+ int rc;
+ struct selectinfo *sip;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_select\n", 0, 0, 0 );
+
+#ifndef HAVE_POLL
+ if ( ldap_int_tblsize == 0 ) ldap_int_ip_init();
+#endif
+
+ sip = (struct selectinfo *)ld->ld_selectinfo;
+ assert( sip != NULL );
+
+#ifdef HAVE_POLL
+ {
+ int to = timeout ? TV2MILLISEC( timeout ) : INFTIM;
+ rc = poll( sip->si_fds, sip->si_maxfd, to );
+ }
+#else
+ sip->si_use_readfds = sip->si_readfds;
+ sip->si_use_writefds = sip->si_writefds;
+
+ rc = select( ldap_int_tblsize,
+ &sip->si_use_readfds, &sip->si_use_writefds,
+ NULL, timeout );
+#endif
+
+ return rc;
+}
diff --git a/libraries/libldap/os-local.c b/libraries/libldap/os-local.c
new file mode 100644
index 0000000..9f7e48b
--- /dev/null
+++ b/libraries/libldap/os-local.c
@@ -0,0 +1,363 @@
+/* os-local.c -- platform-specific domain socket code */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* Portions (C) Copyright PADL Software Pty Ltd. 1999
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that this notice is preserved
+ * and that due credit is given to PADL Software Pty Ltd. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#ifdef LDAP_PF_LOCAL
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif /* HAVE_IO_H */
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+
+#ifdef LDAP_DEBUG
+
+#define oslocal_debug(ld,fmt,arg1,arg2,arg3) \
+do { \
+ ldap_log_printf(ld, LDAP_DEBUG_TRACE, fmt, arg1, arg2, arg3); \
+} while(0)
+
+#else
+
+#define oslocal_debug(ld,fmt,arg1,arg2,arg3) ((void)0)
+
+#endif /* LDAP_DEBUG */
+
+static void
+ldap_pvt_set_errno(int err)
+{
+ errno = err;
+}
+
+static int
+ldap_pvt_ndelay_on(LDAP *ld, int fd)
+{
+ oslocal_debug(ld, "ldap_ndelay_on: %d\n",fd,0,0);
+ return ber_pvt_socket_set_nonblock( fd, 1 );
+}
+
+static int
+ldap_pvt_ndelay_off(LDAP *ld, int fd)
+{
+ oslocal_debug(ld, "ldap_ndelay_off: %d\n",fd,0,0);
+ return ber_pvt_socket_set_nonblock( fd, 0 );
+}
+
+static ber_socket_t
+ldap_pvt_socket(LDAP *ld)
+{
+ ber_socket_t s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ oslocal_debug(ld, "ldap_new_socket: %d\n",s,0,0);
+#ifdef FD_CLOEXEC
+ fcntl(s, F_SETFD, FD_CLOEXEC);
+#endif
+ return ( s );
+}
+
+static int
+ldap_pvt_close_socket(LDAP *ld, int s)
+{
+ oslocal_debug(ld, "ldap_close_socket: %d\n",s,0,0);
+ return tcp_close(s);
+}
+
+#undef TRACE
+#define TRACE do { \
+ char ebuf[128]; \
+ oslocal_debug(ld, \
+ "ldap_is_socket_ready: error on socket %d: errno: %d (%s)\n", \
+ s, \
+ errno, \
+ AC_STRERROR_R(errno, ebuf, sizeof ebuf)); \
+} while( 0 )
+
+/*
+ * check the socket for errors after select returned.
+ */
+static int
+ldap_pvt_is_socket_ready(LDAP *ld, int s)
+{
+ oslocal_debug(ld, "ldap_is_sock_ready: %d\n",s,0,0);
+
+#if defined( notyet ) /* && defined( SO_ERROR ) */
+{
+ int so_errno;
+ ber_socklen_t dummy = sizeof(so_errno);
+ if ( getsockopt( s, SOL_SOCKET, SO_ERROR, &so_errno, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ return -1;
+ }
+ if ( so_errno ) {
+ ldap_pvt_set_errno(so_errno);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#else
+{
+ /* error slippery */
+ struct sockaddr_un sa;
+ char ch;
+ ber_socklen_t dummy = sizeof(sa);
+ if ( getpeername( s, (struct sockaddr *) &sa, &dummy )
+ == AC_SOCKET_ERROR )
+ {
+ /* XXX: needs to be replace with ber_stream_read() */
+ (void)read(s, &ch, 1);
+ TRACE;
+ return -1;
+ }
+ return 0;
+}
+#endif
+ return -1;
+}
+#undef TRACE
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+static const char abandonPDU[] = {LDAP_TAG_MESSAGE, 6,
+ LDAP_TAG_MSGID, 1, 0, LDAP_REQ_ABANDON, 1, 0};
+#endif
+
+static int
+ldap_pvt_connect(LDAP *ld, ber_socket_t s, struct sockaddr_un *sa, int async)
+{
+ int rc;
+ struct timeval tv, *opt_tv = NULL;
+
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_net;
+ opt_tv = &tv;
+ }
+
+ oslocal_debug(ld, "ldap_connect_timeout: fd: %d tm: %ld async: %d\n",
+ s, opt_tv ? tv.tv_sec : -1L, async);
+
+ if ( ldap_pvt_ndelay_on(ld, s) == -1 ) return -1;
+
+ if ( connect(s, (struct sockaddr *) sa, sizeof(struct sockaddr_un))
+ != AC_SOCKET_ERROR )
+ {
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ /* Send a dummy message with access rights. Remote side will
+ * obtain our uid/gid by fstat'ing this descriptor. The
+ * descriptor permissions must match exactly, and we also
+ * send the socket name, which must also match.
+ */
+sendcred:
+ {
+ int fds[2];
+ ber_socklen_t salen = sizeof(*sa);
+ if (pipe(fds) == 0) {
+ /* Abandon, noop, has no reply */
+ struct iovec iov;
+ struct msghdr msg = {0};
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+# ifndef CMSG_SPACE
+# define CMSG_SPACE(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + _CMSG_ALIGN(len) )
+# endif
+# ifndef CMSG_LEN
+# define CMSG_LEN(len) (_CMSG_ALIGN( sizeof(struct cmsghdr)) + (len) )
+# endif
+ union {
+ struct cmsghdr cm;
+ unsigned char control[CMSG_SPACE(sizeof(int))];
+ } control_un;
+ struct cmsghdr *cmsg;
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ iov.iov_base = (char *) abandonPDU;
+ iov.iov_len = sizeof abandonPDU;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+# ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
+ msg.msg_control = control_un.control;
+ msg.msg_controllen = sizeof( control_un.control );
+ msg.msg_flags = 0;
+
+ cmsg = CMSG_FIRSTHDR( &msg );
+ cmsg->cmsg_len = CMSG_LEN( sizeof(int) );
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ *((int *)CMSG_DATA(cmsg)) = fds[0];
+# else
+ msg.msg_accrights = (char *)fds;
+ msg.msg_accrightslen = sizeof(int);
+# endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */
+ getpeername( s, (struct sockaddr *) sa, &salen );
+ fchmod( fds[0], S_ISUID|S_IRWXU );
+ write( fds[1], sa, salen );
+ sendmsg( s, &msg, 0 );
+ close(fds[0]);
+ close(fds[1]);
+ }
+ }
+#endif
+ return 0;
+ }
+
+ if ( errno != EINPROGRESS && errno != EWOULDBLOCK ) return -1;
+
+#ifdef notyet
+ if ( async ) return -2;
+#endif
+
+#ifdef HAVE_POLL
+ {
+ struct pollfd fd;
+ int timeout = INFTIM;
+
+ if( opt_tv != NULL ) timeout = TV2MILLISEC( &tv );
+
+ fd.fd = s;
+ fd.events = POLL_WRITE;
+
+ do {
+ fd.revents = 0;
+ rc = poll( &fd, 1, timeout );
+ } while( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if( rc == AC_SOCKET_ERROR ) return rc;
+
+ if( fd.revents & POLL_WRITE ) {
+ if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1;
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ goto sendcred;
+#else
+ return ( 0 );
+#endif
+ }
+ }
+#else
+ {
+ fd_set wfds, *z=NULL;
+
+#ifdef FD_SETSIZE
+ if ( s >= FD_SETSIZE ) {
+ rc = AC_SOCKET_ERROR;
+ tcp_close( s );
+ ldap_pvt_set_errno( EMFILE );
+ return rc;
+ }
+#endif
+ do {
+ FD_ZERO(&wfds);
+ FD_SET(s, &wfds );
+ rc = select( ldap_int_tblsize, z, &wfds, z, opt_tv ? &tv : NULL );
+ } while( rc == AC_SOCKET_ERROR && errno == EINTR &&
+ LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART ));
+
+ if( rc == AC_SOCKET_ERROR ) return rc;
+
+ if ( FD_ISSET(s, &wfds) ) {
+ if ( ldap_pvt_is_socket_ready(ld, s) == -1 ) return -1;
+ if ( ldap_pvt_ndelay_off(ld, s) == -1 ) return -1;
+#ifdef LDAP_PF_LOCAL_SENDMSG
+ goto sendcred;
+#else
+ return ( 0 );
+#endif
+ }
+ }
+#endif
+
+ oslocal_debug(ld, "ldap_connect_timeout: timed out\n",0,0,0);
+ ldap_pvt_set_errno( ETIMEDOUT );
+ return ( -1 );
+}
+
+int
+ldap_connect_to_path(LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv, int async)
+{
+ struct sockaddr_un server;
+ ber_socket_t s;
+ int rc;
+ const char *path = srv->lud_host;
+
+ oslocal_debug(ld, "ldap_connect_to_path\n",0,0,0);
+
+ if ( path == NULL || path[0] == '\0' ) {
+ path = LDAPI_SOCK;
+ } else {
+ if ( strlen(path) > (sizeof( server.sun_path ) - 1) ) {
+ ldap_pvt_set_errno( ENAMETOOLONG );
+ return -1;
+ }
+ }
+
+ s = ldap_pvt_socket( ld );
+ if ( s == AC_SOCKET_INVALID ) {
+ return -1;
+ }
+
+ oslocal_debug(ld, "ldap_connect_to_path: Trying %s\n", path, 0, 0);
+
+ memset( &server, '\0', sizeof(server) );
+ server.sun_family = AF_LOCAL;
+ strcpy( server.sun_path, path );
+
+ rc = ldap_pvt_connect(ld, s, &server, async);
+
+ if (rc == 0) {
+ rc = ldap_int_connect_cbs( ld, sb, &s, srv, (struct sockaddr *)&server );
+ }
+ if ( rc ) {
+ ldap_pvt_close_socket(ld, s);
+ }
+ return rc;
+}
+#else
+static int dummy; /* generate also a warning: 'dummy' defined but not used (at least here) */
+#endif /* LDAP_PF_LOCAL */
diff --git a/libraries/libldap/pagectrl.c b/libraries/libldap/pagectrl.c
new file mode 100644
index 0000000..b3a5b72
--- /dev/null
+++ b/libraries/libldap/pagectrl.c
@@ -0,0 +1,271 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Copyright 2006 Hans Leidekker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* ---------------------------------------------------------------------------
+ ldap_create_page_control_value
+
+ Create and encode the value of the paged results control (RFC 2696).
+
+ ld (IN) An LDAP session handle
+ pagesize (IN) Page size requested
+ cookie (IN) Opaque structure used by the server to track its
+ location in the search results. NULL on the
+ first call.
+ value (OUT) Control value, SHOULD be freed by calling
+ ldap_memfree() when done.
+
+ pagedResultsControl ::= SEQUENCE {
+ controlType 1.2.840.113556.1.4.319,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue searchControlValue }
+
+ searchControlValue ::= SEQUENCE {
+ size INTEGER (0..maxInt),
+ -- requested page size from client
+ -- result set size estimate from server
+ cookie OCTET STRING }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_page_control_value(
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+ struct berval null_cookie = { 0, NULL };
+
+ if ( ld == NULL || value == NULL ||
+ pagesize < 1 || pagesize > LDAP_MAXINT )
+ {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( cookie == NULL ) {
+ cookie = &null_cookie;
+ }
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{iO}", pagesize, cookie );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_page_control
+
+ Create and encode a page control.
+
+ ld (IN) An LDAP session handle
+ pagesize (IN) Page size requested
+ cookie (IN) Opaque structure used by the server to track its
+ location in the search results. NULL on the
+ first call.
+ value (OUT) Control value, SHOULD be freed by calling
+ ldap_memfree() when done.
+ iscritical (IN) Criticality
+ ctrlp (OUT) LDAP control, SHOULD be freed by calling
+ ldap_control_free() when done.
+
+ pagedResultsControl ::= SEQUENCE {
+ controlType 1.2.840.113556.1.4.319,
+ criticality BOOLEAN DEFAULT FALSE,
+ controlValue searchControlValue }
+
+ searchControlValue ::= SEQUENCE {
+ size INTEGER (0..maxInt),
+ -- requested page size from client
+ -- result set size estimate from server
+ cookie OCTET STRING }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_page_control(
+ LDAP *ld,
+ ber_int_t pagesize,
+ struct berval *cookie,
+ int iscritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_page_control_value( ld,
+ pagesize, cookie, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_PAGEDRESULTS,
+ iscritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_pageresponse_control
+
+ Decode a page control.
+
+ ld (IN) An LDAP session handle
+ ctrl (IN) The page response control
+ count (OUT) The number of entries in the page.
+ cookie (OUT) Opaque cookie. Use ldap_memfree() to
+ free the bv_val member of this structure.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_pageresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *countp,
+ struct berval *cookie )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_int_t count;
+
+ if ( ld == NULL || ctrl == NULL || cookie == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ /* Extract the count and cookie from the control. */
+ tag = ber_scanf( ber, "{io}", &count, cookie );
+ ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ } else {
+ ld->ld_errno = LDAP_SUCCESS;
+
+ if ( countp != NULL ) {
+ *countp = (unsigned long)count;
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_page_control
+
+ Decode a page control.
+
+ ld (IN) An LDAP session handle
+ ctrls (IN) Response controls
+ count (OUT) The number of entries in the page.
+ cookie (OUT) Opaque cookie. Use ldap_memfree() to
+ free the bv_val member of this structure.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_page_control(
+ LDAP *ld,
+ LDAPControl **ctrls,
+ ber_int_t *countp,
+ struct berval **cookiep )
+{
+ LDAPControl *c;
+ struct berval cookie;
+
+ if ( cookiep == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ if ( ctrls == NULL ) {
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ c = ldap_control_find( LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL );
+ if ( c == NULL ) {
+ /* No page control was found. */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_parse_pageresponse_control( ld, c, countp, &cookie );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ *cookiep = LDAP_MALLOC( sizeof( struct berval ) );
+ if ( *cookiep == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ } else {
+ **cookiep = cookie;
+ }
+ }
+
+ return ld->ld_errno;
+}
+
diff --git a/libraries/libldap/passwd.c b/libraries/libldap/passwd.c
new file mode 100644
index 0000000..9d4904a
--- /dev/null
+++ b/libraries/libldap/passwd.c
@@ -0,0 +1,170 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was orignally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * LDAP Password Modify (Extended) Operation (RFC 3062)
+ */
+
+int ldap_parse_passwd(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval *newpasswd )
+{
+ int rc;
+ struct berval *retdata = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( newpasswd != NULL );
+
+ newpasswd->bv_val = NULL;
+ newpasswd->bv_len = 0;
+
+ rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( retdata != NULL ) {
+ ber_tag_t tag;
+ BerElement *ber = ber_init( retdata );
+
+ if ( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ /* we should check the tag */
+ tag = ber_scanf( ber, "{o}", newpasswd );
+ ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ }
+ }
+
+done:;
+ ber_bvfree( retdata );
+
+ return rc;
+}
+
+int
+ldap_passwd( LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ struct berval bv = BER_BVNULL;
+ BerElement *ber = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ if( user != NULL || oldpw != NULL || newpw != NULL ) {
+ /* build change password control */
+ ber = ber_alloc_t( LBER_USE_DER );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ ber_printf( ber, "{" /*}*/ );
+
+ if( user != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_ID, user );
+ }
+
+ if( oldpw != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldpw );
+ }
+
+ if( newpw != NULL ) {
+ ber_printf( ber, "tO",
+ LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newpw );
+ }
+
+ ber_printf( ber, /*{*/ "N}" );
+
+ rc = ber_flatten2( ber, &bv, 0 );
+
+ if( rc < 0 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ }
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_MODIFY_PASSWD,
+ bv.bv_val ? &bv : NULL, sctrls, cctrls, msgidp );
+
+ ber_free( ber, 1 );
+
+ return rc;
+}
+
+int
+ldap_passwd_s(
+ LDAP *ld,
+ struct berval *user,
+ struct berval *oldpw,
+ struct berval *newpw,
+ struct berval *newpasswd,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_passwd( ld, user, oldpw, newpw, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ return rc;
+ }
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ rc = ldap_parse_passwd( ld, res, newpasswd );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
diff --git a/libraries/libldap/ppolicy.c b/libraries/libldap/ppolicy.c
new file mode 100644
index 0000000..77cb20b
--- /dev/null
+++ b/libraries/libldap/ppolicy.c
@@ -0,0 +1,256 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2004-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2004 Hewlett-Packard Company.
+ * Portions Copyright 2004 Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Howard Chu for inclusion in
+ * OpenLDAP Software, based on prior work by Neil Dunbar (HP).
+ * This work was sponsored by the Hewlett-Packard Company.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_PASSWORDPOLICYREQUEST
+
+/* IMPLICIT TAGS, all context-specific */
+#define PPOLICY_WARNING 0xa0L /* constructed + 0 */
+#define PPOLICY_ERROR 0x81L /* primitive + 1 */
+
+#define PPOLICY_EXPIRE 0x80L /* primitive + 0 */
+#define PPOLICY_GRACE 0x81L /* primitive + 1 */
+
+/*---
+ ldap_create_passwordpolicy_control
+
+ Create and encode the Password Policy Request
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ ctrlp (OUT) A result parameter that will be assigned the address
+ of an LDAPControl structure that contains the
+ passwordPolicyRequest control created by this function.
+ The memory occupied by the LDAPControl structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_control_free().
+
+
+ There is no control value for a password policy request
+ ---*/
+
+int
+ldap_create_passwordpolicy_control( LDAP *ld,
+ LDAPControl **ctrlp )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrlp != NULL );
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+ 0, NULL, 0, ctrlp );
+
+ return ld->ld_errno;
+}
+
+
+/*---
+ ldap_parse_passwordpolicy_control
+
+ Decode the passwordPolicyResponse control and return information.
+
+ ld (IN) An LDAP session handle.
+
+ ctrl (IN) The address of an
+ LDAPControl structure, either obtained
+ by running thorugh the list of response controls or
+ by a call to ldap_control_find().
+
+ exptimep (OUT) This result parameter is filled in with the number of seconds before
+ the password will expire, if expiration is imminent
+ (imminency defined by the password policy). If expiration
+ is not imminent, the value is set to -1.
+
+ gracep (OUT) This result parameter is filled in with the number of grace logins after
+ the password has expired, before no further login attempts
+ will be allowed.
+
+ errorcodep (OUT) This result parameter is filled in with the error code of the password operation
+ If no error was detected, this error is set to PP_noError.
+
+ Ber encoding
+
+ PasswordPolicyResponseValue ::= SEQUENCE {
+ warning [0] CHOICE {
+ timeBeforeExpiration [0] INTEGER (0 .. maxInt),
+ graceLoginsRemaining [1] INTEGER (0 .. maxInt) } OPTIONAL
+ error [1] ENUMERATED {
+ passwordExpired (0),
+ accountLocked (1),
+ changeAfterReset (2),
+ passwordModNotAllowed (3),
+ mustSupplyOldPassword (4),
+ invalidPasswordSyntax (5),
+ passwordTooShort (6),
+ passwordTooYoung (7),
+ passwordInHistory (8) } OPTIONAL }
+
+---*/
+
+int
+ldap_parse_passwordpolicy_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *expirep,
+ ber_int_t *gracep,
+ LDAPPasswordPolicyError *errorp )
+{
+ BerElement *ber;
+ int exp = -1, grace = -1;
+ ber_tag_t tag;
+ ber_len_t berLen;
+ char *last;
+ int err = PP_noError;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( !ctrl->ldctl_value.bv_val ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ tag = ber_peek_tag( ber, &berLen );
+ if (tag != LBER_SEQUENCE) goto exit;
+
+ for( tag = ber_first_element( ber, &berLen, &last );
+ tag != LBER_DEFAULT;
+ tag = ber_next_element( ber, &berLen, last ) )
+ {
+ switch (tag) {
+ case PPOLICY_WARNING:
+ ber_skip_tag(ber, &berLen );
+ tag = ber_peek_tag( ber, &berLen );
+ switch( tag ) {
+ case PPOLICY_EXPIRE:
+ if (ber_get_int( ber, &exp ) == LBER_DEFAULT) goto exit;
+ break;
+ case PPOLICY_GRACE:
+ if (ber_get_int( ber, &grace ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ break;
+ case PPOLICY_ERROR:
+ if (ber_get_enum( ber, &err ) == LBER_DEFAULT) goto exit;
+ break;
+ default:
+ goto exit;
+ }
+ }
+
+ ber_free(ber, 1);
+
+ /* Return data to the caller for items that were requested. */
+ if (expirep) *expirep = exp;
+ if (gracep) *gracep = grace;
+ if (errorp) *errorp = err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+const char *
+ldap_passwordpolicy_err2txt( LDAPPasswordPolicyError err )
+{
+ switch(err) {
+ case PP_passwordExpired: return "Password expired";
+ case PP_accountLocked: return "Account locked";
+ case PP_changeAfterReset: return "Password must be changed";
+ case PP_passwordModNotAllowed: return "Policy prevents password modification";
+ case PP_mustSupplyOldPassword: return "Policy requires old password in order to change password";
+ case PP_insufficientPasswordQuality: return "Password fails quality checks";
+ case PP_passwordTooShort: return "Password is too short for policy";
+ case PP_passwordTooYoung: return "Password has been changed too recently";
+ case PP_passwordInHistory: return "New password is in list of old passwords";
+ case PP_noError: return "No error";
+ default: return "Unknown error code";
+ }
+}
+
+#endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
+
+#ifdef LDAP_CONTROL_X_PASSWORD_EXPIRING
+
+int
+ldap_parse_password_expiring_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ long *secondsp )
+{
+ long seconds = 0;
+ char buf[sizeof("-2147483648")];
+ char *next;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ctrl != NULL );
+
+ if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ||
+ ctrl->ldctl_value.bv_len >= sizeof(buf) ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ memcpy( buf, ctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_len );
+ buf[ctrl->ldctl_value.bv_len] = '\0';
+
+ seconds = strtol( buf, &next, 10 );
+ if ( next == buf || next[0] != '\0' ) goto exit;
+
+ if ( secondsp != NULL ) {
+ *secondsp = seconds;
+ }
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+
+ exit:
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+}
+
+#endif /* LDAP_CONTROL_X_PASSWORD_EXPIRING */
diff --git a/libraries/libldap/print.c b/libraries/libldap/print.c
new file mode 100644
index 0000000..5f7650a
--- /dev/null
+++ b/libraries/libldap/print.c
@@ -0,0 +1,62 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/stdarg.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * ldap log
+ */
+
+static int ldap_log_check( LDAP *ld, int loglvl )
+{
+ int errlvl;
+
+ if(ld == NULL) {
+ errlvl = ldap_debug;
+ } else {
+ errlvl = ld->ld_debug;
+ }
+
+ return errlvl & loglvl ? 1 : 0;
+}
+
+int ldap_log_printf( LDAP *ld, int loglvl, const char *fmt, ... )
+{
+ char buf[ 1024 ];
+ va_list ap;
+
+ if ( !ldap_log_check( ld, loglvl )) {
+ return 0;
+ }
+
+ va_start( ap, fmt );
+
+ buf[sizeof(buf) - 1] = '\0';
+ vsnprintf( buf, sizeof(buf)-1, fmt, ap );
+
+ va_end(ap);
+
+ (*ber_pvt_log_print)( buf );
+ return 1;
+}
diff --git a/libraries/libldap/references.c b/libraries/libldap/references.c
new file mode 100644
index 0000000..01e9c24
--- /dev/null
+++ b/libraries/libldap/references.c
@@ -0,0 +1,147 @@
+/* references.c */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+LDAPMessage *
+ldap_first_reference( LDAP *ld, LDAPMessage *chain )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( chain != NULL );
+
+ return chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE
+ ? chain
+ : ldap_next_reference( ld, chain );
+}
+
+LDAPMessage *
+ldap_next_reference( LDAP *ld, LDAPMessage *ref )
+{
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ref != NULL );
+
+ for (
+ ref = ref->lm_chain;
+ ref != NULL;
+ ref = ref->lm_chain )
+ {
+ if( ref->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ return( ref );
+ }
+ }
+
+ return( NULL );
+}
+
+int
+ldap_count_references( LDAP *ld, LDAPMessage *chain )
+{
+ int i;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ for ( i = 0; chain != NULL; chain = chain->lm_chain ) {
+ if( chain->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ) {
+ i++;
+ }
+ }
+
+ return( i );
+}
+
+int
+ldap_parse_reference(
+ LDAP *ld,
+ LDAPMessage *ref,
+ char ***referralsp,
+ LDAPControl ***serverctrls,
+ int freeit)
+{
+ BerElement be;
+ char **refs = NULL;
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( ref != NULL );
+
+ if( ref->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* make a private copy of BerElement */
+ AC_MEMCPY(&be, ref->lm_ber, sizeof(be));
+
+ if ( ber_scanf( &be, "{v" /*}*/, &refs ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ if ( serverctrls == NULL ) {
+ rc = LDAP_SUCCESS;
+ goto free_and_return;
+ }
+
+ if ( ber_scanf( &be, /*{*/ "}" ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+ goto free_and_return;
+ }
+
+ rc = ldap_pvt_get_controls( &be, serverctrls );
+
+free_and_return:
+
+ if( referralsp != NULL ) {
+ /* provide references regradless of return code */
+ *referralsp = refs;
+
+ } else {
+ LDAP_VFREE( refs );
+ }
+
+ if( freeit ) {
+ ldap_msgfree( ref );
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ ld->ld_errno = rc;
+
+ if( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ }
+
+ return rc;
+}
diff --git a/libraries/libldap/request.c b/libraries/libldap/request.c
new file mode 100644
index 0000000..e551bd9
--- /dev/null
+++ b/libraries/libldap/request.c
@@ -0,0 +1,1699 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1995 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Modification to OpenLDAP source by Novell, Inc.
+ * April 2000 sfs Added code to chase V3 referrals
+ * request.c - sending of ldap requests; handling of referrals
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "lber.h"
+
+/* used by ldap_send_server_request and ldap_new_connection */
+#ifdef LDAP_R_COMPILE
+#define LDAP_CONN_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_conn_mutex ); }
+#define LDAP_CONN_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex ); }
+#define LDAP_REQ_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_req_mutex ); }
+#define LDAP_REQ_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex ); }
+#define LDAP_RES_LOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_LOCK( &ld->ld_res_mutex ); }
+#define LDAP_RES_UNLOCK_IF(nolock) \
+ { if (nolock) LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex ); }
+#else
+#define LDAP_CONN_LOCK_IF(nolock)
+#define LDAP_CONN_UNLOCK_IF(nolock)
+#define LDAP_REQ_LOCK_IF(nolock)
+#define LDAP_REQ_UNLOCK_IF(nolock)
+#define LDAP_RES_LOCK_IF(nolock)
+#define LDAP_RES_UNLOCK_IF(nolock)
+#endif
+
+static LDAPConn *find_connection LDAP_P(( LDAP *ld, LDAPURLDesc *srv, int any ));
+static void use_connection LDAP_P(( LDAP *ld, LDAPConn *lc ));
+static void ldap_free_request_int LDAP_P(( LDAP *ld, LDAPRequest *lr ));
+
+static BerElement *
+re_encode_request( LDAP *ld,
+ BerElement *origber,
+ ber_int_t msgid,
+ int sref,
+ LDAPURLDesc *srv,
+ int *type );
+
+BerElement *
+ldap_alloc_ber_with_options( LDAP *ld )
+{
+ BerElement *ber;
+
+ ber = ber_alloc_t( ld->ld_lberoptions );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ return( ber );
+}
+
+
+void
+ldap_set_ber_options( LDAP *ld, BerElement *ber )
+{
+ /* ld_lberoptions is constant, hence no lock */
+ ber->ber_options = ld->ld_lberoptions;
+}
+
+
+/* sets needed mutexes - no mutexes set to this point */
+ber_int_t
+ldap_send_initial_request(
+ LDAP *ld,
+ ber_tag_t msgtype,
+ const char *dn,
+ BerElement *ber,
+ ber_int_t msgid)
+{
+ int rc = 1;
+ ber_socket_t sd = AC_SOCKET_INVALID;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_send_initial_request\n", 0, 0, 0 );
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ if ( ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ) == -1 ) {
+ /* not connected yet */
+ rc = ldap_open_defconn( ld );
+ if ( rc == 0 ) {
+ ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
+ LBER_SB_OPT_GET_FD, &sd );
+ }
+ }
+ if ( ld->ld_defconn && ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING )
+ rc = ldap_int_check_async_open( ld, sd );
+ if( rc < 0 ) {
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( -1 );
+ } else if ( rc == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_open_defconn: successful\n",
+ 0, 0, 0 );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ if (msgtype == LDAP_REQ_BIND) {
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ if (ld->ld_options.ldo_cldapdn)
+ ldap_memfree(ld->ld_options.ldo_cldapdn);
+ ld->ld_options.ldo_cldapdn = ldap_strdup(dn);
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return 0;
+ }
+ if (msgtype != LDAP_REQ_ABANDON && msgtype != LDAP_REQ_SEARCH)
+ {
+ ber_free( ber, 1 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return LDAP_PARAM_ERROR;
+ }
+ }
+#endif
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ rc = ldap_send_server_request( ld, ber, msgid, NULL,
+ NULL, NULL, NULL, 0, 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return(rc);
+}
+
+
+/* protected by conn_mutex */
+int
+ldap_int_flush_request(
+ LDAP *ld,
+ LDAPRequest *lr )
+{
+ LDAPConn *lc = lr->lr_conn;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ if ( ber_flush2( lc->lconn_sb, lr->lr_ber, LBER_FLUSH_FREE_NEVER ) != 0 ) {
+ if (( sock_errno() == EAGAIN ) || ( sock_errno() == ENOTCONN )) {
+ /* ENOTCONN is returned in Solaris 10 */
+ /* need to continue write later */
+ lr->lr_status = LDAP_REQST_WRITING;
+ ldap_mark_select_write( ld, lc->lconn_sb );
+ ld->ld_errno = LDAP_BUSY;
+ return -2;
+ } else {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ ldap_free_request( ld, lr );
+ ldap_free_connection( ld, lc, 0, 0 );
+ return( -1 );
+ }
+ } else {
+ if ( lr->lr_parent == NULL ) {
+ lr->lr_ber->ber_end = lr->lr_ber->ber_ptr;
+ lr->lr_ber->ber_ptr = lr->lr_ber->ber_buf;
+ }
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+
+ /* sent -- waiting for a response */
+ ldap_mark_select_read( ld, lc->lconn_sb );
+ ldap_clear_select_write( ld, lc->lconn_sb );
+ }
+ return 0;
+}
+
+/*
+ * protected by req_mutex
+ * if m_noconn then protect using conn_lock
+ * else already protected with conn_lock
+ * if m_res then also protected by res_mutex
+ */
+
+int
+ldap_send_server_request(
+ LDAP *ld,
+ BerElement *ber,
+ ber_int_t msgid,
+ LDAPRequest *parentreq,
+ LDAPURLDesc **srvlist,
+ LDAPConn *lc,
+ LDAPreqinfo *bind,
+ int m_noconn,
+ int m_res )
+{
+ LDAPRequest *lr;
+ int incparent, rc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug( LDAP_DEBUG_TRACE, "ldap_send_server_request\n", 0, 0, 0 );
+
+ incparent = 0;
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+
+ LDAP_CONN_LOCK_IF(m_noconn);
+ if ( lc == NULL ) {
+ if ( srvlist == NULL ) {
+ lc = ld->ld_defconn;
+ } else {
+ lc = find_connection( ld, *srvlist, 1 );
+ if ( lc == NULL ) {
+ if ( (bind != NULL) && (parentreq != NULL) ) {
+ /* Remember the bind in the parent */
+ incparent = 1;
+ ++parentreq->lr_outrefcnt;
+ }
+ lc = ldap_new_connection( ld, srvlist, 0,
+ 1, bind, 1, m_res );
+ }
+ }
+ }
+
+ /* async connect... */
+ if ( lc != NULL && lc->lconn_status == LDAP_CONNST_CONNECTING ) {
+ ber_socket_t sd = AC_SOCKET_ERROR;
+ struct timeval tv = { 0 };
+
+ ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_GET_FD, &sd );
+
+ /* poll ... */
+ switch ( ldap_int_poll( ld, sd, &tv, 1 ) ) {
+ case 0:
+ /* go on! */
+ lc->lconn_status = LDAP_CONNST_CONNECTED;
+ break;
+
+ case -2:
+ /* async only occurs if a network timeout is set */
+
+ /* honor network timeout */
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ if ( time( NULL ) - lc->lconn_created <= ld->ld_options.ldo_tm_net.tv_sec )
+ {
+ /* caller will have to call again */
+ ld->ld_errno = LDAP_X_CONNECTING;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ /* fallthru */
+
+ default:
+ /* error */
+ break;
+ }
+ }
+
+ if ( lc == NULL || lc->lconn_status != LDAP_CONNST_CONNECTED ) {
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ }
+
+ ber_free( ber, 1 );
+ if ( incparent ) {
+ /* Forget about the bind */
+ --parentreq->lr_outrefcnt;
+ }
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( -1 );
+ }
+
+ use_connection( ld, lc );
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP( ld )) {
+ BerElement tmpber = *ber;
+ ber_rewind( &tmpber );
+ LDAP_MUTEX_LOCK( &ld->ld_options.ldo_mutex );
+ rc = ber_write( &tmpber, ld->ld_options.ldo_peer,
+ sizeof( struct sockaddr_storage ), 0 );
+ LDAP_MUTEX_UNLOCK( &ld->ld_options.ldo_mutex );
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return rc;
+ }
+ }
+#endif
+
+ /* If we still have an incomplete write, try to finish it before
+ * dealing with the new request. If we don't finish here, return
+ * LDAP_BUSY and let the caller retry later. We only allow a single
+ * request to be in WRITING state.
+ */
+ rc = 0;
+ if ( ld->ld_requests &&
+ ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
+ ldap_int_flush_request( ld, ld->ld_requests ) < 0 )
+ {
+ rc = -1;
+ }
+ if ( rc ) {
+ ber_free( ber, 1 );
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return rc;
+ }
+
+ lr = (LDAPRequest *)LDAP_CALLOC( 1, sizeof( LDAPRequest ) );
+ if ( lr == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ ldap_free_connection( ld, lc, 0, 0 );
+ ber_free( ber, 1 );
+ if ( incparent ) {
+ /* Forget about the bind */
+ --parentreq->lr_outrefcnt;
+ }
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( -1 );
+ }
+ lr->lr_msgid = msgid;
+ lr->lr_status = LDAP_REQST_INPROGRESS;
+ lr->lr_res_errno = LDAP_SUCCESS; /* optimistic */
+ lr->lr_ber = ber;
+ lr->lr_conn = lc;
+ if ( parentreq != NULL ) { /* sub-request */
+ if ( !incparent ) {
+ /* Increment if we didn't do it before the bind */
+ ++parentreq->lr_outrefcnt;
+ }
+ lr->lr_origid = parentreq->lr_origid;
+ lr->lr_parentcnt = ++parentreq->lr_parentcnt;
+ lr->lr_parent = parentreq;
+ lr->lr_refnext = parentreq->lr_child;
+ parentreq->lr_child = lr;
+ } else { /* original request */
+ lr->lr_origid = lr->lr_msgid;
+ }
+
+ /* Extract requestDN for future reference */
+#ifdef LDAP_CONNECTIONLESS
+ if ( !LDAP_IS_UDP(ld) )
+#endif
+ {
+ BerElement tmpber = *ber;
+ ber_int_t bint;
+ ber_tag_t tag, rtag;
+
+ ber_reset( &tmpber, 1 );
+ rtag = ber_scanf( &tmpber, "{it", /*}*/ &bint, &tag );
+ switch ( tag ) {
+ case LDAP_REQ_BIND:
+ rtag = ber_scanf( &tmpber, "{i" /*}*/, &bint );
+ break;
+ case LDAP_REQ_DELETE:
+ break;
+ default:
+ rtag = ber_scanf( &tmpber, "{" /*}*/ );
+ case LDAP_REQ_ABANDON:
+ break;
+ }
+ if ( tag != LDAP_REQ_ABANDON ) {
+ ber_skip_tag( &tmpber, &lr->lr_dn.bv_len );
+ lr->lr_dn.bv_val = tmpber.ber_ptr;
+ }
+ }
+
+ lr->lr_prev = NULL;
+ lr->lr_next = ld->ld_requests;
+ if ( lr->lr_next != NULL ) {
+ lr->lr_next->lr_prev = lr;
+ }
+ ld->ld_requests = lr;
+
+ ld->ld_errno = LDAP_SUCCESS;
+ if ( ldap_int_flush_request( ld, lr ) == -1 ) {
+ msgid = -1;
+ }
+
+ LDAP_CONN_UNLOCK_IF(m_noconn);
+ return( msgid );
+}
+
+/* return 0 if no StartTLS ext, 1 if present, 2 if critical */
+static int
+find_tls_ext( LDAPURLDesc *srv )
+{
+ int i, crit;
+ char *ext;
+
+ if ( !srv->lud_exts )
+ return 0;
+
+ for (i=0; srv->lud_exts[i]; i++) {
+ crit = 0;
+ ext = srv->lud_exts[i];
+ if ( ext[0] == '!') {
+ ext++;
+ crit = 1;
+ }
+ if ( !strcasecmp( ext, "StartTLS" ) ||
+ !strcasecmp( ext, "X-StartTLS" ) ||
+ !strcmp( ext, LDAP_EXOP_START_TLS )) {
+ return crit + 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * always protected by conn_mutex
+ * optionally protected by req_mutex and res_mutex
+ */
+LDAPConn *
+ldap_new_connection( LDAP *ld, LDAPURLDesc **srvlist, int use_ldsb,
+ int connect, LDAPreqinfo *bind, int m_req, int m_res )
+{
+ LDAPConn *lc;
+ int async = 0;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ Debug( LDAP_DEBUG_TRACE, "ldap_new_connection %d %d %d\n",
+ use_ldsb, connect, (bind != NULL) );
+ /*
+ * make a new LDAP server connection
+ * XXX open connection synchronously for now
+ */
+ lc = (LDAPConn *)LDAP_CALLOC( 1, sizeof( LDAPConn ) );
+ if ( lc == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+
+ if ( use_ldsb ) {
+ assert( ld->ld_sb != NULL );
+ lc->lconn_sb = ld->ld_sb;
+
+ } else {
+ lc->lconn_sb = ber_sockbuf_alloc();
+ if ( lc->lconn_sb == NULL ) {
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+ }
+
+ if ( connect ) {
+ LDAPURLDesc **srvp, *srv = NULL;
+
+ async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
+
+ for ( srvp = srvlist; *srvp != NULL; srvp = &(*srvp)->lud_next ) {
+ int rc;
+
+ rc = ldap_int_open_connection( ld, lc, *srvp, async );
+ if ( rc != -1 ) {
+ srv = *srvp;
+
+ /* If we fully connected, async is moot */
+ if ( rc == 0 )
+ async = 0;
+
+ if ( ld->ld_urllist_proc && ( !async || rc != -2 ) ) {
+ ld->ld_urllist_proc( ld, srvlist, srvp, ld->ld_urllist_params );
+ }
+
+ break;
+ }
+ }
+
+ if ( srv == NULL ) {
+ if ( !use_ldsb ) {
+ ber_sockbuf_free( lc->lconn_sb );
+ }
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ return( NULL );
+ }
+
+ lc->lconn_server = ldap_url_dup( srv );
+ if ( !lc->lconn_server ) {
+ if ( !use_ldsb )
+ ber_sockbuf_free( lc->lconn_sb );
+ LDAP_FREE( (char *)lc );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( NULL );
+ }
+ }
+
+ lc->lconn_status = async ? LDAP_CONNST_CONNECTING : LDAP_CONNST_CONNECTED;
+ lc->lconn_next = ld->ld_conns;
+ ld->ld_conns = lc;
+
+ if ( connect ) {
+#ifdef HAVE_TLS
+ if ( lc->lconn_server->lud_exts ) {
+ int rc, ext = find_tls_ext( lc->lconn_server );
+ if ( ext ) {
+ LDAPConn *savedefconn;
+
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ rc = ldap_start_tls_s( ld, NULL, NULL );
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( rc != LDAP_SUCCESS && ext == 2 ) {
+ ldap_free_connection( ld, lc, 1, 0 );
+ return NULL;
+ }
+ }
+ }
+#endif
+ }
+
+ if ( bind != NULL ) {
+ int err = 0;
+ LDAPConn *savedefconn;
+
+ /* Set flag to prevent additional referrals
+ * from being processed on this
+ * connection until the bind has completed
+ */
+ lc->lconn_rebind_inprogress = 1;
+ /* V3 rebind function */
+ if ( ld->ld_rebind_proc != NULL) {
+ LDAPURLDesc *srvfunc;
+
+ srvfunc = ldap_url_dup( *srvlist );
+ if ( srvfunc == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ err = -1;
+ } else {
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ Debug( LDAP_DEBUG_TRACE, "Call application rebind_proc\n", 0, 0, 0);
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ err = (*ld->ld_rebind_proc)( ld,
+ bind->ri_url, bind->ri_request, bind->ri_msgid,
+ ld->ld_rebind_params );
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( err != 0 ) {
+ err = -1;
+ ldap_free_connection( ld, lc, 1, 0 );
+ lc = NULL;
+ }
+ ldap_free_urldesc( srvfunc );
+ }
+
+ } else {
+ int msgid, rc;
+ struct berval passwd = BER_BVNULL;
+
+ savedefconn = ld->ld_defconn;
+ ++lc->lconn_refcnt; /* avoid premature free */
+ ld->ld_defconn = lc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "anonymous rebind via ldap_sasl_bind(\"\")\n",
+ 0, 0, 0);
+
+ LDAP_REQ_UNLOCK_IF(m_req);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_RES_UNLOCK_IF(m_res);
+ rc = ldap_sasl_bind( ld, "", LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &msgid );
+ if ( rc != LDAP_SUCCESS ) {
+ err = -1;
+
+ } else {
+ for ( err = 1; err > 0; ) {
+ struct timeval tv = { 0, 100000 };
+ LDAPMessage *res = NULL;
+
+ switch ( ldap_result( ld, msgid, LDAP_MSG_ALL, &tv, &res ) ) {
+ case -1:
+ err = -1;
+ break;
+
+ case 0:
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_yield();
+#endif
+ break;
+
+ case LDAP_RES_BIND:
+ rc = ldap_parse_result( ld, res, &err, NULL, NULL, NULL, NULL, 1 );
+ if ( rc != LDAP_SUCCESS ) {
+ err = -1;
+
+ } else if ( err != LDAP_SUCCESS ) {
+ err = -1;
+ }
+ /* else err == LDAP_SUCCESS == 0 */
+ break;
+
+ default:
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_new_connection %p: "
+ "unexpected response %d "
+ "from BIND request id=%d\n",
+ (void *) ld, ldap_msgtype( res ), msgid );
+ err = -1;
+ break;
+ }
+ }
+ }
+ LDAP_RES_LOCK_IF(m_res);
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ LDAP_REQ_LOCK_IF(m_req);
+ ld->ld_defconn = savedefconn;
+ --lc->lconn_refcnt;
+
+ if ( err != 0 ) {
+ ldap_free_connection( ld, lc, 1, 0 );
+ lc = NULL;
+ }
+ }
+ if ( lc != NULL )
+ lc->lconn_rebind_inprogress = 0;
+ }
+ return( lc );
+}
+
+
+/* protected by ld_conn_mutex */
+static LDAPConn *
+find_connection( LDAP *ld, LDAPURLDesc *srv, int any )
+/*
+ * return an existing connection (if any) to the server srv
+ * if "any" is non-zero, check for any server in the "srv" chain
+ */
+{
+ LDAPConn *lc;
+ LDAPURLDesc *lcu, *lsu;
+ int lcu_port, lsu_port;
+ int found = 0;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
+ lcu = lc->lconn_server;
+ lcu_port = ldap_pvt_url_scheme_port( lcu->lud_scheme,
+ lcu->lud_port );
+
+ for ( lsu = srv; lsu != NULL; lsu = lsu->lud_next ) {
+ lsu_port = ldap_pvt_url_scheme_port( lsu->lud_scheme,
+ lsu->lud_port );
+
+ if ( lsu_port == lcu_port
+ && strcmp( lcu->lud_scheme, lsu->lud_scheme ) == 0
+ && lcu->lud_host != NULL && lsu->lud_host != NULL
+ && strcasecmp( lsu->lud_host, lcu->lud_host ) == 0 )
+ {
+ found = 1;
+ break;
+ }
+
+ if ( !any ) break;
+ }
+ if ( found )
+ break;
+ }
+ return lc;
+}
+
+
+
+/* protected by ld_conn_mutex */
+static void
+use_connection( LDAP *ld, LDAPConn *lc )
+{
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ ++lc->lconn_refcnt;
+ lc->lconn_lastused = time( NULL );
+}
+
+
+/* protected by ld_conn_mutex */
+void
+ldap_free_connection( LDAP *ld, LDAPConn *lc, int force, int unbind )
+{
+ LDAPConn *tmplc, *prevlc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_free_connection %d %d\n",
+ force, unbind, 0 );
+
+ if ( force || --lc->lconn_refcnt <= 0 ) {
+ /* remove from connections list first */
+
+ for ( prevlc = NULL, tmplc = ld->ld_conns;
+ tmplc != NULL;
+ tmplc = tmplc->lconn_next )
+ {
+ if ( tmplc == lc ) {
+ if ( prevlc == NULL ) {
+ ld->ld_conns = tmplc->lconn_next;
+ } else {
+ prevlc->lconn_next = tmplc->lconn_next;
+ }
+ if ( ld->ld_defconn == lc ) {
+ ld->ld_defconn = NULL;
+ }
+ break;
+ }
+ prevlc = tmplc;
+ }
+
+ /* process connection callbacks */
+ {
+ struct ldapoptions *lo;
+ ldaplist *ll;
+ ldap_conncb *cb;
+
+ lo = &ld->ld_options;
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ lo = LDAP_INT_GLOBAL_OPT();
+ LDAP_MUTEX_LOCK( &lo->ldo_mutex );
+ if ( lo->ldo_conn_cbs ) {
+ for ( ll=lo->ldo_conn_cbs; ll; ll=ll->ll_next ) {
+ cb = ll->ll_data;
+ cb->lc_del( ld, lc->lconn_sb, cb );
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &lo->ldo_mutex );
+ }
+
+ if ( lc->lconn_status == LDAP_CONNST_CONNECTED ) {
+ ldap_mark_select_clear( ld, lc->lconn_sb );
+ if ( unbind ) {
+ ldap_send_unbind( ld, lc->lconn_sb,
+ NULL, NULL );
+ }
+ }
+
+ if ( lc->lconn_ber != NULL ) {
+ ber_free( lc->lconn_ber, 1 );
+ }
+
+ ldap_int_sasl_close( ld, lc );
+#ifdef HAVE_GSSAPI
+ ldap_int_gssapi_close( ld, lc );
+#endif
+
+ ldap_free_urllist( lc->lconn_server );
+
+ /* FIXME: is this at all possible?
+ * ldap_ld_free() in unbind.c calls ldap_free_connection()
+ * with force == 1 __after__ explicitly calling
+ * ldap_free_request() on all requests */
+ if ( force ) {
+ LDAPRequest *lr;
+
+ for ( lr = ld->ld_requests; lr; ) {
+ LDAPRequest *lr_next = lr->lr_next;
+
+ if ( lr->lr_conn == lc ) {
+ ldap_free_request_int( ld, lr );
+ }
+
+ lr = lr_next;
+ }
+ }
+
+ if ( lc->lconn_sb != ld->ld_sb ) {
+ ber_sockbuf_free( lc->lconn_sb );
+ } else {
+ ber_int_sb_close( lc->lconn_sb );
+ }
+
+ if ( lc->lconn_rebind_queue != NULL) {
+ int i;
+ for( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
+ LDAP_VFREE( lc->lconn_rebind_queue[i] );
+ }
+ LDAP_FREE( lc->lconn_rebind_queue );
+ }
+
+ LDAP_FREE( lc );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_free_connection: actually freed\n",
+ 0, 0, 0 );
+
+ } else {
+ lc->lconn_lastused = time( NULL );
+ Debug( LDAP_DEBUG_TRACE, "ldap_free_connection: refcnt %d\n",
+ lc->lconn_refcnt, 0, 0 );
+ }
+}
+
+
+/* Protects self with ld_conn_mutex */
+#ifdef LDAP_DEBUG
+void
+ldap_dump_connection( LDAP *ld, LDAPConn *lconns, int all )
+{
+ LDAPConn *lc;
+ char timebuf[32];
+
+ Debug( LDAP_DEBUG_TRACE, "** ld %p Connection%s:\n", (void *)ld, all ? "s" : "", 0 );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ for ( lc = lconns; lc != NULL; lc = lc->lconn_next ) {
+ if ( lc->lconn_server != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "* host: %s port: %d%s\n",
+ ( lc->lconn_server->lud_host == NULL ) ? "(null)"
+ : lc->lconn_server->lud_host,
+ lc->lconn_server->lud_port, ( lc->lconn_sb ==
+ ld->ld_sb ) ? " (default)" : "" );
+ }
+ Debug( LDAP_DEBUG_TRACE, " refcnt: %d status: %s\n", lc->lconn_refcnt,
+ ( lc->lconn_status == LDAP_CONNST_NEEDSOCKET )
+ ? "NeedSocket" :
+ ( lc->lconn_status == LDAP_CONNST_CONNECTING )
+ ? "Connecting" : "Connected", 0 );
+ Debug( LDAP_DEBUG_TRACE, " last used: %s%s\n",
+ ldap_pvt_ctime( &lc->lconn_lastused, timebuf ),
+ lc->lconn_rebind_inprogress ? " rebind in progress" : "", 0 );
+ if ( lc->lconn_rebind_inprogress ) {
+ if ( lc->lconn_rebind_queue != NULL) {
+ int i;
+
+ for ( i = 0; lc->lconn_rebind_queue[i] != NULL; i++ ) {
+ int j;
+ for( j = 0; lc->lconn_rebind_queue[i][j] != 0; j++ ) {
+ Debug( LDAP_DEBUG_TRACE, " queue %d entry %d - %s\n",
+ i, j, lc->lconn_rebind_queue[i][j] );
+ }
+ }
+ } else {
+ Debug( LDAP_DEBUG_TRACE, " queue is empty\n", 0, 0, 0 );
+ }
+ }
+ Debug( LDAP_DEBUG_TRACE, "\n", 0, 0, 0 );
+ if ( !all ) {
+ break;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+}
+
+
+/* protected by req_mutex and res_mutex */
+void
+ldap_dump_requests_and_responses( LDAP *ld )
+{
+ LDAPRequest *lr;
+ LDAPMessage *lm, *l;
+ int i;
+
+ Debug( LDAP_DEBUG_TRACE, "** ld %p Outstanding Requests:\n",
+ (void *)ld, 0, 0 );
+ lr = ld->ld_requests;
+ if ( lr == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 );
+ }
+ for ( i = 0; lr != NULL; lr = lr->lr_next, i++ ) {
+ Debug( LDAP_DEBUG_TRACE, " * msgid %d, origid %d, status %s\n",
+ lr->lr_msgid, lr->lr_origid,
+ ( lr->lr_status == LDAP_REQST_INPROGRESS ) ? "InProgress" :
+ ( lr->lr_status == LDAP_REQST_CHASINGREFS ) ? "ChasingRefs" :
+ ( lr->lr_status == LDAP_REQST_NOTCONNECTED ) ? "NotConnected" :
+ ( lr->lr_status == LDAP_REQST_WRITING ) ? "Writing" :
+ ( lr->lr_status == LDAP_REQST_COMPLETED ) ? "RequestCompleted"
+ : "InvalidStatus" );
+ Debug( LDAP_DEBUG_TRACE, " outstanding referrals %d, parent count %d\n",
+ lr->lr_outrefcnt, lr->lr_parentcnt, 0 );
+ }
+ Debug( LDAP_DEBUG_TRACE, " ld %p request count %d (abandoned %lu)\n",
+ (void *)ld, i, ld->ld_nabandoned );
+ Debug( LDAP_DEBUG_TRACE, "** ld %p Response Queue:\n", (void *)ld, 0, 0 );
+ if ( ( lm = ld->ld_responses ) == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " Empty\n", 0, 0, 0 );
+ }
+ for ( i = 0; lm != NULL; lm = lm->lm_next, i++ ) {
+ Debug( LDAP_DEBUG_TRACE, " * msgid %d, type %lu\n",
+ lm->lm_msgid, (unsigned long)lm->lm_msgtype, 0 );
+ if ( lm->lm_chain != NULL ) {
+ Debug( LDAP_DEBUG_TRACE, " chained responses:\n", 0, 0, 0 );
+ for ( l = lm->lm_chain; l != NULL; l = l->lm_chain ) {
+ Debug( LDAP_DEBUG_TRACE,
+ " * msgid %d, type %lu\n",
+ l->lm_msgid,
+ (unsigned long)l->lm_msgtype, 0 );
+ }
+ }
+ }
+ Debug( LDAP_DEBUG_TRACE, " ld %p response count %d\n", (void *)ld, i, 0 );
+}
+#endif /* LDAP_DEBUG */
+
+/* protected by req_mutex */
+static void
+ldap_free_request_int( LDAP *ld, LDAPRequest *lr )
+{
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ /* if lr_refcnt > 0, the request has been looked up
+ * by ldap_find_request_by_msgid(); if in the meanwhile
+ * the request is free()'d by someone else, just decrease
+ * the reference count and extract it from the request
+ * list; later on, it will be freed. */
+ if ( lr->lr_prev == NULL ) {
+ if ( lr->lr_refcnt == 0 ) {
+ /* free'ing the first request? */
+ assert( ld->ld_requests == lr );
+ }
+
+ if ( ld->ld_requests == lr ) {
+ ld->ld_requests = lr->lr_next;
+ }
+
+ } else {
+ lr->lr_prev->lr_next = lr->lr_next;
+ }
+
+ if ( lr->lr_next != NULL ) {
+ lr->lr_next->lr_prev = lr->lr_prev;
+ }
+
+ if ( lr->lr_refcnt > 0 ) {
+ lr->lr_refcnt = -lr->lr_refcnt;
+
+ lr->lr_prev = NULL;
+ lr->lr_next = NULL;
+
+ return;
+ }
+
+ if ( lr->lr_ber != NULL ) {
+ ber_free( lr->lr_ber, 1 );
+ lr->lr_ber = NULL;
+ }
+
+ if ( lr->lr_res_error != NULL ) {
+ LDAP_FREE( lr->lr_res_error );
+ lr->lr_res_error = NULL;
+ }
+
+ if ( lr->lr_res_matched != NULL ) {
+ LDAP_FREE( lr->lr_res_matched );
+ lr->lr_res_matched = NULL;
+ }
+
+ LDAP_FREE( lr );
+}
+
+/* protected by req_mutex */
+void
+ldap_free_request( LDAP *ld, LDAPRequest *lr )
+{
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug( LDAP_DEBUG_TRACE, "ldap_free_request (origid %d, msgid %d)\n",
+ lr->lr_origid, lr->lr_msgid, 0 );
+
+ /* free all referrals (child requests) */
+ while ( lr->lr_child ) {
+ ldap_free_request( ld, lr->lr_child );
+ }
+
+ if ( lr->lr_parent != NULL ) {
+ LDAPRequest **lrp;
+
+ --lr->lr_parent->lr_outrefcnt;
+ for ( lrp = &lr->lr_parent->lr_child;
+ *lrp && *lrp != lr;
+ lrp = &(*lrp)->lr_refnext );
+
+ if ( *lrp == lr ) {
+ *lrp = lr->lr_refnext;
+ }
+ }
+ ldap_free_request_int( ld, lr );
+}
+
+/*
+ * call first time with *cntp = -1
+ * when returns *cntp == -1, no referrals are left
+ *
+ * NOTE: may replace *refsp, or shuffle the contents
+ * of the original array.
+ */
+static int ldap_int_nextref(
+ LDAP *ld,
+ char ***refsp,
+ int *cntp,
+ void *params )
+{
+ assert( refsp != NULL );
+ assert( *refsp != NULL );
+ assert( cntp != NULL );
+
+ if ( *cntp < -1 ) {
+ *cntp = -1;
+ return -1;
+ }
+
+ (*cntp)++;
+
+ if ( (*refsp)[ *cntp ] == NULL ) {
+ *cntp = -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Chase v3 referrals
+ *
+ * Parameters:
+ * (IN) ld = LDAP connection handle
+ * (IN) lr = LDAP Request structure
+ * (IN) refs = array of pointers to referral strings that we will chase
+ * The array will be free'd by this function when no longer needed
+ * (IN) sref != 0 if following search reference
+ * (OUT) errstrp = Place to return a string of referrals which could not be followed
+ * (OUT) hadrefp = 1 if sucessfully followed referral
+ *
+ * Return value - number of referrals followed
+ *
+ * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg)
+ */
+int
+ldap_chase_v3referrals( LDAP *ld, LDAPRequest *lr, char **refs, int sref, char **errstrp, int *hadrefp )
+{
+ char *unfollowed;
+ int unfollowedcnt = 0;
+ LDAPRequest *origreq;
+ LDAPURLDesc *srv = NULL;
+ BerElement *ber;
+ char **refarray = NULL;
+ LDAPConn *lc;
+ int rc, count, i, j, id;
+ LDAPreqinfo rinfo;
+ LDAP_NEXTREF_PROC *nextref_proc = ld->ld_nextref_proc ? ld->ld_nextref_proc : ldap_int_nextref;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug( LDAP_DEBUG_TRACE, "ldap_chase_v3referrals\n", 0, 0, 0 );
+
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+ *hadrefp = 0;
+
+ unfollowed = NULL;
+ rc = count = 0;
+
+ /* If no referrals in array, return */
+ if ( (refs == NULL) || ( (refs)[0] == NULL) ) {
+ rc = 0;
+ goto done;
+ }
+
+ /* Check for hop limit exceeded */
+ if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
+ Debug( LDAP_DEBUG_ANY,
+ "more than %d referral hops (dropping)\n", ld->ld_refhoplimit, 0, 0 );
+ ld->ld_errno = LDAP_REFERRAL_LIMIT_EXCEEDED;
+ rc = -1;
+ goto done;
+ }
+
+ /* find original request */
+ for ( origreq = lr;
+ origreq->lr_parent != NULL;
+ origreq = origreq->lr_parent )
+ {
+ /* empty */ ;
+ }
+
+ refarray = refs;
+ refs = NULL;
+
+ /* parse out & follow referrals */
+ /* NOTE: if nextref_proc == ldap_int_nextref, params is ignored */
+ i = -1;
+ for ( nextref_proc( ld, &refarray, &i, ld->ld_nextref_params );
+ i != -1;
+ nextref_proc( ld, &refarray, &i, ld->ld_nextref_params ) )
+ {
+
+ /* Parse the referral URL */
+ rc = ldap_url_parse_ext( refarray[i], &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ /* ldap_url_parse_ext() returns LDAP_URL_* errors
+ * which do not map on API errors */
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ rc = -1;
+ goto done;
+ }
+
+ if( srv->lud_crit_exts ) {
+ int ok = 0;
+#ifdef HAVE_TLS
+ /* If StartTLS is the only critical ext, OK. */
+ if ( find_tls_ext( srv ) == 2 && srv->lud_crit_exts == 1 )
+ ok = 1;
+#endif
+ if ( !ok ) {
+ /* we do not support any other extensions */
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ rc = -1;
+ goto done;
+ }
+ }
+
+ /* check connection for re-bind in progress */
+ if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
+ /* See if we've already requested this DN with this conn */
+ LDAPRequest *lp;
+ int looped = 0;
+ ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
+ for ( lp = origreq; lp; ) {
+ if ( lp->lr_conn == lc
+ && len == lp->lr_dn.bv_len
+ && len
+ && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) == 0 )
+ {
+ looped = 1;
+ break;
+ }
+ if ( lp == origreq ) {
+ lp = lp->lr_child;
+ } else {
+ lp = lp->lr_refnext;
+ }
+ }
+ if ( looped ) {
+ ldap_free_urllist( srv );
+ srv = NULL;
+ ld->ld_errno = LDAP_CLIENT_LOOP;
+ rc = -1;
+ continue;
+ }
+
+ if ( lc->lconn_rebind_inprogress ) {
+ /* We are already chasing a referral or search reference and a
+ * bind on that connection is in progress. We must queue
+ * referrals on that connection, so we don't get a request
+ * going out before the bind operation completes. This happens
+ * if two search references come in one behind the other
+ * for the same server with different contexts.
+ */
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referrals: queue referral \"%s\"\n",
+ refarray[i], 0, 0);
+ if( lc->lconn_rebind_queue == NULL ) {
+ /* Create a referral list */
+ lc->lconn_rebind_queue =
+ (char ***) LDAP_MALLOC( sizeof(void *) * 2);
+
+ if( lc->lconn_rebind_queue == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+
+ lc->lconn_rebind_queue[0] = refarray;
+ lc->lconn_rebind_queue[1] = NULL;
+ refarray = NULL;
+
+ } else {
+ /* Count how many referral arrays we already have */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++) {
+ /* empty */;
+ }
+
+ /* Add the new referral to the list */
+ lc->lconn_rebind_queue = (char ***) LDAP_REALLOC(
+ lc->lconn_rebind_queue, sizeof(void *) * (j + 2));
+
+ if( lc->lconn_rebind_queue == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ rc = -1;
+ goto done;
+ }
+ lc->lconn_rebind_queue[j] = refarray;
+ lc->lconn_rebind_queue[j+1] = NULL;
+ refarray = NULL;
+ }
+
+ /* We have queued the referral/reference, now just return */
+ rc = 0;
+ *hadrefp = 1;
+ count = 1; /* Pretend we already followed referral */
+ goto done;
+ }
+ }
+ /* Re-encode the request with the new starting point of the search.
+ * Note: In the future we also need to replace the filter if one
+ * was provided with the search reference
+ */
+
+ /* For references we don't want old dn if new dn empty */
+ if ( sref && srv->lud_dn == NULL ) {
+ srv->lud_dn = LDAP_STRDUP( "" );
+ }
+
+ LDAP_NEXT_MSGID( ld, id );
+ ber = re_encode_request( ld, origreq->lr_ber, id,
+ sref, srv, &rinfo.ri_request );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ rc = -1;
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chase_v3referral: msgid %d, url \"%s\"\n",
+ lr->lr_msgid, refarray[i], 0);
+
+ /* Send the new request to the server - may require a bind */
+ rinfo.ri_msgid = origreq->lr_origid;
+ rinfo.ri_url = refarray[i];
+ rc = ldap_send_server_request( ld, ber, id,
+ origreq, &srv, NULL, &rinfo, 0, 1 );
+ if ( rc < 0 ) {
+ /* Failure, try next referral in the list */
+ Debug( LDAP_DEBUG_ANY, "Unable to chase referral \"%s\" (%d: %s)\n",
+ refarray[i], ld->ld_errno, ldap_err2string( ld->ld_errno ) );
+ unfollowedcnt += ldap_append_referral( ld, &unfollowed, refarray[i] );
+ ldap_free_urllist( srv );
+ srv = NULL;
+ ld->ld_errno = LDAP_REFERRAL;
+ } else {
+ /* Success, no need to try this referral list further */
+ rc = 0;
+ ++count;
+ *hadrefp = 1;
+
+ /* check if there is a queue of referrals that came in during bind */
+ if ( lc == NULL) {
+ lc = find_connection( ld, srv, 1 );
+ if ( lc == NULL ) {
+ ld->ld_errno = LDAP_OPERATIONS_ERROR;
+ rc = -1;
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ goto done;
+ }
+ }
+
+ if ( lc->lconn_rebind_queue != NULL ) {
+ /* Release resources of previous list */
+ LDAP_VFREE( refarray );
+ refarray = NULL;
+ ldap_free_urllist( srv );
+ srv = NULL;
+
+ /* Pull entries off end of queue so list always null terminated */
+ for( j = 0; lc->lconn_rebind_queue[j] != NULL; j++ )
+ ;
+ refarray = lc->lconn_rebind_queue[j - 1];
+ lc->lconn_rebind_queue[j-1] = NULL;
+ /* we pulled off last entry from queue, free queue */
+ if ( j == 1 ) {
+ LDAP_FREE( lc->lconn_rebind_queue );
+ lc->lconn_rebind_queue = NULL;
+ }
+ /* restart the loop the with new referral list */
+ i = -1;
+ continue;
+ }
+ break; /* referral followed, break out of for loop */
+ }
+ } /* end for loop */
+done:
+ LDAP_VFREE( refarray );
+ ldap_free_urllist( srv );
+ LDAP_FREE( *errstrp );
+
+ if( rc == 0 ) {
+ *errstrp = NULL;
+ LDAP_FREE( unfollowed );
+ return count;
+ } else {
+ *errstrp = unfollowed;
+ return rc;
+ }
+}
+
+/*
+ * XXX merging of errors in this routine needs to be improved
+ * Protected by res_mutex, conn_mutex and req_mutex (try_read1msg)
+ */
+int
+ldap_chase_referrals( LDAP *ld,
+ LDAPRequest *lr,
+ char **errstrp,
+ int sref,
+ int *hadrefp )
+{
+ int rc, count, id;
+ unsigned len;
+ char *p, *ref, *unfollowed;
+ LDAPRequest *origreq;
+ LDAPURLDesc *srv;
+ BerElement *ber;
+ LDAPreqinfo rinfo;
+ LDAPConn *lc;
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+ Debug( LDAP_DEBUG_TRACE, "ldap_chase_referrals\n", 0, 0, 0 );
+
+ ld->ld_errno = LDAP_SUCCESS; /* optimistic */
+ *hadrefp = 0;
+
+ if ( *errstrp == NULL ) {
+ return( 0 );
+ }
+
+ len = strlen( *errstrp );
+ for ( p = *errstrp; len >= LDAP_REF_STR_LEN; ++p, --len ) {
+ if ( strncasecmp( p, LDAP_REF_STR, LDAP_REF_STR_LEN ) == 0 ) {
+ *p = '\0';
+ p += LDAP_REF_STR_LEN;
+ break;
+ }
+ }
+
+ if ( len < LDAP_REF_STR_LEN ) {
+ return( 0 );
+ }
+
+ if ( lr->lr_parentcnt >= ld->ld_refhoplimit ) {
+ Debug( LDAP_DEBUG_ANY,
+ "more than %d referral hops (dropping)\n",
+ ld->ld_refhoplimit, 0, 0 );
+ /* XXX report as error in ld->ld_errno? */
+ return( 0 );
+ }
+
+ /* find original request */
+ for ( origreq = lr; origreq->lr_parent != NULL;
+ origreq = origreq->lr_parent ) {
+ /* empty */;
+ }
+
+ unfollowed = NULL;
+ rc = count = 0;
+
+ /* parse out & follow referrals */
+ for ( ref = p; rc == 0 && ref != NULL; ref = p ) {
+ p = strchr( ref, '\n' );
+ if ( p != NULL ) {
+ *p++ = '\0';
+ }
+
+ rc = ldap_url_parse_ext( ref, &srv, LDAP_PVT_URL_PARSE_NOEMPTY_DN );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ignoring %s referral <%s>\n",
+ ref, rc == LDAP_URL_ERR_BADSCHEME ? "unknown" : "incorrect", 0 );
+ rc = ldap_append_referral( ld, &unfollowed, ref );
+ *hadrefp = 1;
+ continue;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "chasing LDAP referral: <%s>\n", ref, 0, 0 );
+
+ *hadrefp = 1;
+
+ /* See if we've already been here */
+ if (( lc = find_connection( ld, srv, 1 )) != NULL ) {
+ LDAPRequest *lp;
+ int looped = 0;
+ ber_len_t len = srv->lud_dn ? strlen( srv->lud_dn ) : 0;
+ for ( lp = lr; lp; lp = lp->lr_parent ) {
+ if ( lp->lr_conn == lc
+ && len == lp->lr_dn.bv_len )
+ {
+ if ( len && strncmp( srv->lud_dn, lp->lr_dn.bv_val, len ) )
+ continue;
+ looped = 1;
+ break;
+ }
+ }
+ if ( looped ) {
+ ldap_free_urllist( srv );
+ ld->ld_errno = LDAP_CLIENT_LOOP;
+ rc = -1;
+ continue;
+ }
+ }
+
+ LDAP_NEXT_MSGID( ld, id );
+ ber = re_encode_request( ld, origreq->lr_ber,
+ id, sref, srv, &rinfo.ri_request );
+
+ if ( ber == NULL ) {
+ ldap_free_urllist( srv );
+ return -1 ;
+ }
+
+ /* copy the complete referral for rebind process */
+ rinfo.ri_url = LDAP_STRDUP( ref );
+
+ rinfo.ri_msgid = origreq->lr_origid;
+
+ rc = ldap_send_server_request( ld, ber, id,
+ lr, &srv, NULL, &rinfo, 0, 1 );
+ LDAP_FREE( rinfo.ri_url );
+
+ if( rc >= 0 ) {
+ ++count;
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "Unable to chase referral \"%s\" (%d: %s)\n",
+ ref, ld->ld_errno, ldap_err2string( ld->ld_errno ) );
+ rc = ldap_append_referral( ld, &unfollowed, ref );
+ }
+
+ ldap_free_urllist(srv);
+ }
+
+ LDAP_FREE( *errstrp );
+ *errstrp = unfollowed;
+
+ return(( rc == 0 ) ? count : rc );
+}
+
+
+int
+ldap_append_referral( LDAP *ld, char **referralsp, char *s )
+{
+ int first;
+
+ if ( *referralsp == NULL ) {
+ first = 1;
+ *referralsp = (char *)LDAP_MALLOC( strlen( s ) + LDAP_REF_STR_LEN
+ + 1 );
+ } else {
+ first = 0;
+ *referralsp = (char *)LDAP_REALLOC( *referralsp,
+ strlen( *referralsp ) + strlen( s ) + 2 );
+ }
+
+ if ( *referralsp == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+
+ if ( first ) {
+ strcpy( *referralsp, LDAP_REF_STR );
+ } else {
+ strcat( *referralsp, "\n" );
+ }
+ strcat( *referralsp, s );
+
+ return( 0 );
+}
+
+
+
+static BerElement *
+re_encode_request( LDAP *ld,
+ BerElement *origber,
+ ber_int_t msgid,
+ int sref,
+ LDAPURLDesc *srv,
+ int *type )
+{
+ /*
+ * XXX this routine knows way too much about how the lber library works!
+ */
+ ber_int_t along;
+ ber_tag_t tag;
+ ber_tag_t rtag;
+ ber_int_t ver;
+ ber_int_t scope;
+ int rc;
+ BerElement tmpber, *ber;
+ struct berval dn;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "re_encode_request: new msgid %ld, new dn <%s>\n",
+ (long) msgid,
+ ( srv == NULL || srv->lud_dn == NULL) ? "NONE" : srv->lud_dn, 0 );
+
+ tmpber = *origber;
+
+ /*
+ * all LDAP requests are sequences that start with a message id.
+ * For all except delete, this is followed by a sequence that is
+ * tagged with the operation code. For delete, the provided DN
+ * is not wrapped by a sequence.
+ */
+ rtag = ber_scanf( &tmpber, "{it", /*}*/ &along, &tag );
+
+ if ( rtag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( NULL );
+ }
+
+ assert( tag != 0);
+ if ( tag == LDAP_REQ_BIND ) {
+ /* bind requests have a version number before the DN & other stuff */
+ rtag = ber_scanf( &tmpber, "{im" /*}*/, &ver, &dn );
+
+ } else if ( tag == LDAP_REQ_DELETE ) {
+ /* delete requests don't have a DN wrapping sequence */
+ rtag = ber_scanf( &tmpber, "m", &dn );
+
+ } else if ( tag == LDAP_REQ_SEARCH ) {
+ /* search requests need to be re-scope-ed */
+ rtag = ber_scanf( &tmpber, "{me" /*"}"*/, &dn, &scope );
+
+ if( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
+ /* use the scope provided in reference */
+ scope = srv->lud_scope;
+
+ } else if ( sref ) {
+ /* use scope implied by previous operation
+ * base -> base
+ * one -> base
+ * subtree -> subtree
+ * subordinate -> subtree
+ */
+ switch( scope ) {
+ default:
+ case LDAP_SCOPE_BASE:
+ case LDAP_SCOPE_ONELEVEL:
+ scope = LDAP_SCOPE_BASE;
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ case LDAP_SCOPE_SUBORDINATE:
+ scope = LDAP_SCOPE_SUBTREE;
+ break;
+ }
+ }
+
+ } else {
+ rtag = ber_scanf( &tmpber, "{m" /*}*/, &dn );
+ }
+
+ if( rtag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return NULL;
+ }
+
+ /* restore character zero'd out by ber_scanf*/
+ dn.bv_val[dn.bv_len] = tmpber.ber_tag;
+
+ if (( ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return NULL;
+ }
+
+ if ( srv->lud_dn ) {
+ ber_str2bv( srv->lud_dn, 0, 0, &dn );
+ }
+
+ if ( tag == LDAP_REQ_BIND ) {
+ rc = ber_printf( ber, "{it{iO" /*}}*/, msgid, tag, ver, &dn );
+ } else if ( tag == LDAP_REQ_DELETE ) {
+ rc = ber_printf( ber, "{itON}", msgid, tag, &dn );
+ } else if ( tag == LDAP_REQ_SEARCH ) {
+ rc = ber_printf( ber, "{it{Oe" /*}}*/, msgid, tag, &dn, scope );
+ } else {
+ rc = ber_printf( ber, "{it{O" /*}}*/, msgid, tag, &dn );
+ }
+
+ if ( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return NULL;
+ }
+
+ if ( tag != LDAP_REQ_DELETE && (
+ ber_write(ber, tmpber.ber_ptr, ( tmpber.ber_end - tmpber.ber_ptr ), 0)
+ != ( tmpber.ber_end - tmpber.ber_ptr ) ||
+ ber_printf( ber, /*{{*/ "N}N}" ) == -1 ) )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return NULL;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ Debug( LDAP_DEBUG_ANY, "re_encode_request new request is:\n",
+ 0, 0, 0 );
+ ber_log_dump( LDAP_DEBUG_BER, ldap_debug, ber, 0 );
+ }
+#endif /* LDAP_DEBUG */
+
+ *type = tag; /* return request type */
+ return ber;
+}
+
+
+/* protected by req_mutex */
+LDAPRequest *
+ldap_find_request_by_msgid( LDAP *ld, ber_int_t msgid )
+{
+ LDAPRequest *lr;
+
+ for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
+ if ( lr->lr_status == LDAP_REQST_COMPLETED ) {
+ continue; /* Skip completed requests */
+ }
+ if ( msgid == lr->lr_msgid ) {
+ lr->lr_refcnt++;
+ break;
+ }
+ }
+
+ return( lr );
+}
+
+/* protected by req_mutex */
+void
+ldap_return_request( LDAP *ld, LDAPRequest *lrx, int freeit )
+{
+ LDAPRequest *lr;
+
+ for ( lr = ld->ld_requests; lr != NULL; lr = lr->lr_next ) {
+ if ( lr == lrx ) {
+ if ( lr->lr_refcnt > 0 ) {
+ lr->lr_refcnt--;
+
+ } else if ( lr->lr_refcnt < 0 ) {
+ lr->lr_refcnt++;
+ if ( lr->lr_refcnt == 0 ) {
+ lr = NULL;
+ }
+ }
+ break;
+ }
+ }
+ if ( lr == NULL ) {
+ ldap_free_request_int( ld, lrx );
+
+ } else if ( freeit ) {
+ ldap_free_request( ld, lrx );
+ }
+}
diff --git a/libraries/libldap/result.c b/libraries/libldap/result.c
new file mode 100644
index 0000000..5b39c11
--- /dev/null
+++ b/libraries/libldap/result.c
@@ -0,0 +1,1382 @@
+/* result.c - wait for an ldap result */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+/* This notice applies to changes, created by or for Novell, Inc.,
+ * to preexisting works for which notices appear elsewhere in this file.
+ *
+ * Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
+ * USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO VERSION
+ * 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS AVAILABLE AT
+ * HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE" IN THE
+ * TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION OF THIS
+ * WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP PUBLIC
+ * LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT THE
+ * PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Modification to OpenLDAP source by Novell, Inc.
+ * April 2000 sfs Add code to process V3 referrals and search results
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 (RFC 4511)
+ * LDAPResult ::= SEQUENCE {
+ * resultCode ENUMERATED { ... },
+ * matchedDN LDAPDN,
+ * diagnosticMessage LDAPString,
+ * referral [3] Referral OPTIONAL
+ * }
+ * Referral ::= SEQUENCE OF LDAPURL (one or more)
+ * LDAPURL ::= LDAPString (limited to URL chars)
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+#include "lutil.h"
+
+static int ldap_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
+static int ldap_mark_abandoned LDAP_P(( LDAP *ld, ber_int_t msgid ));
+static int wait4msg LDAP_P(( LDAP *ld, ber_int_t msgid, int all, struct timeval *timeout,
+ LDAPMessage **result ));
+static ber_tag_t try_read1msg LDAP_P(( LDAP *ld, ber_int_t msgid,
+ int all, LDAPConn *lc, LDAPMessage **result ));
+static ber_tag_t build_result_ber LDAP_P(( LDAP *ld, BerElement **bp, LDAPRequest *lr ));
+static void merge_error_info LDAP_P(( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr ));
+static LDAPMessage * chkResponseList LDAP_P(( LDAP *ld, int msgid, int all));
+
+#define LDAP_MSG_X_KEEP_LOOKING (-2)
+
+
+/*
+ * ldap_result - wait for an ldap result response to a message from the
+ * ldap server. If msgid is LDAP_RES_ANY (-1), any message will be
+ * accepted. If msgid is LDAP_RES_UNSOLICITED (0), any unsolicited
+ * message is accepted. Otherwise ldap_result will wait for a response
+ * with msgid. If all is LDAP_MSG_ONE (0) the first message with id
+ * msgid will be accepted, otherwise, ldap_result will wait for all
+ * responses with id msgid and then return a pointer to the entire list
+ * of messages. In general, this is only useful for search responses,
+ * which can be of three message types (zero or more entries, zero or
+ * search references, followed by an ldap result). An extension to
+ * LDAPv3 allows partial extended responses to be returned in response
+ * to any request. The type of the first message received is returned.
+ * When waiting, any messages that have been abandoned/discarded are
+ * discarded.
+ *
+ * Example:
+ * ldap_result( s, msgid, all, timeout, result )
+ */
+int
+ldap_result(
+ LDAP *ld,
+ int msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( result != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_result ld %p msgid %d\n", (void *)ld, msgid, 0 );
+
+ if (ld->ld_errno == LDAP_LOCAL_ERROR || ld->ld_errno == LDAP_SERVER_DOWN)
+ return -1;
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ rc = wait4msg( ld, msgid, all, timeout, result );
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+ return rc;
+}
+
+/* protected by res_mutex */
+static LDAPMessage *
+chkResponseList(
+ LDAP *ld,
+ int msgid,
+ int all)
+{
+ LDAPMessage *lm, **lastlm, *nextlm;
+ int cnt = 0;
+
+ /*
+ * Look through the list of responses we have received on
+ * this association and see if the response we're interested in
+ * is there. If it is, return it. If not, call wait4msg() to
+ * wait until it arrives or timeout occurs.
+ */
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+
+ lastlm = &ld->ld_responses;
+ for ( lm = ld->ld_responses; lm != NULL; lm = nextlm ) {
+ nextlm = lm->lm_next;
+ ++cnt;
+
+ if ( ldap_abandoned( ld, lm->lm_msgid ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "response list msg abandoned, "
+ "msgid %d message type %s\n",
+ lm->lm_msgid, ldap_int_msgtype2str( lm->lm_msgtype ), 0 );
+
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ break;
+
+ default:
+ /* there's no need to keep the id
+ * in the abandoned list any longer */
+ ldap_mark_abandoned( ld, lm->lm_msgid );
+ break;
+ }
+
+ /* Remove this entry from list */
+ *lastlm = nextlm;
+
+ ldap_msgfree( lm );
+
+ continue;
+ }
+
+ if ( msgid == LDAP_RES_ANY || lm->lm_msgid == msgid ) {
+ LDAPMessage *tmp;
+
+ if ( all == LDAP_MSG_ONE ||
+ all == LDAP_MSG_RECEIVED ||
+ msgid == LDAP_RES_UNSOLICITED )
+ {
+ break;
+ }
+
+ tmp = lm->lm_chain_tail;
+ if ( tmp->lm_msgtype == LDAP_RES_SEARCH_ENTRY ||
+ tmp->lm_msgtype == LDAP_RES_SEARCH_REFERENCE ||
+ tmp->lm_msgtype == LDAP_RES_INTERMEDIATE )
+ {
+ tmp = NULL;
+ }
+
+ if ( tmp == NULL ) {
+ lm = NULL;
+ }
+
+ break;
+ }
+ lastlm = &lm->lm_next;
+ }
+
+ if ( lm != NULL ) {
+ /* Found an entry, remove it from the list */
+ if ( all == LDAP_MSG_ONE && lm->lm_chain != NULL ) {
+ *lastlm = lm->lm_chain;
+ lm->lm_chain->lm_next = lm->lm_next;
+ lm->lm_chain->lm_chain_tail = ( lm->lm_chain_tail != lm ) ? lm->lm_chain_tail : lm->lm_chain;
+ lm->lm_chain = NULL;
+ lm->lm_chain_tail = NULL;
+ } else {
+ *lastlm = lm->lm_next;
+ }
+ lm->lm_next = NULL;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( lm == NULL) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList returns ld %p NULL\n", (void *)ld, 0, 0);
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_chkResponseList returns ld %p msgid %d, type 0x%02lx\n",
+ (void *)ld, lm->lm_msgid, (unsigned long)lm->lm_msgtype );
+ }
+#endif
+
+ return lm;
+}
+
+/* protected by res_mutex */
+static int
+wait4msg(
+ LDAP *ld,
+ ber_int_t msgid,
+ int all,
+ struct timeval *timeout,
+ LDAPMessage **result )
+{
+ int rc;
+ struct timeval tv = { 0 },
+ tv0 = { 0 },
+ start_time_tv = { 0 },
+ *tvp = NULL;
+ LDAPConn *lc;
+
+ assert( ld != NULL );
+ assert( result != NULL );
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+
+ if ( timeout == NULL && ld->ld_options.ldo_tm_api.tv_sec >= 0 ) {
+ tv = ld->ld_options.ldo_tm_api;
+ timeout = &tv;
+ }
+
+#ifdef LDAP_DEBUG
+ if ( timeout == NULL ) {
+ Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (infinite timeout)\n",
+ (void *)ld, msgid, 0 );
+ } else {
+ Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p msgid %d (timeout %ld usec)\n",
+ (void *)ld, msgid, (long)timeout->tv_sec * 1000000 + timeout->tv_usec );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( timeout != NULL && timeout->tv_sec != -1 ) {
+ tv0 = *timeout;
+ tv = *timeout;
+ tvp = &tv;
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ start_time_tv.tv_sec = time( NULL );
+ start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+ }
+
+ rc = LDAP_MSG_X_KEEP_LOOKING;
+ while ( rc == LDAP_MSG_X_KEEP_LOOKING ) {
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_TRACE ) {
+ Debug( LDAP_DEBUG_TRACE, "wait4msg continue ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+ ldap_dump_connection( ld, ld->ld_conns, 1 );
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ ldap_dump_requests_and_responses( ld );
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( ( *result = chkResponseList( ld, msgid, all ) ) != NULL ) {
+ rc = (*result)->lm_msgtype;
+
+ } else {
+ int lc_ready = 0;
+
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+ for ( lc = ld->ld_conns; lc != NULL; lc = lc->lconn_next ) {
+ if ( ber_sockbuf_ctrl( lc->lconn_sb,
+ LBER_SB_OPT_DATA_READY, NULL ) )
+ {
+ lc_ready = 2; /* ready at ber level, not socket level */
+ break;
+ }
+ }
+
+ if ( !lc_ready ) {
+ int err;
+ rc = ldap_int_select( ld, tvp );
+ if ( rc == -1 ) {
+ err = sock_errno();
+#ifdef LDAP_DEBUG
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_int_select returned -1: errno %d\n",
+ err, 0, 0 );
+#endif
+ }
+
+ if ( rc == 0 || ( rc == -1 && (
+ !LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_RESTART)
+ || err != EINTR ) ) )
+ {
+ ld->ld_errno = (rc == -1 ? LDAP_SERVER_DOWN :
+ LDAP_TIMEOUT);
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ return( rc );
+ }
+
+ if ( rc == -1 ) {
+ rc = LDAP_MSG_X_KEEP_LOOKING; /* select interrupted: loop */
+
+ } else {
+ lc_ready = 1;
+ }
+ }
+ if ( lc_ready ) {
+ LDAPConn *lnext;
+ int serviced = 0;
+ rc = LDAP_MSG_X_KEEP_LOOKING;
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ if ( ld->ld_requests &&
+ ld->ld_requests->lr_status == LDAP_REQST_WRITING &&
+ ldap_is_write_ready( ld,
+ ld->ld_requests->lr_conn->lconn_sb ) )
+ {
+ serviced = 1;
+ ldap_int_flush_request( ld, ld->ld_requests );
+ }
+ for ( lc = ld->ld_conns;
+ rc == LDAP_MSG_X_KEEP_LOOKING && lc != NULL;
+ lc = lnext )
+ {
+ if ( lc->lconn_status == LDAP_CONNST_CONNECTED &&
+ ldap_is_read_ready( ld, lc->lconn_sb ) )
+ {
+ serviced = 1;
+ /* Don't let it get freed out from under us */
+ ++lc->lconn_refcnt;
+ rc = try_read1msg( ld, msgid, all, lc, result );
+ lnext = lc->lconn_next;
+
+ /* Only take locks if we're really freeing */
+ if ( lc->lconn_refcnt <= 1 ) {
+ ldap_free_connection( ld, lc, 0, 1 );
+ } else {
+ --lc->lconn_refcnt;
+ }
+ } else {
+ lnext = lc->lconn_next;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ /* Quit looping if no one handled any socket events */
+ if (!serviced && lc_ready == 1)
+ rc = -1;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ }
+
+ if ( rc == LDAP_MSG_X_KEEP_LOOKING && tvp != NULL ) {
+ struct timeval curr_time_tv = { 0 },
+ delta_time_tv = { 0 };
+
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ curr_time_tv.tv_sec = time( NULL );
+ curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+ /* delta_time = tmp_time - start_time */
+ delta_time_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+ delta_time_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+ if ( delta_time_tv.tv_usec < 0 ) {
+ delta_time_tv.tv_sec--;
+ delta_time_tv.tv_usec += 1000000;
+ }
+
+ /* tv0 < delta_time ? */
+ if ( ( tv0.tv_sec < delta_time_tv.tv_sec ) ||
+ ( ( tv0.tv_sec == delta_time_tv.tv_sec ) && ( tv0.tv_usec < delta_time_tv.tv_usec ) ) )
+ {
+ rc = 0; /* timed out */
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+
+ /* tv0 -= delta_time */
+ tv0.tv_sec -= delta_time_tv.tv_sec;
+ tv0.tv_usec -= delta_time_tv.tv_usec;
+ if ( tv0.tv_usec < 0 ) {
+ tv0.tv_sec--;
+ tv0.tv_usec += 1000000;
+ }
+
+ tv.tv_sec = tv0.tv_sec;
+ tv.tv_usec = tv0.tv_usec;
+
+ Debug( LDAP_DEBUG_TRACE, "wait4msg ld %p %ld s %ld us to go\n",
+ (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+
+ start_time_tv.tv_sec = curr_time_tv.tv_sec;
+ start_time_tv.tv_usec = curr_time_tv.tv_usec;
+ }
+ }
+
+ return( rc );
+}
+
+
+/* protected by res_mutex, conn_mutex and req_mutex */
+static ber_tag_t
+try_read1msg(
+ LDAP *ld,
+ ber_int_t msgid,
+ int all,
+ LDAPConn *lc,
+ LDAPMessage **result )
+{
+ BerElement *ber;
+ LDAPMessage *newmsg, *l, *prev;
+ ber_int_t id;
+ ber_tag_t tag;
+ ber_len_t len;
+ int foundit = 0;
+ LDAPRequest *lr, *tmplr, dummy_lr = { 0 };
+ BerElement tmpber;
+ int rc, refer_cnt, hadref, simple_request, err;
+ ber_int_t lderr;
+
+#ifdef LDAP_CONNECTIONLESS
+ LDAPMessage *tmp = NULL, *chain_head = NULL;
+ int moremsgs = 0, isv2 = 0;
+#endif
+
+ assert( ld != NULL );
+ assert( lc != NULL );
+
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_res_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_conn_mutex );
+ LDAP_ASSERT_MUTEX_OWNER( &ld->ld_req_mutex );
+
+ Debug( LDAP_DEBUG_TRACE, "read1msg: ld %p msgid %d all %d\n",
+ (void *)ld, msgid, all );
+
+retry:
+ if ( lc->lconn_ber == NULL ) {
+ lc->lconn_ber = ldap_alloc_ber_with_options( ld );
+
+ if ( lc->lconn_ber == NULL ) {
+ return -1;
+ }
+ }
+
+ ber = lc->lconn_ber;
+ assert( LBER_VALID (ber) );
+
+ /* get the next message */
+ sock_errset(0);
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage from;
+ if ( ber_int_sb_read( lc->lconn_sb, &from, sizeof(struct sockaddr_storage) ) < 0 )
+ goto fail;
+ if ( ld->ld_options.ldo_version == LDAP_VERSION2 ) isv2 = 1;
+ }
+nextresp3:
+#endif
+ tag = ber_get_next( lc->lconn_sb, &len, ber );
+ switch ( tag ) {
+ case LDAP_TAG_MESSAGE:
+ /*
+ * We read a complete message.
+ * The connection should no longer need this ber.
+ */
+ lc->lconn_ber = NULL;
+ break;
+
+ case LBER_DEFAULT:
+fail:
+ err = sock_errno();
+#ifdef LDAP_DEBUG
+ Debug( LDAP_DEBUG_CONNS,
+ "ber_get_next failed, errno=%d.\n", err, 0, 0 );
+#endif
+ if ( err == EWOULDBLOCK ) return LDAP_MSG_X_KEEP_LOOKING;
+ if ( err == EAGAIN ) return LDAP_MSG_X_KEEP_LOOKING;
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ --lc->lconn_refcnt;
+ lc->lconn_status = 0;
+ return -1;
+
+ default:
+ ld->ld_errno = LDAP_LOCAL_ERROR;
+ return -1;
+ }
+
+ /* message id */
+ if ( ber_get_int( ber, &id ) == LBER_ERROR ) {
+ ber_free( ber, 1 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return( -1 );
+ }
+
+ /* id == 0 iff unsolicited notification message (RFC 4511) */
+
+ /* id < 0 is invalid, just toss it. FIXME: should we disconnect? */
+ if ( id < 0 ) {
+ goto retry_ber;
+ }
+
+ /* if it's been abandoned, toss it */
+ if ( id > 0 ) {
+ if ( ldap_abandoned( ld, id ) ) {
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ case LBER_ERROR:
+ break;
+
+ default:
+ /* there's no need to keep the id
+ * in the abandoned list any longer */
+ ldap_mark_abandoned( ld, id );
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "abandoned/discarded ld %p msgid %d message type %s\n",
+ (void *)ld, id, ldap_int_msgtype2str( tag ) );
+
+retry_ber:
+ ber_free( ber, 1 );
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
+ goto retry;
+ }
+ return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
+ }
+
+ lr = ldap_find_request_by_msgid( ld, id );
+ if ( lr == NULL ) {
+ const char *msg = "unknown";
+
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ switch ( tag ) {
+ case LBER_ERROR:
+ break;
+
+ default:
+ msg = ldap_int_msgtype2str( tag );
+ break;
+ }
+
+ Debug( LDAP_DEBUG_ANY,
+ "no request for response on ld %p msgid %d message type %s (tossing)\n",
+ (void *)ld, id, msg );
+
+ goto retry_ber;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) && isv2 ) {
+ ber_scanf(ber, "x{");
+ }
+nextresp2:
+ ;
+#endif
+ }
+
+ /* the message type */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( -1 );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: ld %p msgid %d message type %s\n",
+ (void *)ld, id, ldap_int_msgtype2str( tag ) );
+
+ if ( id == 0 ) {
+ /* unsolicited notification message (RFC 4511) */
+ if ( tag != LDAP_RES_EXTENDED ) {
+ /* toss it */
+ goto retry_ber;
+
+ /* strictly speaking, it's an error; from RFC 4511:
+
+4.4. Unsolicited Notification
+
+ An unsolicited notification is an LDAPMessage sent from the server to
+ the client that is not in response to any LDAPMessage received by the
+ server. It is used to signal an extraordinary condition in the
+ server or in the LDAP session between the client and the server. The
+ notification is of an advisory nature, and the server will not expect
+ any response to be returned from the client.
+
+ The unsolicited notification is structured as an LDAPMessage in which
+ the messageID is zero and protocolOp is set to the extendedResp
+ choice using the ExtendedResponse type (See Section 4.12). The
+ responseName field of the ExtendedResponse always contains an LDAPOID
+ that is unique for this notification.
+
+ * however, since unsolicited responses
+ * are of advisory nature, better
+ * toss it, right now
+ */
+
+#if 0
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( -1 );
+#endif
+ }
+
+ lr = &dummy_lr;
+ }
+
+ id = lr->lr_origid;
+ refer_cnt = 0;
+ hadref = simple_request = 0;
+ rc = LDAP_MSG_X_KEEP_LOOKING; /* default is to keep looking (no response found) */
+ lr->lr_res_msgtype = tag;
+
+ /*
+ * Check for V3 search reference
+ */
+ if ( tag == LDAP_RES_SEARCH_REFERENCE ) {
+ if ( ld->ld_version > LDAP_VERSION2 ) {
+ /* This is a V3 search reference */
+ if ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
+ lr->lr_parent != NULL )
+ {
+ char **refs = NULL;
+ tmpber = *ber;
+
+ /* Get the referral list */
+ if ( ber_scanf( &tmpber, "{v}", &refs ) == LBER_ERROR ) {
+ rc = LDAP_DECODING_ERROR;
+
+ } else {
+ /* Note: refs array is freed by ldap_chase_v3referrals */
+ refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
+ 1, &lr->lr_res_error, &hadref );
+ if ( refer_cnt > 0 ) {
+ /* successfully chased reference */
+ /* If haven't got end search, set chasing referrals */
+ if ( lr->lr_status != LDAP_REQST_COMPLETED ) {
+ lr->lr_status = LDAP_REQST_CHASINGREFS;
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: search ref chased, "
+ "mark request chasing refs, "
+ "id = %d\n",
+ lr->lr_msgid, 0, 0 );
+ }
+ }
+ }
+ }
+ }
+
+ } else if ( tag != LDAP_RES_SEARCH_ENTRY && tag != LDAP_RES_INTERMEDIATE ) {
+ /* All results that just return a status, i.e. don't return data
+ * go through the following code. This code also chases V2 referrals
+ * and checks if all referrals have been chased.
+ */
+ char *lr_res_error = NULL;
+
+ tmpber = *ber; /* struct copy */
+ if ( ber_scanf( &tmpber, "{eAA", &lderr,
+ &lr->lr_res_matched, &lr_res_error )
+ != LBER_ERROR )
+ {
+ if ( lr_res_error != NULL ) {
+ if ( lr->lr_res_error != NULL ) {
+ (void)ldap_append_referral( ld, &lr->lr_res_error, lr_res_error );
+ LDAP_FREE( (char *)lr_res_error );
+
+ } else {
+ lr->lr_res_error = lr_res_error;
+ }
+ lr_res_error = NULL;
+ }
+
+ /* Do we need to check for referrals? */
+ if ( tag != LDAP_RES_BIND &&
+ ( LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_REFERRALS) ||
+ lr->lr_parent != NULL ))
+ {
+ char **refs = NULL;
+ ber_len_t len;
+
+ /* Check if V3 referral */
+ if ( ber_peek_tag( &tmpber, &len ) == LDAP_TAG_REFERRAL ) {
+ if ( ld->ld_version > LDAP_VERSION2 ) {
+ /* Get the referral list */
+ if ( ber_scanf( &tmpber, "{v}", &refs) == LBER_ERROR) {
+ rc = LDAP_DECODING_ERROR;
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: referral decode error, "
+ "mark request completed, ld %p msgid %d\n",
+ (void *)ld, lr->lr_msgid, 0 );
+
+ } else {
+ /* Chase the referral
+ * refs array is freed by ldap_chase_v3referrals
+ */
+ refer_cnt = ldap_chase_v3referrals( ld, lr, refs,
+ 0, &lr->lr_res_error, &hadref );
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: referral %s chased, "
+ "mark request completed, ld %p msgid %d\n",
+ refer_cnt > 0 ? "" : "not",
+ (void *)ld, lr->lr_msgid);
+ if ( refer_cnt < 0 ) {
+ refer_cnt = 0;
+ }
+ }
+ }
+ } else {
+ switch ( lderr ) {
+ case LDAP_SUCCESS:
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ break;
+
+ default:
+ if ( lr->lr_res_error == NULL ) {
+ break;
+ }
+
+ /* pedantic, should never happen */
+ if ( lr->lr_res_error[ 0 ] == '\0' ) {
+ LDAP_FREE( lr->lr_res_error );
+ lr->lr_res_error = NULL;
+ break;
+ }
+
+ /* V2 referrals are in error string */
+ refer_cnt = ldap_chase_referrals( ld, lr,
+ &lr->lr_res_error, -1, &hadref );
+ lr->lr_status = LDAP_REQST_COMPLETED;
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: V2 referral chased, "
+ "mark request completed, id = %d\n",
+ lr->lr_msgid, 0, 0 );
+ break;
+ }
+ }
+ }
+
+ /* save errno, message, and matched string */
+ if ( !hadref || lr->lr_res_error == NULL ) {
+ lr->lr_res_errno =
+ lderr == LDAP_PARTIAL_RESULTS
+ ? LDAP_SUCCESS : lderr;
+
+ } else if ( ld->ld_errno != LDAP_SUCCESS ) {
+ lr->lr_res_errno = ld->ld_errno;
+
+ } else {
+ lr->lr_res_errno = LDAP_PARTIAL_RESULTS;
+ }
+ }
+
+ /* in any case, don't leave any lr_res_error 'round */
+ if ( lr_res_error ) {
+ LDAP_FREE( lr_res_error );
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: ld %p %d new referrals\n",
+ (void *)ld, refer_cnt, 0 );
+
+ if ( refer_cnt != 0 ) { /* chasing referrals */
+ ber_free( ber, 1 );
+ ber = NULL;
+ if ( refer_cnt < 0 ) {
+ ldap_return_request( ld, lr, 0 );
+ return( -1 ); /* fatal error */
+ }
+ lr->lr_res_errno = LDAP_SUCCESS; /* sucessfully chased referral */
+ if ( lr->lr_res_matched ) {
+ LDAP_FREE( lr->lr_res_matched );
+ lr->lr_res_matched = NULL;
+ }
+
+ } else {
+ if ( lr->lr_outrefcnt <= 0 && lr->lr_parent == NULL ) {
+ /* request without any referrals */
+ simple_request = ( hadref ? 0 : 1 );
+
+ } else {
+ /* request with referrals or child request */
+ ber_free( ber, 1 );
+ ber = NULL;
+ }
+
+ lr->lr_status = LDAP_REQST_COMPLETED; /* declare this request done */
+ Debug( LDAP_DEBUG_TRACE,
+ "read1msg: mark request completed, ld %p msgid %d\n",
+ (void *)ld, lr->lr_msgid, 0);
+ tmplr = lr;
+ while ( lr->lr_parent != NULL ) {
+ merge_error_info( ld, lr->lr_parent, lr );
+
+ lr = lr->lr_parent;
+ if ( --lr->lr_outrefcnt > 0 ) {
+ break; /* not completely done yet */
+ }
+ }
+ /* ITS#6744: Original lr was refcounted when we retrieved it,
+ * must release it now that we're working with the parent
+ */
+ if ( tmplr->lr_parent ) {
+ ldap_return_request( ld, tmplr, 0 );
+ }
+
+ /* Check if all requests are finished, lr is now parent */
+ tmplr = lr;
+ if ( tmplr->lr_status == LDAP_REQST_COMPLETED ) {
+ for ( tmplr = lr->lr_child;
+ tmplr != NULL;
+ tmplr = tmplr->lr_refnext )
+ {
+ if ( tmplr->lr_status != LDAP_REQST_COMPLETED ) break;
+ }
+ }
+
+ /* This is the parent request if the request has referrals */
+ if ( lr->lr_outrefcnt <= 0 &&
+ lr->lr_parent == NULL &&
+ tmplr == NULL )
+ {
+ id = lr->lr_msgid;
+ tag = lr->lr_res_msgtype;
+ Debug( LDAP_DEBUG_TRACE, "request done: ld %p msgid %d\n",
+ (void *)ld, id, 0 );
+ Debug( LDAP_DEBUG_TRACE,
+ "res_errno: %d, res_error: <%s>, "
+ "res_matched: <%s>\n",
+ lr->lr_res_errno,
+ lr->lr_res_error ? lr->lr_res_error : "",
+ lr->lr_res_matched ? lr->lr_res_matched : "" );
+ if ( !simple_request ) {
+ ber_free( ber, 1 );
+ ber = NULL;
+ if ( build_result_ber( ld, &ber, lr )
+ == LBER_ERROR )
+ {
+ rc = -1; /* fatal error */
+ }
+ }
+
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 1 );
+ }
+ lr = NULL;
+ }
+
+ /*
+ * RFC 4511 unsolicited (id == 0) responses
+ * shouldn't necessarily end the connection
+ */
+ if ( lc != NULL && id != 0 ) {
+ --lc->lconn_refcnt;
+ lc = NULL;
+ }
+ }
+ }
+
+ if ( lr != NULL ) {
+ if ( lr != &dummy_lr ) {
+ ldap_return_request( ld, lr, 0 );
+ }
+ lr = NULL;
+ }
+
+ if ( ber == NULL ) {
+ return( rc );
+ }
+
+ /* try to handle unsolicited responses as appropriate */
+ if ( id == 0 && msgid > LDAP_RES_UNSOLICITED ) {
+ int is_nod = 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+
+ /* we have a res oid */
+ if ( tag == LDAP_TAG_EXOP_RES_OID ) {
+ static struct berval bv_nod = BER_BVC( LDAP_NOTICE_OF_DISCONNECTION );
+ struct berval resoid = BER_BVNULL;
+
+ if ( ber_scanf( &tmpber, "m", &resoid ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ assert( !BER_BVISEMPTY( &resoid ) );
+
+ is_nod = ber_bvcmp( &resoid, &bv_nod ) == 0;
+
+ tag = ber_peek_tag( &tmpber, &len );
+ }
+
+#if 0 /* don't need right now */
+ /* we have res data */
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ struct berval resdata;
+
+ if ( ber_scanf( &tmpber, "m", &resdata ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 0 );
+ return ld->ld_errno;
+ }
+
+ /* use it... */
+ }
+#endif
+
+ /* handle RFC 4511 "Notice of Disconnection" locally */
+
+ if ( is_nod ) {
+ if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return -1;
+ }
+
+ /* get rid of the connection... */
+ if ( lc != NULL ) {
+ --lc->lconn_refcnt;
+ }
+
+ /* need to return -1, because otherwise
+ * a valid result is expected */
+ ld->ld_errno = lderr;
+ return -1;
+ }
+ }
+
+ /* make a new ldap message */
+ newmsg = (LDAPMessage *) LDAP_CALLOC( 1, sizeof(LDAPMessage) );
+ if ( newmsg == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+ newmsg->lm_msgid = (int)id;
+ newmsg->lm_msgtype = tag;
+ newmsg->lm_ber = ber;
+ newmsg->lm_chain_tail = newmsg;
+
+#ifdef LDAP_CONNECTIONLESS
+ /* CLDAP replies all fit in a single datagram. In LDAPv2 RFC1798
+ * the responses are all a sequence wrapped in one message. In
+ * LDAPv3 each response is in its own message. The datagram must
+ * end with a SearchResult. We can't just parse each response in
+ * separate calls to try_read1msg because the header info is only
+ * present at the beginning of the datagram, not at the beginning
+ * of each response. So parse all the responses at once and queue
+ * them up, then pull off the first response to return to the
+ * caller when all parsing is complete.
+ */
+ if ( LDAP_IS_UDP(ld) ) {
+ /* If not a result, look for more */
+ if ( tag != LDAP_RES_SEARCH_RESULT ) {
+ int ok = 0;
+ moremsgs = 1;
+ if (isv2) {
+ /* LDAPv2: dup the current ber, skip past the current
+ * response, and see if there are any more after it.
+ */
+ ber = ber_dup( ber );
+ ber_scanf( ber, "x" );
+ if ( ber_peek_tag( ber, &len ) != LBER_DEFAULT ) {
+ /* There's more - dup the ber buffer so they can all be
+ * individually freed by ldap_msgfree.
+ */
+ struct berval bv;
+ ber_get_option( ber, LBER_OPT_BER_REMAINING_BYTES, &len );
+ bv.bv_val = LDAP_MALLOC( len );
+ if ( bv.bv_val ) {
+ ok = 1;
+ ber_read( ber, bv.bv_val, len );
+ bv.bv_len = len;
+ ber_init2( ber, &bv, ld->ld_lberoptions );
+ }
+ }
+ } else {
+ /* LDAPv3: Just allocate a new ber. Since this is a buffered
+ * datagram, if the sockbuf is readable we still have data
+ * to parse.
+ */
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) ok = 1;
+ }
+ /* set up response chain */
+ if ( tmp == NULL ) {
+ newmsg->lm_next = ld->ld_responses;
+ ld->ld_responses = newmsg;
+ chain_head = newmsg;
+ } else {
+ tmp->lm_chain = newmsg;
+ }
+ chain_head->lm_chain_tail = newmsg;
+ tmp = newmsg;
+ /* "ok" means there's more to parse */
+ if ( ok ) {
+ if ( isv2 ) {
+ goto nextresp2;
+
+ } else {
+ goto nextresp3;
+ }
+ } else {
+ /* got to end of datagram without a SearchResult. Free
+ * our dup'd ber, but leave any buffer alone. For v2 case,
+ * the previous response is still using this buffer. For v3,
+ * the new ber has no buffer to free yet.
+ */
+ ber_free( ber, 0 );
+ return -1;
+ }
+ } else if ( moremsgs ) {
+ /* got search result, and we had multiple responses in 1 datagram.
+ * stick the result onto the end of the chain, and then pull the
+ * first response off the head of the chain.
+ */
+ tmp->lm_chain = newmsg;
+ chain_head->lm_chain_tail = newmsg;
+ *result = chkResponseList( ld, msgid, all );
+ ld->ld_errno = LDAP_SUCCESS;
+ return( (*result)->lm_msgtype );
+ }
+ }
+#endif /* LDAP_CONNECTIONLESS */
+
+ /* is this the one we're looking for? */
+ if ( msgid == LDAP_RES_ANY || id == msgid ) {
+ if ( all == LDAP_MSG_ONE
+ || ( newmsg->lm_msgtype != LDAP_RES_SEARCH_RESULT
+ && newmsg->lm_msgtype != LDAP_RES_SEARCH_ENTRY
+ && newmsg->lm_msgtype != LDAP_RES_INTERMEDIATE
+ && newmsg->lm_msgtype != LDAP_RES_SEARCH_REFERENCE ) )
+ {
+ *result = newmsg;
+ ld->ld_errno = LDAP_SUCCESS;
+ return( tag );
+
+ } else if ( newmsg->lm_msgtype == LDAP_RES_SEARCH_RESULT) {
+ foundit = 1; /* return the chain later */
+ }
+ }
+
+ /*
+ * if not, we must add it to the list of responses. if
+ * the msgid is already there, it must be part of an existing
+ * search response.
+ */
+
+ prev = NULL;
+ for ( l = ld->ld_responses; l != NULL; l = l->lm_next ) {
+ if ( l->lm_msgid == newmsg->lm_msgid ) {
+ break;
+ }
+ prev = l;
+ }
+
+ /* not part of an existing search response */
+ if ( l == NULL ) {
+ if ( foundit ) {
+ *result = newmsg;
+ goto exit;
+ }
+
+ newmsg->lm_next = ld->ld_responses;
+ ld->ld_responses = newmsg;
+ goto exit;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "adding response ld %p msgid %d type %ld:\n",
+ (void *)ld, newmsg->lm_msgid, (long) newmsg->lm_msgtype );
+
+ /* part of a search response - add to end of list of entries */
+ l->lm_chain_tail->lm_chain = newmsg;
+ l->lm_chain_tail = newmsg;
+
+ /* return the whole chain if that's what we were looking for */
+ if ( foundit ) {
+ if ( prev == NULL ) {
+ ld->ld_responses = l->lm_next;
+ } else {
+ prev->lm_next = l->lm_next;
+ }
+ *result = l;
+ }
+
+exit:
+ if ( foundit ) {
+ ld->ld_errno = LDAP_SUCCESS;
+ return( tag );
+ }
+ if ( lc && ber_sockbuf_ctrl( lc->lconn_sb, LBER_SB_OPT_DATA_READY, NULL ) ) {
+ goto retry;
+ }
+ return( LDAP_MSG_X_KEEP_LOOKING ); /* continue looking */
+}
+
+
+static ber_tag_t
+build_result_ber( LDAP *ld, BerElement **bp, LDAPRequest *lr )
+{
+ ber_len_t len;
+ ber_tag_t tag;
+ ber_int_t along;
+ BerElement *ber;
+
+ *bp = NULL;
+ ber = ldap_alloc_ber_with_options( ld );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return LBER_ERROR;
+ }
+
+ if ( ber_printf( ber, "{it{ess}}", lr->lr_msgid,
+ lr->lr_res_msgtype, lr->lr_res_errno,
+ lr->lr_res_matched ? lr->lr_res_matched : "",
+ lr->lr_res_error ? lr->lr_res_error : "" ) == -1 )
+ {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ ber_reset( ber, 1 );
+
+ if ( ber_skip_tag( ber, &len ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ if ( ber_get_enum( ber, &along ) == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ tag = ber_peek_tag( ber, &len );
+
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ ber_free( ber, 1 );
+ return( LBER_ERROR );
+ }
+
+ *bp = ber;
+ return tag;
+}
+
+
+/*
+ * Merge error information in "lr" with "parentr" error code and string.
+ */
+static void
+merge_error_info( LDAP *ld, LDAPRequest *parentr, LDAPRequest *lr )
+{
+ if ( lr->lr_res_errno == LDAP_PARTIAL_RESULTS ) {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( lr->lr_res_error != NULL ) {
+ (void)ldap_append_referral( ld, &parentr->lr_res_error,
+ lr->lr_res_error );
+ }
+
+ } else if ( lr->lr_res_errno != LDAP_SUCCESS &&
+ parentr->lr_res_errno == LDAP_SUCCESS )
+ {
+ parentr->lr_res_errno = lr->lr_res_errno;
+ if ( parentr->lr_res_error != NULL ) {
+ LDAP_FREE( parentr->lr_res_error );
+ }
+ parentr->lr_res_error = lr->lr_res_error;
+ lr->lr_res_error = NULL;
+ if ( LDAP_NAME_ERROR( lr->lr_res_errno ) ) {
+ if ( parentr->lr_res_matched != NULL ) {
+ LDAP_FREE( parentr->lr_res_matched );
+ }
+ parentr->lr_res_matched = lr->lr_res_matched;
+ lr->lr_res_matched = NULL;
+ }
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "merged parent (id %d) error info: ",
+ parentr->lr_msgid, 0, 0 );
+ Debug( LDAP_DEBUG_TRACE, "result errno %d, error <%s>, matched <%s>\n",
+ parentr->lr_res_errno,
+ parentr->lr_res_error ? parentr->lr_res_error : "",
+ parentr->lr_res_matched ? parentr->lr_res_matched : "" );
+}
+
+
+
+int
+ldap_msgtype( LDAPMessage *lm )
+{
+ assert( lm != NULL );
+ return ( lm != NULL ) ? (int)lm->lm_msgtype : -1;
+}
+
+
+int
+ldap_msgid( LDAPMessage *lm )
+{
+ assert( lm != NULL );
+
+ return ( lm != NULL ) ? lm->lm_msgid : -1;
+}
+
+
+const char *
+ldap_int_msgtype2str( ber_tag_t tag )
+{
+ switch( tag ) {
+ case LDAP_RES_ADD: return "add";
+ case LDAP_RES_BIND: return "bind";
+ case LDAP_RES_COMPARE: return "compare";
+ case LDAP_RES_DELETE: return "delete";
+ case LDAP_RES_EXTENDED: return "extended-result";
+ case LDAP_RES_INTERMEDIATE: return "intermediate";
+ case LDAP_RES_MODIFY: return "modify";
+ case LDAP_RES_RENAME: return "rename";
+ case LDAP_RES_SEARCH_ENTRY: return "search-entry";
+ case LDAP_RES_SEARCH_REFERENCE: return "search-reference";
+ case LDAP_RES_SEARCH_RESULT: return "search-result";
+ }
+ return "unknown";
+}
+
+int
+ldap_msgfree( LDAPMessage *lm )
+{
+ LDAPMessage *next;
+ int type = 0;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_msgfree\n", 0, 0, 0 );
+
+ for ( ; lm != NULL; lm = next ) {
+ next = lm->lm_chain;
+ type = lm->lm_msgtype;
+ ber_free( lm->lm_ber, 1 );
+ LDAP_FREE( (char *) lm );
+ }
+
+ return type;
+}
+
+/*
+ * ldap_msgdelete - delete a message. It returns:
+ * 0 if the entire message was deleted
+ * -1 if the message was not found, or only part of it was found
+ */
+int
+ldap_msgdelete( LDAP *ld, int msgid )
+{
+ LDAPMessage *lm, *prev;
+ int rc = 0;
+
+ assert( ld != NULL );
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_msgdelete ld=%p msgid=%d\n",
+ (void *)ld, msgid, 0 );
+
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ prev = NULL;
+ for ( lm = ld->ld_responses; lm != NULL; lm = lm->lm_next ) {
+ if ( lm->lm_msgid == msgid ) {
+ break;
+ }
+ prev = lm;
+ }
+
+ if ( lm == NULL ) {
+ rc = -1;
+
+ } else {
+ if ( prev == NULL ) {
+ ld->ld_responses = lm->lm_next;
+ } else {
+ prev->lm_next = lm->lm_next;
+ }
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+ if ( lm ) {
+ switch ( ldap_msgfree( lm ) ) {
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ case LDAP_RES_INTERMEDIATE:
+ rc = -1;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * ldap_abandoned
+ *
+ * return the location of the message id in the array of abandoned
+ * message ids, or -1
+ */
+static int
+ldap_abandoned( LDAP *ld, ber_int_t msgid )
+{
+ int ret, idx;
+ assert( msgid >= 0 );
+
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+ ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+}
+
+/*
+ * ldap_mark_abandoned
+ */
+static int
+ldap_mark_abandoned( LDAP *ld, ber_int_t msgid )
+{
+ int ret, idx;
+
+ assert( msgid >= 0 );
+ LDAP_MUTEX_LOCK( &ld->ld_abandon_mutex );
+ ret = ldap_int_bisect_find( ld->ld_abandoned, ld->ld_nabandoned, msgid, &idx );
+ if (ret <= 0) { /* error or already deleted by another thread */
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+ }
+ /* still in abandoned array, so delete */
+ ret = ldap_int_bisect_delete( &ld->ld_abandoned, &ld->ld_nabandoned,
+ msgid, idx );
+ LDAP_MUTEX_UNLOCK( &ld->ld_abandon_mutex );
+ return ret;
+}
diff --git a/libraries/libldap/sasl.c b/libraries/libldap/sasl.c
new file mode 100644
index 0000000..ff6988d
--- /dev/null
+++ b/libraries/libldap/sasl.c
@@ -0,0 +1,868 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+
+#include "ldap-int.h"
+
+BerElement *
+ldap_build_bind_req(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t *msgidp )
+{
+ BerElement *ber;
+ int rc;
+
+ if( mechanism == LDAP_SASL_SIMPLE ) {
+ if( dn == NULL && cred != NULL && cred->bv_len ) {
+ /* use default binddn */
+ dn = ld->ld_defbinddn;
+ }
+
+ } else if( ld->ld_version < LDAP_VERSION3 ) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return( NULL );
+ }
+
+ if ( dn == NULL ) {
+ dn = "";
+ }
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ LDAP_NEXT_MSGID( ld, *msgidp );
+ if( mechanism == LDAP_SASL_SIMPLE ) {
+ /* simple bind */
+ rc = ber_printf( ber, "{it{istON}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SIMPLE,
+ cred );
+
+ } else if ( cred == NULL || cred->bv_val == NULL ) {
+ /* SASL bind w/o credentials */
+ rc = ber_printf( ber, "{it{ist{sN}N}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SASL,
+ mechanism );
+
+ } else {
+ /* SASL bind w/ credentials */
+ rc = ber_printf( ber, "{it{ist{sON}N}" /*}*/,
+ *msgidp, LDAP_REQ_BIND,
+ ld->ld_version, dn, LDAP_AUTH_SASL,
+ mechanism, cred );
+ }
+
+ if( rc == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+/*
+ * ldap_sasl_bind - bind to the ldap server (and X.500).
+ * The dn (usually NULL), mechanism, and credentials are provided.
+ * The message id of the request initiated is provided upon successful
+ * (LDAP_SUCCESS) return.
+ *
+ * Example:
+ * ldap_sasl_bind( ld, NULL, "mechanism",
+ * cred, NULL, NULL, &msgid )
+ */
+
+int
+ldap_sasl_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ BerElement *ber;
+ int rc;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ ber = ldap_build_bind_req( ld, dn, mechanism, cred, sctrls, cctrls, &id );
+ if( !ber )
+ return ld->ld_errno;
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_BIND, dn, ber, id );
+
+ if(*msgidp < 0)
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+
+int
+ldap_sasl_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *mechanism,
+ struct berval *cred,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct berval **servercredp )
+{
+ int rc, msgid;
+ LDAPMessage *result;
+ struct berval *scredp = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_sasl_bind_s\n", 0, 0, 0 );
+
+ /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
+ if( servercredp != NULL ) {
+ if (ld->ld_version < LDAP_VERSION3) {
+ ld->ld_errno = LDAP_NOT_SUPPORTED;
+ return ld->ld_errno;
+ }
+ *servercredp = NULL;
+ }
+
+ rc = ldap_sasl_bind( ld, dn, mechanism, cred, sctrls, cctrls, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ return( rc );
+ }
+#endif
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+
+ /* parse the results */
+ scredp = NULL;
+ if( servercredp != NULL ) {
+ rc = ldap_parse_sasl_bind_result( ld, result, &scredp, 0 );
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( result );
+ return( rc );
+ }
+
+ rc = ldap_result2error( ld, result, 1 );
+
+ if ( rc == LDAP_SUCCESS || rc == LDAP_SASL_BIND_IN_PROGRESS ) {
+ if( servercredp != NULL ) {
+ *servercredp = scredp;
+ scredp = NULL;
+ }
+ }
+
+ if ( scredp != NULL ) {
+ ber_bvfree(scredp);
+ }
+
+ return rc;
+}
+
+
+/*
+* Parse BindResponse:
+*
+* BindResponse ::= [APPLICATION 1] SEQUENCE {
+* COMPONENTS OF LDAPResult,
+* serverSaslCreds [7] OCTET STRING OPTIONAL }
+*
+* LDAPResult ::= SEQUENCE {
+* resultCode ENUMERATED,
+* matchedDN LDAPDN,
+* errorMessage LDAPString,
+* referral [3] Referral OPTIONAL }
+*/
+
+int
+ldap_parse_sasl_bind_result(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **servercredp,
+ int freeit )
+{
+ ber_int_t errcode;
+ struct berval* scred;
+
+ ber_tag_t tag;
+ BerElement *ber;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_parse_sasl_bind_result\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+
+ if( servercredp != NULL ) {
+ if( ld->ld_version < LDAP_VERSION2 ) {
+ return LDAP_NOT_SUPPORTED;
+ }
+ *servercredp = NULL;
+ }
+
+ if( res->lm_msgtype != LDAP_RES_BIND ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ scred = NULL;
+
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+ if ( ld->ld_matched ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ /* parse results */
+
+ ber = ber_dup( res->lm_ber );
+
+ if( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ if ( ld->ld_version < LDAP_VERSION2 ) {
+ tag = ber_scanf( ber, "{iA}",
+ &errcode, &ld->ld_error );
+
+ if( tag == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ } else {
+ ber_len_t len;
+
+ tag = ber_scanf( ber, "{eAA" /*}*/,
+ &errcode, &ld->ld_matched, &ld->ld_error );
+
+ if( tag == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag(ber, &len);
+
+ if( tag == LDAP_TAG_REFERRAL ) {
+ /* skip 'em */
+ if( ber_scanf( ber, "x" ) == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+
+ tag = ber_peek_tag(ber, &len);
+ }
+
+ if( tag == LDAP_TAG_SASL_RES_CREDS ) {
+ if( ber_scanf( ber, "O", &scred ) == LBER_ERROR ) {
+ ber_free( ber, 0 );
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return ld->ld_errno;
+ }
+ }
+ }
+
+ ber_free( ber, 0 );
+
+ if ( servercredp != NULL ) {
+ *servercredp = scred;
+
+ } else if ( scred != NULL ) {
+ ber_bvfree( scred );
+ }
+
+ ld->ld_errno = errcode;
+
+ if ( freeit ) {
+ ldap_msgfree( res );
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+int
+ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
+{
+ /* we need to query the server for supported mechs anyway */
+ LDAPMessage *res, *e;
+ char *attrs[] = { "supportedSASLMechanisms", NULL };
+ char **values, *mechlist;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_getmech\n", 0, 0, 0 );
+
+ rc = ldap_search_s( ld, "", LDAP_SCOPE_BASE,
+ NULL, attrs, 0, &res );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return ld->ld_errno;
+ }
+
+ e = ldap_first_entry( ld, res );
+ if ( e == NULL ) {
+ ldap_msgfree( res );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = LDAP_NO_SUCH_OBJECT;
+ }
+ return ld->ld_errno;
+ }
+
+ values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
+ if ( values == NULL ) {
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_SUCH_ATTRIBUTE;
+ return ld->ld_errno;
+ }
+
+ mechlist = ldap_charray2str( values, " " );
+ if ( mechlist == NULL ) {
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ LDAP_VFREE( values );
+ ldap_msgfree( res );
+
+ *pmechlist = mechlist;
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * ldap_sasl_interactive_bind - interactive SASL authentication
+ *
+ * This routine uses interactive callbacks.
+ *
+ * LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further
+ * calls are needed.
+ */
+int
+ldap_sasl_interactive_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *mechs,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults,
+ LDAPMessage *result,
+ const char **rmech,
+ int *msgid )
+{
+ char *smechs = NULL;
+ int rc;
+
+#ifdef LDAP_CONNECTIONLESS
+ if( LDAP_IS_UDP(ld) ) {
+ /* Just force it to simple bind, silly to make the user
+ * ask all the time. No, we don't ever actually bind, but I'll
+ * let the final bind handler take care of saving the cdn.
+ */
+ rc = ldap_simple_bind( ld, dn, NULL );
+ rc = rc < 0 ? rc : 0;
+ goto done;
+ } else
+#endif
+
+ /* First time */
+ if ( !result ) {
+
+#ifdef HAVE_CYRUS_SASL
+ if( mechs == NULL || *mechs == '\0' ) {
+ mechs = ld->ld_options.ldo_def_sasl_mech;
+ }
+#endif
+
+ if( mechs == NULL || *mechs == '\0' ) {
+ /* FIXME: this needs to be asynchronous too;
+ * perhaps NULL should be disallowed for async usage?
+ */
+ rc = ldap_pvt_sasl_getmechs( ld, &smechs );
+ if( rc != LDAP_SUCCESS ) {
+ goto done;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_sasl_interactive_bind: server supports: %s\n",
+ smechs, 0, 0 );
+
+ mechs = smechs;
+
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "ldap_sasl_interactive_bind: user selected: %s\n",
+ mechs, 0, 0 );
+ }
+ }
+ rc = ldap_int_sasl_bind( ld, dn, mechs,
+ serverControls, clientControls,
+ flags, interact, defaults, result, rmech, msgid );
+
+done:
+ if ( smechs ) LDAP_FREE( smechs );
+
+ return rc;
+}
+
+/*
+ * ldap_sasl_interactive_bind_s - interactive SASL authentication
+ *
+ * This routine uses interactive callbacks.
+ *
+ * LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ */
+int
+ldap_sasl_interactive_bind_s(
+ LDAP *ld,
+ LDAP_CONST char *dn, /* usually NULL */
+ LDAP_CONST char *mechs,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+ unsigned flags,
+ LDAP_SASL_INTERACT_PROC *interact,
+ void *defaults )
+{
+ const char *rmech = NULL;
+ LDAPMessage *result = NULL;
+ int rc, msgid;
+
+ do {
+ rc = ldap_sasl_interactive_bind( ld, dn, mechs,
+ serverControls, clientControls,
+ flags, interact, defaults, result, &rmech, &msgid );
+
+ ldap_msgfree( result );
+
+ if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
+ break;
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld)) {
+ break;
+ }
+#endif
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+ return( ld->ld_errno ); /* ldap_result sets ld_errno */
+ }
+ } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+ return rc;
+}
+
+#ifdef HAVE_CYRUS_SASL
+
+#ifdef HAVE_SASL_SASL_H
+#include <sasl/sasl.h>
+#else
+#include <sasl.h>
+#endif
+
+#endif /* HAVE_CYRUS_SASL */
+
+static int
+sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod );
+
+static int
+sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct sb_sasl_generic_data *p;
+ struct sb_sasl_generic_install *i;
+
+ assert( sbiod != NULL );
+
+ i = (struct sb_sasl_generic_install *)arg;
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL )
+ return -1;
+ p->ops = i->ops;
+ p->ops_private = i->ops_private;
+ p->sbiod = sbiod;
+ p->flags = 0;
+ ber_pvt_sb_buf_init( &p->sec_buf_in );
+ ber_pvt_sb_buf_init( &p->buf_in );
+ ber_pvt_sb_buf_init( &p->buf_out );
+
+ sbiod->sbiod_pvt = p;
+
+ p->ops->init( p, &p->min_send, &p->max_send, &p->max_recv );
+
+ if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, p->min_send ) < 0 ) {
+ sb_sasl_generic_remove( sbiod );
+ sock_errset(ENOMEM);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+sb_sasl_generic_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct sb_sasl_generic_data *p;
+
+ assert( sbiod != NULL );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ p->ops->fini(p);
+
+ ber_pvt_sb_buf_destroy( &p->sec_buf_in );
+ ber_pvt_sb_buf_destroy( &p->buf_in );
+ ber_pvt_sb_buf_destroy( &p->buf_out );
+ LBER_FREE( p );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static ber_len_t
+sb_sasl_generic_pkt_length(
+ struct sb_sasl_generic_data *p,
+ const unsigned char *buf,
+ int debuglevel )
+{
+ ber_len_t size;
+
+ assert( buf != NULL );
+
+ size = buf[0] << 24
+ | buf[1] << 16
+ | buf[2] << 8
+ | buf[3];
+
+ if ( size > p->max_recv ) {
+ /* somebody is trying to mess me up. */
+ ber_log_printf( LDAP_DEBUG_ANY, debuglevel,
+ "sb_sasl_generic_pkt_length: "
+ "received illegal packet length of %lu bytes\n",
+ (unsigned long)size );
+ size = 16; /* this should lead to an error. */
+ }
+
+ return size + 4; /* include the size !!! */
+}
+
+/* Drop a processed packet from the input buffer */
+static void
+sb_sasl_generic_drop_packet (
+ struct sb_sasl_generic_data *p,
+ int debuglevel )
+{
+ ber_slen_t len;
+
+ len = p->sec_buf_in.buf_ptr - p->sec_buf_in.buf_end;
+ if ( len > 0 )
+ AC_MEMCPY( p->sec_buf_in.buf_base, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_end, len );
+
+ if ( len >= 4 ) {
+ p->sec_buf_in.buf_end = sb_sasl_generic_pkt_length(p,
+ (unsigned char *) p->sec_buf_in.buf_base, debuglevel);
+ }
+ else {
+ p->sec_buf_in.buf_end = 0;
+ }
+ p->sec_buf_in.buf_ptr = len;
+}
+
+static ber_slen_t
+sb_sasl_generic_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct sb_sasl_generic_data *p;
+ ber_slen_t ret, bufptr;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ /* Are there anything left in the buffer? */
+ ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len );
+ bufptr = ret;
+ len -= ret;
+
+ if ( len == 0 )
+ return bufptr;
+
+ p->ops->reset_buf( p, &p->buf_in );
+
+ /* Read the length of the packet */
+ while ( p->sec_buf_in.buf_ptr < 4 ) {
+ ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_ptr,
+ 4 - p->sec_buf_in.buf_ptr );
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return bufptr ? bufptr : ret;
+
+ p->sec_buf_in.buf_ptr += ret;
+ }
+
+ /* The new packet always starts at p->sec_buf_in.buf_base */
+ ret = sb_sasl_generic_pkt_length(p, (unsigned char *) p->sec_buf_in.buf_base,
+ sbiod->sbiod_sb->sb_debug );
+
+ /* Grow the packet buffer if neccessary */
+ if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) &&
+ ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 )
+ {
+ sock_errset(ENOMEM);
+ return -1;
+ }
+ p->sec_buf_in.buf_end = ret;
+
+ /* Did we read the whole encrypted packet? */
+ while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) {
+ /* No, we have got only a part of it */
+ ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr;
+
+ ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base +
+ p->sec_buf_in.buf_ptr, ret );
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( errno == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return bufptr ? bufptr : ret;
+
+ p->sec_buf_in.buf_ptr += ret;
+ }
+
+ /* Decode the packet */
+ ret = p->ops->decode( p, &p->sec_buf_in, &p->buf_in );
+
+ /* Drop the packet from the input buffer */
+ sb_sasl_generic_drop_packet( p, sbiod->sbiod_sb->sb_debug );
+
+ if ( ret != 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_generic_read: failed to decode packet\n" );
+ sock_errset(EIO);
+ return -1;
+ }
+
+ bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len );
+
+ return bufptr;
+}
+
+static ber_slen_t
+sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct sb_sasl_generic_data *p;
+ int ret;
+ ber_len_t len2;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ /* Is there anything left in the buffer? */
+ if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+ if ( ret < 0 ) return ret;
+
+ /* Still have something left?? */
+ if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ sock_errset(EAGAIN);
+ return -1;
+ }
+ }
+
+ len2 = p->max_send - 100; /* For safety margin */
+ len2 = len > len2 ? len2 : len;
+
+ /* If we're just retrying a partial write, tell the
+ * caller it's done. Let them call again if there's
+ * still more left to write.
+ */
+ if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) {
+ p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE;
+ return len2;
+ }
+
+ /* now encode the next packet. */
+ p->ops->reset_buf( p, &p->buf_out );
+
+ ret = p->ops->encode( p, buf, len2, &p->buf_out );
+
+ if ( ret != 0 ) {
+ ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
+ "sb_sasl_generic_write: failed to encode packet\n" );
+ sock_errset(EIO);
+ return -1;
+ }
+
+ ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
+
+ if ( ret < 0 ) {
+ /* error? */
+ int err = sock_errno();
+ /* caller can retry this */
+ if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR )
+ p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+ return ret;
+ } else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+ /* partial write? pretend nothing got written */
+ p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+ sock_errset(EAGAIN);
+ len2 = -1;
+ }
+
+ /* return number of bytes encoded, not written, to ensure
+ * no byte is encoded twice (even if only sent once).
+ */
+ return len2;
+}
+
+static int
+sb_sasl_generic_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct sb_sasl_generic_data *p;
+
+ p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) return 1;
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+Sockbuf_IO ldap_pvt_sockbuf_io_sasl_generic = {
+ sb_sasl_generic_setup, /* sbi_setup */
+ sb_sasl_generic_remove, /* sbi_remove */
+ sb_sasl_generic_ctrl, /* sbi_ctrl */
+ sb_sasl_generic_read, /* sbi_read */
+ sb_sasl_generic_write, /* sbi_write */
+ NULL /* sbi_close */
+};
+
+int ldap_pvt_sasl_generic_install(
+ Sockbuf *sb,
+ struct sb_sasl_generic_install *install_arg )
+{
+ Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_generic_install\n",
+ 0, 0, 0 );
+
+ /* don't install the stuff unless security has been negotiated */
+
+ if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO,
+ &ldap_pvt_sockbuf_io_sasl_generic ) )
+ {
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_generic_" );
+#endif
+ ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
+ LBER_SBIOD_LEVEL_APPLICATION, install_arg );
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void ldap_pvt_sasl_generic_remove( Sockbuf *sb )
+{
+ ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl_generic,
+ LBER_SBIOD_LEVEL_APPLICATION );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_APPLICATION );
+#endif
+}
diff --git a/libraries/libldap/sbind.c b/libraries/libldap/sbind.c
new file mode 100644
index 0000000..2b0d087
--- /dev/null
+++ b/libraries/libldap/sbind.c
@@ -0,0 +1,115 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1993 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+/*
+ * BindRequest ::= SEQUENCE {
+ * version INTEGER,
+ * name DistinguishedName, -- who
+ * authentication CHOICE {
+ * simple [0] OCTET STRING -- passwd
+ * krbv42ldap [1] OCTET STRING -- OBSOLETE
+ * krbv42dsa [2] OCTET STRING -- OBSOLETE
+ * sasl [3] SaslCredentials -- LDAPv3
+ * }
+ * }
+ *
+ * BindResponse ::= SEQUENCE {
+ * COMPONENTS OF LDAPResult,
+ * serverSaslCreds OCTET STRING OPTIONAL -- LDAPv3
+ * }
+ *
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * ldap_simple_bind - bind to the ldap server (and X.500). The dn and
+ * password of the entry to which to bind are supplied. The message id
+ * of the request initiated is returned.
+ *
+ * Example:
+ * ldap_simple_bind( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret" )
+ */
+
+int
+ldap_simple_bind(
+ LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAP_CONST char *passwd )
+{
+ int rc;
+ int msgid;
+ struct berval cred;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_simple_bind\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( passwd != NULL ) {
+ cred.bv_val = (char *) passwd;
+ cred.bv_len = strlen( passwd );
+ } else {
+ cred.bv_val = "";
+ cred.bv_len = 0;
+ }
+
+ rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, &msgid );
+
+ return rc == LDAP_SUCCESS ? msgid : -1;
+}
+
+/*
+ * ldap_simple_bind - bind to the ldap server (and X.500) using simple
+ * authentication. The dn and password of the entry to which to bind are
+ * supplied. LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ *
+ * Example:
+ * ldap_simple_bind_s( ld, "cn=manager, o=university of michigan, c=us",
+ * "secret" )
+ */
+
+int
+ldap_simple_bind_s( LDAP *ld, LDAP_CONST char *dn, LDAP_CONST char *passwd )
+{
+ struct berval cred;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_simple_bind_s\n", 0, 0, 0 );
+
+ if ( passwd != NULL ) {
+ cred.bv_val = (char *) passwd;
+ cred.bv_len = strlen( passwd );
+ } else {
+ cred.bv_val = "";
+ cred.bv_len = 0;
+ }
+
+ return ldap_sasl_bind_s( ld, dn, LDAP_SASL_SIMPLE, &cred,
+ NULL, NULL, NULL );
+}
diff --git a/libraries/libldap/schema.c b/libraries/libldap/schema.c
new file mode 100644
index 0000000..24c6efe
--- /dev/null
+++ b/libraries/libldap/schema.c
@@ -0,0 +1,3385 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * schema.c: parsing routines used by servers and clients to process
+ * schema definitions
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#include <ldap_schema.h>
+
+static const char EndOfInput[] = "end of input";
+
+static const char *
+choose_name( char *names[], const char *fallback )
+{
+ return (names != NULL && names[0] != NULL) ? names[0] : fallback;
+}
+
+LDAP_CONST char *
+ldap_syntax2name( LDAPSyntax * syn )
+{
+ if (!syn) return NULL;
+ return( syn->syn_oid );
+}
+
+LDAP_CONST char *
+ldap_matchingrule2name( LDAPMatchingRule * mr )
+{
+ if (!mr) return NULL;
+ return( choose_name( mr->mr_names, mr->mr_oid ) );
+}
+
+LDAP_CONST char *
+ldap_matchingruleuse2name( LDAPMatchingRuleUse * mru )
+{
+ if (!mru) return NULL;
+ return( choose_name( mru->mru_names, mru->mru_oid ) );
+}
+
+LDAP_CONST char *
+ldap_attributetype2name( LDAPAttributeType * at )
+{
+ if (!at) return NULL;
+ return( choose_name( at->at_names, at->at_oid ) );
+}
+
+LDAP_CONST char *
+ldap_objectclass2name( LDAPObjectClass * oc )
+{
+ if (!oc) return NULL;
+ return( choose_name( oc->oc_names, oc->oc_oid ) );
+}
+
+LDAP_CONST char *
+ldap_contentrule2name( LDAPContentRule * cr )
+{
+ if (!cr) return NULL;
+ return( choose_name( cr->cr_names, cr->cr_oid ) );
+}
+
+LDAP_CONST char *
+ldap_nameform2name( LDAPNameForm * nf )
+{
+ if (!nf) return NULL;
+ return( choose_name( nf->nf_names, nf->nf_oid ) );
+}
+
+LDAP_CONST char *
+ldap_structurerule2name( LDAPStructureRule * sr )
+{
+ if (!sr) return NULL;
+ return( choose_name( sr->sr_names, NULL ) );
+}
+
+/*
+ * When pretty printing the entities we will be appending to a buffer.
+ * Since checking for overflow, realloc'ing and checking if no error
+ * is extremely boring, we will use a protection layer that will let
+ * us blissfully ignore the error until the end. This layer is
+ * implemented with the help of the next type.
+ */
+
+typedef struct safe_string {
+ char * val;
+ ber_len_t size;
+ ber_len_t pos;
+ int at_whsp;
+} safe_string;
+
+static safe_string *
+new_safe_string(int size)
+{
+ safe_string * ss;
+
+ ss = LDAP_MALLOC(sizeof(safe_string));
+ if ( !ss )
+ return(NULL);
+
+ ss->val = LDAP_MALLOC(size);
+ if ( !ss->val ) {
+ LDAP_FREE(ss);
+ return(NULL);
+ }
+
+ ss->size = size;
+ ss->pos = 0;
+ ss->at_whsp = 0;
+
+ return ss;
+}
+
+static void
+safe_string_free(safe_string * ss)
+{
+ if ( !ss )
+ return;
+ LDAP_FREE(ss->val);
+ LDAP_FREE(ss);
+}
+
+#if 0 /* unused */
+static char *
+safe_string_val(safe_string * ss)
+{
+ ss->val[ss->pos] = '\0';
+ return(ss->val);
+}
+#endif
+
+static char *
+safe_strdup(safe_string * ss)
+{
+ char *ret = LDAP_MALLOC(ss->pos+1);
+ if (!ret)
+ return NULL;
+ AC_MEMCPY(ret, ss->val, ss->pos);
+ ret[ss->pos] = '\0';
+ return ret;
+}
+
+static int
+append_to_safe_string(safe_string * ss, char * s)
+{
+ int l = strlen(s);
+ char * temp;
+
+ /*
+ * Some runaway process is trying to append to a string that
+ * overflowed and we could not extend.
+ */
+ if ( !ss->val )
+ return -1;
+
+ /* We always make sure there is at least one position available */
+ if ( ss->pos + l >= ss->size-1 ) {
+ ss->size *= 2;
+ if ( ss->pos + l >= ss->size-1 ) {
+ ss->size = ss->pos + l + 1;
+ }
+
+ temp = LDAP_REALLOC(ss->val, ss->size);
+ if ( !temp ) {
+ /* Trouble, out of memory */
+ LDAP_FREE(ss->val);
+ return -1;
+ }
+ ss->val = temp;
+ }
+ strncpy(&ss->val[ss->pos], s, l);
+ ss->pos += l;
+ if ( ss->pos > 0 && LDAP_SPACE(ss->val[ss->pos-1]) )
+ ss->at_whsp = 1;
+ else
+ ss->at_whsp = 0;
+
+ return 0;
+}
+
+static int
+print_literal(safe_string *ss, char *s)
+{
+ return(append_to_safe_string(ss,s));
+}
+
+static int
+print_whsp(safe_string *ss)
+{
+ if ( ss->at_whsp )
+ return(append_to_safe_string(ss,""));
+ else
+ return(append_to_safe_string(ss," "));
+}
+
+static int
+print_numericoid(safe_string *ss, char *s)
+{
+ if ( s )
+ return(append_to_safe_string(ss,s));
+ else
+ return(append_to_safe_string(ss,""));
+}
+
+/* This one is identical to print_qdescr */
+static int
+print_qdstring(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ print_literal(ss,"'");
+ append_to_safe_string(ss,s);
+ print_literal(ss,"'");
+ return(print_whsp(ss));
+}
+
+static int
+print_qdescr(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ print_literal(ss,"'");
+ append_to_safe_string(ss,s);
+ print_literal(ss,"'");
+ return(print_whsp(ss));
+}
+
+static int
+print_qdescrlist(safe_string *ss, char **sa)
+{
+ char **sp;
+ int ret = 0;
+
+ for (sp=sa; *sp; sp++) {
+ ret = print_qdescr(ss,*sp);
+ }
+ /* If the list was empty, we return zero that is potentially
+ * incorrect, but since we will be still appending things, the
+ * overflow will be detected later. Maybe FIX.
+ */
+ return(ret);
+}
+
+static int
+print_qdescrs(safe_string *ss, char **sa)
+{
+ /* The only way to represent an empty list is as a qdescrlist
+ * so, if the list is empty we treat it as a long list.
+ * Really, this is what the syntax mandates. We should not
+ * be here if the list was empty, but if it happens, a label
+ * has already been output and we cannot undo it.
+ */
+ if ( !sa[0] || ( sa[0] && sa[1] ) ) {
+ print_whsp(ss);
+ print_literal(ss,"("/*)*/);
+ print_qdescrlist(ss,sa);
+ print_literal(ss,/*(*/")");
+ return(print_whsp(ss));
+ } else {
+ return(print_qdescr(ss,*sa));
+ }
+}
+
+static int
+print_woid(safe_string *ss, char *s)
+{
+ print_whsp(ss);
+ append_to_safe_string(ss,s);
+ return print_whsp(ss);
+}
+
+static int
+print_oidlist(safe_string *ss, char **sa)
+{
+ char **sp;
+
+ for (sp=sa; *(sp+1); sp++) {
+ print_woid(ss,*sp);
+ print_literal(ss,"$");
+ }
+ return(print_woid(ss,*sp));
+}
+
+static int
+print_oids(safe_string *ss, char **sa)
+{
+ if ( sa[0] && sa[1] ) {
+ print_literal(ss,"("/*)*/);
+ print_oidlist(ss,sa);
+ print_whsp(ss);
+ return(print_literal(ss,/*(*/")"));
+ } else {
+ return(print_woid(ss,*sa));
+ }
+}
+
+static int
+print_noidlen(safe_string *ss, char *s, int l)
+{
+ char buf[64];
+ int ret;
+
+ ret = print_numericoid(ss,s);
+ if ( l ) {
+ snprintf(buf, sizeof buf, "{%d}",l);
+ ret = print_literal(ss,buf);
+ }
+ return(ret);
+}
+
+static int
+print_ruleid(safe_string *ss, int rid)
+{
+ char buf[64];
+ snprintf(buf, sizeof buf, "%d", rid);
+ return print_literal(ss,buf);
+}
+
+static int
+print_ruleids(safe_string *ss, int n, int *rids)
+{
+ int i;
+
+ if( n == 1 ) {
+ print_ruleid(ss,rids[0]);
+ return print_whsp(ss);
+ } else {
+ print_literal(ss,"("/*)*/);
+ for( i=0; i<n; i++ ) {
+ print_whsp(ss);
+ print_ruleid(ss,rids[i]);
+ }
+ print_whsp(ss);
+ return print_literal(ss,/*(*/")");
+ }
+}
+
+
+static int
+print_extensions(safe_string *ss, LDAPSchemaExtensionItem **extensions)
+{
+ LDAPSchemaExtensionItem **ext;
+
+ if ( extensions ) {
+ print_whsp(ss);
+ for ( ext = extensions; *ext != NULL; ext++ ) {
+ print_literal(ss, (*ext)->lsei_name);
+ print_whsp(ss);
+ /* Should be print_qdstrings */
+ print_qdescrs(ss, (*ext)->lsei_values);
+ print_whsp(ss);
+ }
+ }
+
+ return 0;
+}
+
+char *
+ldap_syntax2str( LDAPSyntax * syn )
+{
+ struct berval bv;
+ if (ldap_syntax2bv( syn, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_syntax2bv( LDAPSyntax * syn, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !syn || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, syn->syn_oid);
+ print_whsp(ss);
+
+ if ( syn->syn_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,syn->syn_desc);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, syn->syn_extensions);
+
+ print_literal(ss,/*(*/ ")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_matchingrule2str( LDAPMatchingRule * mr )
+{
+ struct berval bv;
+ if (ldap_matchingrule2bv( mr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_matchingrule2bv( LDAPMatchingRule * mr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !mr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"(" /*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, mr->mr_oid);
+ print_whsp(ss);
+
+ if ( mr->mr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,mr->mr_names);
+ }
+
+ if ( mr->mr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,mr->mr_desc);
+ }
+
+ if ( mr->mr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( mr->mr_syntax_oid ) {
+ print_literal(ss,"SYNTAX");
+ print_whsp(ss);
+ print_literal(ss, mr->mr_syntax_oid);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, mr->mr_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_matchingruleuse2str( LDAPMatchingRuleUse * mru )
+{
+ struct berval bv;
+ if (ldap_matchingruleuse2bv( mru, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_matchingruleuse2bv( LDAPMatchingRuleUse * mru, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !mru || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"(" /*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, mru->mru_oid);
+ print_whsp(ss);
+
+ if ( mru->mru_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,mru->mru_names);
+ }
+
+ if ( mru->mru_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,mru->mru_desc);
+ }
+
+ if ( mru->mru_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( mru->mru_applies_oids ) {
+ print_literal(ss,"APPLIES");
+ print_whsp(ss);
+ print_oids(ss, mru->mru_applies_oids);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, mru->mru_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_objectclass2str( LDAPObjectClass * oc )
+{
+ struct berval bv;
+ if (ldap_objectclass2bv( oc, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_objectclass2bv( LDAPObjectClass * oc, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !oc || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, oc->oc_oid);
+ print_whsp(ss);
+
+ if ( oc->oc_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,oc->oc_names);
+ }
+
+ if ( oc->oc_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,oc->oc_desc);
+ }
+
+ if ( oc->oc_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( oc->oc_sup_oids ) {
+ print_literal(ss,"SUP");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_sup_oids);
+ print_whsp(ss);
+ }
+
+ switch (oc->oc_kind) {
+ case LDAP_SCHEMA_ABSTRACT:
+ print_literal(ss,"ABSTRACT");
+ break;
+ case LDAP_SCHEMA_STRUCTURAL:
+ print_literal(ss,"STRUCTURAL");
+ break;
+ case LDAP_SCHEMA_AUXILIARY:
+ print_literal(ss,"AUXILIARY");
+ break;
+ default:
+ print_literal(ss,"KIND-UNKNOWN");
+ break;
+ }
+ print_whsp(ss);
+
+ if ( oc->oc_at_oids_must ) {
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_at_oids_must);
+ print_whsp(ss);
+ }
+
+ if ( oc->oc_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,oc->oc_at_oids_may);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, oc->oc_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_contentrule2str( LDAPContentRule * cr )
+{
+ struct berval bv;
+ if (ldap_contentrule2bv( cr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_contentrule2bv( LDAPContentRule * cr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !cr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, cr->cr_oid);
+ print_whsp(ss);
+
+ if ( cr->cr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,cr->cr_names);
+ }
+
+ if ( cr->cr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,cr->cr_desc);
+ }
+
+ if ( cr->cr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_oc_oids_aux ) {
+ print_literal(ss,"AUX");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_oc_oids_aux);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_must ) {
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_must);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_may);
+ print_whsp(ss);
+ }
+
+ if ( cr->cr_at_oids_not ) {
+ print_literal(ss,"NOT");
+ print_whsp(ss);
+ print_oids(ss,cr->cr_at_oids_not);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, cr->cr_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_structurerule2str( LDAPStructureRule * sr )
+{
+ struct berval bv;
+ if (ldap_structurerule2bv( sr, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_structurerule2bv( LDAPStructureRule * sr, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !sr || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_ruleid(ss, sr->sr_ruleid);
+ print_whsp(ss);
+
+ if ( sr->sr_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,sr->sr_names);
+ }
+
+ if ( sr->sr_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,sr->sr_desc);
+ }
+
+ if ( sr->sr_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ print_literal(ss,"FORM");
+ print_whsp(ss);
+ print_woid(ss,sr->sr_nameform);
+ print_whsp(ss);
+
+ if ( sr->sr_nsup_ruleids ) {
+ print_literal(ss,"SUP");
+ print_whsp(ss);
+ print_ruleids(ss,sr->sr_nsup_ruleids,sr->sr_sup_ruleids);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, sr->sr_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+
+char *
+ldap_nameform2str( LDAPNameForm * nf )
+{
+ struct berval bv;
+ if (ldap_nameform2bv( nf, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_nameform2bv( LDAPNameForm * nf, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !nf || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, nf->nf_oid);
+ print_whsp(ss);
+
+ if ( nf->nf_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,nf->nf_names);
+ }
+
+ if ( nf->nf_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,nf->nf_desc);
+ }
+
+ if ( nf->nf_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ print_literal(ss,"OC");
+ print_whsp(ss);
+ print_woid(ss,nf->nf_objectclass);
+ print_whsp(ss);
+
+ print_literal(ss,"MUST");
+ print_whsp(ss);
+ print_oids(ss,nf->nf_at_oids_must);
+ print_whsp(ss);
+
+
+ if ( nf->nf_at_oids_may ) {
+ print_literal(ss,"MAY");
+ print_whsp(ss);
+ print_oids(ss,nf->nf_at_oids_may);
+ print_whsp(ss);
+ }
+
+ print_whsp(ss);
+ print_extensions(ss, nf->nf_extensions);
+
+ print_literal(ss, /*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+char *
+ldap_attributetype2str( LDAPAttributeType * at )
+{
+ struct berval bv;
+ if (ldap_attributetype2bv( at, &bv ))
+ return(bv.bv_val);
+ else
+ return NULL;
+}
+
+struct berval *
+ldap_attributetype2bv( LDAPAttributeType * at, struct berval *bv )
+{
+ safe_string * ss;
+
+ if ( !at || !bv )
+ return NULL;
+
+ ss = new_safe_string(256);
+ if ( !ss )
+ return NULL;
+
+ print_literal(ss,"("/*)*/);
+ print_whsp(ss);
+
+ print_numericoid(ss, at->at_oid);
+ print_whsp(ss);
+
+ if ( at->at_names ) {
+ print_literal(ss,"NAME");
+ print_qdescrs(ss,at->at_names);
+ }
+
+ if ( at->at_desc ) {
+ print_literal(ss,"DESC");
+ print_qdstring(ss,at->at_desc);
+ }
+
+ if ( at->at_obsolete ) {
+ print_literal(ss, "OBSOLETE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_sup_oid ) {
+ print_literal(ss,"SUP");
+ print_woid(ss,at->at_sup_oid);
+ }
+
+ if ( at->at_equality_oid ) {
+ print_literal(ss,"EQUALITY");
+ print_woid(ss,at->at_equality_oid);
+ }
+
+ if ( at->at_ordering_oid ) {
+ print_literal(ss,"ORDERING");
+ print_woid(ss,at->at_ordering_oid);
+ }
+
+ if ( at->at_substr_oid ) {
+ print_literal(ss,"SUBSTR");
+ print_woid(ss,at->at_substr_oid);
+ }
+
+ if ( at->at_syntax_oid ) {
+ print_literal(ss,"SYNTAX");
+ print_whsp(ss);
+ print_noidlen(ss,at->at_syntax_oid,at->at_syntax_len);
+ print_whsp(ss);
+ }
+
+ if ( at->at_single_value == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"SINGLE-VALUE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_collective == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"COLLECTIVE");
+ print_whsp(ss);
+ }
+
+ if ( at->at_no_user_mod == LDAP_SCHEMA_YES ) {
+ print_literal(ss,"NO-USER-MODIFICATION");
+ print_whsp(ss);
+ }
+
+ if ( at->at_usage != LDAP_SCHEMA_USER_APPLICATIONS ) {
+ print_literal(ss,"USAGE");
+ print_whsp(ss);
+ switch (at->at_usage) {
+ case LDAP_SCHEMA_DIRECTORY_OPERATION:
+ print_literal(ss,"directoryOperation");
+ break;
+ case LDAP_SCHEMA_DISTRIBUTED_OPERATION:
+ print_literal(ss,"distributedOperation");
+ break;
+ case LDAP_SCHEMA_DSA_OPERATION:
+ print_literal(ss,"dSAOperation");
+ break;
+ default:
+ print_literal(ss,"UNKNOWN");
+ break;
+ }
+ }
+
+ print_whsp(ss);
+
+ print_extensions(ss, at->at_extensions);
+
+ print_literal(ss,/*(*/")");
+
+ bv->bv_val = safe_strdup(ss);
+ bv->bv_len = ss->pos;
+ safe_string_free(ss);
+ return(bv);
+}
+
+/*
+ * Now come the parsers. There is one parser for each entity type:
+ * objectclasses, attributetypes, etc.
+ *
+ * Each of them is written as a recursive-descent parser, except that
+ * none of them is really recursive. But the idea is kept: there
+ * is one routine per non-terminal that eithers gobbles lexical tokens
+ * or calls lower-level routines, etc.
+ *
+ * The scanner is implemented in the routine get_token. Actually,
+ * get_token is more than a scanner and will return tokens that are
+ * in fact non-terminals in the grammar. So you can see the whole
+ * approach as the combination of a low-level bottom-up recognizer
+ * combined with a scanner and a number of top-down parsers. Or just
+ * consider that the real grammars recognized by the parsers are not
+ * those of the standards. As a matter of fact, our parsers are more
+ * liberal than the spec when there is no ambiguity.
+ *
+ * The difference is pretty academic (modulo bugs or incorrect
+ * interpretation of the specs).
+ */
+
+typedef enum tk_t {
+ TK_NOENDQUOTE = -2,
+ TK_OUTOFMEM = -1,
+ TK_EOS = 0,
+ TK_UNEXPCHAR = 1,
+ TK_BAREWORD = 2,
+ TK_QDSTRING = 3,
+ TK_LEFTPAREN = 4,
+ TK_RIGHTPAREN = 5,
+ TK_DOLLAR = 6,
+ TK_QDESCR = TK_QDSTRING
+} tk_t;
+
+static tk_t
+get_token( const char ** sp, char ** token_val )
+{
+ tk_t kind;
+ const char * p;
+ const char * q;
+ char * res;
+
+ *token_val = NULL;
+ switch (**sp) {
+ case '\0':
+ kind = TK_EOS;
+ (*sp)++;
+ break;
+ case '(':
+ kind = TK_LEFTPAREN;
+ (*sp)++;
+ break;
+ case ')':
+ kind = TK_RIGHTPAREN;
+ (*sp)++;
+ break;
+ case '$':
+ kind = TK_DOLLAR;
+ (*sp)++;
+ break;
+ case '\'':
+ kind = TK_QDSTRING;
+ (*sp)++;
+ p = *sp;
+ while ( **sp != '\'' && **sp != '\0' )
+ (*sp)++;
+ if ( **sp == '\'' ) {
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ (*sp)++;
+ } else {
+ kind = TK_NOENDQUOTE;
+ }
+ break;
+ default:
+ kind = TK_BAREWORD;
+ p = *sp;
+ while ( !LDAP_SPACE(**sp) &&
+ **sp != '(' &&
+ **sp != ')' &&
+ **sp != '$' &&
+ **sp != '\'' &&
+ /* for suggested minimum upper bound on the number
+ * of characters (RFC 4517) */
+ **sp != '{' &&
+ **sp != '\0' )
+ (*sp)++;
+ q = *sp;
+ res = LDAP_MALLOC(q-p+1);
+ if ( !res ) {
+ kind = TK_OUTOFMEM;
+ } else {
+ strncpy(res,p,q-p);
+ res[q-p] = '\0';
+ *token_val = res;
+ }
+ break;
+/* kind = TK_UNEXPCHAR; */
+/* break; */
+ }
+
+ return kind;
+}
+
+/* Gobble optional whitespace */
+static void
+parse_whsp(const char **sp)
+{
+ while (LDAP_SPACE(**sp))
+ (*sp)++;
+}
+
+/* TBC:!!
+ * General note for all parsers: to guarantee the algorithm halts they
+ * must always advance the pointer even when an error is found. For
+ * this one is not that important since an error here is fatal at the
+ * upper layers, but it is a simple strategy that will not get in
+ * endless loops.
+ */
+
+/* Parse a sequence of dot-separated decimal strings */
+char *
+ldap_int_parse_numericoid(const char **sp, int *code, const int flags)
+{
+ char * res = NULL;
+ const char * start = *sp;
+ int len;
+ int quoted = 0;
+
+ /* Netscape puts the SYNTAX value in quotes (incorrectly) */
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && **sp == '\'' ) {
+ quoted = 1;
+ (*sp)++;
+ start++;
+ }
+ /* Each iteration of this loop gets one decimal string */
+ while (**sp) {
+ if ( !LDAP_DIGIT(**sp) ) {
+ /*
+ * Initial char is not a digit or char after dot is
+ * not a digit
+ */
+ *code = LDAP_SCHERR_NODIGIT;
+ return NULL;
+ }
+ (*sp)++;
+ while ( LDAP_DIGIT(**sp) )
+ (*sp)++;
+ if ( **sp != '.' )
+ break;
+ /* Otherwise, gobble the dot and loop again */
+ (*sp)++;
+ }
+ /* Now *sp points at the char past the numericoid. Perfect. */
+ len = *sp - start;
+ if ( flags & LDAP_SCHEMA_ALLOW_QUOTED && quoted ) {
+ if ( **sp == '\'' ) {
+ (*sp)++;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return NULL;
+ }
+ }
+ if (flags & LDAP_SCHEMA_SKIP) {
+ res = (char *)start;
+ } else {
+ res = LDAP_MALLOC(len+1);
+ if (!res) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ strncpy(res,start,len);
+ res[len] = '\0';
+ }
+ return(res);
+}
+
+/* Parse a sequence of dot-separated decimal strings */
+int
+ldap_int_parse_ruleid(const char **sp, int *code, const int flags, int *ruleid)
+{
+ *ruleid=0;
+
+ if ( !LDAP_DIGIT(**sp) ) {
+ *code = LDAP_SCHERR_NODIGIT;
+ return -1;
+ }
+ *ruleid = (**sp) - '0';
+ (*sp)++;
+
+ while ( LDAP_DIGIT(**sp) ) {
+ *ruleid *= 10;
+ *ruleid += (**sp) - '0';
+ (*sp)++;
+ }
+
+ return 0;
+}
+
+/* Parse a qdescr or a list of them enclosed in () */
+static char **
+parse_qdescrs(const char **sp, int *code)
+{
+ char ** res;
+ char ** res1;
+ tk_t kind;
+ char * sval;
+ int size;
+ int pos;
+
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ while (1) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_QDESCR ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(char *));
+ if ( !res1 ) {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ parse_whsp(sp);
+ } else {
+ LDAP_VFREE(res);
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return(NULL);
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_QDESCR ) {
+ res = LDAP_CALLOC(2,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+/* Parse a woid */
+static char *
+parse_woid(const char **sp, int *code)
+{
+ char * sval;
+ tk_t kind;
+
+ parse_whsp(sp);
+ kind = get_token(sp, &sval);
+ if ( kind != TK_BAREWORD ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ return NULL;
+ }
+ parse_whsp(sp);
+ return sval;
+}
+
+/* Parse a noidlen */
+static char *
+parse_noidlen(const char **sp, int *code, int *len, int flags)
+{
+ char * sval;
+ const char *savepos;
+ int quoted = 0;
+ int allow_quoted = ( flags & LDAP_SCHEMA_ALLOW_QUOTED );
+ int allow_oidmacro = ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO );
+
+ *len = 0;
+ /* Netscape puts the SYNTAX value in quotes (incorrectly) */
+ if ( allow_quoted && **sp == '\'' ) {
+ quoted = 1;
+ (*sp)++;
+ }
+ savepos = *sp;
+ sval = ldap_int_parse_numericoid(sp, code, 0);
+ if ( !sval ) {
+ if ( allow_oidmacro
+ && *sp == savepos
+ && *code == LDAP_SCHERR_NODIGIT )
+ {
+ if ( get_token(sp, &sval) != TK_BAREWORD ) {
+ if ( sval != NULL ) {
+ LDAP_FREE(sval);
+ }
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+ }
+ if ( **sp == '{' /*}*/ ) {
+ (*sp)++;
+ *len = atoi(*sp);
+ while ( LDAP_DIGIT(**sp) )
+ (*sp)++;
+ if ( **sp != /*{*/ '}' ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ return NULL;
+ }
+ (*sp)++;
+ }
+ if ( allow_quoted && quoted ) {
+ if ( **sp == '\'' ) {
+ (*sp)++;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ return NULL;
+ }
+ }
+ return sval;
+}
+
+/*
+ * Next routine will accept a qdstring in place of an oid if
+ * allow_quoted is set. This is necessary to interoperate with
+ * Netscape Directory server that will improperly quote each oid (at
+ * least those of the descr kind) in the SUP clause.
+ */
+
+/* Parse a woid or a $-separated list of them enclosed in () */
+static char **
+parse_oids(const char **sp, int *code, const int allow_quoted)
+{
+ char ** res;
+ char ** res1;
+ tk_t kind;
+ char * sval;
+ int size;
+ int pos;
+
+ /*
+ * Strictly speaking, doing this here accepts whsp before the
+ * ( at the begining of an oidlist, but this is harmless. Also,
+ * we are very liberal in what we accept as an OID. Maybe
+ * refine later.
+ */
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_LEFTPAREN ) {
+ /* Let's presume there will be at least 2 entries */
+ size = 3;
+ res = LDAP_CALLOC(3,sizeof(char *));
+ if ( !res ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ pos = 0;
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else if ( kind == TK_RIGHTPAREN ) {
+ /* FIXME: be liberal in what we accept... */
+ parse_whsp(sp);
+ LDAP_FREE(res);
+ return NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ while (1) {
+ kind = get_token(sp,&sval);
+ if ( kind == TK_RIGHTPAREN )
+ break;
+ if ( kind == TK_DOLLAR ) {
+ parse_whsp(sp);
+ kind = get_token(sp,&sval);
+ if ( kind == TK_BAREWORD ||
+ ( allow_quoted &&
+ kind == TK_QDSTRING ) ) {
+ if ( pos == size-2 ) {
+ size++;
+ res1 = LDAP_REALLOC(res,size*sizeof(char *));
+ if ( !res1 ) {
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return(NULL);
+ }
+ res = res1;
+ }
+ res[pos++] = sval;
+ res[pos] = NULL;
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ parse_whsp(sp);
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ LDAP_FREE(sval);
+ LDAP_VFREE(res);
+ return NULL;
+ }
+ }
+ parse_whsp(sp);
+ return(res);
+ } else if ( kind == TK_BAREWORD ||
+ ( allow_quoted && kind == TK_QDSTRING ) ) {
+ res = LDAP_CALLOC(2,sizeof(char *));
+ if ( !res ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ res[0] = sval;
+ res[1] = NULL;
+ parse_whsp(sp);
+ return res;
+ } else {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_BADNAME;
+ return NULL;
+ }
+}
+
+static int
+add_extension(LDAPSchemaExtensionItem ***extensions,
+ char * name, char ** values)
+{
+ int n;
+ LDAPSchemaExtensionItem **tmp, *ext;
+
+ ext = LDAP_CALLOC(1, sizeof(LDAPSchemaExtensionItem));
+ if ( !ext )
+ return 1;
+ ext->lsei_name = name;
+ ext->lsei_values = values;
+
+ if ( !*extensions ) {
+ *extensions =
+ LDAP_CALLOC(2, sizeof(LDAPSchemaExtensionItem *));
+ if ( !*extensions ) {
+ LDAP_FREE( ext );
+ return 1;
+ }
+ n = 0;
+ } else {
+ for ( n=0; (*extensions)[n] != NULL; n++ )
+ ;
+ tmp = LDAP_REALLOC(*extensions,
+ (n+2)*sizeof(LDAPSchemaExtensionItem *));
+ if ( !tmp ) {
+ LDAP_FREE( ext );
+ return 1;
+ }
+ *extensions = tmp;
+ }
+ (*extensions)[n] = ext;
+ (*extensions)[n+1] = NULL;
+ return 0;
+}
+
+static void
+free_extensions(LDAPSchemaExtensionItem **extensions)
+{
+ LDAPSchemaExtensionItem **ext;
+
+ if ( extensions ) {
+ for ( ext = extensions; *ext != NULL; ext++ ) {
+ LDAP_FREE((*ext)->lsei_name);
+ LDAP_VFREE((*ext)->lsei_values);
+ LDAP_FREE(*ext);
+ }
+ LDAP_FREE(extensions);
+ }
+}
+
+void
+ldap_syntax_free( LDAPSyntax * syn )
+{
+ if ( !syn ) return;
+ LDAP_FREE(syn->syn_oid);
+ if (syn->syn_names) LDAP_VFREE(syn->syn_names);
+ if (syn->syn_desc) LDAP_FREE(syn->syn_desc);
+ free_extensions(syn->syn_extensions);
+ LDAP_FREE(syn);
+}
+
+LDAPSyntax *
+ldap_str2syntax( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ LDAPSyntax * syn;
+ char ** ext_vals;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ syn = LDAP_CALLOC(1,sizeof(LDAPSyntax));
+
+ if ( !syn ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ LDAP_FREE(sval);
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ syn->syn_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !syn->syn_oid ) {
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_syntax_free(syn);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return syn;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return(NULL);
+ }
+ seen_name = 1;
+ syn->syn_names = parse_qdescrs(&ss,code);
+ if ( !syn->syn_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ syn->syn_desc = sval;
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ if ( add_extension(&syn->syn_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_syntax_free(syn);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_matchingrule_free( LDAPMatchingRule * mr )
+{
+ if (!mr) return;
+ LDAP_FREE(mr->mr_oid);
+ if (mr->mr_names) LDAP_VFREE(mr->mr_names);
+ if (mr->mr_desc) LDAP_FREE(mr->mr_desc);
+ if (mr->mr_syntax_oid) LDAP_FREE(mr->mr_syntax_oid);
+ free_extensions(mr->mr_extensions);
+ LDAP_FREE(mr);
+}
+
+LDAPMatchingRule *
+ldap_str2matchingrule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_syntax = 0;
+ LDAPMatchingRule * mr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ mr = LDAP_CALLOC(1,sizeof(LDAPMatchingRule));
+
+ if ( !mr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ savepos = ss;
+ mr->mr_oid = ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mr->mr_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SYNTAX") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else {
+ /* Non-numerical OID, ignore */
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_syntax ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ return mr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_name = 1;
+ mr->mr_names = parse_qdescrs(&ss,code);
+ if ( !mr->mr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ mr->mr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ mr->mr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SYNTAX") ) {
+ LDAP_FREE(sval);
+ if ( seen_syntax ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return(NULL);
+ }
+ seen_syntax = 1;
+ parse_whsp(&ss);
+ mr->mr_syntax_oid =
+ ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mr->mr_syntax_oid ) {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ if ( add_extension(&mr->mr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingrule_free(mr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_matchingruleuse_free( LDAPMatchingRuleUse * mru )
+{
+ if (!mru) return;
+ LDAP_FREE(mru->mru_oid);
+ if (mru->mru_names) LDAP_VFREE(mru->mru_names);
+ if (mru->mru_desc) LDAP_FREE(mru->mru_desc);
+ if (mru->mru_applies_oids) LDAP_VFREE(mru->mru_applies_oids);
+ free_extensions(mru->mru_extensions);
+ LDAP_FREE(mru);
+}
+
+LDAPMatchingRuleUse *
+ldap_str2matchingruleuse( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_applies = 0;
+ LDAPMatchingRuleUse * mru;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ mru = LDAP_CALLOC(1,sizeof(LDAPMatchingRuleUse));
+
+ if ( !mru ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+
+ parse_whsp(&ss);
+ savepos = ss;
+ mru->mru_oid = ldap_int_parse_numericoid(&ss,code,flags);
+ if ( !mru->mru_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_NO_OID ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "APPLIES") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else {
+ /* Non-numerical OID, ignore */
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_applies ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ return mru;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_name = 1;
+ mru->mru_names = parse_qdescrs(&ss,code);
+ if ( !mru->mru_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ mru->mru_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ mru->mru_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"APPLIES") ) {
+ LDAP_FREE(sval);
+ if ( seen_applies ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return(NULL);
+ }
+ seen_applies = 1;
+ mru->mru_applies_oids = parse_oids(&ss,
+ code,
+ flags);
+ if ( !mru->mru_applies_oids && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ if ( add_extension(&mru->mru_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_matchingruleuse_free(mru);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_attributetype_free(LDAPAttributeType * at)
+{
+ if (!at) return;
+ LDAP_FREE(at->at_oid);
+ if (at->at_names) LDAP_VFREE(at->at_names);
+ if (at->at_desc) LDAP_FREE(at->at_desc);
+ if (at->at_sup_oid) LDAP_FREE(at->at_sup_oid);
+ if (at->at_equality_oid) LDAP_FREE(at->at_equality_oid);
+ if (at->at_ordering_oid) LDAP_FREE(at->at_ordering_oid);
+ if (at->at_substr_oid) LDAP_FREE(at->at_substr_oid);
+ if (at->at_syntax_oid) LDAP_FREE(at->at_syntax_oid);
+ free_extensions(at->at_extensions);
+ LDAP_FREE(at);
+}
+
+LDAPAttributeType *
+ldap_str2attributetype( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_sup = 0;
+ int seen_equality = 0;
+ int seen_ordering = 0;
+ int seen_substr = 0;
+ int seen_syntax = 0;
+ int seen_usage = 0;
+ LDAPAttributeType * at;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ at = LDAP_CALLOC(1,sizeof(LDAPAttributeType));
+
+ if ( !at ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ at->at_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !at->at_oid ) {
+ if ( ( flags & ( LDAP_SCHEMA_ALLOW_NO_OID
+ | LDAP_SCHEMA_ALLOW_OID_MACRO ) )
+ && (ss == savepos) )
+ {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SUP") ||
+ !strcasecmp(sval, "EQUALITY") ||
+ !strcasecmp(sval, "ORDERING") ||
+ !strcasecmp(sval, "SUBSTR") ||
+ !strcasecmp(sval, "SYNTAX") ||
+ !strcasecmp(sval, "SINGLE-VALUE") ||
+ !strcasecmp(sval, "COLLECTIVE") ||
+ !strcasecmp(sval, "NO-USER-MODIFICATION") ||
+ !strcasecmp(sval, "USAGE") ||
+ !strncasecmp(sval, "X-", 2) )
+ {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags
+ & LDAP_SCHEMA_ALLOW_OID_MACRO)
+ {
+ /* Non-numerical OID ... */
+ int len = ss-savepos;
+ at->at_oid = LDAP_MALLOC(len+1);
+ strncpy(at->at_oid, savepos, len);
+ at->at_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal and accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_attributetype_free(at);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return at;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_name = 1;
+ at->at_names = parse_qdescrs(&ss,code);
+ if ( !at->at_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ at->at_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ at->at_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SUP") ) {
+ LDAP_FREE(sval);
+ if ( seen_sup ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_sup = 1;
+ at->at_sup_oid = parse_woid(&ss,code);
+ if ( !at->at_sup_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"EQUALITY") ) {
+ LDAP_FREE(sval);
+ if ( seen_equality ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_equality = 1;
+ at->at_equality_oid = parse_woid(&ss,code);
+ if ( !at->at_equality_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"ORDERING") ) {
+ LDAP_FREE(sval);
+ if ( seen_ordering ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_ordering = 1;
+ at->at_ordering_oid = parse_woid(&ss,code);
+ if ( !at->at_ordering_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"SUBSTR") ) {
+ LDAP_FREE(sval);
+ if ( seen_substr ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_substr = 1;
+ at->at_substr_oid = parse_woid(&ss,code);
+ if ( !at->at_substr_oid ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"SYNTAX") ) {
+ LDAP_FREE(sval);
+ if ( seen_syntax ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_syntax = 1;
+ parse_whsp(&ss);
+ savepos = ss;
+ at->at_syntax_oid =
+ parse_noidlen(&ss,
+ code,
+ &at->at_syntax_len,
+ flags);
+ if ( !at->at_syntax_oid ) {
+ if ( flags & LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ kind = get_token(&ss,&sval);
+ if (kind == TK_BAREWORD)
+ {
+ char *sp = strchr(sval, '{');
+ at->at_syntax_oid = sval;
+ if (sp)
+ {
+ *sp++ = 0;
+ at->at_syntax_len = atoi(sp);
+ while ( LDAP_DIGIT(*sp) )
+ sp++;
+ if ( *sp != '}' ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ }
+ } else {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SINGLE-VALUE") ) {
+ LDAP_FREE(sval);
+ if ( at->at_single_value ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_single_value = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"COLLECTIVE") ) {
+ LDAP_FREE(sval);
+ if ( at->at_collective ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_collective = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"NO-USER-MODIFICATION") ) {
+ LDAP_FREE(sval);
+ if ( at->at_no_user_mod ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ at->at_no_user_mod = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"USAGE") ) {
+ LDAP_FREE(sval);
+ if ( seen_usage ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return(NULL);
+ }
+ seen_usage = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_BAREWORD ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ if ( !strcasecmp(sval,"userApplications") )
+ at->at_usage =
+ LDAP_SCHEMA_USER_APPLICATIONS;
+ else if ( !strcasecmp(sval,"directoryOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DIRECTORY_OPERATION;
+ else if ( !strcasecmp(sval,"distributedOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DISTRIBUTED_OPERATION;
+ else if ( !strcasecmp(sval,"dSAOperation") )
+ at->at_usage =
+ LDAP_SCHEMA_DSA_OPERATION;
+ else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ LDAP_FREE(sval);
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ if ( add_extension(&at->at_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_attributetype_free(at);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_objectclass_free(LDAPObjectClass * oc)
+{
+ if (!oc) return;
+ LDAP_FREE(oc->oc_oid);
+ if (oc->oc_names) LDAP_VFREE(oc->oc_names);
+ if (oc->oc_desc) LDAP_FREE(oc->oc_desc);
+ if (oc->oc_sup_oids) LDAP_VFREE(oc->oc_sup_oids);
+ if (oc->oc_at_oids_must) LDAP_VFREE(oc->oc_at_oids_must);
+ if (oc->oc_at_oids_may) LDAP_VFREE(oc->oc_at_oids_may);
+ free_extensions(oc->oc_extensions);
+ LDAP_FREE(oc);
+}
+
+LDAPObjectClass *
+ldap_str2objectclass( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_sup = 0;
+ int seen_kind = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ LDAPObjectClass * oc;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ oc = LDAP_CALLOC(1,sizeof(LDAPObjectClass));
+
+ if ( !oc ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ oc->oc_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !oc->oc_oid ) {
+ if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "SUP") ||
+ !strcasecmp(sval, "ABSTRACT") ||
+ !strcasecmp(sval, "STRUCTURAL") ||
+ !strcasecmp(sval, "AUXILIARY") ||
+ !strcasecmp(sval, "MUST") ||
+ !strcasecmp(sval, "MAY") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags &
+ LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ /* Non-numerical OID, ignore */
+ int len = ss-savepos;
+ oc->oc_oid = LDAP_MALLOC(len+1);
+ strncpy(oc->oc_oid, savepos, len);
+ oc->oc_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ *code = 0;
+ } else {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_objectclass_free(oc);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return oc;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_name = 1;
+ oc->oc_names = parse_qdescrs(&ss,code);
+ if ( !oc->oc_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ oc->oc_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ oc->oc_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"SUP") ) {
+ LDAP_FREE(sval);
+ if ( seen_sup ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_sup = 1;
+ oc->oc_sup_oids = parse_oids(&ss,
+ code,
+ flags);
+ if ( !oc->oc_sup_oids && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ } else if ( !strcasecmp(sval,"ABSTRACT") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_ABSTRACT;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"STRUCTURAL") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_STRUCTURAL;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"AUXILIARY") ) {
+ LDAP_FREE(sval);
+ if ( seen_kind ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_kind = 1;
+ oc->oc_kind = LDAP_SCHEMA_AUXILIARY;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_must = 1;
+ oc->oc_at_oids_must = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return(NULL);
+ }
+ seen_may = 1;
+ oc->oc_at_oids_may = parse_oids(&ss,code,0);
+ if ( !oc->oc_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ *code = 0;
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ *code = 0;
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ if ( add_extension(&oc->oc_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_objectclass_free(oc);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_contentrule_free(LDAPContentRule * cr)
+{
+ if (!cr) return;
+ LDAP_FREE(cr->cr_oid);
+ if (cr->cr_names) LDAP_VFREE(cr->cr_names);
+ if (cr->cr_desc) LDAP_FREE(cr->cr_desc);
+ if (cr->cr_oc_oids_aux) LDAP_VFREE(cr->cr_oc_oids_aux);
+ if (cr->cr_at_oids_must) LDAP_VFREE(cr->cr_at_oids_must);
+ if (cr->cr_at_oids_may) LDAP_VFREE(cr->cr_at_oids_may);
+ if (cr->cr_at_oids_not) LDAP_VFREE(cr->cr_at_oids_not);
+ free_extensions(cr->cr_extensions);
+ LDAP_FREE(cr);
+}
+
+LDAPContentRule *
+ldap_str2contentrule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_aux = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ int seen_not = 0;
+ LDAPContentRule * cr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ cr = LDAP_CALLOC(1,sizeof(LDAPContentRule));
+
+ if ( !cr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ cr->cr_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !cr->cr_oid ) {
+ if ( (flags & LDAP_SCHEMA_ALLOW_ALL) && (ss == savepos) ) {
+ /* Backtracking */
+ ss = savepos;
+ kind = get_token(&ss,&sval);
+ if ( kind == TK_BAREWORD ) {
+ if ( !strcasecmp(sval, "NAME") ||
+ !strcasecmp(sval, "DESC") ||
+ !strcasecmp(sval, "OBSOLETE") ||
+ !strcasecmp(sval, "AUX") ||
+ !strcasecmp(sval, "MUST") ||
+ !strcasecmp(sval, "MAY") ||
+ !strcasecmp(sval, "NOT") ||
+ !strncasecmp(sval, "X-", 2) ) {
+ /* Missing OID, backtrack */
+ ss = savepos;
+ } else if ( flags &
+ LDAP_SCHEMA_ALLOW_OID_MACRO ) {
+ /* Non-numerical OID, ignore */
+ int len = ss-savepos;
+ cr->cr_oid = LDAP_MALLOC(len+1);
+ strncpy(cr->cr_oid, savepos, len);
+ cr->cr_oid[len] = 0;
+ }
+ }
+ LDAP_FREE(sval);
+ } else {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_contentrule_free(cr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ return cr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_name = 1;
+ cr->cr_names = parse_qdescrs(&ss,code);
+ if ( !cr->cr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ cr->cr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ cr->cr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"AUX") ) {
+ LDAP_FREE(sval);
+ if ( seen_aux ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_aux = 1;
+ cr->cr_oc_oids_aux = parse_oids(&ss,code,0);
+ if ( !cr->cr_oc_oids_aux ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_must = 1;
+ cr->cr_at_oids_must = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_may = 1;
+ cr->cr_at_oids_may = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"NOT") ) {
+ LDAP_FREE(sval);
+ if ( seen_not ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return(NULL);
+ }
+ seen_not = 1;
+ cr->cr_at_oids_not = parse_oids(&ss,code,0);
+ if ( !cr->cr_at_oids_not && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ if ( add_extension(&cr->cr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_contentrule_free(cr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_structurerule_free(LDAPStructureRule * sr)
+{
+ if (!sr) return;
+ if (sr->sr_names) LDAP_VFREE(sr->sr_names);
+ if (sr->sr_desc) LDAP_FREE(sr->sr_desc);
+ if (sr->sr_nameform) LDAP_FREE(sr->sr_nameform);
+ if (sr->sr_sup_ruleids) LDAP_FREE(sr->sr_sup_ruleids);
+ free_extensions(sr->sr_extensions);
+ LDAP_FREE(sr);
+}
+
+LDAPStructureRule *
+ldap_str2structurerule( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ int ret;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_nameform = 0;
+ LDAPStructureRule * sr;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ sr = LDAP_CALLOC(1,sizeof(LDAPStructureRule));
+
+ if ( !sr ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with a ruleid.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ ret = ldap_int_parse_ruleid(&ss,code,0,&sr->sr_ruleid);
+ if ( ret ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_structurerule_free(sr);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_nameform ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ return sr;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_name = 1;
+ sr->sr_names = parse_qdescrs(&ss,code);
+ if ( !sr->sr_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ sr->sr_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ sr->sr_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"FORM") ) {
+ LDAP_FREE(sval);
+ if ( seen_nameform ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return(NULL);
+ }
+ seen_nameform = 1;
+ sr->sr_nameform = parse_woid(&ss,code);
+ if ( !sr->sr_nameform ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ if ( add_extension(&sr->sr_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_structurerule_free(sr);
+ return NULL;
+ }
+ }
+}
+
+void
+ldap_nameform_free(LDAPNameForm * nf)
+{
+ if (!nf) return;
+ LDAP_FREE(nf->nf_oid);
+ if (nf->nf_names) LDAP_VFREE(nf->nf_names);
+ if (nf->nf_desc) LDAP_FREE(nf->nf_desc);
+ if (nf->nf_objectclass) LDAP_FREE(nf->nf_objectclass);
+ if (nf->nf_at_oids_must) LDAP_VFREE(nf->nf_at_oids_must);
+ if (nf->nf_at_oids_may) LDAP_VFREE(nf->nf_at_oids_may);
+ free_extensions(nf->nf_extensions);
+ LDAP_FREE(nf);
+}
+
+LDAPNameForm *
+ldap_str2nameform( LDAP_CONST char * s,
+ int * code,
+ LDAP_CONST char ** errp,
+ LDAP_CONST unsigned flags )
+{
+ tk_t kind;
+ const char * ss = s;
+ char * sval;
+ int seen_name = 0;
+ int seen_desc = 0;
+ int seen_obsolete = 0;
+ int seen_class = 0;
+ int seen_must = 0;
+ int seen_may = 0;
+ LDAPNameForm * nf;
+ char ** ext_vals;
+ const char * savepos;
+
+ if ( !s ) {
+ *code = LDAP_SCHERR_EMPTY;
+ *errp = "";
+ return NULL;
+ }
+
+ *errp = s;
+ nf = LDAP_CALLOC(1,sizeof(LDAPNameForm));
+
+ if ( !nf ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ return NULL;
+ }
+
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_LEFTPAREN ) {
+ *code = LDAP_SCHERR_NOLEFTPAREN;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+
+ /*
+ * Definitions MUST begin with an OID in the numericoid format.
+ * However, this routine is used by clients to parse the response
+ * from servers and very well known servers will provide an OID
+ * in the wrong format or even no OID at all. We do our best to
+ * extract info from those servers.
+ */
+ parse_whsp(&ss);
+ savepos = ss;
+ nf->nf_oid = ldap_int_parse_numericoid(&ss,code,0);
+ if ( !nf->nf_oid ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+
+ /*
+ * Beyond this point we will be liberal an accept the items
+ * in any order.
+ */
+ while (1) {
+ kind = get_token(&ss,&sval);
+ switch (kind) {
+ case TK_EOS:
+ *code = LDAP_SCHERR_NORIGHTPAREN;
+ *errp = EndOfInput;
+ ldap_nameform_free(nf);
+ return NULL;
+ case TK_RIGHTPAREN:
+ if( !seen_class || !seen_must ) {
+ *code = LDAP_SCHERR_MISSING;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ return nf;
+ case TK_BAREWORD:
+ if ( !strcasecmp(sval,"NAME") ) {
+ LDAP_FREE(sval);
+ if ( seen_name ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_name = 1;
+ nf->nf_names = parse_qdescrs(&ss,code);
+ if ( !nf->nf_names ) {
+ if ( *code != LDAP_SCHERR_OUTOFMEM )
+ *code = LDAP_SCHERR_BADNAME;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"DESC") ) {
+ LDAP_FREE(sval);
+ if ( seen_desc ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_desc = 1;
+ parse_whsp(&ss);
+ kind = get_token(&ss,&sval);
+ if ( kind != TK_QDSTRING ) {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ nf->nf_desc = sval;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OBSOLETE") ) {
+ LDAP_FREE(sval);
+ if ( seen_obsolete ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_obsolete = 1;
+ nf->nf_obsolete = LDAP_SCHEMA_YES;
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"OC") ) {
+ LDAP_FREE(sval);
+ if ( seen_class ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_class = 1;
+ nf->nf_objectclass = parse_woid(&ss,code);
+ if ( !nf->nf_objectclass ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else if ( !strcasecmp(sval,"MUST") ) {
+ LDAP_FREE(sval);
+ if ( seen_must ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_must = 1;
+ nf->nf_at_oids_must = parse_oids(&ss,code,0);
+ if ( !nf->nf_at_oids_must && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( !strcasecmp(sval,"MAY") ) {
+ LDAP_FREE(sval);
+ if ( seen_may ) {
+ *code = LDAP_SCHERR_DUPOPT;
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return(NULL);
+ }
+ seen_may = 1;
+ nf->nf_at_oids_may = parse_oids(&ss,code,0);
+ if ( !nf->nf_at_oids_may && *code != LDAP_SUCCESS ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ parse_whsp(&ss);
+ } else if ( sval[0] == 'X' && sval[1] == '-' ) {
+ /* Should be parse_qdstrings */
+ ext_vals = parse_qdescrs(&ss, code);
+ if ( !ext_vals ) {
+ *errp = ss;
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ if ( add_extension(&nf->nf_extensions,
+ sval, ext_vals) ) {
+ *code = LDAP_SCHERR_OUTOFMEM;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ } else {
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ break;
+ default:
+ *code = LDAP_SCHERR_UNEXPTOKEN;
+ *errp = ss;
+ LDAP_FREE(sval);
+ ldap_nameform_free(nf);
+ return NULL;
+ }
+ }
+}
+
+static char *const err2text[] = {
+ N_("Success"),
+ N_("Out of memory"),
+ N_("Unexpected token"),
+ N_("Missing opening parenthesis"),
+ N_("Missing closing parenthesis"),
+ N_("Expecting digit"),
+ N_("Expecting a name"),
+ N_("Bad description"),
+ N_("Bad superiors"),
+ N_("Duplicate option"),
+ N_("Unexpected end of data"),
+ N_("Missing required field"),
+ N_("Out of order field")
+};
+
+char *
+ldap_scherr2str(int code)
+{
+ if ( code < 0 || code >= (int)(sizeof(err2text)/sizeof(char *)) ) {
+ return _("Unknown error");
+ } else {
+ return _(err2text[code]);
+ }
+}
diff --git a/libraries/libldap/search.c b/libraries/libldap/search.c
new file mode 100644
index 0000000..a148fb5
--- /dev/null
+++ b/libraries/libldap/search.c
@@ -0,0 +1,545 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+/*
+ * ldap_search_ext - initiate an ldap search operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * base DN of the base object
+ * scope the search scope - one of
+ * LDAP_SCOPE_BASE (baseObject),
+ * LDAP_SCOPE_ONELEVEL (oneLevel),
+ * LDAP_SCOPE_SUBTREE (subtree), or
+ * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
+ * filter a string containing the search filter
+ * (e.g., "(|(cn=bob)(sn=bob))")
+ * attrs list of attribute types to return for matches
+ * attrsonly 1 => attributes only 0 => attributes and values
+ *
+ * Example:
+ * char *attrs[] = { "mail", "title", 0 };
+ * ldap_search_ext( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
+ * attrs, attrsonly, sctrls, ctrls, timeout, sizelimit,
+ * &msgid );
+ */
+int
+ldap_search_ext(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int *msgidp )
+{
+ return ldap_pvt_search( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timeout, sizelimit, -1, msgidp );
+}
+
+int
+ldap_pvt_search(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ int *msgidp )
+{
+ int rc;
+ BerElement *ber;
+ int timelimit;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_search_ext\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ /*
+ * if timeout is provided, both tv_sec and tv_usec must
+ * not be zero
+ */
+ if( timeout != NULL ) {
+ if( timeout->tv_sec == 0 && timeout->tv_usec == 0 ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* timelimit must be non-zero if timeout is provided */
+ timelimit = timeout->tv_sec != 0 ? timeout->tv_sec : 1;
+
+ } else {
+ /* no timeout, no timelimit */
+ timelimit = -1;
+ }
+
+ ber = ldap_build_search_req( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timelimit, sizelimit, deref, &id );
+
+ if ( ber == NULL ) {
+ return ld->ld_errno;
+ }
+
+
+ /* send the message */
+ *msgidp = ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id );
+
+ if( *msgidp < 0 )
+ return ld->ld_errno;
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_search_ext_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ LDAPMessage **res )
+{
+ return ldap_pvt_search_s( ld, base, scope, filter, attrs,
+ attrsonly, sctrls, cctrls, timeout, sizelimit, -1, res );
+}
+
+int
+ldap_pvt_search_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct timeval *timeout,
+ int sizelimit,
+ int deref,
+ LDAPMessage **res )
+{
+ int rc;
+ int msgid;
+
+ *res = NULL;
+
+ rc = ldap_pvt_search( ld, base, scope, filter, attrs, attrsonly,
+ sctrls, cctrls, timeout, sizelimit, deref, &msgid );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ rc = ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res );
+
+ if( rc <= 0 ) {
+ /* error(-1) or timeout(0) */
+ if ( ld->ld_errno == LDAP_TIMEOUT ) {
+ /* cleanup request */
+ (void) ldap_abandon( ld, msgid );
+ ld->ld_errno = LDAP_TIMEOUT;
+ }
+ return( ld->ld_errno );
+ }
+
+ if( rc == LDAP_RES_SEARCH_REFERENCE || rc == LDAP_RES_INTERMEDIATE ) {
+ return( ld->ld_errno );
+ }
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+/*
+ * ldap_search - initiate an ldap search operation.
+ *
+ * Parameters:
+ *
+ * ld LDAP descriptor
+ * base DN of the base object
+ * scope the search scope - one of
+ * LDAP_SCOPE_BASE (baseObject),
+ * LDAP_SCOPE_ONELEVEL (oneLevel),
+ * LDAP_SCOPE_SUBTREE (subtree), or
+ * LDAP_SCOPE_SUBORDINATE (children) -- OpenLDAP extension
+ * filter a string containing the search filter
+ * (e.g., "(|(cn=bob)(sn=bob))")
+ * attrs list of attribute types to return for matches
+ * attrsonly 1 => attributes only 0 => attributes and values
+ *
+ * Example:
+ * char *attrs[] = { "mail", "title", 0 };
+ * msgid = ldap_search( ld, "dc=example,dc=com", LDAP_SCOPE_SUBTREE, "cn~=bob",
+ * attrs, attrsonly );
+ */
+int
+ldap_search(
+ LDAP *ld, LDAP_CONST char *base, int scope, LDAP_CONST char *filter,
+ char **attrs, int attrsonly )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_search\n", 0, 0, 0 );
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ ber = ldap_build_search_req( ld, base, scope, filter, attrs,
+ attrsonly, NULL, NULL, -1, -1, -1, &id );
+
+ if ( ber == NULL ) {
+ return( -1 );
+ }
+
+
+ /* send the message */
+ return ( ldap_send_initial_request( ld, LDAP_REQ_SEARCH, base, ber, id ));
+}
+
+
+BerElement *
+ldap_build_search_req(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ ber_int_t scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ ber_int_t attrsonly,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ ber_int_t timelimit,
+ ber_int_t sizelimit,
+ ber_int_t deref,
+ ber_int_t *idp)
+{
+ BerElement *ber;
+ int err;
+
+ /*
+ * Create the search request. It looks like this:
+ * SearchRequest := [APPLICATION 3] SEQUENCE {
+ * baseObject DistinguishedName,
+ * scope ENUMERATED {
+ * baseObject (0),
+ * singleLevel (1),
+ * wholeSubtree (2)
+ * },
+ * derefAliases ENUMERATED {
+ * neverDerefaliases (0),
+ * derefInSearching (1),
+ * derefFindingBaseObj (2),
+ * alwaysDerefAliases (3)
+ * },
+ * sizelimit INTEGER (0 .. 65535),
+ * timelimit INTEGER (0 .. 65535),
+ * attrsOnly BOOLEAN,
+ * filter Filter,
+ * attributes SEQUENCE OF AttributeType
+ * }
+ * wrapped in an ldap message.
+ */
+
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( NULL );
+ }
+
+ if ( base == NULL ) {
+ /* no base provided, use session default base */
+ base = ld->ld_options.ldo_defbase;
+
+ if ( base == NULL ) {
+ /* no session default base, use top */
+ base = "";
+ }
+ }
+
+ LDAP_NEXT_MSGID( ld, *idp );
+#ifdef LDAP_CONNECTIONLESS
+ if ( LDAP_IS_UDP(ld) ) {
+ struct sockaddr_storage sa = {0};
+ /* dummy, filled with ldo_peer in request.c */
+ err = ber_write( ber, (char *) &sa, sizeof( sa ), 0 );
+ }
+ if ( LDAP_IS_UDP(ld) && ld->ld_options.ldo_version == LDAP_VERSION2) {
+ char *dn = ld->ld_options.ldo_cldapdn;
+ if (!dn) dn = "";
+ err = ber_printf( ber, "{ist{seeiib", *idp, dn,
+ LDAP_REQ_SEARCH, base, (ber_int_t) scope,
+ (deref < 0) ? ld->ld_deref : deref,
+ (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
+ (timelimit < 0) ? ld->ld_timelimit : timelimit,
+ attrsonly );
+ } else
+#endif
+ {
+ err = ber_printf( ber, "{it{seeiib", *idp,
+ LDAP_REQ_SEARCH, base, (ber_int_t) scope,
+ (deref < 0) ? ld->ld_deref : deref,
+ (sizelimit < 0) ? ld->ld_sizelimit : sizelimit,
+ (timelimit < 0) ? ld->ld_timelimit : timelimit,
+ attrsonly );
+ }
+
+ if ( err == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if( filter == NULL ) {
+ filter = "(objectclass=*)";
+ }
+
+ err = ldap_pvt_put_filter( ber, filter );
+
+ if ( err == -1 ) {
+ ld->ld_errno = LDAP_FILTER_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+#ifdef LDAP_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_ARGS ) {
+ char buf[ BUFSIZ ], *ptr = " *";
+
+ if ( attrs != NULL ) {
+ int i, len, rest = sizeof( buf );
+
+ for ( i = 0; attrs[ i ] != NULL && rest > 0; i++ ) {
+ ptr = &buf[ sizeof( buf ) - rest ];
+ len = snprintf( ptr, rest, " %s", attrs[ i ] );
+ rest -= (len >= 0 ? len : (int) sizeof( buf ));
+ }
+
+ if ( rest <= 0 ) {
+ AC_MEMCPY( &buf[ sizeof( buf ) - STRLENOF( "...(truncated)" ) - 1 ],
+ "...(truncated)", STRLENOF( "...(truncated)" ) + 1 );
+ }
+ ptr = buf;
+ }
+
+ Debug( LDAP_DEBUG_ARGS, "ldap_build_search_req ATTRS:%s\n", ptr, 0,0 );
+ }
+#endif /* LDAP_DEBUG */
+
+ if ( ber_printf( ber, /*{*/ "{v}N}", attrs ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}" ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( NULL );
+ }
+
+ return( ber );
+}
+
+int
+ldap_search_st(
+ LDAP *ld, LDAP_CONST char *base, int scope,
+ LDAP_CONST char *filter, char **attrs,
+ int attrsonly, struct timeval *timeout, LDAPMessage **res )
+{
+ int msgid;
+
+ *res = NULL;
+
+ if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
+ == -1 )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, timeout, res ) == -1 || !*res )
+ return( ld->ld_errno );
+
+ if ( ld->ld_errno == LDAP_TIMEOUT ) {
+ (void) ldap_abandon( ld, msgid );
+ ld->ld_errno = LDAP_TIMEOUT;
+ return( ld->ld_errno );
+ }
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+int
+ldap_search_s(
+ LDAP *ld,
+ LDAP_CONST char *base,
+ int scope,
+ LDAP_CONST char *filter,
+ char **attrs,
+ int attrsonly,
+ LDAPMessage **res )
+{
+ int msgid;
+
+ *res = NULL;
+
+ if ( (msgid = ldap_search( ld, base, scope, filter, attrs, attrsonly ))
+ == -1 )
+ return( ld->ld_errno );
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, res ) == -1 || !*res )
+ return( ld->ld_errno );
+
+ return( ldap_result2error( ld, *res, 0 ) );
+}
+
+static char escape[128] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 1, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 1
+};
+#define NEEDFLTESCAPE(c) ((c) & 0x80 || escape[ (unsigned)(c) ])
+
+/*
+ * compute the length of the escaped value
+ */
+ber_len_t
+ldap_bv2escaped_filter_value_len( struct berval *in )
+{
+ ber_len_t i, l;
+
+ assert( in != NULL );
+
+ if ( in->bv_len == 0 ) {
+ return 0;
+ }
+
+ for( l = 0, i = 0; i < in->bv_len; l++, i++ ) {
+ char c = in->bv_val[ i ];
+ if ( NEEDFLTESCAPE( c ) ) {
+ l += 2;
+ }
+ }
+
+ return l;
+}
+
+int
+ldap_bv2escaped_filter_value( struct berval *in, struct berval *out )
+{
+ return ldap_bv2escaped_filter_value_x( in, out, 0, NULL );
+}
+
+int
+ldap_bv2escaped_filter_value_x( struct berval *in, struct berval *out, int inplace, void *ctx )
+{
+ ber_len_t i, l;
+
+ assert( in != NULL );
+ assert( out != NULL );
+
+ BER_BVZERO( out );
+
+ if ( in->bv_len == 0 ) {
+ return 0;
+ }
+
+ /* assume we'll escape everything */
+ l = ldap_bv2escaped_filter_value_len( in );
+ if ( l == in->bv_len ) {
+ if ( inplace ) {
+ *out = *in;
+ } else {
+ ber_dupbv( out, in );
+ }
+ return 0;
+ }
+ out->bv_val = LDAP_MALLOCX( l + 1, ctx );
+ if ( out->bv_val == NULL ) {
+ return -1;
+ }
+
+ for ( i = 0; i < in->bv_len; i++ ) {
+ char c = in->bv_val[ i ];
+ if ( NEEDFLTESCAPE( c ) ) {
+ assert( out->bv_len < l - 2 );
+ out->bv_val[out->bv_len++] = '\\';
+ out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & (c>>4)];
+ out->bv_val[out->bv_len++] = "0123456789ABCDEF"[0x0f & c];
+
+ } else {
+ assert( out->bv_len < l );
+ out->bv_val[out->bv_len++] = c;
+ }
+ }
+
+ out->bv_val[out->bv_len] = '\0';
+
+ return 0;
+}
+
diff --git a/libraries/libldap/sort.c b/libraries/libldap/sort.c
new file mode 100644
index 0000000..27ea682
--- /dev/null
+++ b/libraries/libldap/sort.c
@@ -0,0 +1,183 @@
+/* sort.c -- LDAP library entry and value sort routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1994 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of Michigan at Ann Arbor. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+
+#include "ldap-int.h"
+
+struct entrything {
+ char **et_vals;
+ LDAPMessage *et_msg;
+ int (*et_cmp_fn) LDAP_P((const char *a, const char *b));
+};
+
+static int et_cmp LDAP_P(( const void *aa, const void *bb));
+
+
+int
+ldap_sort_strcasecmp(
+ LDAP_CONST void *a,
+ LDAP_CONST void *b
+)
+{
+ return( strcasecmp( *(char *const *)a, *(char *const *)b ) );
+}
+
+static int
+et_cmp(
+ const void *aa,
+ const void *bb
+)
+{
+ int i, rc;
+ const struct entrything *a = (const struct entrything *)aa;
+ const struct entrything *b = (const struct entrything *)bb;
+
+ if ( a->et_vals == NULL && b->et_vals == NULL )
+ return( 0 );
+ if ( a->et_vals == NULL )
+ return( -1 );
+ if ( b->et_vals == NULL )
+ return( 1 );
+
+ for ( i = 0; a->et_vals[i] && b->et_vals[i]; i++ ) {
+ if ( (rc = a->et_cmp_fn( a->et_vals[i], b->et_vals[i] )) != 0 ) {
+ return( rc );
+ }
+ }
+
+ if ( a->et_vals[i] == NULL && b->et_vals[i] == NULL )
+ return( 0 );
+ if ( a->et_vals[i] == NULL )
+ return( -1 );
+ return( 1 );
+}
+
+int
+ldap_sort_entries(
+ LDAP *ld,
+ LDAPMessage **chain,
+ LDAP_CONST char *attr, /* NULL => sort by DN */
+ int (*cmp) (LDAP_CONST char *, LDAP_CONST char *)
+)
+{
+ int i, count = 0;
+ struct entrything *et;
+ LDAPMessage *e, *ehead = NULL, *etail = NULL;
+ LDAPMessage *ohead = NULL, *otail = NULL;
+ LDAPMessage **ep;
+
+ assert( ld != NULL );
+
+ /* Separate entries from non-entries */
+ for ( e = *chain; e; e=e->lm_chain ) {
+ if ( e->lm_msgtype == LDAP_RES_SEARCH_ENTRY ) {
+ count++;
+ if ( !ehead ) ehead = e;
+ if ( etail ) etail->lm_chain = e;
+ etail = e;
+ } else {
+ if ( !ohead ) ohead = e;
+ if ( otail ) otail->lm_chain = e;
+ otail = e;
+ }
+ }
+
+ if ( count < 2 ) {
+ /* zero or one entries -- already sorted! */
+ if ( ehead ) {
+ etail->lm_chain = ohead;
+ *chain = ehead;
+ } else {
+ *chain = ohead;
+ }
+ return 0;
+ }
+
+ if ( (et = (struct entrything *) LDAP_MALLOC( count *
+ sizeof(struct entrything) )) == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return( -1 );
+ }
+
+ e = ehead;
+ for ( i = 0; i < count; i++ ) {
+ et[i].et_cmp_fn = cmp;
+ et[i].et_msg = e;
+ if ( attr == NULL ) {
+ char *dn;
+
+ dn = ldap_get_dn( ld, e );
+ et[i].et_vals = ldap_explode_dn( dn, 1 );
+ LDAP_FREE( dn );
+ } else {
+ et[i].et_vals = ldap_get_values( ld, e, attr );
+ }
+
+ e = e->lm_chain;
+ }
+
+ qsort( et, count, sizeof(struct entrything), et_cmp );
+
+ ep = chain;
+ for ( i = 0; i < count; i++ ) {
+ *ep = et[i].et_msg;
+ ep = &(*ep)->lm_chain;
+
+ LDAP_VFREE( et[i].et_vals );
+ }
+ *ep = ohead;
+ (*chain)->lm_chain_tail = otail ? otail : etail;
+
+ LDAP_FREE( (char *) et );
+
+ return( 0 );
+}
+
+int
+ldap_sort_values(
+ LDAP *ld,
+ char **vals,
+ int (*cmp) (LDAP_CONST void *, LDAP_CONST void *)
+)
+{
+ int nel;
+
+ for ( nel = 0; vals[nel] != NULL; nel++ )
+ ; /* NULL */
+
+ qsort( vals, nel, sizeof(char *), cmp );
+
+ return( 0 );
+}
diff --git a/libraries/libldap/sortctrl.c b/libraries/libldap/sortctrl.c
new file mode 100644
index 0000000..fb79555
--- /dev/null
+++ b/libraries/libldap/sortctrl.c
@@ -0,0 +1,552 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ */
+/* Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_MATCHRULE_IDENTIFIER 0x80L
+#define LDAP_REVERSEORDER_IDENTIFIER 0x81L
+#define LDAP_ATTRTYPES_IDENTIFIER 0x80L
+
+
+
+/* ---------------------------------------------------------------------------
+ countKeys
+
+ Internal function to determine the number of keys in the string.
+
+ keyString (IN) String of items separated by whitespace.
+ ---------------------------------------------------------------------------*/
+
+static int countKeys(char *keyString)
+{
+ char *p = keyString;
+ int count = 0;
+
+ for (;;)
+ {
+ while (LDAP_SPACE(*p)) /* Skip leading whitespace */
+ p++;
+
+ if (*p == '\0') /* End of string? */
+ return count;
+
+ count++; /* Found start of a key */
+
+ while (!LDAP_SPACE(*p)) /* Skip till next space or end of string. */
+ if (*p++ == '\0')
+ return count;
+ }
+}
+
+
+/* ---------------------------------------------------------------------------
+ readNextKey
+
+ Internal function to parse the next sort key in the string.
+ Allocate an LDAPSortKey structure and initialize it with
+ attribute name, reverse flag, and matching rule OID.
+
+ Each sort key in the string has the format:
+ [whitespace][-]attribute[:[OID]]
+
+ pNextKey (IN/OUT) Points to the next key in the sortkey string to parse.
+ The pointer is updated to point to the next character
+ after the sortkey being parsed.
+
+ key (OUT) Points to the address of an LDAPSortKey stucture
+ which has been allocated by this routine and
+ initialized with information from the next sortkey.
+ ---------------------------------------------------------------------------*/
+
+static int readNextKey( char **pNextKey, LDAPSortKey **key)
+{
+ char *p = *pNextKey;
+ int rev = 0;
+ char *attrStart;
+ int attrLen;
+ char *oidStart = NULL;
+ int oidLen = 0;
+
+ /* Skip leading white space. */
+ while (LDAP_SPACE(*p))
+ p++;
+
+ if (*p == '-') /* Check if the reverse flag is present. */
+ {
+ rev=1;
+ p++;
+ }
+
+ /* We're now positioned at the start of the attribute. */
+ attrStart = p;
+
+ /* Get the length of the attribute until the next whitespace or ":". */
+ attrLen = strcspn(p, " \t:");
+ p += attrLen;
+
+ if (attrLen == 0) /* If no attribute name was present, quit. */
+ return LDAP_PARAM_ERROR;
+
+ if (*p == ':')
+ {
+ oidStart = ++p; /* Start of the OID, after the colon */
+ oidLen = strcspn(p, " \t"); /* Get length of OID till next whitespace */
+ p += oidLen;
+ }
+
+ *pNextKey = p; /* Update argument to point to next key */
+
+ /* Allocate an LDAPSortKey structure */
+ *key = LDAP_MALLOC(sizeof(LDAPSortKey));
+ if (*key == NULL) return LDAP_NO_MEMORY;
+
+ /* Allocate memory for the attribute and copy to it. */
+ (*key)->attributeType = LDAP_MALLOC(attrLen+1);
+ if ((*key)->attributeType == NULL) {
+ LDAP_FREE(*key);
+ return LDAP_NO_MEMORY;
+ }
+
+ strncpy((*key)->attributeType, attrStart, attrLen);
+ (*key)->attributeType[attrLen] = 0;
+
+ /* If present, allocate memory for the OID and copy to it. */
+ if (oidLen) {
+ (*key)->orderingRule = LDAP_MALLOC(oidLen+1);
+ if ((*key)->orderingRule == NULL) {
+ LDAP_FREE((*key)->attributeType);
+ LDAP_FREE(*key);
+ return LDAP_NO_MEMORY;
+ }
+ strncpy((*key)->orderingRule, oidStart, oidLen);
+ (*key)->orderingRule[oidLen] = 0;
+
+ } else {
+ (*key)->orderingRule = NULL;
+ }
+
+ (*key)->reverseOrder = rev;
+
+ return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_keylist
+
+ Create an array of pointers to LDAPSortKey structures, containing the
+ information specified by the string representation of one or more
+ sort keys.
+
+ sortKeyList (OUT) Points to a null-terminated array of pointers to
+ LDAPSortKey structures allocated by this routine.
+ This memory SHOULD be freed by the calling program
+ using ldap_free_sort_keylist().
+
+ keyString (IN) Points to a string of one or more sort keys.
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_keylist ( LDAPSortKey ***sortKeyList, char *keyString )
+{
+ int numKeys, rc, i;
+ char *nextKey;
+ LDAPSortKey **keyList = NULL;
+
+ assert( sortKeyList != NULL );
+ assert( keyString != NULL );
+
+ *sortKeyList = NULL;
+
+ /* Determine the number of sort keys so we can allocate memory. */
+ if (( numKeys = countKeys(keyString)) == 0) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ /* Allocate the array of pointers. Initialize to NULL. */
+ keyList=(LDAPSortKey**)LBER_CALLOC(numKeys+1, sizeof(LDAPSortKey*));
+ if ( keyList == NULL) return LDAP_NO_MEMORY;
+
+ /* For each sort key in the string, create an LDAPSortKey structure
+ and add it to the list.
+ */
+ nextKey = keyString; /* Points to the next key in the string */
+ for (i=0; i < numKeys; i++) {
+ rc = readNextKey(&nextKey, &keyList[i]);
+
+ if (rc != LDAP_SUCCESS) {
+ ldap_free_sort_keylist(keyList);
+ return rc;
+ }
+ }
+
+ *sortKeyList = keyList;
+ return LDAP_SUCCESS;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_free_sort_keylist
+
+ Frees the sort key structures created by ldap_create_sort_keylist().
+ Frees the memory referenced by the LDAPSortKey structures,
+ the LDAPSortKey structures themselves, and the array of pointers
+ to the structures.
+
+ keyList (IN) Points to an array of pointers to LDAPSortKey structures.
+ ---------------------------------------------------------------------------*/
+
+void
+ldap_free_sort_keylist ( LDAPSortKey **keyList )
+{
+ int i;
+ LDAPSortKey *nextKeyp;
+
+ if (keyList == NULL) return;
+
+ i=0;
+ while ( 0 != (nextKeyp = keyList[i++]) ) {
+ if (nextKeyp->attributeType) {
+ LBER_FREE(nextKeyp->attributeType);
+ }
+
+ if (nextKeyp->orderingRule != NULL) {
+ LBER_FREE(nextKeyp->orderingRule);
+ }
+
+ LBER_FREE(nextKeyp);
+ }
+
+ LBER_FREE(keyList);
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_control_value
+
+ Create and encode the value of the server-side sort control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ keyList (IN) Points to a null-terminated array of pointers to
+ LDAPSortKey structures, containing a description of
+ each of the sort keys to be used. The description
+ consists of an attribute name, ascending/descending flag,
+ and an optional matching rule (OID) to use.
+
+ value (OUT) Contains the control value; the bv_val member of the berval structure
+ SHOULD be freed by calling ldap_memfree() when done.
+
+
+ Ber encoding
+
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_control_value(
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ struct berval *value )
+{
+ int i;
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) return LDAP_PARAM_ERROR;
+ if ( keyList == NULL || value == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{" /*}*/ );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ for ( i = 0; keyList[i] != NULL; i++ ) {
+ tag = ber_printf( ber, "{s" /*}*/, keyList[i]->attributeType );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( keyList[i]->orderingRule != NULL ) {
+ tag = ber_printf( ber, "ts",
+ LDAP_MATCHRULE_IDENTIFIER,
+ keyList[i]->orderingRule );
+
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ if ( keyList[i]->reverseOrder ) {
+ tag = ber_printf( ber, "tb",
+ LDAP_REVERSEORDER_IDENTIFIER,
+ keyList[i]->reverseOrder );
+
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ if ( 0 ) {
+error_return:;
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_create_sort_control
+
+ Create and encode the server-side sort control.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ keyList (IN) Points to a null-terminated array of pointers to
+ LDAPSortKey structures, containing a description of
+ each of the sort keys to be used. The description
+ consists of an attribute name, ascending/descending flag,
+ and an optional matching rule (OID) to use.
+
+ isCritical (IN) 0 - Indicates the control is not critical to the operation.
+ non-zero - The control is critical to the operation.
+
+ ctrlp (OUT) Returns a pointer to the LDAPControl created. This control
+ SHOULD be freed by calling ldap_control_free() when done.
+
+
+ Ber encoding
+
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_create_sort_control(
+ LDAP *ld,
+ LDAPSortKey **keyList,
+ int isCritical,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if ( ld == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_sort_control_value( ld, keyList, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_SORTREQUEST,
+ isCritical, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/* ---------------------------------------------------------------------------
+ ldap_parse_sortedresult_control
+
+ Decode the server-side sort control return information.
+
+ ld (IN) An LDAP session handle, as obtained from a call to
+ ldap_init().
+
+ ctrl (IN) The address of the LDAP Control Structure.
+
+ returnCode (OUT) This result parameter is filled in with the sort control
+ result code. This parameter MUST not be NULL.
+
+ attribute (OUT) If an error occured the server may return a string
+ indicating the first attribute in the sortkey list
+ that was in error. If a string is returned, the memory
+ should be freed with ldap_memfree. If this parameter is
+ NULL, no string is returned.
+
+
+ Ber encoding for sort control
+
+ SortResult ::= SEQUENCE {
+ sortResult ENUMERATED {
+ success (0), -- results are sorted
+ operationsError (1), -- server internal failure
+ timeLimitExceeded (3), -- timelimit reached before
+ -- sorting was completed
+ strongAuthRequired (8), -- refused to return sorted
+ -- results via insecure
+ -- protocol
+ adminLimitExceeded (11), -- too many matching entries
+ -- for the server to sort
+ noSuchAttribute (16), -- unrecognized attribute
+ -- type in sort key
+ inappropriateMatching (18), -- unrecognized or inappro-
+ -- priate matching rule in
+ -- sort key
+ insufficientAccessRights (50), -- refused to return sorted
+ -- results to this client
+ busy (51), -- too busy to process
+ unwillingToPerform (53), -- unable to sort
+ other (80)
+ },
+ attributeType [0] AttributeDescription OPTIONAL }
+ ---------------------------------------------------------------------------*/
+
+int
+ldap_parse_sortresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *returnCode,
+ char **attribute )
+{
+ BerElement *ber;
+ ber_tag_t tag, berTag;
+ ber_len_t berLen;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if (ld == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ if (ctrl == NULL) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return(ld->ld_errno);
+ }
+
+ if (attribute) {
+ *attribute = NULL;
+ }
+
+ if ( strcmp(LDAP_CONTROL_SORTRESPONSE, ctrl->ldctl_oid) != 0 ) {
+ /* Not sort result control */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ /* Extract the result code from the control. */
+ tag = ber_scanf(ber, "{e" /*}*/, returnCode);
+
+ if( tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+ /* If caller wants the attribute name, and if it's present in the control,
+ extract the attribute name which caused the error. */
+ if (attribute && (LDAP_ATTRTYPES_IDENTIFIER == ber_peek_tag(ber, &berLen)))
+ {
+ tag = ber_scanf(ber, "ta", &berTag, attribute);
+
+ if (tag == LBER_ERROR ) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ }
+
+ ber_free(ber,1);
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+}
diff --git a/libraries/libldap/stctrl.c b/libraries/libldap/stctrl.c
new file mode 100644
index 0000000..8d2d06a
--- /dev/null
+++ b/libraries/libldap/stctrl.c
@@ -0,0 +1,302 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2007 Pierangelo Masarati.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was developed by Pierangelo Masarati for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#ifdef LDAP_CONTROL_X_SESSION_TRACKING
+
+/*
+ * Client-side of <draft-wahl-ldap-session-03>
+ */
+
+int
+ldap_create_session_tracking_value(
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ struct berval *value )
+{
+ BerElement *ber = NULL;
+ ber_tag_t tag;
+
+ struct berval ip, name, oid, id;
+
+ if ( ld == NULL ||
+ formatOID == NULL ||
+ value == NULL )
+ {
+param_error:;
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+ ld->ld_errno = LDAP_SUCCESS;
+
+ /* check sizes according to I.D. */
+ if ( sessionSourceIp == NULL ) {
+ BER_BVSTR( &ip, "" );
+
+ } else {
+ ber_str2bv( sessionSourceIp, 0, 0, &ip );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( ip.bv_len > 128 ) goto param_error;
+ }
+
+ if ( sessionSourceName == NULL ) {
+ BER_BVSTR( &name, "" );
+
+ } else {
+ ber_str2bv( sessionSourceName, 0, 0, &name );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( name.bv_len > 65536 ) goto param_error;
+ }
+
+ ber_str2bv( formatOID, 0, 0, &oid );
+ /* NOTE: we're strict because we don't want
+ * to send out bad data */
+ if ( oid.bv_len > 1024 ) goto param_error;
+
+ if ( sessionTrackingIdentifier == NULL ||
+ sessionTrackingIdentifier->bv_val == NULL )
+ {
+ BER_BVSTR( &id, "" );
+
+ } else {
+ id = *sessionTrackingIdentifier;
+ }
+
+ /* prepare value */
+ value->bv_val = NULL;
+ value->bv_len = 0;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{OOOO}", &ip, &name, &oid, &id );
+ if ( tag == LBER_ERROR ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+done:;
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+/*
+ * NOTE: this API is bad; it could be much more efficient...
+ */
+int
+ldap_create_session_tracking_control(
+ LDAP *ld,
+ char *sessionSourceIp,
+ char *sessionSourceName,
+ char *formatOID,
+ struct berval *sessionTrackingIdentifier,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_session_tracking_value( ld,
+ sessionSourceIp, sessionSourceName, formatOID,
+ sessionTrackingIdentifier, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_X_SESSION_TRACKING,
+ 0, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+int
+ldap_parse_session_tracking_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ struct berval *ip,
+ struct berval *name,
+ struct berval *oid,
+ struct berval *id )
+{
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_len_t len;
+
+ if ( ld == NULL ||
+ ctrl == NULL ||
+ ip == NULL ||
+ name == NULL ||
+ oid == NULL ||
+ id == NULL )
+ {
+ if ( ld ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ }
+
+ /* NOTE: we want the caller to get all or nothing;
+ * we could allow some of the pointers to be NULL,
+ * if one does not want part of the data */
+ return LDAP_PARAM_ERROR;
+ }
+
+ BER_BVZERO( ip );
+ BER_BVZERO( name );
+ BER_BVZERO( oid );
+ BER_BVZERO( id );
+
+ ber = ber_init( &ctrl->ldctl_value );
+
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_skip_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ /* sessionSourceIp */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+ if ( len > 128 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", ip );
+ }
+
+ /* sessionSourceName */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+ if ( len > 65536 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", name );
+ }
+
+ /* formatOID */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ goto error;
+
+ } else {
+ if ( len > 1024 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+ tag = ber_scanf( ber, "o", oid );
+ }
+
+ /* FIXME: should check if it is an OID... leave it to the caller */
+
+ /* sessionTrackingIdentifier */
+ tag = ber_peek_tag( ber, &len );
+ if ( tag == LBER_DEFAULT ) {
+ tag = LBER_ERROR;
+ goto error;
+ }
+
+ if ( len == 0 ) {
+ tag = ber_skip_tag( ber, &len );
+
+ } else {
+#if 0
+ if ( len > 65536 ) {
+ /* should be LDAP_DECODING_ERROR,
+ * but we're liberal in what we accept */
+ }
+#endif
+ tag = ber_scanf( ber, "o", id );
+ }
+
+ /* closure */
+ tag = ber_skip_tag( ber, &len );
+ if ( tag == LBER_DEFAULT && len == 0 ) {
+ tag = 0;
+ }
+
+error:;
+ (void)ber_free( ber, 1 );
+
+ if ( tag == LBER_ERROR ) {
+ return LDAP_DECODING_ERROR;
+ }
+
+ return ld->ld_errno;
+}
+
+#endif /* LDAP_CONTROL_X_SESSION_TRACKING */
diff --git a/libraries/libldap/string.c b/libraries/libldap/string.c
new file mode 100644
index 0000000..62b9b3d
--- /dev/null
+++ b/libraries/libldap/string.c
@@ -0,0 +1,177 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * Locale-specific 1-byte character versions
+ * See utf-8.c for UTF-8 versions
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/ctype.h>
+
+#include "ldap-int.h"
+
+
+#if defined ( HAVE_STRSPN )
+#define int_strspn strspn
+#else
+static int int_strspn( const char *str, const char *delim )
+{
+ int pos;
+ const char *p=delim;
+
+ for( pos=0; (*str) ; pos++,str++) {
+ if (*str!=*p) {
+ for( p=delim; (*p) ; p++ ) {
+ if (*str==*p) {
+ break;
+ }
+ }
+ }
+
+ if (*p=='\0') {
+ return pos;
+ }
+ }
+ return pos;
+}
+#endif
+
+#if defined( HAVE_STRPBRK )
+#define int_strpbrk strpbrk
+#else
+static char *(int_strpbrk)( const char *str, const char *accept )
+{
+ const char *p;
+
+ for( ; (*str) ; str++ ) {
+ for( p=accept; (*p) ; p++) {
+ if (*str==*p) {
+ return str;
+ }
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+char *(ldap_pvt_strtok)( char *str, const char *delim, char **pos )
+{
+ char *p;
+
+ if (pos==NULL) {
+ return NULL;
+ }
+
+ if (str==NULL) {
+ if (*pos==NULL) {
+ return NULL;
+ }
+
+ str=*pos;
+ }
+
+ /* skip any initial delimiters */
+ str += int_strspn( str, delim );
+ if (*str == '\0') {
+ return NULL;
+ }
+
+ p = int_strpbrk( str, delim );
+ if (p==NULL) {
+ *pos = NULL;
+
+ } else {
+ *p ='\0';
+ *pos = p+1;
+ }
+
+ return str;
+}
+
+char *
+ldap_pvt_str2upper( char *str )
+{
+ char *s;
+
+ /* to upper */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOUPPER( (unsigned char) *s );
+ }
+ }
+
+ return( str );
+}
+
+struct berval *
+ldap_pvt_str2upperbv( char *str, struct berval *bv )
+{
+ char *s = NULL;
+
+ assert( bv != NULL );
+
+ /* to upper */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOUPPER( (unsigned char) *s );
+ }
+ }
+
+ bv->bv_val = str;
+ bv->bv_len = (ber_len_t)(s - str);
+
+ return( bv );
+}
+
+char *
+ldap_pvt_str2lower( char *str )
+{
+ char *s;
+
+ /* to lower */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOLOWER( (unsigned char) *s );
+ }
+ }
+
+ return( str );
+}
+
+struct berval *
+ldap_pvt_str2lowerbv( char *str, struct berval *bv )
+{
+ char *s = NULL;
+
+ assert( bv != NULL );
+
+ /* to lower */
+ if ( str ) {
+ for ( s = str; *s; s++ ) {
+ *s = TOLOWER( (unsigned char) *s );
+ }
+ }
+
+ bv->bv_val = str;
+ bv->bv_len = (ber_len_t)(s - str);
+
+ return( bv );
+}
diff --git a/libraries/libldap/t61.c b/libraries/libldap/t61.c
new file mode 100644
index 0000000..4556439
--- /dev/null
+++ b/libraries/libldap/t61.c
@@ -0,0 +1,692 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * Basic T.61 <-> UTF-8 conversion
+ *
+ * These routines will perform a lossless translation from T.61 to UTF-8
+ * and a lossy translation from UTF-8 to T.61.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_utf8.h"
+
+#include "ldap_defaults.h"
+
+/*
+ * T.61 is somewhat braindead; even in the 7-bit space it is not
+ * completely equivalent to 7-bit US-ASCII. Our definition of the
+ * character set comes from RFC 1345 with a slightly more readable
+ * rendition at http://std.dkuug.dk/i18n/charmaps/T.61-8BIT.
+ *
+ * Even though '#' and '$' are present in the 7-bit US-ASCII space,
+ * (x23 and x24, resp.) in T.61 they are mapped to 8-bit characters
+ * xA6 and xA4.
+ *
+ * Also T.61 lacks
+ * backslash \ (x5C)
+ * caret ^ (x5E)
+ * backquote ` (x60)
+ * left brace { (x7B)
+ * right brace } (x7D)
+ * tilde ~ (x7E)
+ *
+ * In T.61, the codes xC1 to xCF (excluding xC9, unused) are non-spacing
+ * accents of some form or another. There are predefined combinations
+ * for certain characters, but they can also be used arbitrarily. The
+ * table at dkuug.dk maps these accents to the E000 "private use" range
+ * of the Unicode space, but I believe they more properly belong in the
+ * 0300 range (non-spacing accents). The transformation is complicated
+ * slightly because Unicode wants the non-spacing character to follow
+ * the base character, while T.61 has the non-spacing character leading.
+ * Also, T.61 specifically recognizes certain combined pairs as "characters"
+ * but doesn't specify how to treat unrecognized pairs. This code will
+ * always attempt to combine pairs when a known Unicode composite exists.
+ */
+
+static const wchar_t t61_tab[] = {
+ 0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007,
+ 0x008, 0x009, 0x00a, 0x00b, 0x00c, 0x00d, 0x00e, 0x00f,
+ 0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017,
+ 0x018, 0x019, 0x01a, 0x01b, 0x01c, 0x01d, 0x01e, 0x01f,
+ 0x020, 0x021, 0x022, 0x000, 0x000, 0x025, 0x026, 0x027,
+ 0x028, 0x029, 0x02a, 0x02b, 0x02c, 0x02d, 0x02e, 0x02f,
+ 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037,
+ 0x038, 0x039, 0x03a, 0x03b, 0x03c, 0x03d, 0x03e, 0x03f,
+ 0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047,
+ 0x048, 0x049, 0x04a, 0x04b, 0x04c, 0x04d, 0x04e, 0x04f,
+ 0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057,
+ 0x058, 0x059, 0x05a, 0x05b, 0x000, 0x05d, 0x000, 0x05f,
+ 0x000, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067,
+ 0x068, 0x069, 0x06a, 0x06b, 0x06c, 0x06d, 0x06e, 0x06f,
+ 0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077,
+ 0x078, 0x079, 0x07a, 0x000, 0x07c, 0x000, 0x000, 0x07f,
+ 0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087,
+ 0x088, 0x089, 0x08a, 0x08b, 0x08c, 0x08d, 0x08e, 0x08f,
+ 0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097,
+ 0x098, 0x099, 0x09a, 0x09b, 0x09c, 0x09d, 0x09e, 0x09f,
+ 0x0a0, 0x0a1, 0x0a2, 0x0a3, 0x024, 0x0a5, 0x023, 0x0a7,
+ 0x0a4, 0x000, 0x000, 0x0ab, 0x000, 0x000, 0x000, 0x000,
+ 0x0b0, 0x0b1, 0x0b2, 0x0b3, 0x0d7, 0x0b5, 0x0b6, 0x0b7,
+ 0x0f7, 0x000, 0x000, 0x0bb, 0x0bc, 0x0bd, 0x0be, 0x0bf,
+ 0x000, 0x300, 0x301, 0x302, 0x303, 0x304, 0x306, 0x307,
+ 0x308, 0x000, 0x30a, 0x327, 0x332, 0x30b, 0x328, 0x30c,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+ 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000,
+ 0x2126, 0xc6, 0x0d0, 0x0aa, 0x126, 0x000, 0x132, 0x13f,
+ 0x141, 0x0d8, 0x152, 0x0ba, 0x0de, 0x166, 0x14a, 0x149,
+ 0x138, 0x0e6, 0x111, 0x0f0, 0x127, 0x131, 0x133, 0x140,
+ 0x142, 0x0f8, 0x153, 0x0df, 0x0fe, 0x167, 0x14b, 0x000
+};
+
+typedef wchar_t wvec16[16];
+typedef wchar_t wvec32[32];
+typedef wchar_t wvec64[64];
+
+/* Substitutions when 0xc1-0xcf appears by itself or with space 0x20 */
+static const wvec16 accents = {
+ 0x000, 0x060, 0x0b4, 0x05e, 0x07e, 0x0af, 0x2d8, 0x2d9,
+ 0x0a8, 0x000, 0x2da, 0x0b8, 0x000, 0x2dd, 0x2db, 0x2c7};
+
+/* In the following tables, base characters commented in (parentheses)
+ * are not defined by T.61 but are mapped anyway since their Unicode
+ * composite exists.
+ */
+
+/* Grave accented chars AEIOU (NWY) */
+static const wvec32 c1_vec1 = {
+ /* Upper case */
+ 0, 0xc0, 0, 0, 0, 0xc8, 0, 0, 0, 0xcc, 0, 0, 0, 0, 0x1f8, 0xd2,
+ 0, 0, 0, 0, 0, 0xd9, 0, 0x1e80, 0, 0x1ef2, 0, 0, 0, 0, 0, 0};
+static const wvec32 c1_vec2 = {
+ /* Lower case */
+ 0, 0xe0, 0, 0, 0, 0xe8, 0, 0, 0, 0xec, 0, 0, 0, 0, 0x1f9, 0xf2,
+ 0, 0, 0, 0, 0, 0xf9, 0, 0x1e81, 0, 0x1ef3, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c1_grave[] = {
+ NULL, NULL, &c1_vec1, &c1_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Acute accented chars AEIOUYCLNRSZ (GKMPW) */
+static const wvec32 c2_vec1 = {
+ /* Upper case */
+ 0, 0xc1, 0, 0x106, 0, 0xc9, 0, 0x1f4,
+ 0, 0xcd, 0, 0x1e30, 0x139, 0x1e3e, 0x143, 0xd3,
+ 0x1e54, 0, 0x154, 0x15a, 0, 0xda, 0, 0x1e82,
+ 0, 0xdd, 0x179, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec2 = {
+ /* Lower case */
+ 0, 0xe1, 0, 0x107, 0, 0xe9, 0, 0x1f5,
+ 0, 0xed, 0, 0x1e31, 0x13a, 0x1e3f, 0x144, 0xf3,
+ 0x1e55, 0, 0x155, 0x15b, 0, 0xfa, 0, 0x1e83,
+ 0, 0xfd, 0x17a, 0, 0, 0, 0, 0};
+static const wvec32 c2_vec3 = {
+ /* (AE and ae) */
+ 0, 0x1fc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x1fd, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static const wvec32 *c2_acute[] = {
+ NULL, NULL, &c2_vec1, &c2_vec2, NULL, NULL, NULL, &c2_vec3
+};
+
+/* Circumflex AEIOUYCGHJSW (Z) */
+static const wvec32 c3_vec1 = {
+ /* Upper case */
+ 0, 0xc2, 0, 0x108, 0, 0xca, 0, 0x11c,
+ 0x124, 0xce, 0x134, 0, 0, 0, 0, 0xd4,
+ 0, 0, 0, 0x15c, 0, 0xdb, 0, 0x174,
+ 0, 0x176, 0x1e90, 0, 0, 0, 0, 0};
+static const wvec32 c3_vec2 = {
+ /* Lower case */
+ 0, 0xe2, 0, 0x109, 0, 0xea, 0, 0x11d,
+ 0x125, 0xee, 0x135, 0, 0, 0, 0, 0xf4,
+ 0, 0, 0, 0x15d, 0, 0xfb, 0, 0x175,
+ 0, 0x177, 0x1e91, 0, 0, 0, 0, 0};
+static const wvec32 *c3_circumflex[] = {
+ NULL, NULL, &c3_vec1, &c3_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Tilde AIOUN (EVY) */
+static const wvec32 c4_vec1 = {
+ /* Upper case */
+ 0, 0xc3, 0, 0, 0, 0x1ebc, 0, 0, 0, 0x128, 0, 0, 0, 0, 0xd1, 0xd5,
+ 0, 0, 0, 0, 0, 0x168, 0x1e7c, 0, 0, 0x1ef8, 0, 0, 0, 0, 0, 0};
+static const wvec32 c4_vec2 = {
+ /* Lower case */
+ 0, 0xe3, 0, 0, 0, 0x1ebd, 0, 0, 0, 0x129, 0, 0, 0, 0, 0xf1, 0xf5,
+ 0, 0, 0, 0, 0, 0x169, 0x1e7d, 0, 0, 0x1ef9, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c4_tilde[] = {
+ NULL, NULL, &c4_vec1, &c4_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Macron AEIOU (YG) */
+static const wvec32 c5_vec1 = {
+ /* Upper case */
+ 0, 0x100, 0, 0, 0, 0x112, 0, 0x1e20, 0, 0x12a, 0, 0, 0, 0, 0, 0x14c,
+ 0, 0, 0, 0, 0, 0x16a, 0, 0, 0, 0x232, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec2 = {
+ /* Lower case */
+ 0, 0x101, 0, 0, 0, 0x113, 0, 0x1e21, 0, 0x12b, 0, 0, 0, 0, 0, 0x14d,
+ 0, 0, 0, 0, 0, 0x16b, 0, 0, 0, 0x233, 0, 0, 0, 0, 0, 0};
+static const wvec32 c5_vec3 = {
+ /* (AE and ae) */
+ 0, 0x1e2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0x1e3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c5_macron[] = {
+ NULL, NULL, &c5_vec1, &c5_vec2, NULL, NULL, NULL, &c5_vec3
+};
+
+/* Breve AUG (EIO) */
+static const wvec32 c6_vec1 = {
+ /* Upper case */
+ 0, 0x102, 0, 0, 0, 0x114, 0, 0x11e, 0, 0x12c, 0, 0, 0, 0, 0, 0x14e,
+ 0, 0, 0, 0, 0, 0x16c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 c6_vec2 = {
+ /* Lower case */
+ 0, 0x103, 0, 0, 0, 0x115, 0, 0x11f, 0, 0x12d, 0, 0, 0, 0, 0, 0x14f,
+ 0, 0, 0, 0, 0, 0x16d, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c6_breve[] = {
+ NULL, NULL, &c6_vec1, &c6_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Dot Above CEGIZ (AOBDFHMNPRSTWXY) */
+static const wvec32 c7_vec1 = {
+ /* Upper case */
+ 0, 0x226, 0x1e02, 0x10a, 0x1e0a, 0x116, 0x1e1e, 0x120,
+ 0x1e22, 0x130, 0, 0, 0, 0x1e40, 0x1e44, 0x22e,
+ 0x1e56, 0, 0x1e58, 0x1e60, 0x1e6a, 0, 0, 0x1e86,
+ 0x1e8a, 0x1e8e, 0x17b, 0, 0, 0, 0, 0};
+static const wvec32 c7_vec2 = {
+ /* Lower case */
+ 0, 0x227, 0x1e03, 0x10b, 0x1e0b, 0x117, 0x1e1f, 0x121,
+ 0x1e23, 0, 0, 0, 0, 0x1e41, 0x1e45, 0x22f,
+ 0x1e57, 0, 0x1e59, 0x1e61, 0x1e6b, 0, 0, 0x1e87,
+ 0x1e8b, 0x1e8f, 0x17c, 0, 0, 0, 0, 0};
+static const wvec32 *c7_dotabove[] = {
+ NULL, NULL, &c7_vec1, &c7_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Diaeresis AEIOUY (HWXt) */
+static const wvec32 c8_vec1 = {
+ /* Upper case */
+ 0, 0xc4, 0, 0, 0, 0xcb, 0, 0, 0x1e26, 0xcf, 0, 0, 0, 0, 0, 0xd6,
+ 0, 0, 0, 0, 0, 0xdc, 0, 0x1e84, 0x1e8c, 0x178, 0, 0, 0, 0, 0, 0};
+static const wvec32 c8_vec2 = {
+ /* Lower case */
+ 0, 0xe4, 0, 0, 0, 0xeb, 0, 0, 0x1e27, 0xef, 0, 0, 0, 0, 0, 0xf6,
+ 0, 0, 0, 0, 0x1e97, 0xfc, 0, 0x1e85, 0x1e8d, 0xff, 0, 0, 0, 0, 0, 0};
+static const wvec32 *c8_diaeresis[] = {
+ NULL, NULL, &c8_vec1, &c8_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ring Above AU (wy) */
+static const wvec32 ca_vec1 = {
+ /* Upper case */
+ 0, 0xc5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x16e, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ca_vec2 = {
+ /* Lower case */
+ 0, 0xe5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0x16f, 0, 0x1e98, 0, 0x1e99, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ca_ringabove[] = {
+ NULL, NULL, &ca_vec1, &ca_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Cedilla CGKLNRST (EDH) */
+static const wvec32 cb_vec1 = {
+ /* Upper case */
+ 0, 0, 0, 0xc7, 0x1e10, 0x228, 0, 0x122,
+ 0x1e28, 0, 0, 0x136, 0x13b, 0, 0x145, 0,
+ 0, 0, 0x156, 0x15e, 0x162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cb_vec2 = {
+ /* Lower case */
+ 0, 0, 0, 0xe7, 0x1e11, 0x229, 0, 0x123,
+ 0x1e29, 0, 0, 0x137, 0x13c, 0, 0x146, 0,
+ 0, 0, 0x157, 0x15f, 0x163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cb_cedilla[] = {
+ NULL, NULL, &cb_vec1, &cb_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Double Acute Accent OU */
+static const wvec32 cd_vec1 = {
+ /* Upper case */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x150,
+ 0, 0, 0, 0, 0, 0x170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 cd_vec2 = {
+ /* Lower case */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x151,
+ 0, 0, 0, 0, 0, 0x171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *cd_doubleacute[] = {
+ NULL, NULL, &cd_vec1, &cd_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Ogonek AEIU (O) */
+static const wvec32 ce_vec1 = {
+ /* Upper case */
+ 0, 0x104, 0, 0, 0, 0x118, 0, 0, 0, 0x12e, 0, 0, 0, 0, 0, 0x1ea,
+ 0, 0, 0, 0, 0, 0x172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 ce_vec2 = {
+ /* Lower case */
+ 0, 0x105, 0, 0, 0, 0x119, 0, 0, 0, 0x12f, 0, 0, 0, 0, 0, 0x1eb,
+ 0, 0, 0, 0, 0, 0x173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static const wvec32 *ce_ogonek[] = {
+ NULL, NULL, &ce_vec1, &ce_vec2, NULL, NULL, NULL, NULL
+};
+
+/* Caron CDELNRSTZ (AIOUGKjH) */
+static const wvec32 cf_vec1 = {
+ /* Upper case */
+ 0, 0x1cd, 0, 0x10c, 0x10e, 0x11a, 0, 0x1e6,
+ 0x21e, 0x1cf, 0, 0x1e8, 0x13d, 0, 0x147, 0x1d1,
+ 0, 0, 0x158, 0x160, 0x164, 0x1d3, 0, 0,
+ 0, 0, 0x17d, 0, 0, 0, 0, 0};
+static const wvec32 cf_vec2 = {
+ /* Lower case */
+ 0, 0x1ce, 0, 0x10d, 0x10f, 0x11b, 0, 0x1e7,
+ 0x21f, 0x1d0, 0x1f0, 0x1e9, 0x13e, 0, 0x148, 0x1d2,
+ 0, 0, 0x159, 0x161, 0x165, 0x1d4, 0, 0,
+ 0, 0, 0x17e, 0, 0, 0, 0, 0};
+static const wvec32 *cf_caron[] = {
+ NULL, NULL, &cf_vec1, &cf_vec2, NULL, NULL, NULL, NULL
+};
+
+static const wvec32 **cx_tab[] = {
+ NULL, c1_grave, c2_acute, c3_circumflex, c4_tilde, c5_macron,
+ c6_breve, c7_dotabove, c8_diaeresis, NULL, ca_ringabove,
+ cb_cedilla, NULL, cd_doubleacute, ce_ogonek, cf_caron };
+
+int ldap_t61s_valid( struct berval *str )
+{
+ unsigned char *c = (unsigned char *)str->bv_val;
+ int i;
+
+ for (i=0; i < str->bv_len; c++,i++)
+ if (!t61_tab[*c])
+ return 0;
+ return 1;
+}
+
+/* Transform a T.61 string to UTF-8.
+ */
+int ldap_t61s_to_utf8s( struct berval *src, struct berval *dst )
+{
+ unsigned char *c;
+ char *d;
+ int i, wlen = 0;
+
+ /* Just count the length of the UTF-8 result first */
+ for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) {
+ /* Invalid T.61 characters? */
+ if (!t61_tab[*c])
+ return LDAP_INVALID_SYNTAX;
+ if ((*c & 0xf0) == 0xc0) {
+ int j = *c & 0x0f;
+ /* If this is the end of the string, or if the base
+ * character is just a space, treat this as a regular
+ * spacing character.
+ */
+ if ((!c[1] || c[1] == 0x20) && accents[j]) {
+ wlen += ldap_x_wc_to_utf8(NULL, accents[j], 0);
+ } else if (cx_tab[j] && cx_tab[j][c[1]>>5] &&
+ /* We have a composite mapping for this pair */
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) {
+ wlen += ldap_x_wc_to_utf8( NULL,
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 0);
+ } else {
+ /* No mapping, just swap it around so the base
+ * character comes first.
+ */
+ wlen += ldap_x_wc_to_utf8(NULL, c[1], 0);
+ wlen += ldap_x_wc_to_utf8(NULL,
+ t61_tab[*c], 0);
+ }
+ c++; i++;
+ continue;
+ } else {
+ wlen += ldap_x_wc_to_utf8(NULL, t61_tab[*c], 0);
+ }
+ }
+
+ /* Now transform the string */
+ dst->bv_len = wlen;
+ dst->bv_val = LDAP_MALLOC( wlen+1 );
+ d = dst->bv_val;
+ if (!d)
+ return LDAP_NO_MEMORY;
+
+ for (i=0,c=(unsigned char *)src->bv_val; i < src->bv_len; c++,i++) {
+ if ((*c & 0xf0) == 0xc0) {
+ int j = *c & 0x0f;
+ /* If this is the end of the string, or if the base
+ * character is just a space, treat this as a regular
+ * spacing character.
+ */
+ if ((!c[1] || c[1] == 0x20) && accents[j]) {
+ d += ldap_x_wc_to_utf8(d, accents[j], 6);
+ } else if (cx_tab[j] && cx_tab[j][c[1]>>5] &&
+ /* We have a composite mapping for this pair */
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f]) {
+ d += ldap_x_wc_to_utf8(d,
+ (*cx_tab[j][c[1]>>5])[c[1]&0x1f], 6);
+ } else {
+ /* No mapping, just swap it around so the base
+ * character comes first.
+ */
+ d += ldap_x_wc_to_utf8(d, c[1], 6);
+ d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6);
+ }
+ c++; i++;
+ continue;
+ } else {
+ d += ldap_x_wc_to_utf8(d, t61_tab[*c], 6);
+ }
+ }
+ *d = '\0';
+ return LDAP_SUCCESS;
+}
+
+/* For the reverse mapping, we just pay attention to the Latin-oriented
+ * code blocks. These are
+ * 0000 - 007f Basic Latin
+ * 0080 - 00ff Latin-1 Supplement
+ * 0100 - 017f Latin Extended-A
+ * 0180 - 024f Latin Extended-B
+ * 1e00 - 1eff Latin Extended Additional
+ *
+ * We have a special case to map Ohm U2126 back to T.61 0xe0. All other
+ * unrecognized characters are replaced with '?' 0x3f.
+ */
+
+static const wvec64 u000 = {
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x00a6, 0x00a4, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f};
+
+/* In this range, we've mapped caret to xc3/x20, backquote to xc1/x20,
+ * and tilde to xc4/x20. T.61 (stupidly!) doesn't define these characters
+ * on their own, even though it provides them as combiners for other
+ * letters. T.61 doesn't define these pairings either, so this may just
+ * have to be replaced with '?' 0x3f if other software can't cope with it.
+ */
+static const wvec64 u001 = {
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+ 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+ 0x0058, 0x0059, 0x005a, 0x005b, 0x003f, 0x005d, 0xc320, 0x005f,
+ 0xc120, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+ 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+ 0x0078, 0x0079, 0x007a, 0x003f, 0x007c, 0x003f, 0xc420, 0x007f};
+
+static const wvec64 u002 = {
+ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+ 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a8, 0x00a5, 0x003f, 0x00a7,
+ 0xc820, 0x003f, 0x00e3, 0x00ab, 0x003f, 0x003f, 0x003f, 0xc520,
+ 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0xc220, 0x00b5, 0x00b6, 0x00b7,
+ 0xcb20, 0x003f, 0x00eb, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf};
+
+static const wvec64 u003 = {
+ 0xc141, 0xc241, 0xc341, 0xc441, 0xc841, 0xca41, 0x00e1, 0xcb43,
+ 0xc145, 0xc245, 0xc345, 0xc845, 0xc149, 0xc249, 0xc349, 0xc849,
+ 0x00e2, 0xc44e, 0xc14f, 0xc24f, 0xc34f, 0xc44f, 0xc84f, 0x00b4,
+ 0x00e9, 0xc155, 0xc255, 0xc355, 0xc855, 0xc259, 0x00ec, 0x00fb,
+ 0xc161, 0xc261, 0xc361, 0xc461, 0xc861, 0xca61, 0x00f1, 0xcb63,
+ 0xc165, 0xc265, 0xc365, 0xc865, 0xc169, 0xc269, 0xc369, 0xc869,
+ 0x00f3, 0xc46e, 0xc16f, 0xc26f, 0xc36f, 0xc46f, 0xc86f, 0x00b8,
+ 0x00f9, 0xc175, 0xc275, 0xc375, 0xc875, 0xc279, 0x00fc, 0xc879};
+
+/* These codes are used here but not defined by T.61:
+ * x114 = xc6/x45, x115 = xc6/x65, x12c = xc6/x49, x12d = xc6/x69
+ */
+static const wvec64 u010 = {
+ 0xc541, 0xc561, 0xc641, 0xc661, 0xce41, 0xce61, 0xc243, 0xc263,
+ 0xc343, 0xc363, 0xc743, 0xc763, 0xcf43, 0xcf63, 0xcf44, 0xcf64,
+ 0x003f, 0x00f2, 0xc545, 0xc565, 0xc645, 0xc665, 0xc745, 0xc765,
+ 0xce45, 0xce65, 0xcf45, 0xcf65, 0xc347, 0xc367, 0xc647, 0xc667,
+ 0xc747, 0xc767, 0xcb47, 0xcb67, 0xc348, 0xc368, 0x00e4, 0x00f4,
+ 0xc449, 0xc469, 0xc549, 0xc569, 0xc649, 0xc669, 0xce49, 0xce69,
+ 0xc749, 0x00f5, 0x00e6, 0x00f6, 0xc34a, 0xc36a, 0xcb4b, 0xcb6b,
+ 0x00f0, 0xc24c, 0xc26c, 0xcb4c, 0xcb6c, 0xcf4c, 0xcf6c, 0x00e7};
+
+/* These codes are used here but not defined by T.61:
+ * x14e = xc6/x4f, x14f = xc6/x6f
+ */
+static const wvec64 u011 = {
+ 0x00f7, 0x00e8, 0x00f8, 0xc24e, 0xc26e, 0xcb4e, 0xcb6e, 0xcf4e,
+ 0xcf6e, 0x00ef, 0x00ee, 0x00fe, 0xc54f, 0xc56f, 0xc64f, 0xc66f,
+ 0xcd4f, 0xcd6f, 0x00ea, 0x00fa, 0xc252, 0xc272, 0xcb52, 0xcb72,
+ 0xcf52, 0xcf72, 0xc253, 0xc273, 0xc353, 0xc373, 0xcb53, 0xcb73,
+ 0xcf53, 0xcf73, 0xcb54, 0xcb74, 0xcf54, 0xcf74, 0x00ed, 0x00fd,
+ 0xc455, 0xc475, 0xc555, 0xc575, 0xc655, 0xc675, 0xca55, 0xca75,
+ 0xcd55, 0xcd75, 0xce55, 0xce75, 0xc357, 0xc377, 0xc359, 0xc379,
+ 0xc859, 0xc25a, 0xc27a, 0xc75a, 0xc77a, 0xcf5a, 0xcf7a, 0x003f};
+
+/* All of the codes in this block are undefined in T.61.
+ */
+static const wvec64 u013 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf41, 0xcf61, 0xcf49,
+ 0xcf69, 0xcf4f, 0xcf6f, 0xcf55, 0xcf75, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc5e1, 0xc5f1, 0x003f, 0x003f, 0xcf47, 0xcf67,
+ 0xcf4b, 0xcf6b, 0xce4f, 0xce6f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xcf6a, 0x003f, 0x003f, 0x003f, 0xc247, 0xc267, 0x003f, 0x003f,
+ 0xc14e, 0xc16e, 0x003f, 0x003f, 0xc2e1, 0xc2f1, 0x003f, 0x003f};
+
+/* All of the codes in this block are undefined in T.61.
+ */
+static const wvec64 u020 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf48, 0xcf68,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc741, 0xc761,
+ 0xcb45, 0xcb65, 0x003f, 0x003f, 0x003f, 0x003f, 0xc74f, 0xc76f,
+ 0x003f, 0x003f, 0xc559, 0xc579, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+static const wvec64 u023 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xcf20,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc620, 0xc720, 0xca20, 0xce20, 0x003f, 0xcd20, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+/* These are the non-spacing characters by themselves. They should
+ * never appear by themselves in actual text.
+ */
+static const wvec64 u030 = {
+ 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x003f, 0x00c6, 0x00c7,
+ 0x00c8, 0x003f, 0x00ca, 0x00cd, 0x00cf, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x00cb,
+ 0x00ce, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x00cc, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f};
+
+/* None of the following blocks are defined in T.61.
+ */
+static const wvec64 u1e0 = {
+ 0x003f, 0x003f, 0xc742, 0xc762, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc744, 0xc764, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xcb44, 0xcb64, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc746, 0xc766,
+ 0xc547, 0xc567, 0xc748, 0xc768, 0x003f, 0x003f, 0xc848, 0xc868,
+ 0xcb48, 0xcb68, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc24b, 0xc26b, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc24d, 0xc26d,
+};
+
+static const wvec64 u1e1 = {
+ 0xc74d, 0xc76d, 0x003f, 0x003f, 0xc74e, 0xc76e, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc250, 0xc270, 0xc750, 0xc770,
+ 0xc752, 0xc772, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc753, 0xc773, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc754, 0xc774, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc456, 0xc476, 0x003f, 0x003f,
+};
+
+static const wvec64 u1e2 = {
+ 0xc157, 0xc177, 0xc257, 0xc277, 0xc857, 0xc877, 0xc757, 0xc777,
+ 0x003f, 0x003f, 0xc758, 0xc778, 0xc858, 0xc878, 0xc759, 0xc779,
+ 0xc35a, 0xc37a, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0xc874,
+ 0xca77, 0xca79, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0xc445, 0xc465, 0x003f, 0x003f,
+};
+
+static const wvec64 u1e3 = {
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0x003f, 0x003f, 0xc159, 0xc179, 0x003f, 0x003f, 0x003f, 0x003f,
+ 0xc459, 0xc479, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f, 0x003f,
+};
+
+static const wvec64 *wc00[] = {
+ &u000, &u001, &u002, &u003,
+ &u010, &u011, NULL, &u013,
+ &u020, NULL, NULL, &u023,
+ &u030, NULL, NULL, NULL};
+
+static const wvec64 *wc1e[] = {
+ &u1e0, &u1e1, &u1e2, &u1e3};
+
+
+int ldap_utf8s_to_t61s( struct berval *src, struct berval *dst )
+{
+ char *c, *d;
+ wchar_t tmp;
+ int i, j, tlen = 0;
+
+ /* Just count the length of the T.61 result first */
+ for (i=0,c=src->bv_val; i < src->bv_len;) {
+ j = ldap_x_utf8_to_wc( &tmp, c );
+ if (j == -1)
+ return LDAP_INVALID_SYNTAX;
+ switch (tmp >> 8) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ if (wc00[tmp >> 6] &&
+ ((*wc00[tmp >> 6])[tmp & 0x3f] & 0xff00)) {
+ tlen++;
+ }
+ tlen++;
+ break;
+ case 0x1e:
+ if ((*wc1e[(tmp >> 6) & 3])[tmp & 0x3f] & 0xff00) {
+ tlen++;
+ }
+ case 0x21:
+ default:
+ tlen ++;
+ break;
+ }
+ i += j;
+ c += j;
+ }
+ dst->bv_len = tlen;
+ dst->bv_val = LDAP_MALLOC( tlen+1 );
+ if (!dst->bv_val)
+ return LDAP_NO_MEMORY;
+
+ d = dst->bv_val;
+ for (i=0,c=src->bv_val; i < src->bv_len;) {
+ j = ldap_x_utf8_to_wc( &tmp, c );
+ switch (tmp >> 8) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ if (wc00[tmp >> 6]) {
+ tmp = (*wc00[tmp >> 6])[tmp & 0x3f];
+ if (tmp & 0xff00)
+ *d++ = (tmp >> 8);
+ *d++ = tmp & 0xff;
+ } else {
+ *d++ = 0x3f;
+ }
+ break;
+ case 0x03:
+ /* swap order of non-spacing characters */
+ if (wc00[tmp >> 6]) {
+ wchar_t t2 = (*wc00[tmp >> 6])[tmp & 0x3f];
+ if (t2 != 0x3f) {
+ d[0] = d[-1];
+ d[-1] = t2;
+ d++;
+ } else {
+ *d++ = 0x3f;
+ }
+ } else {
+ *d++ = 0x3f;
+ }
+ break;
+ case 0x1e:
+ tmp = (*wc1e[(tmp >> 6) & 3])[tmp & 0x3f];
+ if (tmp & 0xff00)
+ *d++ = (tmp >> 8);
+ *d++ = tmp & 0xff;
+ break;
+ case 0x21:
+ if (tmp == 0x2126) {
+ *d++ = 0xe0;
+ break;
+ }
+ /* FALLTHRU */
+ default:
+ *d++ = 0x3f;
+ break;
+ }
+ i += j;
+ c += j;
+ }
+ *d = '\0';
+ return LDAP_SUCCESS;
+}
diff --git a/libraries/libldap/test.c b/libraries/libldap/test.c
new file mode 100644
index 0000000..eb44430
--- /dev/null
+++ b/libraries/libldap/test.c
@@ -0,0 +1,807 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+
+#include <fcntl.h>
+
+/* including the "internal" defs is legit and nec. since this test routine has
+ * a-priori knowledge of libldap internal workings.
+ * hodges@stanford.edu 5-Feb-96
+ */
+#include "ldap-int.h"
+
+/* local functions */
+static char *get_line LDAP_P(( char *line, int len, FILE *fp, const char *prompt ));
+static char **get_list LDAP_P(( const char *prompt ));
+static int file_read LDAP_P(( const char *path, struct berval *bv ));
+static LDAPMod **get_modlist LDAP_P(( const char *prompt1,
+ const char *prompt2, const char *prompt3 ));
+static void handle_result LDAP_P(( LDAP *ld, LDAPMessage *lm ));
+static void print_ldap_result LDAP_P(( LDAP *ld, LDAPMessage *lm,
+ const char *s ));
+static void print_search_entry LDAP_P(( LDAP *ld, LDAPMessage *res ));
+static void free_list LDAP_P(( char **list ));
+
+static char *dnsuffix;
+
+static char *
+get_line( char *line, int len, FILE *fp, const char *prompt )
+{
+ fputs(prompt, stdout);
+
+ if ( fgets( line, len, fp ) == NULL )
+ return( NULL );
+
+ line[ strlen( line ) - 1 ] = '\0';
+
+ return( line );
+}
+
+static char **
+get_list( const char *prompt )
+{
+ static char buf[256];
+ int num;
+ char **result;
+
+ num = 0;
+ result = (char **) 0;
+ while ( 1 ) {
+ get_line( buf, sizeof(buf), stdin, prompt );
+
+ if ( *buf == '\0' )
+ break;
+
+ if ( result == (char **) 0 )
+ result = (char **) malloc( sizeof(char *) );
+ else
+ result = (char **) realloc( result,
+ sizeof(char *) * (num + 1) );
+
+ result[num++] = (char *) strdup( buf );
+ }
+ if ( result == (char **) 0 )
+ return( NULL );
+ result = (char **) realloc( result, sizeof(char *) * (num + 1) );
+ result[num] = NULL;
+
+ return( result );
+}
+
+
+static void
+free_list( char **list )
+{
+ int i;
+
+ if ( list != NULL ) {
+ for ( i = 0; list[ i ] != NULL; ++i ) {
+ free( list[ i ] );
+ }
+ free( (char *)list );
+ }
+}
+
+
+static int
+file_read( const char *path, struct berval *bv )
+{
+ FILE *fp;
+ ber_slen_t rlen;
+ int eof;
+
+ if (( fp = fopen( path, "r" )) == NULL ) {
+ perror( path );
+ return( -1 );
+ }
+
+ if ( fseek( fp, 0L, SEEK_END ) != 0 ) {
+ perror( path );
+ fclose( fp );
+ return( -1 );
+ }
+
+ bv->bv_len = ftell( fp );
+
+ if (( bv->bv_val = (char *)malloc( bv->bv_len )) == NULL ) {
+ perror( "malloc" );
+ fclose( fp );
+ return( -1 );
+ }
+
+ if ( fseek( fp, 0L, SEEK_SET ) != 0 ) {
+ perror( path );
+ fclose( fp );
+ return( -1 );
+ }
+
+ rlen = fread( bv->bv_val, 1, bv->bv_len, fp );
+ eof = feof( fp );
+ fclose( fp );
+
+ if ( (ber_len_t) rlen != bv->bv_len ) {
+ perror( path );
+ free( bv->bv_val );
+ return( -1 );
+ }
+
+ return( bv->bv_len );
+}
+
+
+static LDAPMod **
+get_modlist(
+ const char *prompt1,
+ const char *prompt2,
+ const char *prompt3 )
+{
+ static char buf[256];
+ int num;
+ LDAPMod tmp = { 0 };
+ LDAPMod **result;
+ struct berval **bvals;
+
+ num = 0;
+ result = NULL;
+ while ( 1 ) {
+ if ( prompt1 ) {
+ get_line( buf, sizeof(buf), stdin, prompt1 );
+ tmp.mod_op = atoi( buf );
+
+ if ( tmp.mod_op == -1 || buf[0] == '\0' )
+ break;
+ }
+
+ get_line( buf, sizeof(buf), stdin, prompt2 );
+ if ( buf[0] == '\0' )
+ break;
+ tmp.mod_type = strdup( buf );
+
+ tmp.mod_values = get_list( prompt3 );
+
+ if ( tmp.mod_values != NULL ) {
+ int i;
+
+ for ( i = 0; tmp.mod_values[i] != NULL; ++i )
+ ;
+ bvals = (struct berval **)calloc( i + 1,
+ sizeof( struct berval *));
+ for ( i = 0; tmp.mod_values[i] != NULL; ++i ) {
+ bvals[i] = (struct berval *)malloc(
+ sizeof( struct berval ));
+ if ( strncmp( tmp.mod_values[i], "{FILE}",
+ 6 ) == 0 ) {
+ if ( file_read( tmp.mod_values[i] + 6,
+ bvals[i] ) < 0 ) {
+ free( bvals );
+ for ( i = 0; i<num; i++ )
+ free( result[ i ] );
+ free( result );
+ return( NULL );
+ }
+ } else {
+ bvals[i]->bv_val = tmp.mod_values[i];
+ bvals[i]->bv_len =
+ strlen( tmp.mod_values[i] );
+ }
+ }
+ tmp.mod_bvalues = bvals;
+ tmp.mod_op |= LDAP_MOD_BVALUES;
+ }
+
+ if ( result == NULL )
+ result = (LDAPMod **) malloc( sizeof(LDAPMod *) );
+ else
+ result = (LDAPMod **) realloc( result,
+ sizeof(LDAPMod *) * (num + 1) );
+
+ result[num] = (LDAPMod *) malloc( sizeof(LDAPMod) );
+ *(result[num]) = tmp; /* struct copy */
+ num++;
+ }
+ if ( result == NULL )
+ return( NULL );
+ result = (LDAPMod **) realloc( result, sizeof(LDAPMod *) * (num + 1) );
+ result[num] = NULL;
+
+ return( result );
+}
+
+
+static int
+bind_prompt( LDAP *ld,
+ LDAP_CONST char *url,
+ ber_tag_t request, ber_int_t msgid,
+ void *params )
+{
+ static char dn[256], passwd[256];
+ int authmethod;
+
+ printf("rebind for request=%ld msgid=%ld url=%s\n",
+ request, (long) msgid, url );
+
+ authmethod = LDAP_AUTH_SIMPLE;
+
+ get_line( dn, sizeof(dn), stdin, "re-bind dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( authmethod == LDAP_AUTH_SIMPLE && dn[0] != '\0' ) {
+ get_line( passwd, sizeof(passwd), stdin,
+ "re-bind password? " );
+ } else {
+ passwd[0] = '\0';
+ }
+
+ return ldap_bind_s( ld, dn, passwd, authmethod);
+}
+
+
+int
+main( int argc, char **argv )
+{
+ LDAP *ld = NULL;
+ int i, c, port, errflg, method, id, msgtype;
+ char line[256], command1, command2, command3;
+ char passwd[64], dn[256], rdn[64], attr[64], value[256];
+ char filter[256], *host, **types;
+ char **exdn;
+ static const char usage[] =
+ "usage: %s [-u] [-h host] [-d level] [-s dnsuffix] [-p port] [-t file] [-T file]\n";
+ int bound, all, scope, attrsonly;
+ LDAPMessage *res;
+ LDAPMod **mods, **attrs;
+ struct timeval timeout;
+ char *copyfname = NULL;
+ int copyoptions = 0;
+ LDAPURLDesc *ludp;
+
+ host = NULL;
+ port = LDAP_PORT;
+ dnsuffix = "";
+ errflg = 0;
+
+ while (( c = getopt( argc, argv, "h:d:s:p:t:T:" )) != -1 ) {
+ switch( c ) {
+ case 'd':
+#ifdef LDAP_DEBUG
+ ldap_debug = atoi( optarg );
+#ifdef LBER_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
+ }
+#endif
+#else
+ printf( "Compile with -DLDAP_DEBUG for debugging\n" );
+#endif
+ break;
+
+ case 'h':
+ host = optarg;
+ break;
+
+ case 's':
+ dnsuffix = optarg;
+ break;
+
+ case 'p':
+ port = atoi( optarg );
+ break;
+
+ case 't': /* copy ber's to given file */
+ copyfname = strdup( optarg );
+/* copyoptions = LBER_TO_FILE; */
+ break;
+
+ case 'T': /* only output ber's to given file */
+ copyfname = strdup( optarg );
+/* copyoptions = (LBER_TO_FILE | LBER_TO_FILE_ONLY); */
+ break;
+
+ default:
+ ++errflg;
+ }
+ }
+
+ if ( host == NULL && optind == argc - 1 ) {
+ host = argv[ optind ];
+ ++optind;
+ }
+
+ if ( errflg || optind < argc - 1 ) {
+ fprintf( stderr, usage, argv[ 0 ] );
+ exit( EXIT_FAILURE );
+ }
+
+ printf( "ldap_init( %s, %d )\n",
+ host == NULL ? "(null)" : host, port );
+
+ ld = ldap_init( host, port );
+
+ if ( ld == NULL ) {
+ perror( "ldap_init" );
+ exit( EXIT_FAILURE );
+ }
+
+ if ( copyfname != NULL ) {
+ if ( ( ld->ld_sb->sb_fd = open( copyfname, O_WRONLY|O_CREAT|O_EXCL,
+ 0600 )) == -1 ) {
+ perror( copyfname );
+ exit ( EXIT_FAILURE );
+ }
+ ld->ld_sb->sb_options = copyoptions;
+ }
+
+ bound = 0;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ (void) memset( line, '\0', sizeof(line) );
+ while ( get_line( line, sizeof(line), stdin, "\ncommand? " ) != NULL ) {
+ command1 = line[0];
+ command2 = line[1];
+ command3 = line[2];
+
+ switch ( command1 ) {
+ case 'a': /* add or abandon */
+ switch ( command2 ) {
+ case 'd': /* add */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (attrs = get_modlist( NULL, "attr? ",
+ "value? " )) == NULL )
+ break;
+ if ( (id = ldap_add( ld, dn, attrs )) == -1 )
+ ldap_perror( ld, "ldap_add" );
+ else
+ printf( "Add initiated with id %d\n",
+ id );
+ break;
+
+ case 'b': /* abandon */
+ get_line( line, sizeof(line), stdin, "msgid? " );
+ id = atoi( line );
+ if ( ldap_abandon( ld, id ) != 0 )
+ ldap_perror( ld, "ldap_abandon" );
+ else
+ printf( "Abandon successful\n" );
+ break;
+ default:
+ printf( "Possibilities: [ad]d, [ab]ort\n" );
+ }
+ break;
+
+ case 'b': /* asynch bind */
+ method = LDAP_AUTH_SIMPLE;
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( method == LDAP_AUTH_SIMPLE && dn[0] != '\0' )
+ get_line( passwd, sizeof(passwd), stdin,
+ "password? " );
+ else
+ passwd[0] = '\0';
+
+ if ( ldap_bind( ld, dn, passwd, method ) == -1 ) {
+ fprintf( stderr, "ldap_bind failed\n" );
+ ldap_perror( ld, "ldap_bind" );
+ } else {
+ printf( "Bind initiated\n" );
+ bound = 1;
+ }
+ break;
+
+ case 'B': /* synch bind */
+ method = LDAP_AUTH_SIMPLE;
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+
+ if ( dn[0] != '\0' )
+ get_line( passwd, sizeof(passwd), stdin,
+ "password? " );
+ else
+ passwd[0] = '\0';
+
+ if ( ldap_bind_s( ld, dn, passwd, method ) !=
+ LDAP_SUCCESS ) {
+ fprintf( stderr, "ldap_bind_s failed\n" );
+ ldap_perror( ld, "ldap_bind_s" );
+ } else {
+ printf( "Bind successful\n" );
+ bound = 1;
+ }
+ break;
+
+ case 'c': /* compare */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ get_line( attr, sizeof(attr), stdin, "attr? " );
+ get_line( value, sizeof(value), stdin, "value? " );
+
+ if ( (id = ldap_compare( ld, dn, attr, value )) == -1 )
+ ldap_perror( ld, "ldap_compare" );
+ else
+ printf( "Compare initiated with id %d\n", id );
+ break;
+
+ case 'd': /* turn on debugging */
+#ifdef LDAP_DEBUG
+ get_line( line, sizeof(line), stdin, "debug level? " );
+ ldap_debug = atoi( line );
+#ifdef LBER_DEBUG
+ if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
+ ber_set_option( NULL, LBER_OPT_DEBUG_LEVEL, &ldap_debug );
+ }
+#endif
+#else
+ printf( "Compile with -DLDAP_DEBUG for debugging\n" );
+#endif
+ break;
+
+ case 'E': /* explode a dn */
+ get_line( line, sizeof(line), stdin, "dn? " );
+ exdn = ldap_explode_dn( line, 0 );
+ for ( i = 0; exdn != NULL && exdn[i] != NULL; i++ ) {
+ printf( "\t%s\n", exdn[i] );
+ }
+ break;
+
+ case 'g': /* set next msgid */
+ get_line( line, sizeof(line), stdin, "msgid? " );
+ ld->ld_msgid = atoi( line );
+ break;
+
+ case 'v': /* set version number */
+ get_line( line, sizeof(line), stdin, "version? " );
+ ld->ld_version = atoi( line );
+ break;
+
+ case 'm': /* modify or modifyrdn */
+ if ( strncmp( line, "modify", 4 ) == 0 ) {
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (mods = get_modlist(
+ "mod (0=>add, 1=>delete, 2=>replace -1=>done)? ",
+ "attribute type? ", "attribute value? " ))
+ == NULL )
+ break;
+ if ( (id = ldap_modify( ld, dn, mods )) == -1 )
+ ldap_perror( ld, "ldap_modify" );
+ else
+ printf( "Modify initiated with id %d\n",
+ id );
+ } else if ( strncmp( line, "modrdn", 4 ) == 0 ) {
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ get_line( rdn, sizeof(rdn), stdin, "newrdn? " );
+ if ( (id = ldap_modrdn( ld, dn, rdn )) == -1 )
+ ldap_perror( ld, "ldap_modrdn" );
+ else
+ printf( "Modrdn initiated with id %d\n",
+ id );
+ } else {
+ printf( "Possibilities: [modi]fy, [modr]dn\n" );
+ }
+ break;
+
+ case 'q': /* quit */
+ ldap_unbind( ld );
+ exit( EXIT_SUCCESS );
+ break;
+
+ case 'r': /* result or remove */
+ switch ( command3 ) {
+ case 's': /* result */
+ get_line( line, sizeof(line), stdin,
+ "msgid (-1=>any)? " );
+ if ( line[0] == '\0' )
+ id = -1;
+ else
+ id = atoi( line );
+ get_line( line, sizeof(line), stdin,
+ "all (0=>any, 1=>all)? " );
+ if ( line[0] == '\0' )
+ all = 1;
+ else
+ all = atoi( line );
+ if (( msgtype = ldap_result( ld, id, all,
+ &timeout, &res )) < 1 ) {
+ ldap_perror( ld, "ldap_result" );
+ break;
+ }
+ printf( "\nresult: msgtype %d msgid %d\n",
+ msgtype, res->lm_msgid );
+ handle_result( ld, res );
+ res = NULL;
+ break;
+
+ case 'm': /* remove */
+ get_line( dn, sizeof(dn), stdin, "dn? " );
+ strcat( dn, dnsuffix );
+ if ( (id = ldap_delete( ld, dn )) == -1 )
+ ldap_perror( ld, "ldap_delete" );
+ else
+ printf( "Remove initiated with id %d\n",
+ id );
+ break;
+
+ default:
+ printf( "Possibilities: [rem]ove, [res]ult\n" );
+ break;
+ }
+ break;
+
+ case 's': /* search */
+ get_line( dn, sizeof(dn), stdin, "searchbase? " );
+ strcat( dn, dnsuffix );
+ get_line( line, sizeof(line), stdin,
+ "scope (0=baseObject, 1=oneLevel, 2=subtree, 3=children)? " );
+ scope = atoi( line );
+ get_line( filter, sizeof(filter), stdin,
+ "search filter (e.g. sn=jones)? " );
+ types = get_list( "attrs to return? " );
+ get_line( line, sizeof(line), stdin,
+ "attrsonly (0=attrs&values, 1=attrs only)? " );
+ attrsonly = atoi( line );
+
+ if (( id = ldap_search( ld, dn, scope, filter,
+ types, attrsonly )) == -1 ) {
+ ldap_perror( ld, "ldap_search" );
+ } else {
+ printf( "Search initiated with id %d\n", id );
+ }
+ free_list( types );
+ break;
+
+ case 't': /* set timeout value */
+ get_line( line, sizeof(line), stdin, "timeout? " );
+ timeout.tv_sec = atoi( line );
+ break;
+
+ case 'p': /* parse LDAP URL */
+ get_line( line, sizeof(line), stdin, "LDAP URL? " );
+ if (( i = ldap_url_parse( line, &ludp )) != 0 ) {
+ fprintf( stderr, "ldap_url_parse: error %d\n", i );
+ } else {
+ printf( "\t host: " );
+ if ( ludp->lud_host == NULL ) {
+ printf( "DEFAULT\n" );
+ } else {
+ printf( "<%s>\n", ludp->lud_host );
+ }
+ printf( "\t port: " );
+ if ( ludp->lud_port == 0 ) {
+ printf( "DEFAULT\n" );
+ } else {
+ printf( "%d\n", ludp->lud_port );
+ }
+ printf( "\t dn: <%s>\n", ludp->lud_dn );
+ printf( "\t attrs:" );
+ if ( ludp->lud_attrs == NULL ) {
+ printf( " ALL" );
+ } else {
+ for ( i = 0; ludp->lud_attrs[ i ] != NULL; ++i ) {
+ printf( " <%s>", ludp->lud_attrs[ i ] );
+ }
+ }
+ printf( "\n\t scope: %s\n",
+ ludp->lud_scope == LDAP_SCOPE_BASE ? "baseObject"
+ : ludp->lud_scope == LDAP_SCOPE_ONELEVEL ? "oneLevel"
+ : ludp->lud_scope == LDAP_SCOPE_SUBTREE ? "subtree"
+#ifdef LDAP_SCOPE_SUBORDINATE
+ : ludp->lud_scope == LDAP_SCOPE_SUBORDINATE ? "children"
+#endif
+ : "**invalid**" );
+ printf( "\tfilter: <%s>\n", ludp->lud_filter );
+ ldap_free_urldesc( ludp );
+ }
+ break;
+
+ case 'n': /* set dn suffix, for convenience */
+ get_line( line, sizeof(line), stdin, "DN suffix? " );
+ strcpy( dnsuffix, line );
+ break;
+
+ case 'o': /* set ldap options */
+ get_line( line, sizeof(line), stdin, "alias deref (0=never, 1=searching, 2=finding, 3=always)?" );
+ ld->ld_deref = atoi( line );
+ get_line( line, sizeof(line), stdin, "timelimit?" );
+ ld->ld_timelimit = atoi( line );
+ get_line( line, sizeof(line), stdin, "sizelimit?" );
+ ld->ld_sizelimit = atoi( line );
+
+ LDAP_BOOL_ZERO(&ld->ld_options);
+
+ get_line( line, sizeof(line), stdin,
+ "Recognize and chase referrals (0=no, 1=yes)?" );
+ if ( atoi( line ) != 0 ) {
+ LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_REFERRALS);
+ get_line( line, sizeof(line), stdin,
+ "Prompt for bind credentials when chasing referrals (0=no, 1=yes)?" );
+ if ( atoi( line ) != 0 ) {
+ ldap_set_rebind_proc( ld, bind_prompt, NULL );
+ }
+ }
+ break;
+
+ case '?': /* help */
+ printf(
+"Commands: [ad]d [ab]andon [b]ind\n"
+" [B]ind async [c]ompare\n"
+" [modi]fy [modr]dn [rem]ove\n"
+" [res]ult [s]earch [q]uit/unbind\n\n"
+" [d]ebug set ms[g]id\n"
+" d[n]suffix [t]imeout [v]ersion\n"
+" [?]help [o]ptions"
+" [E]xplode dn [p]arse LDAP URL\n" );
+ break;
+
+ default:
+ printf( "Invalid command. Type ? for help.\n" );
+ break;
+ }
+
+ (void) memset( line, '\0', sizeof(line) );
+ }
+
+ return( 0 );
+}
+
+static void
+handle_result( LDAP *ld, LDAPMessage *lm )
+{
+ switch ( lm->lm_msgtype ) {
+ case LDAP_RES_COMPARE:
+ printf( "Compare result\n" );
+ print_ldap_result( ld, lm, "compare" );
+ break;
+
+ case LDAP_RES_SEARCH_RESULT:
+ printf( "Search result\n" );
+ print_ldap_result( ld, lm, "search" );
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+ printf( "Search entry\n" );
+ print_search_entry( ld, lm );
+ break;
+
+ case LDAP_RES_ADD:
+ printf( "Add result\n" );
+ print_ldap_result( ld, lm, "add" );
+ break;
+
+ case LDAP_RES_DELETE:
+ printf( "Delete result\n" );
+ print_ldap_result( ld, lm, "delete" );
+ break;
+
+ case LDAP_RES_MODRDN:
+ printf( "ModRDN result\n" );
+ print_ldap_result( ld, lm, "modrdn" );
+ break;
+
+ case LDAP_RES_BIND:
+ printf( "Bind result\n" );
+ print_ldap_result( ld, lm, "bind" );
+ break;
+
+ default:
+ printf( "Unknown result type 0x%lx\n",
+ (unsigned long) lm->lm_msgtype );
+ print_ldap_result( ld, lm, "unknown" );
+ }
+}
+
+static void
+print_ldap_result( LDAP *ld, LDAPMessage *lm, const char *s )
+{
+ ldap_result2error( ld, lm, 1 );
+ ldap_perror( ld, s );
+/*
+ if ( ld->ld_error != NULL && *ld->ld_error != '\0' )
+ fprintf( stderr, "Additional info: %s\n", ld->ld_error );
+ if ( LDAP_NAME_ERROR( ld->ld_errno ) && ld->ld_matched != NULL )
+ fprintf( stderr, "Matched DN: %s\n", ld->ld_matched );
+*/
+}
+
+static void
+print_search_entry( LDAP *ld, LDAPMessage *res )
+{
+ LDAPMessage *e;
+
+ for ( e = ldap_first_entry( ld, res ); e != NULL;
+ e = ldap_next_entry( ld, e ) )
+ {
+ BerElement *ber = NULL;
+ char *a, *dn, *ufn;
+
+ if ( e->lm_msgtype == LDAP_RES_SEARCH_RESULT )
+ break;
+
+ dn = ldap_get_dn( ld, e );
+ printf( "\tDN: %s\n", dn );
+
+ ufn = ldap_dn2ufn( dn );
+ printf( "\tUFN: %s\n", ufn );
+
+ free( dn );
+ free( ufn );
+
+ for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL;
+ a = ldap_next_attribute( ld, e, ber ) )
+ {
+ struct berval **vals;
+
+ printf( "\t\tATTR: %s\n", a );
+ if ( (vals = ldap_get_values_len( ld, e, a ))
+ == NULL ) {
+ printf( "\t\t\t(no values)\n" );
+ } else {
+ int i;
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ int j, nonascii;
+
+ nonascii = 0;
+ for ( j = 0; (ber_len_t) j < vals[i]->bv_len; j++ )
+ if ( !isascii( vals[i]->bv_val[j] ) ) {
+ nonascii = 1;
+ break;
+ }
+
+ if ( nonascii ) {
+ printf( "\t\t\tlength (%ld) (not ascii)\n", vals[i]->bv_len );
+#ifdef BPRINT_NONASCII
+ ber_bprint( vals[i]->bv_val,
+ vals[i]->bv_len );
+#endif /* BPRINT_NONASCII */
+ continue;
+ }
+ printf( "\t\t\tlength (%ld) %s\n",
+ vals[i]->bv_len, vals[i]->bv_val );
+ }
+ ber_bvecfree( vals );
+ }
+ }
+
+ if(ber != NULL) {
+ ber_free( ber, 0 );
+ }
+ }
+
+ if ( res->lm_msgtype == LDAP_RES_SEARCH_RESULT
+ || res->lm_chain != NULL )
+ print_ldap_result( ld, res, "search" );
+}
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
new file mode 100644
index 0000000..82ca527
--- /dev/null
+++ b/libraries/libldap/tls2.c
@@ -0,0 +1,1413 @@
+/* tls.c - Handle tls/ssl. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: restructured by Howard Chu.
+ */
+
+#include "portable.h"
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+
+#include "ldap-int.h"
+
+#ifdef HAVE_TLS
+
+#include "ldap-tls.h"
+
+static tls_impl *tls_imp = &ldap_int_tls_impl;
+#define HAS_TLS( sb ) ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \
+ (void *)tls_imp->ti_sbio )
+
+#endif /* HAVE_TLS */
+
+#ifdef LDAP_DEVEL
+#define LDAP_USE_NON_BLOCKING_TLS
+#endif /* LDAP_DEVEL */
+
+/* RFC2459 minimum required set of supported attribute types
+ * in a certificate DN
+ */
+typedef struct oid_name {
+ struct berval oid;
+ struct berval name;
+} oid_name;
+
+static oid_name oids[] = {
+ { BER_BVC("2.5.4.3"), BER_BVC("cn") },
+ { BER_BVC("2.5.4.4"), BER_BVC("sn") },
+ { BER_BVC("2.5.4.6"), BER_BVC("c") },
+ { BER_BVC("2.5.4.7"), BER_BVC("l") },
+ { BER_BVC("2.5.4.8"), BER_BVC("st") },
+ { BER_BVC("2.5.4.10"), BER_BVC("o") },
+ { BER_BVC("2.5.4.11"), BER_BVC("ou") },
+ { BER_BVC("2.5.4.12"), BER_BVC("title") },
+ { BER_BVC("2.5.4.41"), BER_BVC("name") },
+ { BER_BVC("2.5.4.42"), BER_BVC("givenName") },
+ { BER_BVC("2.5.4.43"), BER_BVC("initials") },
+ { BER_BVC("2.5.4.44"), BER_BVC("generationQualifier") },
+ { BER_BVC("2.5.4.46"), BER_BVC("dnQualifier") },
+ { BER_BVC("1.2.840.113549.1.9.1"), BER_BVC("email") },
+ { BER_BVC("0.9.2342.19200300.100.1.25"), BER_BVC("dc") },
+ { BER_BVNULL, BER_BVNULL }
+};
+
+#ifdef HAVE_TLS
+
+void
+ldap_pvt_tls_ctx_free ( void *c )
+{
+ if ( !c ) return;
+ tls_imp->ti_ctx_free( c );
+}
+
+static void
+tls_ctx_ref( tls_ctx *ctx )
+{
+ if ( !ctx ) return;
+
+ tls_imp->ti_ctx_ref( ctx );
+}
+
+#ifdef LDAP_R_COMPILE
+/*
+ * an extra mutex for the default ctx.
+ */
+static ldap_pvt_thread_mutex_t tls_def_ctx_mutex;
+#endif
+
+void
+ldap_int_tls_destroy( struct ldapoptions *lo )
+{
+ if ( lo->ldo_tls_ctx ) {
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ }
+
+ if ( lo->ldo_tls_certfile ) {
+ LDAP_FREE( lo->ldo_tls_certfile );
+ lo->ldo_tls_certfile = NULL;
+ }
+ if ( lo->ldo_tls_keyfile ) {
+ LDAP_FREE( lo->ldo_tls_keyfile );
+ lo->ldo_tls_keyfile = NULL;
+ }
+ if ( lo->ldo_tls_dhfile ) {
+ LDAP_FREE( lo->ldo_tls_dhfile );
+ lo->ldo_tls_dhfile = NULL;
+ }
+ if ( lo->ldo_tls_ecname ) {
+ LDAP_FREE( lo->ldo_tls_ecname );
+ lo->ldo_tls_ecname = NULL;
+ }
+ if ( lo->ldo_tls_cacertfile ) {
+ LDAP_FREE( lo->ldo_tls_cacertfile );
+ lo->ldo_tls_cacertfile = NULL;
+ }
+ if ( lo->ldo_tls_cacertdir ) {
+ LDAP_FREE( lo->ldo_tls_cacertdir );
+ lo->ldo_tls_cacertdir = NULL;
+ }
+ if ( lo->ldo_tls_ciphersuite ) {
+ LDAP_FREE( lo->ldo_tls_ciphersuite );
+ lo->ldo_tls_ciphersuite = NULL;
+ }
+ if ( lo->ldo_tls_crlfile ) {
+ LDAP_FREE( lo->ldo_tls_crlfile );
+ lo->ldo_tls_crlfile = NULL;
+ }
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+void
+ldap_pvt_tls_destroy( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+
+ ldap_int_tls_destroy( lo );
+
+ tls_imp->ti_tls_destroy();
+}
+
+/*
+ * Initialize a particular TLS implementation.
+ * Called once per implementation.
+ */
+static int
+tls_init(tls_impl *impl )
+{
+ static int tls_initialized = 0;
+
+ if ( !tls_initialized++ ) {
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex );
+#endif
+ }
+
+ if ( impl->ti_inited++ ) return 0;
+
+#ifdef LDAP_R_COMPILE
+ impl->ti_thr_init();
+#endif
+ return impl->ti_tls_init();
+}
+
+/*
+ * Initialize TLS subsystem. Called once per implementation.
+ */
+int
+ldap_pvt_tls_init( void )
+{
+ return tls_init( tls_imp );
+}
+
+/*
+ * initialize a new TLS context
+ */
+static int
+ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
+{
+ int rc = 0;
+ tls_impl *ti = tls_imp;
+ struct ldaptls lts = lo->ldo_tls_info;
+
+ if ( lo->ldo_tls_ctx )
+ return 0;
+
+ tls_init( ti );
+
+ if ( is_server && !lts.lt_certfile && !lts.lt_keyfile &&
+ !lts.lt_cacertfile && !lts.lt_cacertdir ) {
+ /* minimum configuration not provided */
+ return LDAP_NOT_SUPPORTED;
+ }
+
+#ifdef HAVE_EBCDIC
+ /* This ASCII/EBCDIC handling is a real pain! */
+ if ( lts.lt_ciphersuite ) {
+ lts.lt_ciphersuite = LDAP_STRDUP( lts.lt_ciphersuite );
+ __atoe( lts.lt_ciphersuite );
+ }
+ if ( lts.lt_cacertfile ) {
+ lts.lt_cacertfile = LDAP_STRDUP( lts.lt_cacertfile );
+ __atoe( lts.lt_cacertfile );
+ }
+ if ( lts.lt_certfile ) {
+ lts.lt_certfile = LDAP_STRDUP( lts.lt_certfile );
+ __atoe( lts.lt_certfile );
+ }
+ if ( lts.lt_keyfile ) {
+ lts.lt_keyfile = LDAP_STRDUP( lts.lt_keyfile );
+ __atoe( lts.lt_keyfile );
+ }
+ if ( lts.lt_crlfile ) {
+ lts.lt_crlfile = LDAP_STRDUP( lts.lt_crlfile );
+ __atoe( lts.lt_crlfile );
+ }
+ if ( lts.lt_cacertdir ) {
+ lts.lt_cacertdir = LDAP_STRDUP( lts.lt_cacertdir );
+ __atoe( lts.lt_cacertdir );
+ }
+ if ( lts.lt_dhfile ) {
+ lts.lt_dhfile = LDAP_STRDUP( lts.lt_dhfile );
+ __atoe( lts.lt_dhfile );
+ }
+ if ( lts.lt_ecname ) {
+ lts.lt_ecname = LDAP_STRDUP( lts.lt_ecname );
+ __atoe( lts.lt_ecname );
+ }
+#endif
+ lo->ldo_tls_ctx = ti->ti_ctx_new( lo );
+ if ( lo->ldo_tls_ctx == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not allocate default ctx.\n",
+ 0,0,0);
+ rc = -1;
+ goto error_exit;
+ }
+
+ rc = ti->ti_ctx_init( lo, &lts, is_server );
+
+error_exit:
+ if ( rc < 0 && lo->ldo_tls_ctx != NULL ) {
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ }
+#ifdef HAVE_EBCDIC
+ LDAP_FREE( lts.lt_ciphersuite );
+ LDAP_FREE( lts.lt_cacertfile );
+ LDAP_FREE( lts.lt_certfile );
+ LDAP_FREE( lts.lt_keyfile );
+ LDAP_FREE( lts.lt_crlfile );
+ LDAP_FREE( lts.lt_cacertdir );
+ LDAP_FREE( lts.lt_dhfile );
+ LDAP_FREE( lts.lt_ecname );
+#endif
+ return rc;
+}
+
+/*
+ * initialize the default context
+ */
+int
+ldap_pvt_tls_init_def_ctx( int is_server )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+ int rc;
+ LDAP_MUTEX_LOCK( &tls_def_ctx_mutex );
+ rc = ldap_int_tls_init_ctx( lo, is_server );
+ LDAP_MUTEX_UNLOCK( &tls_def_ctx_mutex );
+ return rc;
+}
+
+static tls_session *
+alloc_handle( void *ctx_arg, int is_server )
+{
+ tls_ctx *ctx;
+ tls_session *ssl;
+
+ if ( ctx_arg ) {
+ ctx = ctx_arg;
+ } else {
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+ if ( ldap_pvt_tls_init_def_ctx( is_server ) < 0 ) return NULL;
+ ctx = lo->ldo_tls_ctx;
+ }
+
+ ssl = tls_imp->ti_session_new( ctx, is_server );
+ if ( ssl == NULL ) {
+ Debug( LDAP_DEBUG_ANY,"TLS: can't create ssl handle.\n",0,0,0);
+ return NULL;
+ }
+ return ssl;
+}
+
+static int
+update_flags( Sockbuf *sb, tls_session * ssl, int rc )
+{
+ sb->sb_trans_needs_read = 0;
+ sb->sb_trans_needs_write = 0;
+
+ return tls_imp->ti_session_upflags( sb, ssl, rc );
+}
+
+/*
+ * Call this to do a TLS connect on a sockbuf. ctx_arg can be
+ * a SSL_CTX * or NULL, in which case the default ctx is used.
+ *
+ * Return value:
+ *
+ * 0 - Success. Connection is ready for communication.
+ * <0 - Error. Can't create a TLS stream.
+ * >0 - Partial success.
+ * Do a select (using information from lber_pvt_sb_needs_{read,write}
+ * and call again.
+ */
+
+static int
+ldap_int_tls_connect( LDAP *ld, LDAPConn *conn, const char *host )
+{
+ Sockbuf *sb = conn->lconn_sb;
+ int err;
+ tls_session *ssl = NULL;
+
+ if ( HAS_TLS( sb )) {
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
+ } else {
+ struct ldapoptions *lo;
+ tls_ctx *ctx;
+
+ ctx = ld->ld_options.ldo_tls_ctx;
+
+ ssl = alloc_handle( ctx, 0 );
+
+ if ( ssl == NULL ) return -1;
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+ ber_sockbuf_add_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
+
+ lo = LDAP_INT_GLOBAL_OPT();
+ if( ctx == NULL ) {
+ ctx = lo->ldo_tls_ctx;
+ ld->ld_options.ldo_tls_ctx = ctx;
+ tls_ctx_ref( ctx );
+ }
+ if ( ld->ld_options.ldo_tls_connect_cb )
+ ld->ld_options.ldo_tls_connect_cb( ld, ssl, ctx,
+ ld->ld_options.ldo_tls_connect_arg );
+ if ( lo && lo->ldo_tls_connect_cb && lo->ldo_tls_connect_cb !=
+ ld->ld_options.ldo_tls_connect_cb )
+ lo->ldo_tls_connect_cb( ld, ssl, ctx, lo->ldo_tls_connect_arg );
+ }
+
+ err = tls_imp->ti_session_connect( ld, ssl );
+
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+
+ if ( err == 0 ) {
+ err = ldap_pvt_tls_check_hostname( ld, ssl, host );
+ }
+
+ if ( err < 0 )
+ {
+ char buf[256], *msg;
+ if ( update_flags( sb, ssl, err )) {
+ return 1;
+ }
+
+ msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) );
+ if ( msg ) {
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP( msg );
+#ifdef HAVE_EBCDIC
+ if ( ld->ld_error ) __etoa(ld->ld_error);
+#endif
+ }
+
+ Debug( LDAP_DEBUG_ANY,"TLS: can't connect: %s.\n",
+ ld->ld_error ? ld->ld_error : "" ,0,0);
+
+ ber_sockbuf_remove_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Call this to do a TLS accept on a sockbuf.
+ * Everything else is the same as with tls_connect.
+ */
+int
+ldap_pvt_tls_accept( Sockbuf *sb, void *ctx_arg )
+{
+ int err;
+ tls_session *ssl = NULL;
+
+ if ( HAS_TLS( sb )) {
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&ssl );
+ } else {
+ ssl = alloc_handle( ctx_arg, 1 );
+ if ( ssl == NULL ) return -1;
+
+#ifdef LDAP_DEBUG
+ ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)"tls_" );
+#endif
+ ber_sockbuf_add_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT, (void *)ssl );
+ }
+
+ err = tls_imp->ti_session_accept( ssl );
+
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+
+ if ( err < 0 )
+ {
+ if ( update_flags( sb, ssl, err )) return 1;
+
+ if ( DebugTest( LDAP_DEBUG_ANY ) ) {
+ char buf[256], *msg;
+ msg = tls_imp->ti_session_errmsg( ssl, err, buf, sizeof(buf) );
+ Debug( LDAP_DEBUG_ANY,"TLS: can't accept: %s.\n",
+ msg ? msg : "(unknown)", 0, 0 );
+ }
+
+ ber_sockbuf_remove_io( sb, tls_imp->ti_sbio,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#ifdef LDAP_DEBUG
+ ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug,
+ LBER_SBIOD_LEVEL_TRANSPORT );
+#endif
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_tls_inplace ( Sockbuf *sb )
+{
+ return HAS_TLS( sb ) ? 1 : 0;
+}
+
+int
+ldap_tls_inplace( LDAP *ld )
+{
+ Sockbuf *sb = NULL;
+
+ if ( ld->ld_defconn && ld->ld_defconn->lconn_sb ) {
+ sb = ld->ld_defconn->lconn_sb;
+
+ } else if ( ld->ld_sb ) {
+ sb = ld->ld_sb;
+
+ } else {
+ return 0;
+ }
+
+ return ldap_pvt_tls_inplace( sb );
+}
+
+int
+ldap_pvt_tls_get_peer_dn( void *s, struct berval *dn,
+ LDAPDN_rewrite_dummy *func, unsigned flags )
+{
+ tls_session *session = s;
+ struct berval bvdn;
+ int rc;
+
+ rc = tls_imp->ti_session_peer_dn( session, &bvdn );
+ if ( rc ) return rc;
+
+ rc = ldap_X509dn2bv( &bvdn, dn,
+ (LDAPDN_rewrite_func *)func, flags);
+ return rc;
+}
+
+int
+ldap_pvt_tls_check_hostname( LDAP *ld, void *s, const char *name_in )
+{
+ tls_session *session = s;
+
+ if (ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_NEVER &&
+ ld->ld_options.ldo_tls_require_cert != LDAP_OPT_X_TLS_ALLOW) {
+ ld->ld_errno = tls_imp->ti_session_chkhost( ld, session, name_in );
+ if (ld->ld_errno != LDAP_SUCCESS) {
+ return ld->ld_errno;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+int
+ldap_int_tls_config( LDAP *ld, int option, const char *arg )
+{
+ int i;
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ case LDAP_OPT_X_TLS_CERTFILE:
+ case LDAP_OPT_X_TLS_KEYFILE:
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ case LDAP_OPT_X_TLS_DHFILE:
+ case LDAP_OPT_X_TLS_ECNAME:
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ return ldap_pvt_tls_set_option( ld, option, (void *) arg );
+
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ case LDAP_OPT_X_TLS:
+ i = -1;
+ if ( strcasecmp( arg, "never" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_NEVER ;
+
+ } else if ( strcasecmp( arg, "demand" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_DEMAND ;
+
+ } else if ( strcasecmp( arg, "allow" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_ALLOW ;
+
+ } else if ( strcasecmp( arg, "try" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_TRY ;
+
+ } else if ( ( strcasecmp( arg, "hard" ) == 0 ) ||
+ ( strcasecmp( arg, "on" ) == 0 ) ||
+ ( strcasecmp( arg, "yes" ) == 0) ||
+ ( strcasecmp( arg, "true" ) == 0 ) )
+ {
+ i = LDAP_OPT_X_TLS_HARD ;
+ }
+
+ if (i >= 0) {
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+ return -1;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN: {
+ char *next;
+ long l;
+ l = strtol( arg, &next, 10 );
+ if ( l < 0 || l > 0xff || next == arg ||
+ ( *next != '\0' && *next != '.' ) )
+ return -1;
+ i = l << 8;
+ if (*next == '.') {
+ arg = next + 1;
+ l = strtol( arg, &next, 10 );
+ if ( l < 0 || l > 0xff || next == arg || *next != '\0' )
+ return -1;
+ i += l;
+ }
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+#ifdef HAVE_OPENSSL_CRL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ i = -1;
+ if ( strcasecmp( arg, "none" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_NONE ;
+ } else if ( strcasecmp( arg, "peer" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_PEER ;
+ } else if ( strcasecmp( arg, "all" ) == 0 ) {
+ i = LDAP_OPT_X_TLS_CRL_ALL ;
+ }
+ if (i >= 0) {
+ return ldap_pvt_tls_set_option( ld, option, &i );
+ }
+ return -1;
+#endif
+ }
+ return -1;
+}
+
+int
+ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
+{
+ struct ldapoptions *lo;
+
+ if( option == LDAP_OPT_X_TLS_PACKAGE ) {
+ *(char **)arg = LDAP_STRDUP( tls_imp->ti_name );
+ return 0;
+ }
+
+ if( ld != NULL ) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+
+ } else {
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if ( lo == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS:
+ *(int *)arg = lo->ldo_tls_mode;
+ break;
+ case LDAP_OPT_X_TLS_CTX:
+ *(void **)arg = lo->ldo_tls_ctx;
+ if ( lo->ldo_tls_ctx ) {
+ tls_ctx_ref( lo->ldo_tls_ctx );
+ }
+ break;
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ *(char **)arg = lo->ldo_tls_cacertfile ?
+ LDAP_STRDUP( lo->ldo_tls_cacertfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ *(char **)arg = lo->ldo_tls_cacertdir ?
+ LDAP_STRDUP( lo->ldo_tls_cacertdir ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CERTFILE:
+ *(char **)arg = lo->ldo_tls_certfile ?
+ LDAP_STRDUP( lo->ldo_tls_certfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_KEYFILE:
+ *(char **)arg = lo->ldo_tls_keyfile ?
+ LDAP_STRDUP( lo->ldo_tls_keyfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_DHFILE:
+ *(char **)arg = lo->ldo_tls_dhfile ?
+ LDAP_STRDUP( lo->ldo_tls_dhfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_ECNAME:
+ *(char **)arg = lo->ldo_tls_ecname ?
+ LDAP_STRDUP( lo->ldo_tls_ecname ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ *(char **)arg = lo->ldo_tls_crlfile ?
+ LDAP_STRDUP( lo->ldo_tls_crlfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ *(int *)arg = lo->ldo_tls_require_cert;
+ break;
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ *(int *)arg = lo->ldo_tls_require_san;
+ break;
+#ifdef HAVE_OPENSSL_CRL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ *(int *)arg = lo->ldo_tls_crlcheck;
+ break;
+#endif
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ *(char **)arg = lo->ldo_tls_ciphersuite ?
+ LDAP_STRDUP( lo->ldo_tls_ciphersuite ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+ *(int *)arg = lo->ldo_tls_protocol_min;
+ break;
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ *(char **)arg = lo->ldo_tls_randfile ?
+ LDAP_STRDUP( lo->ldo_tls_randfile ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_SSL_CTX: {
+ void *retval = 0;
+ if ( ld != NULL ) {
+ LDAPConn *conn = ld->ld_defconn;
+ if ( conn != NULL ) {
+ Sockbuf *sb = conn->lconn_sb;
+ retval = ldap_pvt_tls_sb_ctx( sb );
+ }
+ }
+ *(void **)arg = retval;
+ break;
+ }
+ case LDAP_OPT_X_TLS_CONNECT_CB:
+ *(LDAP_TLS_CONNECT_CB **)arg = lo->ldo_tls_connect_cb;
+ break;
+ case LDAP_OPT_X_TLS_CONNECT_ARG:
+ *(void **)arg = lo->ldo_tls_connect_arg;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
+{
+ struct ldapoptions *lo;
+
+ if( ld != NULL ) {
+ assert( LDAP_VALID( ld ) );
+
+ if( !LDAP_VALID( ld ) ) {
+ return LDAP_OPT_ERROR;
+ }
+
+ lo = &ld->ld_options;
+
+ } else {
+ /* Get pointer to global option structure */
+ lo = LDAP_INT_GLOBAL_OPT();
+ if ( lo == NULL ) {
+ return LDAP_NO_MEMORY;
+ }
+ }
+
+ switch( option ) {
+ case LDAP_OPT_X_TLS:
+ if ( !arg ) return -1;
+
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ if (lo != NULL) {
+ lo->ldo_tls_mode = *(int *)arg;
+ }
+
+ return 0;
+ }
+ return -1;
+
+ case LDAP_OPT_X_TLS_CTX:
+ if ( lo->ldo_tls_ctx )
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = arg;
+ tls_ctx_ref( lo->ldo_tls_ctx );
+ return 0;
+ case LDAP_OPT_X_TLS_CONNECT_CB:
+ lo->ldo_tls_connect_cb = (LDAP_TLS_CONNECT_CB *)arg;
+ return 0;
+ case LDAP_OPT_X_TLS_CONNECT_ARG:
+ lo->ldo_tls_connect_arg = arg;
+ return 0;
+ case LDAP_OPT_X_TLS_CACERTFILE:
+ if ( lo->ldo_tls_cacertfile ) LDAP_FREE( lo->ldo_tls_cacertfile );
+ lo->ldo_tls_cacertfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CACERTDIR:
+ if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir );
+ lo->ldo_tls_cacertdir = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CERTFILE:
+ if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile );
+ lo->ldo_tls_certfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_KEYFILE:
+ if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile );
+ lo->ldo_tls_keyfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_DHFILE:
+ if ( lo->ldo_tls_dhfile ) LDAP_FREE( lo->ldo_tls_dhfile );
+ lo->ldo_tls_dhfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_ECNAME:
+ if ( lo->ldo_tls_ecname ) LDAP_FREE( lo->ldo_tls_ecname );
+ lo->ldo_tls_ecname = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_CRLFILE: /* GnuTLS only */
+ if ( lo->ldo_tls_crlfile ) LDAP_FREE( lo->ldo_tls_crlfile );
+ lo->ldo_tls_crlfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+ case LDAP_OPT_X_TLS_REQUIRE_CERT:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ lo->ldo_tls_require_cert = * (int *) arg;
+ return 0;
+ }
+ return -1;
+ case LDAP_OPT_X_TLS_REQUIRE_SAN:
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_NEVER:
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_ALLOW:
+ case LDAP_OPT_X_TLS_TRY:
+ case LDAP_OPT_X_TLS_HARD:
+ lo->ldo_tls_require_san = * (int *) arg;
+ return 0;
+ }
+ return -1;
+#ifdef HAVE_OPENSSL_CRL
+ case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
+ if ( !arg ) return -1;
+ switch( *(int *) arg ) {
+ case LDAP_OPT_X_TLS_CRL_NONE:
+ case LDAP_OPT_X_TLS_CRL_PEER:
+ case LDAP_OPT_X_TLS_CRL_ALL:
+ lo->ldo_tls_crlcheck = * (int *) arg;
+ return 0;
+ }
+ return -1;
+#endif
+ case LDAP_OPT_X_TLS_CIPHER_SUITE:
+ if ( lo->ldo_tls_ciphersuite ) LDAP_FREE( lo->ldo_tls_ciphersuite );
+ lo->ldo_tls_ciphersuite = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ return 0;
+
+ case LDAP_OPT_X_TLS_PROTOCOL_MIN:
+ if ( !arg ) return -1;
+ lo->ldo_tls_protocol_min = *(int *)arg;
+ return 0;
+ case LDAP_OPT_X_TLS_RANDOM_FILE:
+ if ( ld != NULL )
+ return -1;
+ if ( lo->ldo_tls_randfile ) LDAP_FREE (lo->ldo_tls_randfile );
+ lo->ldo_tls_randfile = (arg && *(char *)arg) ? LDAP_STRDUP( (char *) arg ) : NULL;
+ break;
+ case LDAP_OPT_X_TLS_NEWCTX:
+ if ( !arg ) return -1;
+ if ( lo->ldo_tls_ctx )
+ ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
+ lo->ldo_tls_ctx = NULL;
+ return ldap_int_tls_init_ctx( lo, *(int *)arg );
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+int
+ldap_int_tls_start ( LDAP *ld, LDAPConn *conn, LDAPURLDesc *srv )
+{
+ Sockbuf *sb;
+ char *host;
+ void *ssl;
+ int ret, async;
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+ struct timeval start_time_tv, tv, tv0;
+ ber_socket_t sd = AC_SOCKET_ERROR;
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+
+ if ( !conn )
+ return LDAP_PARAM_ERROR;
+
+ sb = conn->lconn_sb;
+ if( srv ) {
+ host = srv->lud_host;
+ } else {
+ host = conn->lconn_server->lud_host;
+ }
+
+ /* avoid NULL host */
+ if( host == NULL ) {
+ host = "localhost";
+ }
+
+ (void) tls_init( tls_imp );
+
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+ /*
+ * Use non-blocking io during SSL Handshake when a timeout is configured
+ */
+ async = LDAP_BOOL_GET( &ld->ld_options, LDAP_BOOL_CONNECT_ASYNC );
+ if ( ld->ld_options.ldo_tm_net.tv_sec >= 0 ) {
+ if ( !async ) {
+ /* if async, this has already been set */
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_SET_NONBLOCK, (void*)1 );
+ }
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_FD, &sd );
+ tv = ld->ld_options.ldo_tm_net;
+ tv0 = tv;
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &start_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &start_time_tv.tv_sec );
+ start_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+ }
+
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+
+ ld->ld_errno = LDAP_SUCCESS;
+ ret = ldap_int_tls_connect( ld, conn, host );
+
+ /* this mainly only happens for non-blocking io
+ * but can also happen when the handshake is too
+ * big for a single network message.
+ */
+ while ( ret > 0 ) {
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+ if ( async ) {
+ struct timeval curr_time_tv, delta_tv;
+ int wr=0;
+
+ if ( sb->sb_trans_needs_read ) {
+ wr=0;
+ } else if ( sb->sb_trans_needs_write ) {
+ wr=1;
+ }
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ldap_int_tls_connect needs %s\n",
+ wr ? "write": "read", 0, 0 );
+
+ /* This is mostly copied from result.c:wait4msg(), should
+ * probably be moved into a separate function */
+#ifdef HAVE_GETTIMEOFDAY
+ gettimeofday( &curr_time_tv, NULL );
+#else /* ! HAVE_GETTIMEOFDAY */
+ time( &curr_time_tv.tv_sec );
+ curr_time_tv.tv_usec = 0;
+#endif /* ! HAVE_GETTIMEOFDAY */
+
+ /* delta = curr - start */
+ delta_tv.tv_sec = curr_time_tv.tv_sec - start_time_tv.tv_sec;
+ delta_tv.tv_usec = curr_time_tv.tv_usec - start_time_tv.tv_usec;
+ if ( delta_tv.tv_usec < 0 ) {
+ delta_tv.tv_sec--;
+ delta_tv.tv_usec += 1000000;
+ }
+
+ /* tv0 < delta ? */
+ if ( ( tv0.tv_sec < delta_tv.tv_sec ) ||
+ ( ( tv0.tv_sec == delta_tv.tv_sec ) &&
+ ( tv0.tv_usec < delta_tv.tv_usec ) ) )
+ {
+ ret = -1;
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+ /* timeout -= delta_time */
+ tv0.tv_sec -= delta_tv.tv_sec;
+ tv0.tv_usec -= delta_tv.tv_usec;
+ if ( tv0.tv_usec < 0 ) {
+ tv0.tv_sec--;
+ tv0.tv_usec += 1000000;
+ }
+ start_time_tv.tv_sec = curr_time_tv.tv_sec;
+ start_time_tv.tv_usec = curr_time_tv.tv_usec;
+ tv = tv0;
+ Debug( LDAP_DEBUG_TRACE, "ldap_int_tls_start: ld %p %ld s %ld us to go\n",
+ (void *)ld, (long) tv.tv_sec, (long) tv.tv_usec );
+ ret = ldap_int_poll( ld, sd, &tv, wr);
+ if ( ret < 0 ) {
+ ld->ld_errno = LDAP_TIMEOUT;
+ break;
+ }
+ }
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+ ret = ldap_int_tls_connect( ld, conn, host );
+ }
+
+ if ( ret < 0 ) {
+ if ( ld->ld_errno == LDAP_SUCCESS )
+ ld->ld_errno = LDAP_CONNECT_ERROR;
+ return (ld->ld_errno);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+void *
+ldap_pvt_tls_sb_ctx( Sockbuf *sb )
+{
+ void *p = NULL;
+
+ ber_sockbuf_ctrl( sb, LBER_SB_OPT_GET_SSL, (void *)&p );
+ return p;
+}
+
+int
+ldap_pvt_tls_get_strength( void *s )
+{
+ tls_session *session = s;
+
+ return tls_imp->ti_session_strength( session );
+}
+
+int
+ldap_pvt_tls_get_my_dn( void *s, struct berval *dn, LDAPDN_rewrite_dummy *func, unsigned flags )
+{
+ tls_session *session = s;
+ struct berval der_dn;
+ int rc;
+
+ rc = tls_imp->ti_session_my_dn( session, &der_dn );
+ if ( rc == LDAP_SUCCESS )
+ rc = ldap_X509dn2bv(&der_dn, dn, (LDAPDN_rewrite_func *)func, flags );
+ return rc;
+}
+#endif /* HAVE_TLS */
+
+int
+ldap_start_tls( LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls,
+ int *msgidp )
+{
+ return ldap_extended_operation( ld, LDAP_EXOP_START_TLS,
+ NULL, serverctrls, clientctrls, msgidp );
+}
+
+int
+ldap_install_tls( LDAP *ld )
+{
+#ifndef HAVE_TLS
+ return LDAP_NOT_SUPPORTED;
+#else
+ if ( ldap_tls_inplace( ld ) ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ return ldap_int_tls_start( ld, ld->ld_defconn, NULL );
+#endif
+}
+
+int
+ldap_start_tls_s ( LDAP *ld,
+ LDAPControl **serverctrls,
+ LDAPControl **clientctrls )
+{
+#ifndef HAVE_TLS
+ return LDAP_NOT_SUPPORTED;
+#else
+ int rc;
+ char *rspoid = NULL;
+ struct berval *rspdata = NULL;
+
+ /* XXYYZ: this initiates operation only on default connection! */
+
+ if ( ldap_tls_inplace( ld ) ) {
+ return LDAP_LOCAL_ERROR;
+ }
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_START_TLS,
+ NULL, serverctrls, clientctrls, &rspoid, &rspdata );
+
+ if ( rspoid != NULL ) {
+ LDAP_FREE(rspoid);
+ }
+
+ if ( rspdata != NULL ) {
+ ber_bvfree( rspdata );
+ }
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = ldap_int_tls_start( ld, ld->ld_defconn, NULL );
+ }
+
+ return rc;
+#endif
+}
+
+/* These tags probably all belong in lber.h, but they're
+ * not normally encountered when processing LDAP, so maybe
+ * they belong somewhere else instead.
+ */
+
+#define LBER_TAG_OID ((ber_tag_t) 0x06UL)
+
+/* Tags for string types used in a DirectoryString.
+ *
+ * Note that IA5string is not one of the defined choices for
+ * DirectoryString in X.520, but it gets used for email AVAs.
+ */
+#define LBER_TAG_UTF8 ((ber_tag_t) 0x0cUL)
+#define LBER_TAG_PRINTABLE ((ber_tag_t) 0x13UL)
+#define LBER_TAG_TELETEX ((ber_tag_t) 0x14UL)
+#define LBER_TAG_IA5 ((ber_tag_t) 0x16UL)
+#define LBER_TAG_UNIVERSAL ((ber_tag_t) 0x1cUL)
+#define LBER_TAG_BMP ((ber_tag_t) 0x1eUL)
+
+static oid_name *
+find_oid( struct berval *oid )
+{
+ int i;
+
+ for ( i=0; !BER_BVISNULL( &oids[i].oid ); i++ ) {
+ if ( oids[i].oid.bv_len != oid->bv_len ) continue;
+ if ( !strcmp( oids[i].oid.bv_val, oid->bv_val ))
+ return &oids[i];
+ }
+ return NULL;
+}
+
+/* Converts BER Bitstring value to LDAP BitString value (RFC4517)
+ *
+ * berValue : IN
+ * rfc4517Value: OUT
+ *
+ * berValue and ldapValue should not be NULL
+ */
+
+#define BITS_PER_BYTE 8
+#define SQUOTE_LENGTH 1
+#define B_CHAR_LENGTH 1
+#define STR_OVERHEAD (2*SQUOTE_LENGTH + B_CHAR_LENGTH)
+
+static int
+der_to_ldap_BitString (struct berval *berValue,
+ struct berval *ldapValue)
+{
+ ber_len_t bitPadding=0;
+ ber_len_t bits, maxBits;
+ char *tmpStr;
+ unsigned char byte;
+ ber_len_t bitLength;
+ ber_len_t valLen;
+ unsigned char* valPtr;
+
+ ldapValue->bv_len=0;
+ ldapValue->bv_val=NULL;
+
+ /* Gets padding and points to binary data */
+ valLen=berValue->bv_len;
+ valPtr=(unsigned char*)berValue->bv_val;
+ if (valLen) {
+ bitPadding=(ber_len_t)(valPtr[0]);
+ valLen--;
+ valPtr++;
+ }
+ /* If Block is non DER encoding fixes to DER encoding */
+ if (bitPadding >= BITS_PER_BYTE) {
+ if (valLen*BITS_PER_BYTE > bitPadding ) {
+ valLen-=(bitPadding/BITS_PER_BYTE);
+ bitPadding%=BITS_PER_BYTE;
+ } else {
+ valLen=0;
+ bitPadding=0;
+ }
+ }
+ /* Just in case bad encoding */
+ if (valLen*BITS_PER_BYTE < bitPadding ) {
+ bitPadding=0;
+ valLen=0;
+ }
+
+ /* Gets buffer to hold RFC4517 Bit String format */
+ bitLength=valLen*BITS_PER_BYTE-bitPadding;
+ tmpStr=LDAP_MALLOC(bitLength + STR_OVERHEAD + 1);
+
+ if (!tmpStr)
+ return LDAP_NO_MEMORY;
+
+ ldapValue->bv_val=tmpStr;
+ ldapValue->bv_len=bitLength + STR_OVERHEAD;
+
+ /* Formatting in '*binary-digit'B format */
+ maxBits=BITS_PER_BYTE;
+ *tmpStr++ ='\'';
+ while(valLen) {
+ byte=*valPtr;
+ if (valLen==1)
+ maxBits-=bitPadding;
+ for (bits=0; bits<maxBits; bits++) {
+ if (0x80 & byte)
+ *tmpStr='1';
+ else
+ *tmpStr='0';
+ tmpStr++;
+ byte<<=1;
+ }
+ valPtr++;
+ valLen--;
+ }
+ *tmpStr++ ='\'';
+ *tmpStr++ ='B';
+ *tmpStr=0;
+
+ return LDAP_SUCCESS;
+}
+
+/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN.
+ * x509_name must be raw DER. If func is non-NULL, the
+ * constructed DN will use numeric OIDs to identify attributeTypes,
+ * and the func() will be invoked to rewrite the DN with the given
+ * flags.
+ *
+ * Otherwise the DN will use shortNames from a hardcoded table.
+ */
+int
+ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func,
+ unsigned flags )
+{
+ LDAPDN newDN;
+ LDAPRDN newRDN;
+ LDAPAVA *newAVA, *baseAVA;
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ char oids[8192], *oidptr = oids, *oidbuf = NULL;
+ void *ptrs[2048];
+ char *dn_end, *rdn_end;
+ int i, navas, nrdns, rc = LDAP_SUCCESS;
+ size_t dnsize, oidrem = sizeof(oids), oidsize = 0;
+ int csize;
+ ber_tag_t tag;
+ ber_len_t len;
+ oid_name *oidname;
+
+ struct berval Oid, Val, oid2, *in = x509_name;
+
+ assert( bv != NULL );
+
+ bv->bv_len = 0;
+ bv->bv_val = NULL;
+
+ navas = 0;
+ nrdns = 0;
+
+ /* A DN is a SEQUENCE of RDNs. An RDN is a SET of AVAs.
+ * An AVA is a SEQUENCE of attr and value.
+ * Count the number of AVAs and RDNs
+ */
+ ber_init2( ber, in, LBER_USE_DER );
+ tag = ber_peek_tag( ber, &len );
+ if ( tag != LBER_SEQUENCE )
+ return LDAP_DECODING_ERROR;
+
+ for ( tag = ber_first_element( ber, &len, &dn_end );
+ tag == LBER_SET;
+ tag = ber_next_element( ber, &len, dn_end )) {
+ nrdns++;
+ for ( tag = ber_first_element( ber, &len, &rdn_end );
+ tag == LBER_SEQUENCE;
+ tag = ber_next_element( ber, &len, rdn_end )) {
+ if ( rdn_end > dn_end )
+ return LDAP_DECODING_ERROR;
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ navas++;
+ }
+ }
+
+ /* Rewind and prepare to extract */
+ ber_rewind( ber );
+ tag = ber_first_element( ber, &len, &dn_end );
+ if ( tag != LBER_SET )
+ return LDAP_DECODING_ERROR;
+
+ /* Allocate the DN/RDN/AVA stuff as a single block */
+ dnsize = sizeof(LDAPRDN) * (nrdns+1);
+ dnsize += sizeof(LDAPAVA *) * (navas+nrdns);
+ dnsize += sizeof(LDAPAVA) * navas;
+ if (dnsize > sizeof(ptrs)) {
+ newDN = (LDAPDN)LDAP_MALLOC( dnsize );
+ if ( newDN == NULL )
+ return LDAP_NO_MEMORY;
+ } else {
+ newDN = (LDAPDN)(char *)ptrs;
+ }
+
+ newDN[nrdns] = NULL;
+ newRDN = (LDAPRDN)(newDN + nrdns+1);
+ newAVA = (LDAPAVA *)(newRDN + navas + nrdns);
+ baseAVA = newAVA;
+
+ for ( i = nrdns - 1; i >= 0; i-- ) {
+ newDN[i] = newRDN;
+
+ for ( tag = ber_first_element( ber, &len, &rdn_end );
+ tag == LBER_SEQUENCE;
+ tag = ber_next_element( ber, &len, rdn_end )) {
+
+ *newRDN++ = newAVA;
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_stringbv( ber, &Oid, LBER_BV_NOTERM );
+ if ( tag != LBER_TAG_OID ) {
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ }
+
+ oid2.bv_val = oidptr;
+ oid2.bv_len = oidrem;
+ if ( ber_decode_oid( &Oid, &oid2 ) < 0 ) {
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ }
+ oidname = find_oid( &oid2 );
+ if ( !oidname ) {
+ newAVA->la_attr = oid2;
+ oidptr += oid2.bv_len + 1;
+ oidrem -= oid2.bv_len + 1;
+
+ /* Running out of OID buffer space? */
+ if (oidrem < 128) {
+ if ( oidsize == 0 ) {
+ oidsize = sizeof(oids) * 2;
+ oidrem = oidsize;
+ oidbuf = LDAP_MALLOC( oidsize );
+ if ( oidbuf == NULL ) goto nomem;
+ oidptr = oidbuf;
+ } else {
+ char *old = oidbuf;
+ oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 );
+ if ( oidbuf == NULL ) goto nomem;
+ /* Buffer moved! Fix AVA pointers */
+ if ( old != oidbuf ) {
+ LDAPAVA *a;
+ long dif = oidbuf - old;
+
+ for (a=baseAVA; a<=newAVA; a++){
+ if (a->la_attr.bv_val >= old &&
+ a->la_attr.bv_val <= (old + oidsize))
+ a->la_attr.bv_val += dif;
+ }
+ }
+ oidptr = oidbuf + oidsize - oidrem;
+ oidrem += oidsize;
+ oidsize *= 2;
+ }
+ }
+ } else {
+ if ( func ) {
+ newAVA->la_attr = oidname->oid;
+ } else {
+ newAVA->la_attr = oidname->name;
+ }
+ }
+ newAVA->la_private = NULL;
+ newAVA->la_flags = LDAP_AVA_STRING;
+ tag = ber_get_stringbv( ber, &Val, LBER_BV_NOTERM );
+ switch(tag) {
+ case LBER_TAG_UNIVERSAL:
+ /* This uses 32-bit ISO 10646-1 */
+ csize = 4; goto to_utf8;
+ case LBER_TAG_BMP:
+ /* This uses 16-bit ISO 10646-1 */
+ csize = 2; goto to_utf8;
+ case LBER_TAG_TELETEX:
+ /* This uses 8-bit, assume ISO 8859-1 */
+ csize = 1;
+to_utf8: rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value );
+ newAVA->la_flags |= LDAP_AVA_NONPRINTABLE;
+allocd:
+ newAVA->la_flags |= LDAP_AVA_FREE_VALUE;
+ if (rc != LDAP_SUCCESS) goto nomem;
+ break;
+ case LBER_TAG_UTF8:
+ newAVA->la_flags |= LDAP_AVA_NONPRINTABLE;
+ /* This is already in UTF-8 encoding */
+ case LBER_TAG_IA5:
+ case LBER_TAG_PRINTABLE:
+ /* These are always 7-bit strings */
+ newAVA->la_value = Val;
+ break;
+ case LBER_BITSTRING:
+ /* X.690 bitString value converted to RFC4517 Bit String */
+ rc = der_to_ldap_BitString( &Val, &newAVA->la_value );
+ goto allocd;
+ case LBER_DEFAULT:
+ /* decode error */
+ rc = LDAP_DECODING_ERROR;
+ goto nomem;
+ default:
+ /* Not a string type at all */
+ newAVA->la_flags = 0;
+ newAVA->la_value = Val;
+ break;
+ }
+ newAVA++;
+ }
+ *newRDN++ = NULL;
+ tag = ber_next_element( ber, &len, dn_end );
+ }
+
+ if ( func ) {
+ rc = func( newDN, flags, NULL );
+ if ( rc != LDAP_SUCCESS )
+ goto nomem;
+ }
+
+ rc = ldap_dn2bv_x( newDN, bv, LDAP_DN_FORMAT_LDAPV3, NULL );
+
+nomem:
+ for (;baseAVA < newAVA; baseAVA++) {
+ if (baseAVA->la_flags & LDAP_AVA_FREE_ATTR)
+ LDAP_FREE( baseAVA->la_attr.bv_val );
+ if (baseAVA->la_flags & LDAP_AVA_FREE_VALUE)
+ LDAP_FREE( baseAVA->la_value.bv_val );
+ }
+
+ if ( oidsize != 0 )
+ LDAP_FREE( oidbuf );
+ if ( newDN != (LDAPDN)(char *) ptrs )
+ LDAP_FREE( newDN );
+ return rc;
+}
+
diff --git a/libraries/libldap/tls_g.c b/libraries/libldap/tls_g.c
new file mode 100644
index 0000000..3b72cd2
--- /dev/null
+++ b/libraries/libldap/tls_g.c
@@ -0,0 +1,940 @@
+/* tls_g.c - Handle tls/ssl using GNUTLS. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: GNUTLS support written by Howard Chu and
+ * Emily Backes; sponsored by The Written Word (thewrittenword.com)
+ * and Stanford University (stanford.edu).
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_GNUTLS
+
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ldap-int.h"
+#include "ldap-tls.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+typedef struct tlsg_ctx {
+ gnutls_certificate_credentials_t cred;
+ gnutls_dh_params_t dh_params;
+ unsigned long verify_depth;
+ int refcount;
+ int reqcert;
+ gnutls_priority_t prios;
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t ref_mutex;
+#endif
+} tlsg_ctx;
+
+typedef struct tlsg_session {
+ gnutls_session_t session;
+ tlsg_ctx *ctx;
+ struct berval peer_der_dn;
+} tlsg_session;
+
+static int tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites );
+static int tlsg_cert_verify( tlsg_session *s );
+
+#ifdef LDAP_R_COMPILE
+
+static void
+tlsg_thr_init( void )
+{
+ /* do nothing */
+}
+#endif /* LDAP_R_COMPILE */
+
+/*
+ * Initialize TLS subsystem. Should be called only once.
+ */
+static int
+tlsg_init( void )
+{
+ gnutls_global_init();
+ return 0;
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+static void
+tlsg_destroy( void )
+{
+ gnutls_global_deinit();
+}
+
+static tls_ctx *
+tlsg_ctx_new ( struct ldapoptions *lo )
+{
+ tlsg_ctx *ctx;
+
+ ctx = ber_memcalloc ( 1, sizeof (*ctx) );
+ if ( ctx ) {
+ if ( gnutls_certificate_allocate_credentials( &ctx->cred )) {
+ ber_memfree( ctx );
+ return NULL;
+ }
+ ctx->refcount = 1;
+ gnutls_priority_init( &ctx->prios, "NORMAL", NULL );
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &ctx->ref_mutex );
+#endif
+ }
+ return (tls_ctx *)ctx;
+}
+
+static void
+tlsg_ctx_ref( tls_ctx *ctx )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ LDAP_MUTEX_LOCK( &c->ref_mutex );
+ c->refcount++;
+ LDAP_MUTEX_UNLOCK( &c->ref_mutex );
+}
+
+static void
+tlsg_ctx_free ( tls_ctx *ctx )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ int refcount;
+
+ if ( !c ) return;
+
+ LDAP_MUTEX_LOCK( &c->ref_mutex );
+ refcount = --c->refcount;
+ LDAP_MUTEX_UNLOCK( &c->ref_mutex );
+ if ( refcount )
+ return;
+ gnutls_priority_deinit( c->prios );
+ gnutls_certificate_free_credentials( c->cred );
+ if ( c->dh_params )
+ gnutls_dh_params_deinit( c->dh_params );
+ ber_memfree ( c );
+}
+
+static int
+tlsg_getfile( const char *path, gnutls_datum_t *buf )
+{
+ int rc = -1, fd;
+ struct stat st;
+
+ fd = open( path, O_RDONLY );
+ if ( fd >= 0 && fstat( fd, &st ) == 0 ) {
+ buf->size = st.st_size;
+ buf->data = LDAP_MALLOC( st.st_size + 1 );
+ if ( buf->data ) {
+ rc = read( fd, buf->data, st.st_size );
+ close( fd );
+ if ( rc < st.st_size )
+ rc = -1;
+ else
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+/* This is the GnuTLS default */
+#define VERIFY_DEPTH 6
+
+/*
+ * initialize a new TLS context
+ */
+static int
+tlsg_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
+{
+ tlsg_ctx *ctx = lo->ldo_tls_ctx;
+ int rc;
+
+ if ( lo->ldo_tls_ciphersuite &&
+ tlsg_parse_ciphers( ctx, lt->lt_ciphersuite )) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list %s.\n",
+ lo->ldo_tls_ciphersuite, 0, 0 );
+ return -1;
+ }
+
+ if (lo->ldo_tls_cacertdir != NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: warning: cacertdir not implemented for gnutls\n",
+ NULL, NULL, NULL );
+ }
+
+ if (lo->ldo_tls_cacertfile != NULL) {
+ rc = gnutls_certificate_set_x509_trust_file(
+ ctx->cred,
+ lt->lt_cacertfile,
+ GNUTLS_X509_FMT_PEM );
+ if ( rc < 0 ) return -1;
+ }
+
+ if ( lo->ldo_tls_certfile && lo->ldo_tls_keyfile ) {
+ gnutls_x509_privkey_t key;
+ gnutls_datum_t buf;
+ gnutls_x509_crt_t certs[VERIFY_DEPTH];
+ unsigned int max = VERIFY_DEPTH;
+
+ rc = gnutls_x509_privkey_init( &key );
+ if ( rc ) return -1;
+
+ /* OpenSSL builds the cert chain for us, but GnuTLS
+ * expects it to be present in the certfile. If it's
+ * not, we have to build it ourselves. So we have to
+ * do some special checks here...
+ */
+ rc = tlsg_getfile( lt->lt_keyfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_x509_privkey_import( key, &buf,
+ GNUTLS_X509_FMT_PEM );
+ LDAP_FREE( buf.data );
+ if ( rc < 0 ) return rc;
+
+ rc = tlsg_getfile( lt->lt_certfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_x509_crt_list_import( certs, &max, &buf,
+ GNUTLS_X509_FMT_PEM, 0 );
+ LDAP_FREE( buf.data );
+ if ( rc < 0 ) return rc;
+
+ /* If there's only one cert and it's not self-signed,
+ * then we have to build the cert chain.
+ */
+ if ( max == 1 && !gnutls_x509_crt_check_issuer( certs[0], certs[0] )) {
+ unsigned int i;
+ for ( i = 1; i<VERIFY_DEPTH; i++ ) {
+ if ( gnutls_certificate_get_issuer( ctx->cred, certs[i-1], &certs[i], 0 ))
+ break;
+ max++;
+ /* If this CA is self-signed, we're done */
+ if ( gnutls_x509_crt_check_issuer( certs[i], certs[i] ))
+ break;
+ }
+ }
+ rc = gnutls_certificate_set_x509_key( ctx->cred, certs, max, key );
+ if ( rc ) return -1;
+ } else if ( lo->ldo_tls_certfile || lo->ldo_tls_keyfile ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: only one of certfile and keyfile specified\n",
+ NULL, NULL, NULL );
+ return -1;
+ }
+
+ if ( lo->ldo_tls_crlfile ) {
+ rc = gnutls_certificate_set_x509_crl_file(
+ ctx->cred,
+ lt->lt_crlfile,
+ GNUTLS_X509_FMT_PEM );
+ if ( rc < 0 ) return -1;
+ rc = 0;
+ }
+
+ /* FIXME: ITS#5992 - this should be configurable,
+ * and V1 CA certs should be phased out ASAP.
+ */
+ gnutls_certificate_set_verify_flags( ctx->cred,
+ GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT );
+
+ if ( is_server && lo->ldo_tls_dhfile ) {
+ gnutls_datum_t buf;
+ rc = tlsg_getfile( lo->ldo_tls_dhfile, &buf );
+ if ( rc ) return -1;
+ rc = gnutls_dh_params_init( &ctx->dh_params );
+ if ( rc == 0 )
+ rc = gnutls_dh_params_import_pkcs3( ctx->dh_params, &buf,
+ GNUTLS_X509_FMT_PEM );
+ LDAP_FREE( buf.data );
+ if ( rc ) return -1;
+ gnutls_certificate_set_dh_params( ctx->cred, ctx->dh_params );
+ }
+
+ ctx->reqcert = lo->ldo_tls_require_cert;
+
+ return 0;
+}
+
+static tls_session *
+tlsg_session_new ( tls_ctx * ctx, int is_server )
+{
+ tlsg_ctx *c = (tlsg_ctx *)ctx;
+ tlsg_session *session;
+
+ session = ber_memcalloc ( 1, sizeof (*session) );
+ if ( !session )
+ return NULL;
+
+ session->ctx = c;
+ gnutls_init( &session->session, is_server ? GNUTLS_SERVER : GNUTLS_CLIENT );
+ gnutls_priority_set( session->session, c->prios );
+ if ( c->cred )
+ gnutls_credentials_set( session->session, GNUTLS_CRD_CERTIFICATE, c->cred );
+
+ if ( is_server ) {
+ int flag = 0;
+ if ( c->reqcert ) {
+ flag = GNUTLS_CERT_REQUEST;
+ if ( c->reqcert == LDAP_OPT_X_TLS_DEMAND ||
+ c->reqcert == LDAP_OPT_X_TLS_HARD )
+ flag = GNUTLS_CERT_REQUIRE;
+ gnutls_certificate_server_set_request( session->session, flag );
+ }
+ }
+ return (tls_session *)session;
+}
+
+static int
+tlsg_session_accept( tls_session *session )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ int rc;
+
+ rc = gnutls_handshake( s->session );
+ if ( rc == 0 && s->ctx->reqcert != LDAP_OPT_X_TLS_NEVER ) {
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list && s->ctx->reqcert == LDAP_OPT_X_TLS_TRY )
+ rc = 0;
+ else {
+ rc = tlsg_cert_verify( s );
+ if ( rc && s->ctx->reqcert == LDAP_OPT_X_TLS_ALLOW )
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static int
+tlsg_session_connect( LDAP *ld, tls_session *session )
+{
+ return tlsg_session_accept( session);
+}
+
+static int
+tlsg_session_upflags( Sockbuf *sb, tls_session *session, int rc )
+{
+ tlsg_session *s = (tlsg_session *)session;
+
+ if ( rc != GNUTLS_E_INTERRUPTED && rc != GNUTLS_E_AGAIN )
+ return 0;
+
+ switch (gnutls_record_get_direction (s->session)) {
+ case 0:
+ sb->sb_trans_needs_read = 1;
+ return 1;
+ case 1:
+ sb->sb_trans_needs_write = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+tlsg_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
+{
+ return (char *)gnutls_strerror( rc );
+}
+
+static void
+tlsg_x509_cert_dn( struct berval *cert, struct berval *dn, int get_subject )
+{
+ BerElementBuffer berbuf;
+ BerElement *ber = (BerElement *)&berbuf;
+ ber_tag_t tag;
+ ber_len_t len;
+ ber_int_t i;
+
+ ber_init2( ber, cert, LBER_USE_DER );
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_skip_tag( ber, &len ); /* Sequence */
+ tag = ber_peek_tag( ber, &len ); /* Context + Constructed (version) */
+ if ( tag == 0xa0 ) { /* Version is optional */
+ tag = ber_skip_tag( ber, &len );
+ tag = ber_get_int( ber, &i ); /* Int: Version */
+ }
+ tag = ber_skip_tag( ber, &len ); /* Int: Serial (can be longer than ber_int_t) */
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Sequence: Signature */
+ ber_skip_data( ber, len );
+ if ( !get_subject ) {
+ tag = ber_peek_tag( ber, &len ); /* Sequence: Issuer DN */
+ } else {
+ tag = ber_skip_tag( ber, &len );
+ ber_skip_data( ber, len );
+ tag = ber_skip_tag( ber, &len ); /* Sequence: Validity */
+ ber_skip_data( ber, len );
+ tag = ber_peek_tag( ber, &len ); /* Sequence: Subject DN */
+ }
+ len = ber_ptrlen( ber );
+ dn->bv_val = cert->bv_val + len;
+ dn->bv_len = cert->bv_len - len;
+}
+
+static int
+tlsg_session_my_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ const gnutls_datum_t *x;
+ struct berval bv;
+
+ x = gnutls_certificate_get_ours( s->session );
+
+ if (!x) return LDAP_INVALID_CREDENTIALS;
+
+ bv.bv_val = (char *) x->data;
+ bv.bv_len = x->size;
+
+ tlsg_x509_cert_dn( &bv, der_dn, 1 );
+ return 0;
+}
+
+static int
+tlsg_session_peer_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ if ( !s->peer_der_dn.bv_val ) {
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+ struct berval bv;
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list ) return LDAP_INVALID_CREDENTIALS;
+
+ bv.bv_len = peer_cert_list->size;
+ bv.bv_val = (char *) peer_cert_list->data;
+
+ tlsg_x509_cert_dn( &bv, &s->peer_der_dn, 1 );
+ }
+ *der_dn = s->peer_der_dn;
+ return 0;
+}
+
+/* what kind of hostname were we given? */
+#define IS_DNS 0
+#define IS_IP4 1
+#define IS_IP6 2
+
+#define CN_OID "2.5.4.3"
+
+static int
+tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ int i, ret;
+ int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
+ const gnutls_datum_t *peer_cert_list;
+ unsigned int list_size;
+ char altname[NI_MAXHOST];
+ size_t altnamesize;
+
+ gnutls_x509_crt_t cert;
+ const char *name;
+ char *ptr;
+ char *domain = NULL;
+#ifdef LDAP_PF_INET6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ int len1 = 0, len2 = 0;
+ int ntype = IS_DNS;
+
+ if( ldap_int_hostname &&
+ ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
+ {
+ name = ldap_int_hostname;
+ } else {
+ name = name_in;
+ }
+
+ peer_cert_list = gnutls_certificate_get_peers( s->session,
+ &list_size );
+ if ( !peer_cert_list ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get peer certificate.\n",
+ 0, 0, 0 );
+ /* If this was a fatal condition, things would have
+ * aborted long before now.
+ */
+ return LDAP_SUCCESS;
+ }
+ ret = gnutls_x509_crt_init( &cert );
+ if ( ret < 0 )
+ return LDAP_LOCAL_ERROR;
+ ret = gnutls_x509_crt_import( cert, peer_cert_list, GNUTLS_X509_FMT_DER );
+ if ( ret ) {
+ gnutls_x509_crt_deinit( cert );
+ return LDAP_LOCAL_ERROR;
+ }
+
+#ifdef LDAP_PF_INET6
+ if (inet_pton(AF_INET6, name, &addr)) {
+ ntype = IS_IP6;
+ } else
+#endif
+ if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
+ if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
+ }
+
+ if (ntype == IS_DNS) {
+ len1 = strlen(name);
+ domain = strchr(name, '.');
+ if (domain) {
+ len2 = len1 - (domain-name);
+ }
+ }
+
+ if (chkSAN) {
+ for ( i=0, ret=0; ret >= 0; i++ ) {
+ altnamesize = sizeof(altname);
+ ret = gnutls_x509_crt_get_subject_alt_name( cert, i,
+ altname, &altnamesize, NULL );
+ if ( ret < 0 ) break;
+
+ gotSAN = 1;
+ /* ignore empty */
+ if ( altnamesize == 0 ) continue;
+
+ if ( ret == GNUTLS_SAN_DNSNAME ) {
+ if (ntype != IS_DNS) continue;
+
+ /* Is this an exact match? */
+ if ((len1 == altnamesize) && !strncasecmp(name, altname, len1)) {
+ break;
+ }
+
+ /* Is this a wildcard match? */
+ if (domain && (altname[0] == '*') && (altname[1] == '.') &&
+ (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2))
+ {
+ break;
+ }
+ } else if ( ret == GNUTLS_SAN_IPADDRESS ) {
+ if (ntype == IS_DNS) continue;
+
+#ifdef LDAP_PF_INET6
+ if (ntype == IS_IP6 && altnamesize != sizeof(struct in6_addr)) {
+ continue;
+ } else
+#endif
+ if (ntype == IS_IP4 && altnamesize != sizeof(struct in_addr)) {
+ continue;
+ }
+ if (!memcmp(altname, &addr, altnamesize)) {
+ break;
+ }
+ }
+ }
+ if ( ret >= 0 ) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ if (ret != LDAP_SUCCESS && chkSAN) {
+ switch(chkSAN) {
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_HARD:
+ if (!gotSAN) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get subjectAltName from peer certificate.\n", 0, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get subjectAltName from peer certificate"));
+ goto done;
+ }
+ /* FALLTHRU */
+ case LDAP_OPT_X_TLS_TRY:
+ if (gotSAN) {
+ Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "subjectAltName in certificate.\n",
+ name, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match subjectAltName in peer certificate"));
+ goto done;
+ }
+ break;
+ case LDAP_OPT_X_TLS_ALLOW:
+ break;
+ }
+ }
+
+ if ( ret != LDAP_SUCCESS ){
+ /* find the last CN */
+ i=0;
+ do {
+ altnamesize = 0;
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i, 1, altname, &altnamesize );
+ if ( ret == GNUTLS_E_SHORT_MEMORY_BUFFER )
+ i++;
+ else
+ break;
+ } while ( 1 );
+
+ if ( i ) {
+ altnamesize = sizeof(altname);
+ ret = gnutls_x509_crt_get_dn_by_oid( cert, CN_OID,
+ i-1, 0, altname, &altnamesize );
+ }
+
+ if ( ret < 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get common name from peer certificate.\n",
+ 0, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get CN from peer certificate"));
+
+ } else {
+ ret = LDAP_LOCAL_ERROR;
+ if ( !len1 ) len1 = strlen( name );
+ if ( len1 == altnamesize && strncasecmp(name, altname, altnamesize) == 0 ) {
+ ret = LDAP_SUCCESS;
+
+ } else if (( altname[0] == '*' ) && ( altname[1] == '.' )) {
+ /* Is this a wildcard match? */
+ if( domain &&
+ (len2 == altnamesize-1) && !strncasecmp(domain, &altname[1], len2)) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if( ret == LDAP_LOCAL_ERROR ) {
+ altname[altnamesize] = '\0';
+ Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "common name in certificate (%s).\n",
+ name, altname, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match name in peer certificate"));
+ }
+ }
+done:
+ gnutls_x509_crt_deinit( cert );
+ return ret;
+}
+
+static int
+tlsg_session_strength( tls_session *session )
+{
+ tlsg_session *s = (tlsg_session *)session;
+ gnutls_cipher_algorithm_t c;
+
+ c = gnutls_cipher_get( s->session );
+ return gnutls_cipher_get_key_size( c ) * 8;
+}
+
+/* suites is a string of colon-separated cipher suite names. */
+static int
+tlsg_parse_ciphers( tlsg_ctx *ctx, char *suites )
+{
+ const char *err;
+ int rc = gnutls_priority_init( &ctx->prios, suites, &err );
+ if ( rc )
+ ctx->prios = NULL;
+ return rc;
+}
+
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+struct tls_data {
+ tlsg_session *session;
+ Sockbuf_IO_Desc *sbiod;
+};
+
+static ssize_t
+tlsg_recv( gnutls_transport_ptr_t ptr, void *buf, size_t len )
+{
+ struct tls_data *p;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)ptr;
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ return LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+}
+
+static ssize_t
+tlsg_send( gnutls_transport_ptr_t ptr, const void *buf, size_t len )
+{
+ struct tls_data *p;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)ptr;
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ return LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
+}
+
+static int
+tlsg_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct tls_data *p;
+ tlsg_session *session = arg;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) {
+ return -1;
+ }
+
+ gnutls_transport_set_ptr( session->session, (gnutls_transport_ptr)p );
+ gnutls_transport_set_pull_function( session->session, tlsg_recv );
+ gnutls_transport_set_push_function( session->session, tlsg_send );
+ p->session = session;
+ p->sbiod = sbiod;
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+tlsg_sb_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ gnutls_deinit ( p->session->session );
+ LBER_FREE( p->session );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+tlsg_sb_close( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ gnutls_bye ( p->session->session, GNUTLS_SHUT_WR );
+ return 0;
+}
+
+static int
+tlsg_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_GET_SSL ) {
+ *((tlsg_session **)arg) = p->session;
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if( gnutls_record_check_pending( p->session->session ) > 0 ) {
+ return 1;
+ }
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+tlsg_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = gnutls_record_recv ( p->session->session, buf, len );
+ switch (ret) {
+ case GNUTLS_E_INTERRUPTED:
+ case GNUTLS_E_AGAIN:
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ sock_errset(EWOULDBLOCK);
+ ret = 0;
+ break;
+ case GNUTLS_E_REHANDSHAKE:
+ for ( ret = gnutls_handshake ( p->session->session );
+ ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN;
+ ret = gnutls_handshake ( p->session->session ) );
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ ret = 0;
+ break;
+ default:
+ sbiod->sbiod_sb->sb_trans_needs_read = 0;
+ }
+ return ret;
+}
+
+static ber_slen_t
+tlsg_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = gnutls_record_send ( p->session->session, (char *)buf, len );
+
+ if ( ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN ) {
+ sbiod->sbiod_sb->sb_trans_needs_write = 1;
+ sock_errset(EWOULDBLOCK);
+ ret = 0;
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_write = 0;
+ }
+ return ret;
+}
+
+static Sockbuf_IO tlsg_sbio =
+{
+ tlsg_sb_setup, /* sbi_setup */
+ tlsg_sb_remove, /* sbi_remove */
+ tlsg_sb_ctrl, /* sbi_ctrl */
+ tlsg_sb_read, /* sbi_read */
+ tlsg_sb_write, /* sbi_write */
+ tlsg_sb_close /* sbi_close */
+};
+
+/* Certs are not automatically varified during the handshake */
+static int
+tlsg_cert_verify( tlsg_session *ssl )
+{
+ unsigned int status = 0;
+ int err;
+ time_t now = time(0);
+ time_t peertime;
+
+ err = gnutls_certificate_verify_peers2( ssl->session, &status );
+ if ( err < 0 ) {
+ Debug( LDAP_DEBUG_ANY,"TLS: gnutls_certificate_verify_peers2 failed %d\n",
+ err,0,0 );
+ return -1;
+ }
+ if ( status ) {
+ Debug( LDAP_DEBUG_TRACE,"TLS: peer cert untrusted or revoked (0x%x)\n",
+ status, 0,0 );
+ return -1;
+ }
+ peertime = gnutls_certificate_expiration_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_expiration_time_peers failed\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( peertime < now ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: peer certificate is expired\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ peertime = gnutls_certificate_activation_time_peers( ssl->session );
+ if ( peertime == (time_t) -1 ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: gnutls_certificate_activation_time_peers failed\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( peertime > now ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: peer certificate not yet active\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ return 0;
+}
+
+tls_impl ldap_int_tls_impl = {
+ "GnuTLS",
+
+ tlsg_init,
+ tlsg_destroy,
+
+ tlsg_ctx_new,
+ tlsg_ctx_ref,
+ tlsg_ctx_free,
+ tlsg_ctx_init,
+
+ tlsg_session_new,
+ tlsg_session_connect,
+ tlsg_session_accept,
+ tlsg_session_upflags,
+ tlsg_session_errmsg,
+ tlsg_session_my_dn,
+ tlsg_session_peer_dn,
+ tlsg_session_chkhost,
+ tlsg_session_strength,
+
+ &tlsg_sbio,
+
+#ifdef LDAP_R_COMPILE
+ tlsg_thr_init,
+#else
+ NULL,
+#endif
+
+ 0
+};
+
+#endif /* HAVE_GNUTLS */
diff --git a/libraries/libldap/tls_m.c b/libraries/libldap/tls_m.c
new file mode 100644
index 0000000..43fbae4
--- /dev/null
+++ b/libraries/libldap/tls_m.c
@@ -0,0 +1,3324 @@
+/* tls_m.c - Handle tls/ssl using Mozilla NSS. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: Initial version written by Howard Chu.
+ * Additional support by Rich Megginson.
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_MOZNSS
+
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#if defined( HAVE_FCNTL_H )
+#include <fcntl.h>
+#endif
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+#include <ac/regex.h>
+
+#include "ldap-int.h"
+#include "ldap-tls.h"
+
+#define READ_PASSWORD_FROM_STDIN
+#define READ_PASSWORD_FROM_FILE
+
+#ifdef READ_PASSWORD_FROM_STDIN
+#include <termios.h> /* for echo on/off */
+#endif
+
+#include <nspr/nspr.h>
+#include <nspr/private/pprio.h>
+#include <nss/nss.h>
+#include <nss/ssl.h>
+#include <nss/sslerr.h>
+#include <nss/sslproto.h>
+#include <nss/pk11pub.h>
+#include <nss/secerr.h>
+#include <nss/keyhi.h>
+#include <nss/secmod.h>
+#include <nss/cert.h>
+
+#undef NSS_VERSION_INT
+#define NSS_VERSION_INT ((NSS_VMAJOR << 24) | (NSS_VMINOR << 16) | \
+ (NSS_VPATCH << 8) | NSS_VBUILD)
+
+/* NSS 3.12.5 and later have NSS_InitContext */
+#if NSS_VERSION_INT >= 0x030c0500
+#define HAVE_NSS_INITCONTEXT 1
+#endif
+
+/* NSS 3.12.9 and later have SECMOD_RestartModules */
+#if NSS_VERSION_INT >= 0x030c0900
+#define HAVE_SECMOD_RESTARTMODULES 1
+#endif
+
+/* InitContext does not currently work in server mode */
+/* #define INITCONTEXT_HACK 1 */
+
+typedef struct tlsm_ctx {
+ PRFileDesc *tc_model;
+ int tc_refcnt;
+ int tc_unique; /* unique number associated with this ctx */
+ PRBool tc_verify_cert;
+ CERTCertDBHandle *tc_certdb;
+ PK11SlotInfo *tc_certdb_slot;
+ CERTCertificate *tc_certificate;
+ SECKEYPrivateKey *tc_private_key;
+ char *tc_pin_file;
+ struct ldaptls *tc_config;
+ int tc_is_server;
+ int tc_require_cert;
+ PRCallOnceType tc_callonce;
+ PRBool tc_using_pem;
+#ifdef HAVE_NSS_INITCONTEXT
+ NSSInitContext *tc_initctx; /* the NSS context */
+#endif
+ PK11GenericObject **tc_pem_objs; /* array of objects to free */
+ int tc_n_pem_objs; /* number of objects */
+ PRBool tc_warn_only; /* only warn of errors in validation */
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_t tc_refmutex;
+#endif
+} tlsm_ctx;
+
+typedef PRFileDesc tlsm_session;
+
+static int tlsm_ctx_count;
+#define TLSM_CERTDB_DESC_FMT "ldap(%d)"
+
+static PRDescIdentity tlsm_layer_id;
+
+static const PRIOMethods tlsm_PR_methods;
+
+#define CERTDB_NONE NULL
+#define PREFIX_NONE NULL
+
+#define PEM_LIBRARY "nsspem"
+#define PEM_MODULE "PEM"
+#define PEM_CA_HASH_FILE_REGEX "^[0-9a-f]{8}\\.[0-9]+$"
+
+static SECMODModule *pem_module;
+
+#define DEFAULT_TOKEN_NAME "default"
+#define TLSM_PEM_SLOT_CACERTS "PEM Token #0"
+#define TLSM_PEM_SLOT_CERTS "PEM Token #1"
+
+#define PK11_SETATTRS(x,id,v,l) (x).type = (id); \
+ (x).pValue=(v); (x).ulValueLen = (l);
+
+/* forward declaration */
+static int tlsm_init( void );
+
+#ifdef LDAP_R_COMPILE
+
+/* it doesn't seem guaranteed that a client will call
+ tlsm_thr_init in a non-threaded context - so we have
+ to wrap the mutex creation in a prcallonce
+*/
+static ldap_pvt_thread_mutex_t tlsm_ctx_count_mutex;
+static ldap_pvt_thread_mutex_t tlsm_init_mutex;
+static ldap_pvt_thread_mutex_t tlsm_pem_mutex;
+static PRCallOnceType tlsm_init_mutex_callonce = {0,0};
+
+static PRStatus PR_CALLBACK
+tlsm_thr_init_callonce( void )
+{
+ if ( ldap_pvt_thread_mutex_init( &tlsm_ctx_count_mutex ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not create mutex for context counter: %d\n", errno, 0, 0 );
+ return PR_FAILURE;
+ }
+
+ if ( ldap_pvt_thread_mutex_init( &tlsm_init_mutex ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not create mutex for moznss initialization: %d\n", errno, 0, 0 );
+ return PR_FAILURE;
+ }
+
+ if ( ldap_pvt_thread_mutex_init( &tlsm_pem_mutex ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not create mutex for PEM module: %d\n", errno, 0, 0 );
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+static void
+tlsm_thr_init( void )
+{
+ ( void )PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce );
+}
+
+#endif /* LDAP_R_COMPILE */
+
+static const char *
+tlsm_dump_cipher_info(PRFileDesc *fd)
+{
+ PRUint16 ii;
+
+ for (ii = 0; ii < SSL_NumImplementedCiphers; ++ii) {
+ PRInt32 cipher = (PRInt32)SSL_ImplementedCiphers[ii];
+ PRBool enabled = PR_FALSE;
+ PRInt32 policy = 0;
+ SSLCipherSuiteInfo info;
+
+ if (fd) {
+ SSL_CipherPrefGet(fd, cipher, &enabled);
+ } else {
+ SSL_CipherPrefGetDefault(cipher, &enabled);
+ }
+ SSL_CipherPolicyGet(cipher, &policy);
+ SSL_GetCipherSuiteInfo(cipher, &info, (PRUintn)sizeof(info));
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: cipher: %d - %s, enabled: %d, ",
+ info.cipherSuite, info.cipherSuiteName, enabled );
+ Debug( LDAP_DEBUG_TRACE,
+ "policy: %d\n", policy, 0, 0 );
+ }
+
+ return "";
+}
+
+/* Cipher definitions */
+typedef struct {
+ char *ossl_name; /* The OpenSSL cipher name */
+ int num; /* The cipher id */
+ int attr; /* cipher attributes: algorithms, etc */
+ int version; /* protocol version valid for this cipher */
+ int bits; /* bits of strength */
+ int alg_bits; /* bits of the algorithm */
+ int strength; /* LOW, MEDIUM, HIGH */
+ int enabled; /* Enabled by default? */
+} cipher_properties;
+
+/* cipher attributes */
+#define SSL_kRSA 0x00000001L
+#define SSL_aRSA 0x00000002L
+#define SSL_aDSS 0x00000004L
+#define SSL_DSS SSL_aDSS
+#define SSL_eNULL 0x00000008L
+#define SSL_DES 0x00000010L
+#define SSL_3DES 0x00000020L
+#define SSL_RC4 0x00000040L
+#define SSL_RC2 0x00000080L
+#define SSL_AES 0x00000100L
+#define SSL_MD5 0x00000200L
+#define SSL_SHA1 0x00000400L
+#define SSL_SHA SSL_SHA1
+#define SSL_RSA (SSL_kRSA|SSL_aRSA)
+
+/* cipher strength */
+#define SSL_NULL 0x00000001L
+#define SSL_EXPORT40 0x00000002L
+#define SSL_EXPORT56 0x00000004L
+#define SSL_LOW 0x00000008L
+#define SSL_MEDIUM 0x00000010L
+#define SSL_HIGH 0x00000020L
+
+#define SSL2 0x00000001L
+#define SSL3 0x00000002L
+/* OpenSSL treats SSL3 and TLSv1 the same */
+#define TLS1 SSL3
+
+/* Cipher translation */
+static cipher_properties ciphers_def[] = {
+ /* SSL 2 ciphers */
+ {"DES-CBC3-MD5", SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_3DES|SSL_MD5, SSL2, 168, 168, SSL_HIGH, SSL_ALLOWED},
+ {"RC2-CBC-MD5", SSL_EN_RC2_128_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL2, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
+ {"RC4-MD5", SSL_EN_RC4_128_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL2, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
+ {"DES-CBC-MD5", SSL_EN_DES_64_CBC_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_MD5, SSL2, 56, 56, SSL_LOW, SSL_ALLOWED},
+ {"EXP-RC2-CBC-MD5", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL2, 40, 128, SSL_EXPORT40, SSL_ALLOWED},
+ {"EXP-RC4-MD5", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL2, 40, 128, SSL_EXPORT40, SSL_ALLOWED},
+
+ /* SSL3 ciphers */
+ {"RC4-MD5", SSL_RSA_WITH_RC4_128_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
+ {"RC4-SHA", SSL_RSA_WITH_RC4_128_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA1, SSL3, 128, 128, SSL_MEDIUM, SSL_ALLOWED},
+ {"DES-CBC3-SHA", SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_3DES|SSL_SHA1, SSL3, 168, 168, SSL_HIGH, SSL_ALLOWED},
+ {"DES-CBC-SHA", SSL_RSA_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA1, SSL3, 56, 56, SSL_LOW, SSL_ALLOWED},
+ {"EXP-RC4-MD5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_MD5, SSL3, 40, 128, SSL_EXPORT40, SSL_ALLOWED},
+ {"EXP-RC2-CBC-MD5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_kRSA|SSL_aRSA|SSL_RC2|SSL_MD5, SSL3, 0, 0, SSL_EXPORT40, SSL_ALLOWED},
+ {"NULL-MD5", SSL_RSA_WITH_NULL_MD5, SSL_kRSA|SSL_aRSA|SSL_eNULL|SSL_MD5, SSL3, 0, 0, SSL_NULL, SSL_NOT_ALLOWED},
+ {"NULL-SHA", SSL_RSA_WITH_NULL_SHA, SSL_kRSA|SSL_aRSA|SSL_eNULL|SSL_SHA1, SSL3, 0, 0, SSL_NULL, SSL_NOT_ALLOWED},
+
+ /* TLSv1 ciphers */
+ {"EXP1024-DES-CBC-SHA", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_DES|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED},
+ {"EXP1024-RC4-SHA", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL_kRSA|SSL_aRSA|SSL_RC4|SSL_SHA, TLS1, 56, 56, SSL_EXPORT56, SSL_ALLOWED},
+ {"AES128-SHA", TLS_RSA_WITH_AES_128_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 128, 128, SSL_HIGH, SSL_ALLOWED},
+ {"AES256-SHA", TLS_RSA_WITH_AES_256_CBC_SHA, SSL_kRSA|SSL_aRSA|SSL_AES|SSL_SHA, TLS1, 256, 256, SSL_HIGH, SSL_ALLOWED},
+};
+
+#define ciphernum (sizeof(ciphers_def)/sizeof(cipher_properties))
+
+/* given err which is the current errno, calls PR_SetError with
+ the corresponding NSPR error code */
+static void
+tlsm_map_error(int err)
+{
+ PRErrorCode prError;
+
+ switch ( err ) {
+ case EACCES:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case EADDRINUSE:
+ prError = PR_ADDRESS_IN_USE_ERROR;
+ break;
+ case EADDRNOTAVAIL:
+ prError = PR_ADDRESS_NOT_AVAILABLE_ERROR;
+ break;
+ case EAFNOSUPPORT:
+ prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
+ break;
+ case EAGAIN:
+ prError = PR_WOULD_BLOCK_ERROR;
+ break;
+ /*
+ * On QNX and Neutrino, EALREADY is defined as EBUSY.
+ */
+#if EALREADY != EBUSY
+ case EALREADY:
+ prError = PR_ALREADY_INITIATED_ERROR;
+ break;
+#endif
+ case EBADF:
+ prError = PR_BAD_DESCRIPTOR_ERROR;
+ break;
+#ifdef EBADMSG
+ case EBADMSG:
+ prError = PR_IO_ERROR;
+ break;
+#endif
+ case EBUSY:
+ prError = PR_FILESYSTEM_MOUNTED_ERROR;
+ break;
+ case ECONNABORTED:
+ prError = PR_CONNECT_ABORTED_ERROR;
+ break;
+ case ECONNREFUSED:
+ prError = PR_CONNECT_REFUSED_ERROR;
+ break;
+ case ECONNRESET:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+ case EDEADLK:
+ prError = PR_DEADLOCK_ERROR;
+ break;
+#ifdef EDIRCORRUPTED
+ case EDIRCORRUPTED:
+ prError = PR_DIRECTORY_CORRUPTED_ERROR;
+ break;
+#endif
+#ifdef EDQUOT
+ case EDQUOT:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+#endif
+ case EEXIST:
+ prError = PR_FILE_EXISTS_ERROR;
+ break;
+ case EFAULT:
+ prError = PR_ACCESS_FAULT_ERROR;
+ break;
+ case EFBIG:
+ prError = PR_FILE_TOO_BIG_ERROR;
+ break;
+ case EHOSTUNREACH:
+ prError = PR_HOST_UNREACHABLE_ERROR;
+ break;
+ case EINPROGRESS:
+ prError = PR_IN_PROGRESS_ERROR;
+ break;
+ case EINTR:
+ prError = PR_PENDING_INTERRUPT_ERROR;
+ break;
+ case EINVAL:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case EIO:
+ prError = PR_IO_ERROR;
+ break;
+ case EISCONN:
+ prError = PR_IS_CONNECTED_ERROR;
+ break;
+ case EISDIR:
+ prError = PR_IS_DIRECTORY_ERROR;
+ break;
+ case ELOOP:
+ prError = PR_LOOP_ERROR;
+ break;
+ case EMFILE:
+ prError = PR_PROC_DESC_TABLE_FULL_ERROR;
+ break;
+ case EMLINK:
+ prError = PR_MAX_DIRECTORY_ENTRIES_ERROR;
+ break;
+ case EMSGSIZE:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+#ifdef EMULTIHOP
+ case EMULTIHOP:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+#endif
+ case ENAMETOOLONG:
+ prError = PR_NAME_TOO_LONG_ERROR;
+ break;
+ case ENETUNREACH:
+ prError = PR_NETWORK_UNREACHABLE_ERROR;
+ break;
+ case ENFILE:
+ prError = PR_SYS_DESC_TABLE_FULL_ERROR;
+ break;
+ /*
+ * On SCO OpenServer 5, ENOBUFS is defined as ENOSR.
+ */
+#if defined(ENOBUFS) && (ENOBUFS != ENOSR)
+ case ENOBUFS:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+#endif
+ case ENODEV:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ENOENT:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case ENOLCK:
+ prError = PR_FILE_IS_LOCKED_ERROR;
+ break;
+#ifdef ENOLINK
+ case ENOLINK:
+ prError = PR_REMOTE_FILE_ERROR;
+ break;
+#endif
+ case ENOMEM:
+ prError = PR_OUT_OF_MEMORY_ERROR;
+ break;
+ case ENOPROTOOPT:
+ prError = PR_INVALID_ARGUMENT_ERROR;
+ break;
+ case ENOSPC:
+ prError = PR_NO_DEVICE_SPACE_ERROR;
+ break;
+#ifdef ENOSR
+ case ENOSR:
+ prError = PR_INSUFFICIENT_RESOURCES_ERROR;
+ break;
+#endif
+ case ENOTCONN:
+ prError = PR_NOT_CONNECTED_ERROR;
+ break;
+ case ENOTDIR:
+ prError = PR_NOT_DIRECTORY_ERROR;
+ break;
+ case ENOTSOCK:
+ prError = PR_NOT_SOCKET_ERROR;
+ break;
+ case ENXIO:
+ prError = PR_FILE_NOT_FOUND_ERROR;
+ break;
+ case EOPNOTSUPP:
+ prError = PR_NOT_TCP_SOCKET_ERROR;
+ break;
+#ifdef EOVERFLOW
+ case EOVERFLOW:
+ prError = PR_BUFFER_OVERFLOW_ERROR;
+ break;
+#endif
+ case EPERM:
+ prError = PR_NO_ACCESS_RIGHTS_ERROR;
+ break;
+ case EPIPE:
+ prError = PR_CONNECT_RESET_ERROR;
+ break;
+#ifdef EPROTO
+ case EPROTO:
+ prError = PR_IO_ERROR;
+ break;
+#endif
+ case EPROTONOSUPPORT:
+ prError = PR_PROTOCOL_NOT_SUPPORTED_ERROR;
+ break;
+ case EPROTOTYPE:
+ prError = PR_ADDRESS_NOT_SUPPORTED_ERROR;
+ break;
+ case ERANGE:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case EROFS:
+ prError = PR_READ_ONLY_FILESYSTEM_ERROR;
+ break;
+ case ESPIPE:
+ prError = PR_INVALID_METHOD_ERROR;
+ break;
+ case ETIMEDOUT:
+ prError = PR_IO_TIMEOUT_ERROR;
+ break;
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+ prError = PR_WOULD_BLOCK_ERROR;
+ break;
+#endif
+ case EXDEV:
+ prError = PR_NOT_SAME_DEVICE_ERROR;
+ break;
+ default:
+ prError = PR_UNKNOWN_ERROR;
+ break;
+ }
+ PR_SetError( prError, err );
+}
+
+/*
+ * cipher_list is an integer array with the following values:
+ * -1: never enable this cipher
+ * 0: cipher disabled
+ * 1: cipher enabled
+ */
+static int
+nss_parse_ciphers(const char *cipherstr, int cipher_list[ciphernum])
+{
+ int i;
+ char *cipher;
+ char *ciphers;
+ char *ciphertip;
+ int action;
+ int rv;
+
+ /* All disabled to start */
+ for (i=0; i<ciphernum; i++)
+ cipher_list[i] = 0;
+
+ ciphertip = strdup(cipherstr);
+ cipher = ciphers = ciphertip;
+
+ while (ciphers && (strlen(ciphers))) {
+ while ((*cipher) && (isspace(*cipher)))
+ ++cipher;
+
+ action = 1;
+ switch(*cipher) {
+ case '+': /* Add something */
+ action = 1;
+ cipher++;
+ break;
+ case '-': /* Subtract something */
+ action = 0;
+ cipher++;
+ break;
+ case '!': /* Disable something */
+ action = -1;
+ cipher++;
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ if ((ciphers = strchr(cipher, ':'))) {
+ *ciphers++ = '\0';
+ }
+
+ /* Do the easy one first */
+ if (!strcmp(cipher, "ALL")) {
+ for (i=0; i<ciphernum; i++) {
+ if (!(ciphers_def[i].attr & SSL_eNULL))
+ cipher_list[i] = action;
+ }
+ } else if (!strcmp(cipher, "COMPLEMENTOFALL")) {
+ for (i=0; i<ciphernum; i++) {
+ if ((ciphers_def[i].attr & SSL_eNULL))
+ cipher_list[i] = action;
+ }
+ } else if (!strcmp(cipher, "DEFAULT")) {
+ for (i=0; i<ciphernum; i++) {
+ cipher_list[i] = ciphers_def[i].enabled == SSL_ALLOWED ? 1 : 0;
+ }
+ } else {
+ int mask = 0;
+ int strength = 0;
+ int protocol = 0;
+ char *c;
+
+ c = cipher;
+ while (c && (strlen(c))) {
+
+ if ((c = strchr(cipher, '+'))) {
+ *c++ = '\0';
+ }
+
+ if (!strcmp(cipher, "RSA")) {
+ mask |= SSL_RSA;
+ } else if ((!strcmp(cipher, "NULL")) || (!strcmp(cipher, "eNULL"))) {
+ mask |= SSL_eNULL;
+ } else if (!strcmp(cipher, "AES")) {
+ mask |= SSL_AES;
+ } else if (!strcmp(cipher, "3DES")) {
+ mask |= SSL_3DES;
+ } else if (!strcmp(cipher, "DES")) {
+ mask |= SSL_DES;
+ } else if (!strcmp(cipher, "RC4")) {
+ mask |= SSL_RC4;
+ } else if (!strcmp(cipher, "RC2")) {
+ mask |= SSL_RC2;
+ } else if (!strcmp(cipher, "MD5")) {
+ mask |= SSL_MD5;
+ } else if ((!strcmp(cipher, "SHA")) || (!strcmp(cipher, "SHA1"))) {
+ mask |= SSL_SHA1;
+ } else if (!strcmp(cipher, "SSLv2")) {
+ protocol |= SSL2;
+ } else if (!strcmp(cipher, "SSLv3")) {
+ protocol |= SSL3;
+ } else if (!strcmp(cipher, "TLSv1")) {
+ protocol |= TLS1;
+ } else if (!strcmp(cipher, "HIGH")) {
+ strength |= SSL_HIGH;
+ } else if (!strcmp(cipher, "MEDIUM")) {
+ strength |= SSL_MEDIUM;
+ } else if (!strcmp(cipher, "LOW")) {
+ strength |= SSL_LOW;
+ } else if ((!strcmp(cipher, "EXPORT")) || (!strcmp(cipher, "EXP"))) {
+ strength |= SSL_EXPORT40|SSL_EXPORT56;
+ } else if (!strcmp(cipher, "EXPORT40")) {
+ strength |= SSL_EXPORT40;
+ } else if (!strcmp(cipher, "EXPORT56")) {
+ strength |= SSL_EXPORT56;
+ }
+
+ if (c)
+ cipher = c;
+
+ } /* while */
+
+ /* If we have a mask, apply it. If not then perhaps they provided
+ * a specific cipher to enable.
+ */
+ if (mask || strength || protocol) {
+ for (i=0; i<ciphernum; i++) {
+ if (((ciphers_def[i].attr & mask) ||
+ (ciphers_def[i].strength & strength) ||
+ (ciphers_def[i].version & protocol)) &&
+ (cipher_list[i] != -1)) {
+ /* Enable the NULL ciphers only if explicity
+ * requested */
+ if (ciphers_def[i].attr & SSL_eNULL) {
+ if (mask & SSL_eNULL)
+ cipher_list[i] = action;
+ } else
+ cipher_list[i] = action;
+ }
+ }
+ } else {
+ for (i=0; i<ciphernum; i++) {
+ if (!strcmp(ciphers_def[i].ossl_name, cipher) &&
+ cipher_list[i] != -1)
+ cipher_list[i] = action;
+ }
+ }
+ }
+
+ if (ciphers)
+ cipher = ciphers;
+ }
+
+ /* See if any ciphers were enabled */
+ rv = 0;
+ for (i=0; i<ciphernum; i++) {
+ if (cipher_list[i] == 1)
+ rv = 1;
+ }
+
+ free(ciphertip);
+
+ return rv;
+}
+
+static int
+tlsm_parse_ciphers(tlsm_ctx *ctx, const char *str)
+{
+ int cipher_state[ciphernum];
+ int rv, i;
+
+ if (!ctx)
+ return 0;
+
+ rv = nss_parse_ciphers(str, cipher_state);
+
+ if (rv) {
+ /* First disable everything */
+ for (i = 0; i < SSL_NumImplementedCiphers; i++)
+ SSL_CipherPrefSet(ctx->tc_model, SSL_ImplementedCiphers[i], SSL_NOT_ALLOWED);
+
+ /* Now enable what was requested */
+ for (i=0; i<ciphernum; i++) {
+ SSLCipherSuiteInfo suite;
+ PRBool enabled;
+
+ if (SSL_GetCipherSuiteInfo(ciphers_def[i].num, &suite, sizeof suite)
+ == SECSuccess) {
+ enabled = cipher_state[i] < 0 ? 0 : cipher_state[i];
+ if (enabled == SSL_ALLOWED) {
+ if (PK11_IsFIPS() && !suite.isFIPS)
+ enabled = SSL_NOT_ALLOWED;
+ }
+ SSL_CipherPrefSet(ctx->tc_model, ciphers_def[i].num, enabled);
+ }
+ }
+ }
+
+ return rv == 1 ? 0 : -1;
+}
+
+static SECStatus
+tlsm_bad_cert_handler(void *arg, PRFileDesc *ssl)
+{
+ SECStatus success = SECSuccess;
+ PRErrorCode err;
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ if (!ssl || !ctx) {
+ return SECFailure;
+ }
+
+ err = PORT_GetError();
+
+ switch (err) {
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ if (ctx->tc_verify_cert) {
+ success = SECFailure;
+ }
+ break;
+ /* we bypass NSS's hostname checks and do our own */
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ break;
+ default:
+ success = SECFailure;
+ break;
+ }
+
+ return success;
+}
+
+static const char *
+tlsm_dump_security_status(PRFileDesc *fd)
+{
+ char * cp; /* bulk cipher name */
+ char * ip; /* cert issuer DN */
+ char * sp; /* cert subject DN */
+ int op; /* High, Low, Off */
+ int kp0; /* total key bits */
+ int kp1; /* secret key bits */
+ SSL3Statistics * ssl3stats = SSL_GetStatistics();
+
+ SSL_SecurityStatus( fd, &op, &cp, &kp0, &kp1, &ip, &sp );
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS certificate verification: subject: %s, issuer: %s, cipher: %s, ",
+ sp ? sp : "-unknown-", ip ? ip : "-unknown-", cp ? cp : "-unknown-" );
+ PR_Free(cp);
+ PR_Free(ip);
+ PR_Free(sp);
+ Debug( LDAP_DEBUG_TRACE,
+ "security level: %s, secret key bits: %d, total key bits: %d, ",
+ ((op == SSL_SECURITY_STATUS_ON_HIGH) ? "high" :
+ ((op == SSL_SECURITY_STATUS_ON_LOW) ? "low" : "off")),
+ kp1, kp0 );
+
+ Debug( LDAP_DEBUG_TRACE,
+ "cache hits: %ld, cache misses: %ld, cache not reusable: %ld\n",
+ ssl3stats->hch_sid_cache_hits, ssl3stats->hch_sid_cache_misses,
+ ssl3stats->hch_sid_cache_not_ok );
+
+ return "";
+}
+
+static void
+tlsm_handshake_complete_cb( PRFileDesc *fd, void *client_data )
+{
+ tlsm_dump_security_status( fd );
+}
+
+#ifdef READ_PASSWORD_FROM_FILE
+static char *
+tlsm_get_pin_from_file(const char *token_name, tlsm_ctx *ctx)
+{
+ char *pwdstr = NULL;
+ char *contents = NULL;
+ char *lasts = NULL;
+ char *line = NULL;
+ char *candidate = NULL;
+ PRFileInfo file_info;
+ PRFileDesc *pwd_fileptr = PR_Open( ctx->tc_pin_file, PR_RDONLY, 00400 );
+
+ /* open the password file */
+ if ( !pwd_fileptr ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not open security pin file %s - error %d:%s.\n",
+ ctx->tc_pin_file, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ /* get the file size */
+ if ( PR_SUCCESS != PR_GetFileInfo( ctx->tc_pin_file, &file_info ) ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not get file info from pin file %s - error %d:%s.\n",
+ ctx->tc_pin_file, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ /* create a buffer to hold the file contents */
+ if ( !( contents = PR_CALLOC( file_info.size + 1 ) ) ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not alloc a buffer for contents of pin file %s - error %d:%s.\n",
+ ctx->tc_pin_file, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ /* read file into the buffer */
+ if( PR_Read( pwd_fileptr, contents, file_info.size ) <= 0 ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read the file contents from pin file %s - error %d:%s.\n",
+ ctx->tc_pin_file, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ /* format is [tokenname:]password EOL [tokenname:]password EOL ... */
+ /* if you want to use a password containing a colon character, use
+ the special tokenname "default" */
+ for ( line = PL_strtok_r( contents, "\r\n", &lasts ); line;
+ line = PL_strtok_r( NULL, "\r\n", &lasts ) ) {
+ char *colon;
+
+ if ( !*line ) {
+ continue; /* skip blank lines */
+ }
+ colon = PL_strchr( line, ':' );
+ if ( colon ) {
+ if ( *(colon + 1) && token_name &&
+ !PL_strncmp( token_name, line, colon-line ) ) {
+ candidate = colon + 1; /* found a definite match */
+ break;
+ } else if ( !PL_strncmp( DEFAULT_TOKEN_NAME, line, colon-line ) ) {
+ candidate = colon + 1; /* found possible match */
+ }
+ } else { /* no token name */
+ candidate = line;
+ }
+ }
+done:
+ if ( pwd_fileptr ) {
+ PR_Close( pwd_fileptr );
+ }
+ if ( candidate ) {
+ pwdstr = PL_strdup( candidate );
+ }
+ PL_strfree( contents );
+
+ return pwdstr;
+}
+#endif /* READ_PASSWORD_FROM_FILE */
+
+#ifdef READ_PASSWORD_FROM_STDIN
+/*
+ * Turn the echoing off on a tty.
+ */
+static void
+echoOff(int fd)
+{
+ if ( isatty( fd ) ) {
+ struct termios tio;
+ tcgetattr( fd, &tio );
+ tio.c_lflag &= ~ECHO;
+ tcsetattr( fd, TCSAFLUSH, &tio );
+ }
+}
+
+/*
+ * Turn the echoing on on a tty.
+ */
+static void
+echoOn(int fd)
+{
+ if ( isatty( fd ) ) {
+ struct termios tio;
+ tcgetattr( fd, &tio );
+ tio.c_lflag |= ECHO;
+ tcsetattr( fd, TCSAFLUSH, &tio );
+ tcsetattr( fd, TCSAFLUSH, &tio );
+ }
+}
+#endif /* READ_PASSWORD_FROM_STDIN */
+
+/*
+ * This does the actual work of reading the pin/password/pass phrase
+ */
+static char *
+tlsm_get_pin(PK11SlotInfo *slot, PRBool retry, tlsm_ctx *ctx)
+{
+ char *token_name = NULL;
+ char *pwdstr = NULL;
+
+ token_name = PK11_GetTokenName( slot );
+#ifdef READ_PASSWORD_FROM_FILE
+ /* Try to get the passwords from the password file if it exists.
+ * THIS IS UNSAFE and is provided for convenience only. Without this
+ * capability the server would have to be started in foreground mode
+ * if using an encrypted key.
+ */
+ if ( ctx && ctx->tc_pin_file ) {
+ pwdstr = tlsm_get_pin_from_file( token_name, ctx );
+ if ( retry && pwdstr != NULL )
+ return NULL;
+ }
+#endif /* RETRIEVE_PASSWORD_FROM_FILE */
+#ifdef READ_PASSWORD_FROM_STDIN
+ if ( !pwdstr ) {
+ int infd = PR_FileDesc2NativeHandle( PR_STDIN );
+ int isTTY = isatty( infd );
+ unsigned char phrase[200];
+ /* Prompt for password */
+ if ( isTTY ) {
+ fprintf( stdout,
+ "Please enter pin, password, or pass phrase for security token '%s': ",
+ token_name ? token_name : DEFAULT_TOKEN_NAME );
+ echoOff( infd );
+ }
+ fgets( (char*)phrase, sizeof(phrase), stdin );
+ if ( isTTY ) {
+ fprintf( stdout, "\n" );
+ echoOn( infd );
+ }
+ /* stomp on newline */
+ phrase[strlen((char*)phrase)-1] = 0;
+
+ pwdstr = PL_strdup( (char*)phrase );
+ }
+
+#endif /* READ_PASSWORD_FROM_STDIN */
+ return pwdstr;
+}
+
+/*
+ * PKCS11 devices (including the internal softokn cert/key database)
+ * may be protected by a pin or password or even pass phrase
+ * MozNSS needs a way for the user to provide that
+ */
+static char *
+tlsm_pin_prompt(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ return tlsm_get_pin( slot, retry, ctx );
+}
+
+static char *
+tlsm_ctx_subject_name(tlsm_ctx *ctx)
+{
+ if ( !ctx || !ctx->tc_certificate )
+ return "(unknown)";
+
+ return ctx->tc_certificate->subjectName;
+}
+
+static SECStatus
+tlsm_get_basic_constraint_extension( CERTCertificate *cert,
+ CERTBasicConstraints *cbcval )
+{
+ SECItem encodedVal = { 0, NULL };
+ SECStatus rc;
+
+ rc = CERT_FindCertExtension( cert, SEC_OID_X509_BASIC_CONSTRAINTS,
+ &encodedVal);
+ if ( rc != SECSuccess ) {
+ return rc;
+ }
+
+ rc = CERT_DecodeBasicConstraintValue( cbcval, &encodedVal );
+
+ /* free the raw extension data */
+ PORT_Free( encodedVal.data );
+
+ return rc;
+}
+
+static PRBool
+tlsm_cert_is_self_issued( CERTCertificate *cert )
+{
+ /* A cert is self-issued if its subject and issuer are equal and
+ * both are of non-zero length.
+ */
+ PRBool is_self_issued = cert &&
+ (PRBool)SECITEM_ItemsAreEqual( &cert->derIssuer,
+ &cert->derSubject ) &&
+ cert->derSubject.len > 0;
+ return is_self_issued;
+}
+
+/*
+ * The private key for used certificate can be already unlocked by other
+ * thread or library. Find the unlocked key if possible.
+ */
+static SECKEYPrivateKey *
+tlsm_find_unlocked_key( tlsm_ctx *ctx, void *pin_arg )
+{
+ SECKEYPrivateKey *result = NULL;
+
+ PK11SlotList *slots = PK11_GetAllSlotsForCert( ctx->tc_certificate, NULL );
+ if ( !slots ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: cannot get all slots for certificate '%s' (error %d: %s)",
+ tlsm_ctx_subject_name( ctx ), errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return result;
+ }
+
+ PK11SlotListElement *le;
+ for ( le = slots->head; le; le = le->next ) {
+ PK11SlotInfo *slot = le->slot;
+ if ( PK11_IsLoggedIn( slot, NULL ) ) {
+ result = PK11_FindKeyByDERCert( slot, ctx->tc_certificate, pin_arg );
+ break;
+ }
+ }
+
+ PK11_FreeSlotList( slots );
+ return result;
+}
+
+static SECStatus
+tlsm_verify_cert(CERTCertDBHandle *handle, CERTCertificate *cert, void *pinarg,
+ PRBool checksig, SECCertificateUsage certUsage, PRBool warn_only,
+ PRBool ignore_issuer )
+{
+ CERTVerifyLog verifylog;
+ SECStatus ret = SECSuccess;
+ const char *name;
+ int debug_level = LDAP_DEBUG_ANY;
+
+ if ( warn_only ) {
+ debug_level = LDAP_DEBUG_TRACE;
+ }
+
+ /* the log captures information about every cert in the chain, so we can tell
+ which cert caused the problem and what the problem was */
+ memset( &verifylog, 0, sizeof( verifylog ) );
+ verifylog.arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+ if ( verifylog.arena == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS certificate verification: Out of memory for certificate verification logger\n",
+ 0, 0, 0 );
+ return SECFailure;
+ }
+ ret = CERT_VerifyCertificate( handle, cert, checksig, certUsage, PR_Now(), pinarg, &verifylog,
+ NULL );
+ if ( ( name = cert->subjectName ) == NULL ) {
+ name = cert->nickname;
+ }
+ if ( verifylog.head == NULL ) {
+ /* it is possible for CERT_VerifyCertificate return with an error with no logging */
+ if ( ret != SECSuccess ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - error %d:%s.\n",
+ name ? name : "(unknown)",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ } else {
+ const char *name;
+ CERTVerifyLogNode *node;
+
+ ret = SECSuccess; /* reset */
+ node = verifylog.head;
+ while ( node ) {
+ if ( ( name = node->cert->subjectName ) == NULL ) {
+ name = node->cert->nickname;
+ }
+ if ( node->error ) {
+ /* NSS does not like CA certs that have the basic constraints extension
+ with the CA flag set to FALSE - openssl doesn't check if the cert
+ is self issued */
+ if ( ( node->error == SEC_ERROR_CA_CERT_INVALID ) &&
+ tlsm_cert_is_self_issued( node->cert ) ) {
+
+ PRErrorCode orig_error = PR_GetError();
+ PRInt32 orig_oserror = PR_GetOSError();
+
+ CERTBasicConstraints basicConstraint;
+ SECStatus rv = tlsm_get_basic_constraint_extension( node->cert, &basicConstraint );
+ if ( ( rv == SECSuccess ) && ( basicConstraint.isCA == PR_FALSE ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: certificate [%s] is not correct because it is a CA cert and the "
+ "BasicConstraint CA flag is set to FALSE - allowing for now, but "
+ "please fix your certs if possible\n", name, 0, 0 );
+ } else { /* does not have basicconstraint, or some other error */
+ ret = SECFailure;
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - CA cert is not valid\n",
+ name, 0, 0 );
+ }
+
+ PR_SetError( orig_error, orig_oserror );
+
+ } else if ( warn_only || ( ignore_issuer && (
+ node->error == SEC_ERROR_UNKNOWN_ISSUER ||
+ node->error == SEC_ERROR_UNTRUSTED_ISSUER )
+ ) ) {
+ ret = SECSuccess;
+ Debug( debug_level,
+ "TLS: Warning: ignoring error for certificate [%s] - error %ld:%s.\n",
+ name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) );
+ } else {
+ ret = SECFailure;
+ Debug( debug_level,
+ "TLS: certificate [%s] is not valid - error %ld:%s.\n",
+ name, node->error, PR_ErrorToString( node->error, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ }
+ CERT_DestroyCertificate( node->cert );
+ node = node->next;
+ }
+ }
+
+ PORT_FreeArena( verifylog.arena, PR_FALSE );
+
+ if ( ret == SECSuccess ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: certificate [%s] is valid\n", name, 0, 0 );
+ }
+
+ return ret;
+}
+
+static SECStatus
+tlsm_auth_cert_handler(void *arg, PRFileDesc *fd,
+ PRBool checksig, PRBool isServer)
+{
+ SECCertificateUsage certUsage = isServer ? certificateUsageSSLClient : certificateUsageSSLServer;
+ SECStatus ret = SECSuccess;
+ CERTCertificate *peercert = SSL_PeerCertificate( fd );
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ ret = tlsm_verify_cert( ctx->tc_certdb, peercert,
+ SSL_RevealPinArg( fd ),
+ checksig, certUsage, ctx->tc_warn_only, PR_FALSE );
+ CERT_DestroyCertificate( peercert );
+
+ return ret;
+}
+
+static PRCallOnceType tlsm_register_shutdown_callonce = {0,0};
+
+static SECStatus
+tlsm_nss_shutdown_cb( void *appData, void *nssData )
+{
+ SECStatus rc = SECSuccess;
+
+ SSL_ShutdownServerSessionIDCache();
+
+ if ( pem_module ) {
+ SECMOD_UnloadUserModule( pem_module );
+ SECMOD_DestroyModule( pem_module );
+ pem_module = NULL;
+ }
+
+ /* init callonce so it can be armed again for cases like persistent daemon with LDAP_OPT_X_TLS_NEWCTX */
+ tlsm_register_shutdown_callonce.initialized = 0;
+ tlsm_register_shutdown_callonce.inProgress = 0;
+ tlsm_register_shutdown_callonce.status = 0;
+
+ return rc;
+}
+
+static PRStatus PR_CALLBACK
+tlsm_register_nss_shutdown_cb( void )
+{
+ if ( SECSuccess == NSS_RegisterShutdown( tlsm_nss_shutdown_cb,
+ NULL ) ) {
+ return PR_SUCCESS;
+ }
+ return PR_FAILURE;
+}
+
+static PRStatus
+tlsm_register_nss_shutdown( void )
+{
+ return PR_CallOnce( &tlsm_register_shutdown_callonce,
+ tlsm_register_nss_shutdown_cb );
+}
+
+static int
+tlsm_init_pem_module( void )
+{
+ int rc = 0;
+ char *fullname = NULL;
+ char *configstring = NULL;
+
+ if ( pem_module ) {
+ return rc;
+ }
+
+ /* not loaded - load it */
+ /* get the system dependent library name */
+ fullname = PR_GetLibraryName( NULL, PEM_LIBRARY );
+ /* Load our PKCS#11 module */
+ configstring = PR_smprintf( "library=%s name=" PEM_MODULE " parameters=\"\"", fullname );
+ PL_strfree( fullname );
+
+ pem_module = SECMOD_LoadUserModule( configstring, NULL, PR_FALSE );
+ PR_smprintf_free( configstring );
+
+ if ( !pem_module || !pem_module->loaded ) {
+ if ( pem_module ) {
+ SECMOD_DestroyModule( pem_module );
+ pem_module = NULL;
+ }
+ rc = -1;
+ }
+
+ return rc;
+}
+
+static void
+tlsm_add_pem_obj( tlsm_ctx *ctx, PK11GenericObject *obj )
+{
+ int idx = ctx->tc_n_pem_objs;
+ ctx->tc_n_pem_objs++;
+ ctx->tc_pem_objs = (PK11GenericObject **)
+ PORT_Realloc( ctx->tc_pem_objs, ctx->tc_n_pem_objs * sizeof( PK11GenericObject * ) );
+ ctx->tc_pem_objs[idx] = obj;
+}
+
+static void
+tlsm_free_pem_objs( tlsm_ctx *ctx )
+{
+ /* free in reverse order of allocation */
+ while ( ctx->tc_n_pem_objs-- ) {
+ PK11_DestroyGenericObject( ctx->tc_pem_objs[ctx->tc_n_pem_objs] );
+ ctx->tc_pem_objs[ctx->tc_n_pem_objs] = NULL;
+ }
+ PORT_Free(ctx->tc_pem_objs);
+ ctx->tc_pem_objs = NULL;
+ ctx->tc_n_pem_objs = 0;
+}
+
+static int
+tlsm_add_cert_from_file( tlsm_ctx *ctx, const char *filename, PRBool isca )
+{
+ PK11SlotInfo *slot;
+ PK11GenericObject *cert;
+ CK_ATTRIBUTE attrs[4];
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS objClass = CKO_CERTIFICATE;
+ char *slotname;
+ PRFileInfo fi;
+ PRStatus status;
+ SECItem certDER = { 0, NULL, 0 };
+
+ memset( &fi, 0, sizeof(fi) );
+ status = PR_GetFileInfo( filename, &fi );
+ if ( PR_SUCCESS != status) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read certificate file %s - error %d:%s.\n",
+ filename, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ if ( fi.type != PR_FILE_FILE ) {
+ PR_SetError(PR_IS_DIRECTORY_ERROR, 0);
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the certificate file %s is not a file.\n",
+ filename, 0 ,0 );
+ return -1;
+ }
+
+ slotname = isca ? TLSM_PEM_SLOT_CACERTS : TLSM_PEM_SLOT_CERTS;
+ slot = PK11_FindSlotByName( slotname );
+
+ if ( !slot ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not find the slot for the certificate '%s' - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) );
+ PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) );
+ PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *) filename, strlen( filename ) + 1 );
+ PK11_SETATTRS( attrs[3], CKA_TRUST, isca ? &cktrue : &ckfalse, sizeof( CK_BBOOL ) );
+
+ cert = PK11_CreateGenericObject( slot, attrs, 4, PR_FALSE /* isPerm */ );
+
+ if ( !cert ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not add the certificate '%s' - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ PK11_FreeSlot( slot );
+ return -1;
+ }
+
+ /* if not CA, we store the certificate in ctx->tc_certificate */
+ if ( !isca ) {
+ if ( PK11_ReadRawAttribute( PK11_TypeGeneric, cert, CKA_VALUE, &certDER ) != SECSuccess ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not get DER of the '%s' certificate - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ PK11_DestroyGenericObject( cert );
+ PK11_FreeSlot( slot );
+ return -1;
+ }
+
+ ctx->tc_certificate = PK11_FindCertFromDERCertItem( slot, &certDER, NULL );
+ SECITEM_FreeItem( &certDER, PR_FALSE );
+
+ if ( !ctx->tc_certificate ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not get certificate '%s' using DER - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ PK11_DestroyGenericObject( cert );
+ PK11_FreeSlot( slot );
+ return -1;
+ }
+ }
+
+ tlsm_add_pem_obj( ctx, cert );
+
+ PK11_FreeSlot( slot );
+
+ return 0;
+}
+
+static int
+tlsm_ctx_load_private_key( tlsm_ctx *ctx )
+{
+ if ( !ctx->tc_certificate )
+ return -1;
+
+ if ( ctx->tc_private_key )
+ return 0;
+
+ void *pin_arg = SSL_RevealPinArg( ctx->tc_model );
+
+ SECKEYPrivateKey *unlocked_key = tlsm_find_unlocked_key( ctx, pin_arg );
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: %s unlocked certificate for certificate '%s'.\n",
+ unlocked_key ? "found" : "no", tlsm_ctx_subject_name( ctx ), 0 );
+
+ /* prefer unlocked key, then key from opened certdb, then any other */
+ if ( unlocked_key )
+ ctx->tc_private_key = unlocked_key;
+ else if ( ctx->tc_certdb_slot && !ctx->tc_using_pem )
+ ctx->tc_private_key = PK11_FindKeyByDERCert( ctx->tc_certdb_slot, ctx->tc_certificate, pin_arg );
+ else
+ ctx->tc_private_key = PK11_FindKeyByAnyCert( ctx->tc_certificate, pin_arg );
+
+ if ( !ctx->tc_private_key ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug(LDAP_DEBUG_ANY,
+ "TLS: cannot find private key for certificate '%s' (error %d: %s)",
+ tlsm_ctx_subject_name( ctx ), errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+tlsm_add_key_from_file( tlsm_ctx *ctx, const char *filename )
+{
+ PK11SlotInfo * slot = NULL;
+ PK11GenericObject *key;
+ CK_ATTRIBUTE attrs[3];
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY;
+ int retcode = 0;
+ PRFileInfo fi;
+ PRStatus status;
+
+ memset( &fi, 0, sizeof(fi) );
+ status = PR_GetFileInfo( filename, &fi );
+ if ( PR_SUCCESS != status) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read key file %s - error %d:%s.\n",
+ filename, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ if ( fi.type != PR_FILE_FILE ) {
+ PR_SetError(PR_IS_DIRECTORY_ERROR, 0);
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the key file %s is not a file.\n",
+ filename, 0 ,0 );
+ return -1;
+ }
+
+ slot = PK11_FindSlotByName( TLSM_PEM_SLOT_CERTS );
+
+ if ( !slot ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not find the slot for the private key '%s' - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+
+ PK11_SETATTRS( attrs[0], CKA_CLASS, &objClass, sizeof( objClass ) );
+ PK11_SETATTRS( attrs[1], CKA_TOKEN, &cktrue, sizeof( CK_BBOOL ) );
+ PK11_SETATTRS( attrs[2], CKA_LABEL, (unsigned char *)filename, strlen( filename ) + 1 );
+
+ key = PK11_CreateGenericObject( slot, attrs, 3, PR_FALSE /* isPerm */ );
+
+ if ( !key ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not add the private key '%s' - error %d:%s.\n",
+ filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ retcode = -1;
+ } else {
+ tlsm_add_pem_obj( ctx, key );
+ retcode = 0;
+
+ /* When adding an encrypted key the PKCS#11 will be set as removed */
+ /* This will force the token to be seen as re-inserted */
+ SECMOD_WaitForAnyTokenEvent( pem_module, 0, 0 );
+ PK11_IsPresent( slot );
+ }
+
+ PK11_FreeSlot( slot );
+
+ return retcode;
+}
+
+static int
+tlsm_init_ca_certs( tlsm_ctx *ctx, const char *cacertfile, const char *cacertdir )
+{
+ PRBool isca = PR_TRUE;
+ PRStatus status = PR_SUCCESS;
+ PRErrorCode errcode = PR_SUCCESS;
+
+ if ( !cacertfile && !cacertdir ) {
+ /* no checking - not good, but allowed */
+ return 0;
+ }
+
+ if ( cacertfile ) {
+ int rc = tlsm_add_cert_from_file( ctx, cacertfile, isca );
+ if ( rc ) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: %s is not a valid CA certificate file - error %d:%s.\n",
+ cacertfile, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ /* failure with cacertfile is a hard failure even if cacertdir is
+ also specified and contains valid CA cert files */
+ status = PR_FAILURE;
+ } else {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: loaded CA certificate file %s.\n",
+ cacertfile, 0, 0 );
+ }
+ }
+
+ /* if cacertfile above failed, we will return failure, even
+ if there is a valid CA cert in cacertdir - but we still
+ process cacertdir in case the user has enabled trace level
+ debugging so they can see the processing for cacertdir too */
+ /* any cacertdir failures are "soft" failures - if the user specifies
+ no cert checking, then we allow the tls/ssl to continue, no matter
+ what was specified for cacertdir, or the contents of the directory
+ - this is different behavior than that of cacertfile */
+ if ( cacertdir ) {
+ PRFileInfo fi;
+ PRDir *dir;
+ PRDirEntry *entry;
+ PRStatus fistatus = PR_FAILURE;
+ regex_t hashfile_re;
+
+ memset( &fi, 0, sizeof(fi) );
+ fistatus = PR_GetFileInfo( cacertdir, &fi );
+ if ( PR_SUCCESS != fistatus) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not get info about the CA certificate directory %s - error %d:%s.\n",
+ cacertdir, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ if ( fi.type != PR_FILE_DIRECTORY ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the CA certificate directory %s is not a directory.\n",
+ cacertdir, 0 ,0 );
+ goto done;
+ }
+
+ dir = PR_OpenDir( cacertdir );
+ if ( NULL == dir ) {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not open the CA certificate directory %s - error %d:%s.\n",
+ cacertdir, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ goto done;
+ }
+
+ if ( regcomp( &hashfile_re, PEM_CA_HASH_FILE_REGEX, REG_NOSUB|REG_EXTENDED ) != 0 ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: cannot compile regex for CA hash files matching\n", 0, 0, 0 );
+ goto done;
+ }
+
+ do {
+ entry = PR_ReadDir( dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN );
+ if ( ( NULL != entry ) && ( NULL != entry->name ) ) {
+ char *fullpath = NULL;
+ int match;
+
+ match = regexec( &hashfile_re, entry->name, 0, NULL, 0 );
+ if ( match == REG_NOMATCH ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: skipping '%s' - filename does not have expected format "
+ "(certificate hash with numeric suffix)\n", entry->name, 0, 0 );
+ continue;
+ } else if ( match != 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: cannot execute regex for CA hash file matching (%d).\n",
+ match, 0, 0 );
+ continue;
+ }
+
+ fullpath = PR_smprintf( "%s/%s", cacertdir, entry->name );
+ if ( !tlsm_add_cert_from_file( ctx, fullpath, isca ) ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: loaded CA certificate file %s from CA certificate directory %s.\n",
+ fullpath, cacertdir, 0 );
+ } else {
+ errcode = PR_GetError();
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: %s is not a valid CA certificate file - error %d:%s.\n",
+ fullpath, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ PR_smprintf_free( fullpath );
+ }
+ } while ( NULL != entry );
+ regfree ( &hashfile_re );
+ PR_CloseDir( dir );
+ }
+done:
+ if ( status != PR_SUCCESS ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * NSS supports having multiple cert/key databases in the same
+ * directory, each one having a unique string prefix e.g.
+ * slapd-01-cert8.db - the prefix here is "slapd-01-"
+ * this function examines the given certdir - if it looks like
+ * /path/to/directory/prefix it will return the
+ * /path/to/directory part in realcertdir, and the prefix in prefix
+ */
+static void
+tlsm_get_certdb_prefix( const char *certdir, char **realcertdir, char **prefix )
+{
+ char sep = PR_GetDirectorySeparator();
+ char *ptr = NULL;
+ struct PRFileInfo prfi;
+ PRStatus prc;
+
+ *realcertdir = (char *)certdir; /* default is the one passed in */
+
+ /* if certdir is not given, just return */
+ if ( !certdir ) {
+ return;
+ }
+
+ prc = PR_GetFileInfo( certdir, &prfi );
+ /* if certdir exists (file or directory) then it cannot specify a prefix */
+ if ( prc == PR_SUCCESS ) {
+ return;
+ }
+
+ /* if certdir was given, and there is a '/' in certdir, see if there
+ is anything after the last '/' - if so, assume it is the prefix */
+ if ( ( ( ptr = strrchr( certdir, sep ) ) ) && *(ptr+1) ) {
+ *realcertdir = PL_strndup( certdir, ptr-certdir );
+ *prefix = PL_strdup( ptr+1 );
+ }
+
+ return;
+}
+
+/*
+ * Currently mutiple MozNSS contexts share one certificate storage. When the
+ * certdb is being opened, only new certificates are added to the storage.
+ * When different databases are used, conflicting nicknames make the
+ * certificate lookup by the nickname impossible. In addition a token
+ * description might be prepended in certain conditions.
+ *
+ * In order to make the certificate lookup by nickname possible, we explicitly
+ * open each database using SECMOD_OpenUserDB and assign it the token
+ * description. The token description is generated using ctx->tc_unique value,
+ * which is unique for each context.
+ */
+static PK11SlotInfo *
+tlsm_init_open_certdb( tlsm_ctx *ctx, const char *dbdir, const char *prefix )
+{
+ PK11SlotInfo *slot = NULL;
+ char *token_desc = NULL;
+ char *config = NULL;
+
+ token_desc = PR_smprintf( TLSM_CERTDB_DESC_FMT, ctx->tc_unique );
+ config = PR_smprintf( "configDir='%s' tokenDescription='%s' certPrefix='%s' keyPrefix='%s' flags=readOnly",
+ dbdir, token_desc, prefix, prefix );
+ Debug( LDAP_DEBUG_TRACE, "TLS: certdb config: %s\n", config, 0, 0 );
+
+ slot = SECMOD_OpenUserDB( config );
+ if ( !slot ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_TRACE, "TLS: cannot open certdb '%s', error %d:%s\n", dbdir, errcode,
+ PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+
+ if ( token_desc )
+ PR_smprintf_free( token_desc );
+ if ( config )
+ PR_smprintf_free( config );
+
+ return slot;
+}
+
+/*
+ * This is the part of the init we defer until we get the
+ * actual security configuration information. This is
+ * only called once, protected by a PRCallOnce
+ * NOTE: This must be done before the first call to SSL_ImportFD,
+ * especially the setting of the policy
+ * NOTE: This must be called after fork()
+ */
+static int
+tlsm_deferred_init( void *arg )
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+ struct ldaptls *lt = ctx->tc_config;
+ const char *securitydirs[3];
+ int ii;
+ int nn;
+ PRErrorCode errcode = 1;
+#ifdef HAVE_NSS_INITCONTEXT
+ NSSInitParameters initParams;
+ NSSInitContext *initctx = NULL;
+ PK11SlotInfo *certdb_slot = NULL;
+#endif
+ SECStatus rc;
+ int done = 0;
+
+#ifdef HAVE_SECMOD_RESTARTMODULES
+ /* NSS enforces the pkcs11 requirement that modules should be unloaded after
+ a fork() - since there is no portable way to determine if NSS has been
+ already initialized in a parent process, we just call SECMOD_RestartModules
+ with force == FALSE - if the module has been unloaded due to a fork, it will
+ be reloaded, otherwise, it is a no-op */
+ if ( SECFailure == ( rc = SECMOD_RestartModules(PR_FALSE /* do not force */) ) ) {
+ errcode = PORT_GetError();
+ if ( errcode != SEC_ERROR_NOT_INITIALIZED ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not restart the security modules: %d:%s\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ } else {
+ errcode = 1;
+ }
+ }
+#endif
+
+#ifdef HAVE_NSS_INITCONTEXT
+ memset( &initParams, 0, sizeof( initParams ) );
+ initParams.length = sizeof( initParams );
+#endif /* HAVE_NSS_INITCONTEXT */
+
+#ifdef LDAP_R_COMPILE
+ if ( PR_CallOnce( &tlsm_init_mutex_callonce, tlsm_thr_init_callonce ) ) {
+ return -1;
+ }
+#endif /* LDAP_R_COMPILE */
+
+#ifndef HAVE_NSS_INITCONTEXT
+ if ( !NSS_IsInitialized() ) {
+#endif /* HAVE_NSS_INITCONTEXT */
+ /*
+ MOZNSS_DIR will override everything else - you can
+ always set MOZNSS_DIR to force the use of this
+ directory
+ If using MOZNSS, specify the location of the moznss db dir
+ in the cacertdir directive of the OpenLDAP configuration.
+ DEFAULT_MOZNSS_DIR will only be used if the code cannot
+ find a security dir to use based on the current
+ settings
+ */
+ nn = 0;
+ securitydirs[nn++] = PR_GetEnv( "MOZNSS_DIR" );
+ securitydirs[nn++] = lt->lt_cacertdir;
+ securitydirs[nn++] = PR_GetEnv( "DEFAULT_MOZNSS_DIR" );
+ for ( ii = 0; !done && ( ii < nn ); ++ii ) {
+ char *realcertdir = NULL;
+ const char *defprefix = "";
+ char *prefix = (char *)defprefix;
+ const char *securitydir = securitydirs[ii];
+ if ( NULL == securitydir ) {
+ continue;
+ }
+
+ tlsm_get_certdb_prefix( securitydir, &realcertdir, &prefix );
+
+ /* initialize only moddb; certdb will be initialized explicitly */
+#ifdef HAVE_NSS_INITCONTEXT
+#ifdef INITCONTEXT_HACK
+ if ( !NSS_IsInitialized() && ctx->tc_is_server ) {
+ rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY );
+ } else {
+ initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB,
+ &initParams, NSS_INIT_READONLY|NSS_INIT_NOCERTDB );
+ }
+#else
+ initctx = NSS_InitContext( realcertdir, prefix, prefix, SECMOD_DB,
+ &initParams, NSS_INIT_READONLY|NSS_INIT_NOCERTDB );
+#endif
+ rc = SECFailure;
+
+ if ( initctx != NULL ) {
+ certdb_slot = tlsm_init_open_certdb( ctx, realcertdir, prefix );
+ if ( certdb_slot ) {
+ rc = SECSuccess;
+ ctx->tc_initctx = initctx;
+ ctx->tc_certdb_slot = certdb_slot;
+ } else {
+ NSS_ShutdownContext( initctx );
+ initctx = NULL;
+ }
+ }
+#else
+ rc = NSS_Initialize( realcertdir, prefix, prefix, SECMOD_DB, NSS_INIT_READONLY );
+#endif
+
+ if ( rc != SECSuccess ) {
+ errcode = PORT_GetError();
+ if ( securitydirs[ii] != lt->lt_cacertdir) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n",
+ realcertdir, prefix, errcode );
+ }
+ } else {
+ /* success */
+ Debug( LDAP_DEBUG_TRACE, "TLS: using moznss security dir %s prefix %s.\n",
+ realcertdir, prefix, 0 );
+ errcode = 0;
+ done = 1;
+ }
+ if ( realcertdir != securitydir ) {
+ PL_strfree( realcertdir );
+ }
+ if ( prefix != defprefix ) {
+ PL_strfree( prefix );
+ }
+ }
+
+ if ( errcode ) { /* no moznss db found, or not using moznss db */
+#ifdef HAVE_NSS_INITCONTEXT
+ int flags = NSS_INIT_READONLY|NSS_INIT_NOCERTDB|NSS_INIT_NOMODDB;
+#ifdef INITCONTEXT_HACK
+ if ( !NSS_IsInitialized() && ctx->tc_is_server ) {
+ rc = NSS_NoDB_Init( NULL );
+ } else {
+ initctx = NSS_InitContext( CERTDB_NONE, PREFIX_NONE, PREFIX_NONE, SECMOD_DB,
+ &initParams, flags );
+ rc = (initctx == NULL) ? SECFailure : SECSuccess;
+ }
+#else
+ initctx = NSS_InitContext( CERTDB_NONE, PREFIX_NONE, PREFIX_NONE, SECMOD_DB,
+ &initParams, flags );
+ if ( initctx ) {
+ ctx->tc_initctx = initctx;
+ rc = SECSuccess;
+ } else {
+ rc = SECFailure;
+ }
+#endif
+#else
+ rc = NSS_NoDB_Init( NULL );
+#endif
+ if ( rc != SECSuccess ) {
+ errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not initialize moznss - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
+ }
+
+ if ( errcode || lt->lt_cacertfile ) {
+ /* initialize the PEM module */
+ if ( tlsm_init_pem_module() ) {
+ int pem_errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not initialize moznss PEM module - error %d:%s.\n",
+ pem_errcode, PR_ErrorToString( pem_errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+
+ if ( errcode ) /* PEM is required */
+ return -1;
+
+ } else if ( !errcode ) {
+ tlsm_init_ca_certs( ctx, lt->lt_cacertfile, NULL );
+ }
+ }
+
+ if ( errcode ) {
+ if ( tlsm_init_ca_certs( ctx, lt->lt_cacertfile, lt->lt_cacertdir ) ) {
+ /* if we tried to use lt->lt_cacertdir as an NSS key/cert db, errcode
+ will be a value other than 1 - print an error message so that the
+ user will know that failed too */
+ if ( ( errcode != 1 ) && ( lt->lt_cacertdir ) ) {
+ char *realcertdir = NULL;
+ char *prefix = NULL;
+ tlsm_get_certdb_prefix( lt->lt_cacertdir, &realcertdir, &prefix );
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: could not initialize moznss using security dir %s prefix %s - error %d.\n",
+ realcertdir, prefix ? prefix : "", errcode );
+ if ( realcertdir != lt->lt_cacertdir ) {
+ PL_strfree( realcertdir );
+ }
+ PL_strfree( prefix );
+ }
+ return -1;
+ }
+ }
+
+ NSS_SetDomesticPolicy();
+
+ PK11_SetPasswordFunc( tlsm_pin_prompt );
+
+ /* register cleanup function */
+ if ( tlsm_register_nss_shutdown() ) {
+ errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not register NSS shutdown function: %d:%s\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
+
+ if ( ctx->tc_is_server ) {
+ /* 0 means use the defaults here */
+ SSL_ConfigServerSessionIDCache( 0, 0, 0, NULL );
+ }
+
+#ifndef HAVE_NSS_INITCONTEXT
+ }
+#endif /* HAVE_NSS_INITCONTEXT */
+
+ return 0;
+}
+
+/*
+ * Find and verify the certificate.
+ * The key is loaded and stored in ctx->tc_private_key
+ */
+static int
+tlsm_find_and_verify_cert_key( tlsm_ctx *ctx )
+{
+ SECCertificateUsage certUsage;
+ PRBool checkSig;
+ SECStatus status;
+ void *pin_arg;
+
+ if ( tlsm_ctx_load_private_key( ctx ) )
+ return -1;
+
+ pin_arg = SSL_RevealPinArg( ctx->tc_model );
+ certUsage = ctx->tc_is_server ? certificateUsageSSLServer : certificateUsageSSLClient;
+ checkSig = ctx->tc_verify_cert ? PR_TRUE : PR_FALSE;
+
+ status = tlsm_verify_cert( ctx->tc_certdb, ctx->tc_certificate, pin_arg,
+ checkSig, certUsage, ctx->tc_warn_only, PR_TRUE );
+
+ return status == SECSuccess ? 0 : -1;
+}
+
+static int
+tlsm_get_client_auth_data( void *arg, PRFileDesc *fd,
+ CERTDistNames *caNames, CERTCertificate **pRetCert,
+ SECKEYPrivateKey **pRetKey )
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+
+ if ( pRetCert )
+ *pRetCert = CERT_DupCertificate( ctx->tc_certificate );
+
+ if ( pRetKey )
+ *pRetKey = SECKEY_CopyPrivateKey( ctx->tc_private_key );
+
+ return SECSuccess;
+}
+
+/*
+ * ctx must have a tc_model that is valid
+*/
+static int
+tlsm_clientauth_init( tlsm_ctx *ctx )
+{
+ SECStatus status = SECFailure;
+ int rc;
+ PRBool saveval;
+
+ saveval = ctx->tc_warn_only;
+ ctx->tc_warn_only = PR_TRUE;
+ rc = tlsm_find_and_verify_cert_key(ctx);
+ ctx->tc_warn_only = saveval;
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: unable to set up client certificate authentication for "
+ "certificate named %s\n", tlsm_ctx_subject_name(ctx), 0, 0 );
+ return -1;
+ }
+
+ status = SSL_GetClientAuthDataHook( ctx->tc_model,
+ tlsm_get_client_auth_data,
+ (void *)ctx );
+
+ return ( status == SECSuccess ? 0 : -1 );
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+static void
+tlsm_destroy( void )
+{
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_destroy( &tlsm_ctx_count_mutex );
+ ldap_pvt_thread_mutex_destroy( &tlsm_init_mutex );
+ ldap_pvt_thread_mutex_destroy( &tlsm_pem_mutex );
+#endif
+}
+
+static struct ldaptls *
+tlsm_copy_config ( const struct ldaptls *config )
+{
+ struct ldaptls *copy;
+
+ assert( config );
+
+ copy = LDAP_MALLOC( sizeof( *copy ) );
+ if ( !copy )
+ return NULL;
+
+ memset( copy, 0, sizeof( *copy ) );
+
+ if ( config->lt_certfile )
+ copy->lt_certfile = LDAP_STRDUP( config->lt_certfile );
+ if ( config->lt_keyfile )
+ copy->lt_keyfile = LDAP_STRDUP( config->lt_keyfile );
+ if ( config->lt_dhfile )
+ copy->lt_dhfile = LDAP_STRDUP( config->lt_dhfile );
+ if ( config->lt_cacertfile )
+ copy->lt_cacertfile = LDAP_STRDUP( config->lt_cacertfile );
+ if ( config->lt_cacertdir )
+ copy->lt_cacertdir = LDAP_STRDUP( config->lt_cacertdir );
+ if ( config->lt_ciphersuite )
+ copy->lt_ciphersuite = LDAP_STRDUP( config->lt_ciphersuite );
+ if ( config->lt_crlfile )
+ copy->lt_crlfile = LDAP_STRDUP( config->lt_crlfile );
+ if ( config->lt_randfile )
+ copy->lt_randfile = LDAP_STRDUP( config->lt_randfile );
+
+ copy->lt_protocol_min = config->lt_protocol_min;
+
+ return copy;
+}
+
+static void
+tlsm_free_config ( struct ldaptls *config )
+{
+ assert( config );
+
+ if ( config->lt_certfile )
+ LDAP_FREE( config->lt_certfile );
+ if ( config->lt_keyfile )
+ LDAP_FREE( config->lt_keyfile );
+ if ( config->lt_dhfile )
+ LDAP_FREE( config->lt_dhfile );
+ if ( config->lt_cacertfile )
+ LDAP_FREE( config->lt_cacertfile );
+ if ( config->lt_cacertdir )
+ LDAP_FREE( config->lt_cacertdir );
+ if ( config->lt_ciphersuite )
+ LDAP_FREE( config->lt_ciphersuite );
+ if ( config->lt_crlfile )
+ LDAP_FREE( config->lt_crlfile );
+ if ( config->lt_randfile )
+ LDAP_FREE( config->lt_randfile );
+
+ LDAP_FREE( config );
+}
+
+static tls_ctx *
+tlsm_ctx_new ( struct ldapoptions *lo )
+{
+ tlsm_ctx *ctx;
+
+ ctx = LDAP_MALLOC( sizeof (*ctx) );
+ if ( ctx ) {
+ ctx->tc_refcnt = 1;
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_init( &ctx->tc_refmutex );
+#endif
+ LDAP_MUTEX_LOCK( &tlsm_ctx_count_mutex );
+ ctx->tc_unique = tlsm_ctx_count++;
+ LDAP_MUTEX_UNLOCK( &tlsm_ctx_count_mutex );
+ ctx->tc_config = NULL; /* populated later by tlsm_ctx_init */
+ ctx->tc_certdb = NULL;
+ ctx->tc_certdb_slot = NULL;
+ ctx->tc_certificate = NULL;
+ ctx->tc_private_key = NULL;
+ ctx->tc_pin_file = NULL;
+ ctx->tc_model = NULL;
+ memset(&ctx->tc_callonce, 0, sizeof(ctx->tc_callonce));
+ ctx->tc_require_cert = lo->ldo_tls_require_cert;
+ ctx->tc_verify_cert = PR_FALSE;
+ ctx->tc_using_pem = PR_FALSE;
+#ifdef HAVE_NSS_INITCONTEXT
+ ctx->tc_initctx = NULL;
+#endif /* HAVE_NSS_INITCONTEXT */
+ ctx->tc_pem_objs = NULL;
+ ctx->tc_n_pem_objs = 0;
+ ctx->tc_warn_only = PR_FALSE;
+ }
+ return (tls_ctx *)ctx;
+}
+
+static void
+tlsm_ctx_ref( tls_ctx *ctx )
+{
+ tlsm_ctx *c = (tlsm_ctx *)ctx;
+ LDAP_MUTEX_LOCK( &c->tc_refmutex );
+ c->tc_refcnt++;
+ LDAP_MUTEX_UNLOCK( &c->tc_refmutex );
+}
+
+static void
+tlsm_ctx_free ( tls_ctx *ctx )
+{
+ tlsm_ctx *c = (tlsm_ctx *)ctx;
+ int refcount;
+
+ if ( !c ) return;
+
+ LDAP_MUTEX_LOCK( &c->tc_refmutex );
+ refcount = --c->tc_refcnt;
+ LDAP_MUTEX_UNLOCK( &c->tc_refmutex );
+ if ( refcount )
+ return;
+
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+ if ( c->tc_model )
+ PR_Close( c->tc_model );
+ if ( c->tc_certificate )
+ CERT_DestroyCertificate( c->tc_certificate );
+ if ( c->tc_private_key )
+ SECKEY_DestroyPrivateKey( c->tc_private_key );
+ c->tc_certdb = NULL; /* if not the default, may have to clean up */
+ if ( c->tc_certdb_slot ) {
+ if ( SECMOD_CloseUserDB( c->tc_certdb_slot ) ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not close certdb slot - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ }
+ }
+ if ( c->tc_pin_file ) {
+ PL_strfree( c->tc_pin_file );
+ c->tc_pin_file = NULL;
+ }
+ tlsm_free_pem_objs( c );
+#ifdef HAVE_NSS_INITCONTEXT
+ if ( c->tc_initctx ) {
+ if ( NSS_ShutdownContext( c->tc_initctx ) ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not shutdown NSS - error %d:%s.\n",
+ errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ }
+ }
+ c->tc_initctx = NULL;
+#endif /* HAVE_NSS_INITCONTEXT */
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_destroy( &c->tc_refmutex );
+#endif
+
+ if ( c->tc_config )
+ tlsm_free_config( c->tc_config );
+
+ LDAP_FREE( c );
+}
+
+/*
+ * initialize a new TLS context
+ */
+static int
+tlsm_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)lo->ldo_tls_ctx;
+ ctx->tc_config = tlsm_copy_config( lt );
+ ctx->tc_is_server = is_server;
+
+ return 0;
+}
+
+/* returns true if the given string looks like
+ "tokenname" ":" "certnickname"
+ This is true if there is a ':' colon character
+ in the string and the colon is not the first
+ or the last character in the string
+*/
+static int
+tlsm_is_tokenname_certnick( const char *certfile )
+{
+ if ( certfile ) {
+ const char *ptr = PL_strchr( certfile, ':' );
+ return ptr && (ptr != certfile) && (*(ptr+1));
+ }
+ return 0;
+}
+
+static int
+tlsm_deferred_ctx_init( void *arg )
+{
+ tlsm_ctx *ctx = (tlsm_ctx *)arg;
+ PRBool sslv2 = PR_FALSE;
+ PRBool sslv3 = PR_TRUE;
+ PRBool tlsv1 = PR_TRUE;
+ PRBool request_cert = PR_FALSE;
+ PRInt32 require_cert = PR_FALSE;
+ PRFileDesc *fd;
+ struct ldaptls *lt;
+
+ if ( tlsm_deferred_init( ctx ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not perform TLS system initialization.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ ctx->tc_certdb = CERT_GetDefaultCertDB(); /* If there is ever a per-context db, change this */
+
+ fd = PR_CreateIOLayerStub( tlsm_layer_id, &tlsm_PR_methods );
+ if ( fd ) {
+ ctx->tc_model = SSL_ImportFD( NULL, fd );
+ }
+
+ if ( !ctx->tc_model ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could perform TLS socket I/O layer initialization - error %d:%s.\n",
+ err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL );
+
+ if ( fd ) {
+ PR_Close( fd );
+ }
+ return -1;
+ }
+
+ if ( SSL_SetPKCS11PinArg(ctx->tc_model, ctx) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set pin prompt argument\n", 0, 0, 0);
+ return -1;
+ }
+
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_SECURITY, PR_TRUE ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set secure mode on.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ lt = ctx->tc_config;
+
+ /* default is sslv3 and tlsv1 */
+ if ( lt->lt_protocol_min ) {
+ if ( lt->lt_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 ) {
+ sslv3 = PR_FALSE;
+ } else if ( lt->lt_protocol_min <= LDAP_OPT_X_TLS_PROTOCOL_SSL2 ) {
+ sslv2 = PR_TRUE;
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: warning: minimum TLS protocol level set to "
+ "include SSLv2 - SSLv2 is insecure - do not use\n", 0, 0, 0 );
+ }
+ }
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_SSL2, sslv2 ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set SSLv2 mode on.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_SSL3, sslv3 ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set SSLv3 mode on.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_ENABLE_TLS, tlsv1 ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set TLSv1 mode on.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_CLIENT, !ctx->tc_is_server ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set handshake as client.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_HANDSHAKE_AS_SERVER, ctx->tc_is_server ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set handshake as server.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ if ( lt->lt_ciphersuite ) {
+ if ( tlsm_parse_ciphers( ctx, lt->lt_ciphersuite ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list %s.\n",
+ lt->lt_ciphersuite, 0, 0 );
+ return -1;
+ }
+ } else if ( tlsm_parse_ciphers( ctx, "DEFAULT" ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list DEFAULT.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ if ( !ctx->tc_require_cert ) {
+ ctx->tc_verify_cert = PR_FALSE;
+ } else if ( !ctx->tc_is_server ) {
+ request_cert = PR_TRUE;
+ require_cert = SSL_REQUIRE_NO_ERROR;
+ if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND ||
+ ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) {
+ require_cert = SSL_REQUIRE_ALWAYS;
+ }
+ if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW )
+ ctx->tc_verify_cert = PR_TRUE;
+ } else { /* server */
+ /* server does not request certs by default */
+ /* if allow - client may send cert, server will ignore if errors */
+ /* if try - client may send cert, server will error if bad cert */
+ /* if hard or demand - client must send cert, server will error if bad cert */
+ request_cert = PR_TRUE;
+ require_cert = SSL_REQUIRE_NO_ERROR;
+ if ( ctx->tc_require_cert == LDAP_OPT_X_TLS_DEMAND ||
+ ctx->tc_require_cert == LDAP_OPT_X_TLS_HARD ) {
+ require_cert = SSL_REQUIRE_ALWAYS;
+ }
+ if ( ctx->tc_require_cert != LDAP_OPT_X_TLS_ALLOW ) {
+ ctx->tc_verify_cert = PR_TRUE;
+ } else {
+ ctx->tc_warn_only = PR_TRUE;
+ }
+ }
+
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_REQUEST_CERTIFICATE, request_cert ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set request certificate mode.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ if ( SECSuccess != SSL_OptionSet( ctx->tc_model, SSL_REQUIRE_CERTIFICATE, require_cert ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set require certificate mode.\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ /* set up our cert and key, if any */
+ if ( lt->lt_certfile ) {
+
+ /* first search in certdb (lt_certfile is nickname) */
+ if ( ctx->tc_certdb ) {
+ char *tmp_certname;
+
+ if ( tlsm_is_tokenname_certnick( lt->lt_certfile )) {
+ /* assume already in form tokenname:certnickname */
+ tmp_certname = PL_strdup( lt->lt_certfile );
+ } else if ( ctx->tc_certdb_slot ) {
+ tmp_certname = PR_smprintf( TLSM_CERTDB_DESC_FMT ":%s", ctx->tc_unique, lt->lt_certfile );
+ } else {
+ tmp_certname = PR_smprintf( "%s", lt->lt_certfile );
+ }
+
+ ctx->tc_certificate = PK11_FindCertFromNickname( tmp_certname, SSL_RevealPinArg( ctx->tc_model ) );
+ PR_smprintf_free( tmp_certname );
+
+ if ( !ctx->tc_certificate ) {
+ PRErrorCode errcode = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: the certificate '%s' could not be found in the database - error %d:%s.\n",
+ lt->lt_certfile, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
+ }
+ }
+
+ /* fallback to PEM module (lt_certfile is filename) */
+ if ( !ctx->tc_certificate ) {
+ if ( !pem_module && tlsm_init_pem_module() ) {
+ int pem_errcode = PORT_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: fallback to PEM impossible, module cannot be loaded - error %d:%s.\n",
+ pem_errcode, PR_ErrorToString( pem_errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
+ return -1;
+ }
+
+ /* this sets ctx->tc_certificate to the correct value */
+ if ( !tlsm_add_cert_from_file( ctx, lt->lt_certfile, PR_FALSE ) ) {
+ ctx->tc_using_pem = PR_TRUE;
+ }
+ }
+
+ if ( ctx->tc_certificate ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: certificate '%s' successfully loaded from %s.\n", lt->lt_certfile,
+ ctx->tc_using_pem ? "PEM file" : "moznss database", 0);
+ } else {
+ return -1;
+ }
+ }
+
+ if ( lt->lt_keyfile ) {
+ /* if using the PEM module, load the PEM file specified by lt_keyfile */
+ /* otherwise, assume this is the pininfo for the key */
+ if ( ctx->tc_using_pem ) {
+ int rc = tlsm_add_key_from_file( ctx, lt->lt_keyfile );
+ if ( rc ) {
+ return rc;
+ }
+ } else {
+ if ( ctx->tc_pin_file )
+ PL_strfree( ctx->tc_pin_file );
+ ctx->tc_pin_file = PL_strdup( lt->lt_keyfile );
+ }
+ }
+
+ /* Set up callbacks for use by clients */
+ if ( !ctx->tc_is_server ) {
+ if ( SSL_OptionSet( ctx->tc_model, SSL_NO_CACHE, PR_TRUE ) != SECSuccess ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: could not set nocache option for moznss - error %d:%s\n",
+ err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL );
+ return -1;
+ }
+
+ if ( SSL_BadCertHook( ctx->tc_model, tlsm_bad_cert_handler, ctx ) != SECSuccess ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: could not set bad cert handler for moznss - error %d:%s\n",
+ err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL );
+ return -1;
+ }
+
+ /*
+ since a cert has been specified, assume the client wants to do cert auth
+ */
+ if ( ctx->tc_certificate ) {
+ if ( tlsm_clientauth_init( ctx ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: unable to set up client certificate authentication using '%s'\n",
+ tlsm_ctx_subject_name(ctx), 0, 0 );
+ return -1;
+ }
+ }
+ } else { /* set up secure server */
+ SSLKEAType certKEA;
+ SECStatus status;
+
+ /* must have a certificate for the server to use */
+ if ( !ctx->tc_certificate ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: no server certificate: must specify a certificate for the server to use\n",
+ 0, 0, 0 );
+ return -1;
+ }
+
+ if ( tlsm_find_and_verify_cert_key( ctx ) ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: unable to find and verify server's cert and key for certificate %s\n",
+ tlsm_ctx_subject_name(ctx), 0, 0 );
+ return -1;
+ }
+
+ /* configure the socket to be a secure server socket */
+ certKEA = NSS_FindCertKEAType( ctx->tc_certificate );
+ status = SSL_ConfigSecureServer( ctx->tc_model, ctx->tc_certificate, ctx->tc_private_key, certKEA );
+
+ if ( SECSuccess != status ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: unable to configure secure server using certificate '%s' - error %d:%s\n",
+ tlsm_ctx_subject_name(ctx), err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) );
+ return -1;
+ }
+ }
+
+ /* Callback for authenticating certificate */
+ if ( SSL_AuthCertificateHook( ctx->tc_model, tlsm_auth_cert_handler,
+ ctx ) != SECSuccess ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: could not set auth cert handler for moznss - error %d:%s\n",
+ err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL );
+ return -1;
+ }
+
+ if ( SSL_HandshakeCallback( ctx->tc_model, tlsm_handshake_complete_cb, ctx ) ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: could not set handshake callback for moznss - error %d:%s\n",
+ err, PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ), NULL );
+ return -1;
+ }
+
+ tlsm_free_config( ctx->tc_config );
+ ctx->tc_config = NULL;
+
+ return 0;
+}
+
+struct tls_data {
+ tlsm_session *session;
+ Sockbuf_IO_Desc *sbiod;
+ /* there seems to be no portable way to determine if the
+ sockbuf sd has been set to nonblocking mode - the
+ call to ber_pvt_socket_set_nonblock() takes place
+ before the tls socket is set up, so we cannot
+ intercept that call either.
+ On systems where fcntl is available, we can just
+ F_GETFL and test for O_NONBLOCK. On other systems,
+ we will just see if the IO op returns EAGAIN or EWOULDBLOCK,
+ and just set this flag */
+ PRBool nonblock;
+ /*
+ * NSS tries hard to be backwards compatible with SSLv2 clients, or
+ * clients that send an SSLv2 client hello. This message is not
+ * tagged in any way, so NSS has no way to know if the incoming
+ * message is a valid SSLv2 client hello or just some bogus data
+ * (or cleartext LDAP). We store the first byte read from the
+ * client here. The most common case will be a client sending
+ * LDAP data instead of SSL encrypted LDAP data. This can happen,
+ * for example, if using ldapsearch -Z - if the starttls fails,
+ * the client will fallback to plain cleartext LDAP. So if we
+ * see that the firstbyte is a valid LDAP tag, we can be
+ * pretty sure this is happening.
+ */
+ ber_tag_t firsttag;
+ /*
+ * NSS doesn't return SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, etc.
+ * when it is blocked, so we have to set a flag in the wrapped send
+ * and recv calls that tells us what operation NSS was last blocked
+ * on
+ */
+#define TLSM_READ 1
+#define TLSM_WRITE 2
+ int io_flag;
+};
+
+static struct tls_data *
+tlsm_get_pvt_tls_data( PRFileDesc *fd )
+{
+ struct tls_data *p;
+ PRFileDesc *myfd;
+
+ if ( !fd ) {
+ return NULL;
+ }
+
+ myfd = PR_GetIdentitiesLayer( fd, tlsm_layer_id );
+
+ if ( !myfd ) {
+ return NULL;
+ }
+
+ p = (struct tls_data *)myfd->secret;
+
+ return p;
+}
+
+static int
+tlsm_is_non_ssl_message( PRFileDesc *fd, ber_tag_t *thebyte )
+{
+ struct tls_data *p;
+
+ if ( thebyte ) {
+ *thebyte = LBER_DEFAULT;
+ }
+
+ p = tlsm_get_pvt_tls_data( fd );
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ if ( p->firsttag == LBER_SEQUENCE ) {
+ if ( thebyte ) {
+ *thebyte = p->firsttag;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static tls_session *
+tlsm_session_new ( tls_ctx * ctx, int is_server )
+{
+ tlsm_ctx *c = (tlsm_ctx *)ctx;
+ tlsm_session *session;
+ PRFileDesc *fd;
+ PRStatus status;
+ int rc;
+
+ c->tc_is_server = is_server;
+ LDAP_MUTEX_LOCK( &tlsm_init_mutex );
+ status = PR_CallOnceWithArg( &c->tc_callonce, tlsm_deferred_ctx_init, c );
+ LDAP_MUTEX_UNLOCK( &tlsm_init_mutex );
+ if ( PR_SUCCESS != status ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: could not initialize moznss security context - error %d:%s\n",
+ err, PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT), NULL );
+ return NULL;
+ }
+
+ fd = PR_CreateIOLayerStub( tlsm_layer_id, &tlsm_PR_methods );
+ if ( !fd ) {
+ return NULL;
+ }
+
+ session = SSL_ImportFD( c->tc_model, fd );
+ if ( !session ) {
+ PR_DELETE( fd );
+ return NULL;
+ }
+
+ rc = SSL_ResetHandshake( session, is_server );
+ if ( rc ) {
+ PRErrorCode err = PR_GetError();
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: error: new session - reset handshake failure %d - error %d:%s\n",
+ rc, err,
+ err ? PR_ErrorToString( err, PR_LANGUAGE_I_DEFAULT ) : "unknown" );
+ PR_DELETE( fd );
+ PR_Close( session );
+ session = NULL;
+ }
+
+ return (tls_session *)session;
+}
+
+static int
+tlsm_session_accept_or_connect( tls_session *session, int is_accept )
+{
+ tlsm_session *s = (tlsm_session *)session;
+ int rc;
+ const char *op = is_accept ? "accept" : "connect";
+
+ if ( pem_module ) {
+ LDAP_MUTEX_LOCK( &tlsm_pem_mutex );
+ }
+ rc = SSL_ForceHandshake( s );
+ if ( pem_module ) {
+ LDAP_MUTEX_UNLOCK( &tlsm_pem_mutex );
+ }
+ if ( rc ) {
+ PRErrorCode err = PR_GetError();
+ rc = -1;
+ if ( err == PR_WOULD_BLOCK_ERROR ) {
+ ber_tag_t thetag = LBER_DEFAULT;
+ /* see if we are blocked because of a bogus packet */
+ if ( tlsm_is_non_ssl_message( s, &thetag ) ) { /* see if we received a non-SSL message */
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: %s - error - received non-SSL message [0x%x]\n",
+ op, (unsigned int)thetag, 0 );
+ /* reset error to something more descriptive */
+ PR_SetError( SSL_ERROR_RX_MALFORMED_HELLO_REQUEST, EPROTO );
+ }
+ } else {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: error: %s - force handshake failure: errno %d - moznss error %d\n",
+ op, errno, err );
+ }
+ }
+
+ return rc;
+}
+static int
+tlsm_session_accept( tls_session *session )
+{
+ return tlsm_session_accept_or_connect( session, 1 );
+}
+
+static int
+tlsm_session_connect( LDAP *ld, tls_session *session )
+{
+ return tlsm_session_accept_or_connect( session, 0 );
+}
+
+static int
+tlsm_session_upflags( Sockbuf *sb, tls_session *session, int rc )
+{
+ int prerror = PR_GetError();
+
+ if ( ( prerror == PR_PENDING_INTERRUPT_ERROR ) || ( prerror == PR_WOULD_BLOCK_ERROR ) ) {
+ tlsm_session *s = (tlsm_session *)session;
+ struct tls_data *p = tlsm_get_pvt_tls_data( s );
+
+ if ( p && ( p->io_flag == TLSM_READ ) ) {
+ sb->sb_trans_needs_read = 1;
+ return 1;
+ } else if ( p && ( p->io_flag == TLSM_WRITE ) ) {
+ sb->sb_trans_needs_write = 1;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static char *
+tlsm_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
+{
+ int i;
+ int prerror = PR_GetError();
+
+ i = PR_GetErrorTextLength();
+ if ( i > len ) {
+ char *msg = LDAP_MALLOC( i+1 );
+ PR_GetErrorText( msg );
+ memcpy( buf, msg, len );
+ LDAP_FREE( msg );
+ } else if ( i ) {
+ PR_GetErrorText( buf );
+ } else if ( prerror ) {
+ i = PR_snprintf( buf, len, "TLS error %d:%s",
+ prerror, PR_ErrorToString( prerror, PR_LANGUAGE_I_DEFAULT ) );
+ }
+
+ return ( i > 0 ) ? buf : NULL;
+}
+
+static int
+tlsm_session_my_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsm_session *s = (tlsm_session *)session;
+ CERTCertificate *cert;
+
+ cert = SSL_LocalCertificate( s );
+ if (!cert) return LDAP_INVALID_CREDENTIALS;
+
+ der_dn->bv_val = (char *)cert->derSubject.data;
+ der_dn->bv_len = cert->derSubject.len;
+ CERT_DestroyCertificate( cert );
+ return 0;
+}
+
+static int
+tlsm_session_peer_dn( tls_session *session, struct berval *der_dn )
+{
+ tlsm_session *s = (tlsm_session *)session;
+ CERTCertificate *cert;
+
+ cert = SSL_PeerCertificate( s );
+ if (!cert) return LDAP_INVALID_CREDENTIALS;
+
+ der_dn->bv_val = (char *)cert->derSubject.data;
+ der_dn->bv_len = cert->derSubject.len;
+ CERT_DestroyCertificate( cert );
+ return 0;
+}
+
+/* what kind of hostname were we given? */
+#define IS_DNS 0
+#define IS_IP4 1
+#define IS_IP6 2
+
+static int
+tlsm_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
+{
+ tlsm_session *s = (tlsm_session *)session;
+ CERTCertificate *cert;
+ const char *name, *domain = NULL, *ptr;
+ int ret, ntype = IS_DNS, nlen, dlen;
+#ifdef LDAP_PF_INET6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ SECItem altname;
+ SECStatus rv;
+
+ if( ldap_int_hostname &&
+ ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
+ {
+ name = ldap_int_hostname;
+ } else {
+ name = name_in;
+ }
+ nlen = strlen( name );
+
+ cert = SSL_PeerCertificate( s );
+ if (!cert) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get peer certificate.\n",
+ 0, 0, 0 );
+ /* if this was a fatal condition, things would have
+ * aborted long before now.
+ */
+ return LDAP_SUCCESS;
+ }
+
+#ifdef LDAP_PF_INET6
+ if (inet_pton(AF_INET6, name, &addr)) {
+ ntype = IS_IP6;
+ } else
+#endif
+ if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
+ if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
+ }
+ if (ntype == IS_DNS ) {
+ domain = strchr( name, '.' );
+ if ( domain )
+ dlen = nlen - ( domain - name );
+ }
+
+ ret = LDAP_LOCAL_ERROR;
+
+ rv = CERT_FindCertExtension( cert, SEC_OID_X509_SUBJECT_ALT_NAME,
+ &altname );
+ if ( rv == SECSuccess && altname.data ) {
+ PRArenaPool *arena;
+ CERTGeneralName *names, *cur;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( !arena ) {
+ ret = LDAP_NO_MEMORY;
+ goto fail;
+ }
+
+ names = cur = CERT_DecodeAltNameExtension(arena, &altname);
+ if ( !cur )
+ goto altfail;
+
+ do {
+ char *host;
+ int hlen;
+
+ /* ignore empty */
+ if ( !cur->name.other.len ) continue;
+
+ host = (char *)cur->name.other.data;
+ hlen = cur->name.other.len;
+
+ if ( cur->type == certDNSName ) {
+ if ( ntype != IS_DNS ) continue;
+
+ /* is this an exact match? */
+ if ( nlen == hlen && !strncasecmp( name, host, nlen )) {
+ ret = LDAP_SUCCESS;
+ break;
+ }
+
+ /* is this a wildcard match? */
+ if ( domain && host[0] == '*' && host[1] == '.' &&
+ dlen == hlen-1 && !strncasecmp( domain, host+1, dlen )) {
+ ret = LDAP_SUCCESS;
+ break;
+ }
+ } else if ( cur->type == certIPAddress ) {
+ if ( ntype == IS_DNS ) continue;
+
+#ifdef LDAP_PF_INET6
+ if (ntype == IS_IP6 && hlen != sizeof(struct in6_addr)) {
+ continue;
+ } else
+#endif
+ if (ntype == IS_IP4 && hlen != sizeof(struct in_addr)) {
+ continue;
+ }
+ if (!memcmp(host, &addr, hlen)) {
+ ret = LDAP_SUCCESS;
+ break;
+ }
+ }
+ } while (( cur = CERT_GetNextGeneralName( cur )) != names );
+altfail:
+ PORT_FreeArena( arena, PR_FALSE );
+ SECITEM_FreeItem( &altname, PR_FALSE );
+ }
+ /* no altnames matched, try the CN */
+ if ( ret != LDAP_SUCCESS ) {
+ /* find the last CN */
+ CERTRDN *rdn, **rdns;
+ CERTAVA *lastava = NULL;
+ char buf[2048];
+
+ buf[0] = '\0';
+ rdns = cert->subject.rdns;
+ while ( rdns && ( rdn = *rdns++ )) {
+ CERTAVA *ava, **avas = rdn->avas;
+ while ( avas && ( ava = *avas++ )) {
+ if ( CERT_GetAVATag( ava ) == SEC_OID_AVA_COMMON_NAME )
+ lastava = ava;
+ }
+ }
+ if ( lastava ) {
+ SECItem *av = CERT_DecodeAVAValue( &lastava->value );
+ if ( av ) {
+ if ( av->len == nlen && !strncasecmp( name, (char *)av->data, nlen )) {
+ ret = LDAP_SUCCESS;
+ } else if ( av->data[0] == '*' && av->data[1] == '.' &&
+ domain && dlen == av->len - 1 && !strncasecmp( domain,
+ (char *)(av->data+1), dlen )) {
+ ret = LDAP_SUCCESS;
+ } else {
+ int len = av->len;
+ if ( len >= sizeof(buf) )
+ len = sizeof(buf)-1;
+ memcpy( buf, av->data, len );
+ buf[len] = '\0';
+ }
+ SECITEM_FreeItem( av, PR_TRUE );
+ }
+ }
+ if ( ret != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "common name in certificate (%s).\n",
+ name, buf, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match CN in peer certificate"));
+ }
+ }
+
+fail:
+ CERT_DestroyCertificate( cert );
+ return ret;
+}
+
+static int
+tlsm_session_strength( tls_session *session )
+{
+ tlsm_session *s = (tlsm_session *)session;
+ int rc, keySize;
+
+ rc = SSL_SecurityStatus( s, NULL, NULL, NULL, &keySize,
+ NULL, NULL );
+ return rc ? 0 : keySize;
+}
+
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+static PRStatus PR_CALLBACK
+tlsm_PR_Close(PRFileDesc *fd)
+{
+ int rc = PR_SUCCESS;
+
+ /* we don't need to actually close anything here, just
+ pop our io layer off the stack */
+ fd->secret = NULL; /* must have been freed before calling PR_Close */
+ if ( fd->lower ) {
+ fd = PR_PopIOLayer( fd, tlsm_layer_id );
+ /* if we are not the last layer, pass the close along */
+ if ( fd ) {
+ if ( fd->dtor ) {
+ fd->dtor( fd );
+ }
+ rc = fd->methods->close( fd );
+ }
+ } else {
+ /* we are the last layer - just call our dtor */
+ fd->dtor(fd);
+ }
+
+ return rc;
+}
+
+static PRStatus PR_CALLBACK
+tlsm_PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
+{
+ int rc = PR_SUCCESS;
+
+ if ( fd->lower ) {
+ rc = PR_Shutdown( fd->lower, how );
+ }
+
+ return rc;
+}
+
+static int PR_CALLBACK
+tlsm_PR_Recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ struct tls_data *p;
+ int rc;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ rc = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+ if (rc <= 0) {
+ tlsm_map_error( errno );
+ if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
+ p->nonblock = PR_TRUE; /* fd is using non-blocking io */
+ } else if ( errno ) { /* real error */
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: error: tlsm_PR_Recv returned %d - error %d:%s\n",
+ rc, errno, STRERROR(errno) );
+ }
+ } else if ( ( rc > 0 ) && ( len > 0 ) && ( p->firsttag == LBER_DEFAULT ) ) {
+ p->firsttag = (ber_tag_t)*((char *)buf);
+ }
+ p->io_flag = TLSM_READ;
+
+ return rc;
+}
+
+static int PR_CALLBACK
+tlsm_PR_Send(PRFileDesc *fd, const void *buf, PRInt32 len, PRIntn flags,
+ PRIntervalTime timeout)
+{
+ struct tls_data *p;
+ int rc;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ rc = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
+ if (rc <= 0) {
+ tlsm_map_error( errno );
+ if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
+ p->nonblock = PR_TRUE;
+ } else if ( errno ) { /* real error */
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS: error: tlsm_PR_Send returned %d - error %d:%s\n",
+ rc, errno, STRERROR(errno) );
+ }
+ }
+ p->io_flag = TLSM_WRITE;
+
+ return rc;
+}
+
+static int PR_CALLBACK
+tlsm_PR_Read(PRFileDesc *fd, void *buf, PRInt32 len)
+{
+ return tlsm_PR_Recv( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
+}
+
+static int PR_CALLBACK
+tlsm_PR_Write(PRFileDesc *fd, const void *buf, PRInt32 len)
+{
+ return tlsm_PR_Send( fd, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
+}
+
+static PRStatus PR_CALLBACK
+tlsm_PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
+{
+ struct tls_data *p;
+ ber_socklen_t len;
+
+ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return PR_FAILURE;
+ }
+ len = sizeof(PRNetAddr);
+ return getpeername( p->sbiod->sbiod_sb->sb_fd, (struct sockaddr *)addr, &len );
+}
+
+static PRStatus PR_CALLBACK
+tlsm_PR_GetSocketOption(PRFileDesc *fd, PRSocketOptionData *data)
+{
+ struct tls_data *p;
+ p = tlsm_get_pvt_tls_data( fd );
+
+ if ( p == NULL || data == NULL ) {
+ return PR_FAILURE;
+ }
+
+ /* only the nonblocking option is supported at this time
+ MozNSS SSL code needs it */
+ if ( data->option != PR_SockOpt_Nonblocking ) {
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+ }
+#ifdef HAVE_FCNTL
+ int flags = fcntl( p->sbiod->sbiod_sb->sb_fd, F_GETFL );
+ data->value.non_blocking = (flags & O_NONBLOCK) ? PR_TRUE : PR_FALSE;
+#else /* punt :P */
+ data->value.non_blocking = p->nonblock;
+#endif
+ return PR_SUCCESS;
+}
+
+static PRStatus PR_CALLBACK
+tlsm_PR_prs_unimp()
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return PR_FAILURE;
+}
+
+static PRFileDesc * PR_CALLBACK
+tlsm_PR_pfd_unimp()
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return NULL;
+}
+
+static PRInt16 PR_CALLBACK
+tlsm_PR_i16_unimp()
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return SECFailure;
+}
+
+static PRInt32 PR_CALLBACK
+tlsm_PR_i32_unimp()
+{
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ return SECFailure;
+}
+
+static PRInt64 PR_CALLBACK
+tlsm_PR_i64_unimp()
+{
+ PRInt64 res;
+
+ PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+ LL_I2L(res, -1L);
+ return res;
+}
+
+static const PRIOMethods tlsm_PR_methods = {
+ PR_DESC_LAYERED,
+ tlsm_PR_Close, /* close */
+ tlsm_PR_Read, /* read */
+ tlsm_PR_Write, /* write */
+ tlsm_PR_i32_unimp, /* available */
+ tlsm_PR_i64_unimp, /* available64 */
+ tlsm_PR_prs_unimp, /* fsync */
+ tlsm_PR_i32_unimp, /* seek */
+ tlsm_PR_i64_unimp, /* seek64 */
+ tlsm_PR_prs_unimp, /* fileInfo */
+ tlsm_PR_prs_unimp, /* fileInfo64 */
+ tlsm_PR_i32_unimp, /* writev */
+ tlsm_PR_prs_unimp, /* connect */
+ tlsm_PR_pfd_unimp, /* accept */
+ tlsm_PR_prs_unimp, /* bind */
+ tlsm_PR_prs_unimp, /* listen */
+ (PRShutdownFN)tlsm_PR_Shutdown, /* shutdown */
+ tlsm_PR_Recv, /* recv */
+ tlsm_PR_Send, /* send */
+ tlsm_PR_i32_unimp, /* recvfrom */
+ tlsm_PR_i32_unimp, /* sendto */
+ (PRPollFN)tlsm_PR_i16_unimp, /* poll */
+ tlsm_PR_i32_unimp, /* acceptread */
+ tlsm_PR_i32_unimp, /* transmitfile */
+ tlsm_PR_prs_unimp, /* getsockname */
+ tlsm_PR_GetPeerName, /* getpeername */
+ tlsm_PR_i32_unimp, /* getsockopt OBSOLETE */
+ tlsm_PR_i32_unimp, /* setsockopt OBSOLETE */
+ tlsm_PR_GetSocketOption, /* getsocketoption */
+ tlsm_PR_i32_unimp, /* setsocketoption */
+ tlsm_PR_i32_unimp, /* Send a (partial) file with header/trailer*/
+ (PRConnectcontinueFN)tlsm_PR_prs_unimp, /* connectcontinue */
+ tlsm_PR_i32_unimp, /* reserved for future use */
+ tlsm_PR_i32_unimp, /* reserved for future use */
+ tlsm_PR_i32_unimp, /* reserved for future use */
+ tlsm_PR_i32_unimp /* reserved for future use */
+};
+
+/*
+ * Initialize TLS subsystem. Should be called only once.
+ * See tlsm_deferred_init for the bulk of the init process
+ */
+static int
+tlsm_init( void )
+{
+ char *nofork = PR_GetEnv( "NSS_STRICT_NOFORK" );
+
+ PR_Init(0, 0, 0);
+
+ tlsm_layer_id = PR_GetUniqueIdentity( "OpenLDAP" );
+
+ /*
+ * There are some applications that acquire a crypto context in the parent process
+ * and expect that crypto context to work after a fork(). This does not work
+ * with NSS using strict PKCS11 compliance mode. We set this environment
+ * variable here to tell the software encryption module/token to allow crypto
+ * contexts to persist across a fork(). However, if you are using some other
+ * module or encryption device that supports and expects full PKCS11 semantics,
+ * the only recourse is to rewrite the application with atfork() handlers to save
+ * the crypto context in the parent and restore (and SECMOD_RestartModules) the
+ * context in the child.
+ */
+ if ( !nofork ) {
+ /* will leak one time */
+ char *noforkenvvar = PL_strdup( "NSS_STRICT_NOFORK=DISABLED" );
+ PR_SetEnv( noforkenvvar );
+ }
+
+ return 0;
+}
+
+static int
+tlsm_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct tls_data *p;
+ tlsm_session *session = arg;
+ PRFileDesc *fd;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) {
+ return -1;
+ }
+
+ fd = PR_GetIdentitiesLayer( session, tlsm_layer_id );
+ if ( !fd ) {
+ LBER_FREE( p );
+ return -1;
+ }
+
+ fd->secret = (PRFilePrivate *)p;
+ p->session = session;
+ p->sbiod = sbiod;
+ p->firsttag = LBER_DEFAULT;
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+tlsm_sb_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ PR_Close( p->session );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+tlsm_sb_close( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ PR_Shutdown( p->session, PR_SHUTDOWN_BOTH );
+ return 0;
+}
+
+static int
+tlsm_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_GET_SSL ) {
+ *((tlsm_session **)arg) = p->session;
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if ( p && ( SSL_DataPending( p->session ) > 0 ) ) {
+ return 1;
+ }
+
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+tlsm_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = PR_Recv( p->session, buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
+ if ( ret < 0 ) {
+ err = PR_GetError();
+ if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) {
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ sock_errset(EWOULDBLOCK);
+ }
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_read = 0;
+ }
+ return ret;
+}
+
+static ber_slen_t
+tlsm_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = PR_Send( p->session, (char *)buf, len, 0, PR_INTERVAL_NO_TIMEOUT );
+ if ( ret < 0 ) {
+ err = PR_GetError();
+ if ( err == PR_PENDING_INTERRUPT_ERROR || err == PR_WOULD_BLOCK_ERROR ) {
+ sbiod->sbiod_sb->sb_trans_needs_write = 1;
+ sock_errset(EWOULDBLOCK);
+ ret = 0;
+ }
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_write = 0;
+ }
+ return ret;
+}
+
+static Sockbuf_IO tlsm_sbio =
+{
+ tlsm_sb_setup, /* sbi_setup */
+ tlsm_sb_remove, /* sbi_remove */
+ tlsm_sb_ctrl, /* sbi_ctrl */
+ tlsm_sb_read, /* sbi_read */
+ tlsm_sb_write, /* sbi_write */
+ tlsm_sb_close /* sbi_close */
+};
+
+tls_impl ldap_int_tls_impl = {
+ "MozNSS",
+
+ tlsm_init,
+ tlsm_destroy,
+
+ tlsm_ctx_new,
+ tlsm_ctx_ref,
+ tlsm_ctx_free,
+ tlsm_ctx_init,
+
+ tlsm_session_new,
+ tlsm_session_connect,
+ tlsm_session_accept,
+ tlsm_session_upflags,
+ tlsm_session_errmsg,
+ tlsm_session_my_dn,
+ tlsm_session_peer_dn,
+ tlsm_session_chkhost,
+ tlsm_session_strength,
+
+ &tlsm_sbio,
+
+#ifdef LDAP_R_COMPILE
+ tlsm_thr_init,
+#else
+ NULL,
+#endif
+
+ 0
+};
+
+#endif /* HAVE_MOZNSS */
+/*
+ emacs settings
+ Local Variables:
+ indent-tabs-mode: t
+ tab-width: 4
+ End:
+*/
diff --git a/libraries/libldap/tls_o.c b/libraries/libldap/tls_o.c
new file mode 100644
index 0000000..a13f11f
--- /dev/null
+++ b/libraries/libldap/tls_o.c
@@ -0,0 +1,1378 @@
+/* tls_o.c - Handle tls/ssl using OpenSSL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS: Rewritten by Howard Chu
+ */
+
+#include "portable.h"
+
+#ifdef HAVE_OPENSSL
+
+#include "ldap_config.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/ctype.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+#include <ac/param.h>
+#include <ac/dirent.h>
+
+#include "ldap-int.h"
+#include "ldap-tls.h"
+
+#ifdef HAVE_OPENSSL_SSL_H
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/safestack.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/dh.h>
+#elif defined( HAVE_SSL_H )
+#include <ssl.h>
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#define ASN1_STRING_data(x) ASN1_STRING_get0_data(x)
+#endif
+
+typedef SSL_CTX tlso_ctx;
+typedef SSL tlso_session;
+
+static BIO_METHOD * tlso_bio_method = NULL;
+static BIO_METHOD * tlso_bio_setup( void );
+
+static int tlso_opt_trace = 1;
+
+static void tlso_report_error( void );
+
+static void tlso_info_cb( const SSL *ssl, int where, int ret );
+static int tlso_verify_cb( int ok, X509_STORE_CTX *ctx );
+static int tlso_verify_ok( int ok, X509_STORE_CTX *ctx );
+static int tlso_seed_PRNG( const char *randfile );
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+/*
+ * OpenSSL 1.1 API and later has new locking code
+*/
+static RSA * tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length );
+
+#ifdef LDAP_R_COMPILE
+/*
+ * provide mutexes for the OpenSSL library.
+ */
+static ldap_pvt_thread_mutex_t tlso_mutexes[CRYPTO_NUM_LOCKS];
+
+static void tlso_locking_cb( int mode, int type, const char *file, int line )
+{
+ if ( mode & CRYPTO_LOCK ) {
+ ldap_pvt_thread_mutex_lock( &tlso_mutexes[type] );
+ } else {
+ ldap_pvt_thread_mutex_unlock( &tlso_mutexes[type] );
+ }
+}
+
+#if OPENSSL_VERSION_NUMBER >= 0x0909000
+static void tlso_thread_self( CRYPTO_THREADID *id )
+{
+ CRYPTO_THREADID_set_pointer( id, (void *)ldap_pvt_thread_self() );
+}
+#define CRYPTO_set_id_callback(foo) CRYPTO_THREADID_set_callback(foo)
+#else
+static unsigned long tlso_thread_self( void )
+{
+ /* FIXME: CRYPTO_set_id_callback only works when ldap_pvt_thread_t
+ * is an integral type that fits in an unsigned long
+ */
+
+ /* force an error if the ldap_pvt_thread_t type is too large */
+ enum { ok = sizeof( ldap_pvt_thread_t ) <= sizeof( unsigned long ) };
+ typedef struct { int dummy: ok ? 1 : -1; } Check[ok ? 1 : -1];
+
+ return (unsigned long) ldap_pvt_thread_self();
+}
+#endif
+
+static void tlso_thr_init( void )
+{
+ int i;
+
+ for( i=0; i< CRYPTO_NUM_LOCKS ; i++ ) {
+ ldap_pvt_thread_mutex_init( &tlso_mutexes[i] );
+ }
+ CRYPTO_set_locking_callback( tlso_locking_cb );
+ CRYPTO_set_id_callback( tlso_thread_self );
+}
+#endif /* LDAP_R_COMPILE */
+#else
+#ifdef LDAP_R_COMPILE
+static void tlso_thr_init( void ) {}
+#endif
+#endif /* OpenSSL 1.1 */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+/*
+ * OpenSSL 1.1 API and later makes the BIO method concrete types internal.
+ */
+
+static BIO_METHOD *
+BIO_meth_new( int type, const char *name )
+{
+ BIO_METHOD *method = LDAP_MALLOC( sizeof(BIO_METHOD) );
+ memset( method, 0, sizeof(BIO_METHOD) );
+
+ method->type = type;
+ method->name = name;
+
+ return method;
+}
+
+static void
+BIO_meth_free( BIO_METHOD *meth )
+{
+ if ( meth == NULL ) {
+ return;
+ }
+
+ LDAP_FREE( meth );
+}
+
+#define BIO_meth_set_write(m, f) (m)->bwrite = (f)
+#define BIO_meth_set_read(m, f) (m)->bread = (f)
+#define BIO_meth_set_puts(m, f) (m)->bputs = (f)
+#define BIO_meth_set_gets(m, f) (m)->bgets = (f)
+#define BIO_meth_set_ctrl(m, f) (m)->ctrl = (f)
+#define BIO_meth_set_create(m, f) (m)->create = (f)
+#define BIO_meth_set_destroy(m, f) (m)->destroy = (f)
+
+#endif /* OpenSSL 1.1 */
+
+static STACK_OF(X509_NAME) *
+tlso_ca_list( char * bundle, char * dir )
+{
+ STACK_OF(X509_NAME) *ca_list = NULL;
+
+ if ( bundle ) {
+ ca_list = SSL_load_client_CA_file( bundle );
+ }
+#if defined(HAVE_DIRENT_H) || defined(dirent)
+ if ( dir ) {
+ int freeit = 0;
+
+ if ( !ca_list ) {
+ ca_list = sk_X509_NAME_new_null();
+ freeit = 1;
+ }
+ if ( !SSL_add_dir_cert_subjects_to_stack( ca_list, dir ) &&
+ freeit ) {
+ sk_X509_NAME_free( ca_list );
+ ca_list = NULL;
+ }
+ }
+#endif
+ return ca_list;
+}
+
+/*
+ * Initialize TLS subsystem. Should be called only once.
+ */
+static int
+tlso_init( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+#ifdef HAVE_EBCDIC
+ {
+ char *file = LDAP_STRDUP( lo->ldo_tls_randfile );
+ if ( file ) __atoe( file );
+ (void) tlso_seed_PRNG( file );
+ LDAP_FREE( file );
+ }
+#else
+ (void) tlso_seed_PRNG( lo->ldo_tls_randfile );
+#endif
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ SSL_load_error_strings();
+ SSL_library_init();
+ OpenSSL_add_all_digests();
+#else
+ OPENSSL_init_ssl(0, NULL);
+#endif
+
+ /* FIXME: mod_ssl does this */
+ X509V3_add_standard_extensions();
+
+ tlso_bio_method = tlso_bio_setup();
+
+ return 0;
+}
+
+/*
+ * Tear down the TLS subsystem. Should only be called once.
+ */
+static void
+tlso_destroy( void )
+{
+ struct ldapoptions *lo = LDAP_INT_GLOBAL_OPT();
+
+ BIO_meth_free( tlso_bio_method );
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ EVP_cleanup();
+#if OPENSSL_VERSION_NUMBER < 0x10000000
+ ERR_remove_state(0);
+#else
+ ERR_remove_thread_state(NULL);
+#endif
+ ERR_free_strings();
+#endif
+
+ if ( lo->ldo_tls_randfile ) {
+ LDAP_FREE( lo->ldo_tls_randfile );
+ lo->ldo_tls_randfile = NULL;
+ }
+}
+
+static tls_ctx *
+tlso_ctx_new( struct ldapoptions *lo )
+{
+ return (tls_ctx *) SSL_CTX_new( SSLv23_method() );
+}
+
+static void
+tlso_ctx_ref( tls_ctx *ctx )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+#define SSL_CTX_up_ref(ctx) CRYPTO_add( &(ctx->references), 1, CRYPTO_LOCK_SSL_CTX )
+#endif
+ SSL_CTX_up_ref( c );
+}
+
+static void
+tlso_ctx_free ( tls_ctx *ctx )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+ SSL_CTX_free( c );
+}
+
+/*
+ * initialize a new TLS context
+ */
+static int
+tlso_ctx_init( struct ldapoptions *lo, struct ldaptls *lt, int is_server )
+{
+ tlso_ctx *ctx = (tlso_ctx *)lo->ldo_tls_ctx;
+ int i;
+
+ if ( is_server ) {
+ SSL_CTX_set_session_id_context( ctx,
+ (const unsigned char *) "OpenLDAP", sizeof("OpenLDAP")-1 );
+ }
+
+#ifdef SSL_OP_NO_TLSv1
+#ifdef SSL_OP_NO_TLSv1_1
+#ifdef SSL_OP_NO_TLSv1_2
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_2)
+ SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
+ SSL_OP_NO_TLSv1_2 );
+ else
+#endif
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_1)
+ SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 );
+ else
+#endif
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_TLS1_0)
+ SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_TLSv1);
+ else
+#endif
+ if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL3 )
+ SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 );
+ else if ( lo->ldo_tls_protocol_min > LDAP_OPT_X_TLS_PROTOCOL_SSL2 )
+ SSL_CTX_set_options( ctx, SSL_OP_NO_SSLv2 );
+
+ if ( lo->ldo_tls_ciphersuite &&
+ !SSL_CTX_set_cipher_list( ctx, lt->lt_ciphersuite ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set cipher list %s.\n",
+ lo->ldo_tls_ciphersuite, 0, 0 );
+ tlso_report_error();
+ return -1;
+ }
+
+ if ( lo->ldo_tls_cacertfile == NULL && lo->ldo_tls_cacertdir == NULL ) {
+ if ( !SSL_CTX_set_default_verify_paths( ctx ) ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: "
+ "could not use default certificate paths", 0, 0, 0 );
+ tlso_report_error();
+ return -1;
+ }
+ } else {
+ if ( !SSL_CTX_load_verify_locations( ctx,
+ lt->lt_cacertfile, lt->lt_cacertdir ) )
+ {
+ Debug( LDAP_DEBUG_ANY, "TLS: "
+ "could not load verify locations (file:`%s',dir:`%s').\n",
+ lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "",
+ lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "",
+ 0 );
+ tlso_report_error();
+ return -1;
+ }
+
+ if ( is_server ) {
+ STACK_OF(X509_NAME) *calist;
+ /* List of CA names to send to a client */
+ calist = tlso_ca_list( lt->lt_cacertfile, lt->lt_cacertdir );
+ if ( !calist ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: "
+ "could not load client CA list (file:`%s',dir:`%s').\n",
+ lo->ldo_tls_cacertfile ? lo->ldo_tls_cacertfile : "",
+ lo->ldo_tls_cacertdir ? lo->ldo_tls_cacertdir : "",
+ 0 );
+ tlso_report_error();
+ return -1;
+ }
+
+ SSL_CTX_set_client_CA_list( ctx, calist );
+ }
+ }
+
+ if ( lo->ldo_tls_certfile &&
+ !SSL_CTX_use_certificate_file( ctx,
+ lt->lt_certfile, SSL_FILETYPE_PEM ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not use certificate `%s'.\n",
+ lo->ldo_tls_certfile,0,0);
+ tlso_report_error();
+ return -1;
+ }
+
+ /* Key validity is checked automatically if cert has already been set */
+ if ( lo->ldo_tls_keyfile &&
+ !SSL_CTX_use_PrivateKey_file( ctx,
+ lt->lt_keyfile, SSL_FILETYPE_PEM ) )
+ {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not use key file `%s'.\n",
+ lo->ldo_tls_keyfile,0,0);
+ tlso_report_error();
+ return -1;
+ }
+
+ if ( is_server && lo->ldo_tls_dhfile ) {
+ DH *dh;
+ BIO *bio;
+
+ if (( bio=BIO_new_file( lt->lt_dhfile,"r" )) == NULL ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not use DH parameters file `%s'.\n",
+ lo->ldo_tls_dhfile,0,0);
+ tlso_report_error();
+ return -1;
+ }
+ if (!( dh=PEM_read_bio_DHparams( bio, NULL, NULL, NULL ))) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not read DH parameters file `%s'.\n",
+ lo->ldo_tls_dhfile,0,0);
+ tlso_report_error();
+ BIO_free( bio );
+ return -1;
+ }
+ BIO_free( bio );
+ SSL_CTX_set_tmp_dh( ctx, dh );
+ SSL_CTX_set_options( ctx, SSL_OP_SINGLE_DH_USE );
+ DH_free( dh );
+ }
+
+ if ( lo->ldo_tls_ecname ) {
+#ifdef OPENSSL_NO_EC
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: Elliptic Curves not supported.\n", 0,0,0 );
+ return -1;
+#else
+ if ( !SSL_CTX_set1_curves_list( ctx, lt->lt_ecname )) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not set EC name `%s'.\n",
+ lo->ldo_tls_ecname,0,0);
+ tlso_report_error();
+ return -1;
+ }
+ /*
+ * This is a NOP in OpenSSL 1.1.0 and later, where curves are always
+ * auto-negotiated.
+ */
+#if OPENSSL_VERSION_NUMBER < 0x10100000UL
+ if ( SSL_CTX_set_ecdh_auto( ctx, 1 ) <= 0 ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: could not enable automatic EC negotiation.\n", 0, 0, 0 );
+ }
+#endif
+#endif /* OPENSSL_NO_EC */
+ }
+
+ if ( tlso_opt_trace ) {
+ SSL_CTX_set_info_callback( ctx, tlso_info_cb );
+ }
+
+ i = SSL_VERIFY_NONE;
+ if ( lo->ldo_tls_require_cert ) {
+ i = SSL_VERIFY_PEER;
+ if ( lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_DEMAND ||
+ lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_HARD ) {
+ i |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ }
+ }
+
+ SSL_CTX_set_verify( ctx, i,
+ lo->ldo_tls_require_cert == LDAP_OPT_X_TLS_ALLOW ?
+ tlso_verify_ok : tlso_verify_cb );
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ SSL_CTX_set_tmp_rsa_callback( ctx, tlso_tmp_rsa_cb );
+#endif
+#ifdef HAVE_OPENSSL_CRL
+ if ( lo->ldo_tls_crlcheck ) {
+ X509_STORE *x509_s = SSL_CTX_get_cert_store( ctx );
+ if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_PEER ) {
+ X509_STORE_set_flags( x509_s, X509_V_FLAG_CRL_CHECK );
+ } else if ( lo->ldo_tls_crlcheck == LDAP_OPT_X_TLS_CRL_ALL ) {
+ X509_STORE_set_flags( x509_s,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL );
+ }
+ }
+#endif
+ return 0;
+}
+
+static tls_session *
+tlso_session_new( tls_ctx *ctx, int is_server )
+{
+ tlso_ctx *c = (tlso_ctx *)ctx;
+ return (tls_session *)SSL_new( c );
+}
+
+static int
+tlso_session_connect( LDAP *ld, tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* Caller expects 0 = success, OpenSSL returns 1 = success */
+ int rc = SSL_connect( s ) - 1;
+#ifdef LDAP_USE_NON_BLOCKING_TLS
+ if ( rc < 0 ) {
+ int sockerr = sock_errno();
+ int sslerr = SSL_get_error( s, rc+1 );
+ if ( sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE ) {
+ rc = 0;
+ } else if ( sslerr == SSL_ERROR_SYSCALL &&
+ ( sockerr == EAGAIN || sockerr == ENOTCONN )) {
+ rc = 0;
+ }
+ }
+#endif /* LDAP_USE_NON_BLOCKING_TLS */
+ return rc;
+}
+
+static int
+tlso_session_accept( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* Caller expects 0 = success, OpenSSL returns 1 = success */
+ return SSL_accept( s ) - 1;
+}
+
+static int
+tlso_session_upflags( Sockbuf *sb, tls_session *sess, int rc )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ /* 1 was subtracted above, offset it back now */
+ rc = SSL_get_error(s, rc+1);
+ if (rc == SSL_ERROR_WANT_READ) {
+ sb->sb_trans_needs_read = 1;
+ return 1;
+
+ } else if (rc == SSL_ERROR_WANT_WRITE) {
+ sb->sb_trans_needs_write = 1;
+ return 1;
+
+ } else if (rc == SSL_ERROR_WANT_CONNECT) {
+ return 1;
+ }
+ return 0;
+}
+
+static char *
+tlso_session_errmsg( tls_session *sess, int rc, char *buf, size_t len )
+{
+ char err[256] = "";
+ const char *certerr=NULL;
+ tlso_session *s = (tlso_session *)sess;
+
+ rc = ERR_peek_error();
+ if ( rc ) {
+ ERR_error_string_n( rc, err, sizeof(err) );
+ if ( ( ERR_GET_LIB(rc) == ERR_LIB_SSL ) &&
+ ( ERR_GET_REASON(rc) == SSL_R_CERTIFICATE_VERIFY_FAILED ) ) {
+ int certrc = SSL_get_verify_result(s);
+ certerr = (char *)X509_verify_cert_error_string(certrc);
+ }
+ snprintf(buf, len, "%s%s%s%s", err, certerr ? " (" :"",
+ certerr ? certerr : "", certerr ? ")" : "" );
+ return buf;
+ }
+ return NULL;
+}
+
+static int
+tlso_session_my_dn( tls_session *sess, struct berval *der_dn )
+{
+ tlso_session *s = (tlso_session *)sess;
+ X509 *x;
+ X509_NAME *xn;
+
+ x = SSL_get_certificate( s );
+
+ if (!x) return LDAP_INVALID_CREDENTIALS;
+
+ xn = X509_get_subject_name(x);
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ der_dn->bv_len = i2d_X509_NAME( xn, NULL );
+ der_dn->bv_val = xn->bytes->data;
+#else
+ {
+ size_t len = 0;
+ der_dn->bv_val = NULL;
+ X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len );
+ der_dn->bv_len = len;
+ }
+#endif
+ /* Don't X509_free, the session is still using it */
+ return 0;
+}
+
+static X509 *
+tlso_get_cert( SSL *s )
+{
+ /* If peer cert was bad, treat as if no cert was given */
+ if (SSL_get_verify_result(s)) {
+ return NULL;
+ }
+ return SSL_get_peer_certificate(s);
+}
+
+static int
+tlso_session_peer_dn( tls_session *sess, struct berval *der_dn )
+{
+ tlso_session *s = (tlso_session *)sess;
+ X509 *x = tlso_get_cert( s );
+ X509_NAME *xn;
+
+ if ( !x )
+ return LDAP_INVALID_CREDENTIALS;
+
+ xn = X509_get_subject_name(x);
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+ der_dn->bv_len = i2d_X509_NAME( xn, NULL );
+ der_dn->bv_val = xn->bytes->data;
+#else
+ {
+ size_t len = 0;
+ der_dn->bv_val = NULL;
+ X509_NAME_get0_der( xn, (const unsigned char **)&der_dn->bv_val, &len );
+ der_dn->bv_len = len;
+ }
+#endif
+ X509_free(x);
+ return 0;
+}
+
+/* what kind of hostname were we given? */
+#define IS_DNS 0
+#define IS_IP4 1
+#define IS_IP6 2
+
+static int
+tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
+{
+ tlso_session *s = (tlso_session *)sess;
+ int i, ret = LDAP_LOCAL_ERROR;
+ int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
+ X509 *x;
+ const char *name;
+ char *ptr;
+ int ntype = IS_DNS, nlen;
+#ifdef LDAP_PF_INET6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+
+ if( ldap_int_hostname &&
+ ( !name_in || !strcasecmp( name_in, "localhost" ) ) )
+ {
+ name = ldap_int_hostname;
+ } else {
+ name = name_in;
+ }
+ nlen = strlen(name);
+
+ x = tlso_get_cert(s);
+ if (!x) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get peer certificate.\n",
+ 0, 0, 0 );
+ /* If this was a fatal condition, things would have
+ * aborted long before now.
+ */
+ return LDAP_SUCCESS;
+ }
+
+#ifdef LDAP_PF_INET6
+ if (inet_pton(AF_INET6, name, &addr)) {
+ ntype = IS_IP6;
+ } else
+#endif
+ if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
+ if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
+ }
+
+ if (chkSAN) {
+ i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
+ if (i >= 0) {
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) *alt;
+
+ ex = X509_get_ext(x, i);
+ alt = X509V3_EXT_d2i(ex);
+ if (alt) {
+ int n, len2 = 0;
+ char *domain = NULL;
+ GENERAL_NAME *gn;
+
+ gotSAN = 1;
+ if (ntype == IS_DNS) {
+ domain = strchr(name, '.');
+ if (domain) {
+ len2 = nlen - (domain-name);
+ }
+ }
+ n = sk_GENERAL_NAME_num(alt);
+ for (i=0; i<n; i++) {
+ char *sn;
+ int sl;
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type == GEN_DNS) {
+ if (ntype != IS_DNS) continue;
+
+ sn = (char *) ASN1_STRING_data(gn->d.ia5);
+ sl = ASN1_STRING_length(gn->d.ia5);
+
+ /* ignore empty */
+ if (sl == 0) continue;
+
+ /* Is this an exact match? */
+ if ((nlen == sl) && !strncasecmp(name, sn, nlen)) {
+ break;
+ }
+
+ /* Is this a wildcard match? */
+ if (domain && (sn[0] == '*') && (sn[1] == '.') &&
+ (len2 == sl-1) && !strncasecmp(domain, &sn[1], len2))
+ {
+ break;
+ }
+
+ } else if (gn->type == GEN_IPADD) {
+ if (ntype == IS_DNS) continue;
+
+ sn = (char *) ASN1_STRING_data(gn->d.ia5);
+ sl = ASN1_STRING_length(gn->d.ia5);
+
+#ifdef LDAP_PF_INET6
+ if (ntype == IS_IP6 && sl != sizeof(struct in6_addr)) {
+ continue;
+ } else
+#endif
+ if (ntype == IS_IP4 && sl != sizeof(struct in_addr)) {
+ continue;
+ }
+ if (!memcmp(sn, &addr, sl)) {
+ break;
+ }
+ }
+ }
+
+ GENERAL_NAMES_free(alt);
+ if (i < n) { /* Found a match */
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+ }
+ if (ret != LDAP_SUCCESS && chkSAN) {
+ switch(chkSAN) {
+ case LDAP_OPT_X_TLS_DEMAND:
+ case LDAP_OPT_X_TLS_HARD:
+ if (!gotSAN) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get subjectAltName from peer certificate.\n", 0, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get subjectAltName from peer certificate"));
+ goto done;
+ }
+ /* FALLTHRU */
+ case LDAP_OPT_X_TLS_TRY:
+ if (gotSAN) {
+ Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "subjectAltName in certificate.\n",
+ name, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match subjectAltName in peer certificate"));
+ goto done;
+ }
+ break;
+ case LDAP_OPT_X_TLS_ALLOW:
+ break;
+ }
+ }
+
+ if (ret != LDAP_SUCCESS) {
+ X509_NAME *xn;
+ X509_NAME_ENTRY *ne;
+ ASN1_OBJECT *obj;
+ ASN1_STRING *cn = NULL;
+ int navas;
+
+ /* find the last CN */
+ obj = OBJ_nid2obj( NID_commonName );
+ if ( !obj ) goto no_cn; /* should never happen */
+
+ xn = X509_get_subject_name(x);
+ navas = X509_NAME_entry_count( xn );
+ for ( i=navas-1; i>=0; i-- ) {
+ ne = X509_NAME_get_entry( xn, i );
+ if ( !OBJ_cmp( X509_NAME_ENTRY_get_object(ne), obj )) {
+ cn = X509_NAME_ENTRY_get_data( ne );
+ break;
+ }
+ }
+
+ if( !cn )
+ {
+no_cn:
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: unable to get common name from peer certificate.\n",
+ 0, 0, 0 );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: unable to get CN from peer certificate"));
+
+ } else if ( cn->length == nlen &&
+ strncasecmp( name, (char *) cn->data, nlen ) == 0 ) {
+ ret = LDAP_SUCCESS;
+
+ } else if (( cn->data[0] == '*' ) && ( cn->data[1] == '.' )) {
+ char *domain = strchr(name, '.');
+ if( domain ) {
+ int dlen;
+
+ dlen = nlen - (domain-name);
+
+ /* Is this a wildcard match? */
+ if ((dlen == cn->length-1) &&
+ !strncasecmp(domain, (char *) &cn->data[1], dlen)) {
+ ret = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if( ret == LDAP_LOCAL_ERROR ) {
+ Debug( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
+ "common name in certificate (%.*s).\n",
+ name, cn->length, cn->data );
+ ret = LDAP_CONNECT_ERROR;
+ if ( ld->ld_error ) {
+ LDAP_FREE( ld->ld_error );
+ }
+ ld->ld_error = LDAP_STRDUP(
+ _("TLS: hostname does not match name in peer certificate"));
+ }
+ }
+done:
+ X509_free(x);
+ return ret;
+}
+
+static int
+tlso_session_strength( tls_session *sess )
+{
+ tlso_session *s = (tlso_session *)sess;
+
+ return SSL_CIPHER_get_bits(SSL_get_current_cipher(s), NULL);
+}
+
+/*
+ * TLS support for LBER Sockbufs
+ */
+
+struct tls_data {
+ tlso_session *session;
+ Sockbuf_IO_Desc *sbiod;
+};
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+#define BIO_set_init(b, x) b->init = x
+#define BIO_set_data(b, x) b->ptr = x
+#define BIO_clear_flags(b, x) b->flags &= ~(x)
+#define BIO_get_data(b) b->ptr
+#endif
+static int
+tlso_bio_create( BIO *b ) {
+ BIO_set_init( b, 1 );
+ BIO_set_data( b, NULL );
+ BIO_clear_flags( b, ~0 );
+ return 1;
+}
+
+static int
+tlso_bio_destroy( BIO *b )
+{
+ if ( b == NULL ) return 0;
+
+ BIO_set_data( b, NULL ); /* sb_tls_remove() will free it */
+ BIO_set_init( b, 0 );
+ BIO_clear_flags( b, ~0 );
+ return 1;
+}
+
+static int
+tlso_bio_read( BIO *b, char *buf, int len )
+{
+ struct tls_data *p;
+ int ret;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)BIO_get_data(b);
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ ret = LBER_SBIOD_READ_NEXT( p->sbiod, buf, len );
+
+ BIO_clear_retry_flags( b );
+ if ( ret < 0 ) {
+ int err = sock_errno();
+ if ( err == EAGAIN || err == EWOULDBLOCK ) {
+ BIO_set_retry_read( b );
+ }
+ }
+
+ return ret;
+}
+
+static int
+tlso_bio_write( BIO *b, const char *buf, int len )
+{
+ struct tls_data *p;
+ int ret;
+
+ if ( buf == NULL || len <= 0 ) return 0;
+
+ p = (struct tls_data *)BIO_get_data(b);
+
+ if ( p == NULL || p->sbiod == NULL ) {
+ return 0;
+ }
+
+ ret = LBER_SBIOD_WRITE_NEXT( p->sbiod, (char *)buf, len );
+
+ BIO_clear_retry_flags( b );
+ if ( ret < 0 ) {
+ int err = sock_errno();
+ if ( err == EAGAIN || err == EWOULDBLOCK ) {
+ BIO_set_retry_write( b );
+ }
+ }
+
+ return ret;
+}
+
+static long
+tlso_bio_ctrl( BIO *b, int cmd, long num, void *ptr )
+{
+ if ( cmd == BIO_CTRL_FLUSH ) {
+ /* The OpenSSL library needs this */
+ return 1;
+ }
+ return 0;
+}
+
+static int
+tlso_bio_gets( BIO *b, char *buf, int len )
+{
+ return -1;
+}
+
+static int
+tlso_bio_puts( BIO *b, const char *str )
+{
+ return tlso_bio_write( b, str, strlen( str ) );
+}
+
+static BIO_METHOD *
+tlso_bio_setup( void )
+{
+ /* it's a source/sink BIO */
+ BIO_METHOD * method = BIO_meth_new( 100 | 0x400, "sockbuf glue" );
+ BIO_meth_set_write( method, tlso_bio_write );
+ BIO_meth_set_read( method, tlso_bio_read );
+ BIO_meth_set_puts( method, tlso_bio_puts );
+ BIO_meth_set_gets( method, tlso_bio_gets );
+ BIO_meth_set_ctrl( method, tlso_bio_ctrl );
+ BIO_meth_set_create( method, tlso_bio_create );
+ BIO_meth_set_destroy( method, tlso_bio_destroy );
+
+ return method;
+}
+
+static int
+tlso_sb_setup( Sockbuf_IO_Desc *sbiod, void *arg )
+{
+ struct tls_data *p;
+ BIO *bio;
+
+ assert( sbiod != NULL );
+
+ p = LBER_MALLOC( sizeof( *p ) );
+ if ( p == NULL ) {
+ return -1;
+ }
+
+ p->session = arg;
+ p->sbiod = sbiod;
+ bio = BIO_new( tlso_bio_method );
+ BIO_set_data( bio, p );
+ SSL_set_bio( p->session, bio, bio );
+ sbiod->sbiod_pvt = p;
+ return 0;
+}
+
+static int
+tlso_sb_remove( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ SSL_free( p->session );
+ LBER_FREE( sbiod->sbiod_pvt );
+ sbiod->sbiod_pvt = NULL;
+ return 0;
+}
+
+static int
+tlso_sb_close( Sockbuf_IO_Desc *sbiod )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+ SSL_shutdown( p->session );
+ return 0;
+}
+
+static int
+tlso_sb_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg )
+{
+ struct tls_data *p;
+
+ assert( sbiod != NULL );
+ assert( sbiod->sbiod_pvt != NULL );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ if ( opt == LBER_SB_OPT_GET_SSL ) {
+ *((tlso_session **)arg) = p->session;
+ return 1;
+
+ } else if ( opt == LBER_SB_OPT_DATA_READY ) {
+ if( SSL_pending( p->session ) > 0 ) {
+ return 1;
+ }
+ }
+
+ return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg );
+}
+
+static ber_slen_t
+tlso_sb_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = SSL_read( p->session, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+ err = SSL_get_error( p->session, ret );
+ if (err == SSL_ERROR_WANT_READ ) {
+ sbiod->sbiod_sb->sb_trans_needs_read = 1;
+ sock_errset(EWOULDBLOCK);
+ }
+ else
+ sbiod->sbiod_sb->sb_trans_needs_read = 0;
+ return ret;
+}
+
+static ber_slen_t
+tlso_sb_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
+{
+ struct tls_data *p;
+ ber_slen_t ret;
+ int err;
+
+ assert( sbiod != NULL );
+ assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
+
+ p = (struct tls_data *)sbiod->sbiod_pvt;
+
+ ret = SSL_write( p->session, (char *)buf, len );
+#ifdef HAVE_WINSOCK
+ errno = WSAGetLastError();
+#endif
+ err = SSL_get_error( p->session, ret );
+ if (err == SSL_ERROR_WANT_WRITE ) {
+ sbiod->sbiod_sb->sb_trans_needs_write = 1;
+ sock_errset(EWOULDBLOCK);
+
+ } else {
+ sbiod->sbiod_sb->sb_trans_needs_write = 0;
+ }
+ return ret;
+}
+
+static Sockbuf_IO tlso_sbio =
+{
+ tlso_sb_setup, /* sbi_setup */
+ tlso_sb_remove, /* sbi_remove */
+ tlso_sb_ctrl, /* sbi_ctrl */
+ tlso_sb_read, /* sbi_read */
+ tlso_sb_write, /* sbi_write */
+ tlso_sb_close /* sbi_close */
+};
+
+/* Derived from openssl/apps/s_cb.c */
+static void
+tlso_info_cb( const SSL *ssl, int where, int ret )
+{
+ int w;
+ char *op;
+ char *state = (char *) SSL_state_string_long( (SSL *)ssl );
+
+ w = where & ~SSL_ST_MASK;
+ if ( w & SSL_ST_CONNECT ) {
+ op = "SSL_connect";
+ } else if ( w & SSL_ST_ACCEPT ) {
+ op = "SSL_accept";
+ } else {
+ op = "undefined";
+ }
+
+#ifdef HAVE_EBCDIC
+ if ( state ) {
+ state = LDAP_STRDUP( state );
+ __etoa( state );
+ }
+#endif
+ if ( where & SSL_CB_LOOP ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:%s\n",
+ op, state, 0 );
+
+ } else if ( where & SSL_CB_ALERT ) {
+ char *atype = (char *) SSL_alert_type_string_long( ret );
+ char *adesc = (char *) SSL_alert_desc_string_long( ret );
+ op = ( where & SSL_CB_READ ) ? "read" : "write";
+#ifdef HAVE_EBCDIC
+ if ( atype ) {
+ atype = LDAP_STRDUP( atype );
+ __etoa( atype );
+ }
+ if ( adesc ) {
+ adesc = LDAP_STRDUP( adesc );
+ __etoa( adesc );
+ }
+#endif
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS trace: SSL3 alert %s:%s:%s\n",
+ op, atype, adesc );
+#ifdef HAVE_EBCDIC
+ if ( atype ) LDAP_FREE( atype );
+ if ( adesc ) LDAP_FREE( adesc );
+#endif
+ } else if ( where & SSL_CB_EXIT ) {
+ if ( ret == 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:failed in %s\n",
+ op, state, 0 );
+ } else if ( ret < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS trace: %s:error in %s\n",
+ op, state, 0 );
+ }
+ }
+#ifdef HAVE_EBCDIC
+ if ( state ) LDAP_FREE( state );
+#endif
+}
+
+static int
+tlso_verify_cb( int ok, X509_STORE_CTX *ctx )
+{
+ X509 *cert;
+ int errnum;
+ int errdepth;
+ X509_NAME *subject;
+ X509_NAME *issuer;
+ char *sname;
+ char *iname;
+ char *certerr = NULL;
+
+ cert = X509_STORE_CTX_get_current_cert( ctx );
+ errnum = X509_STORE_CTX_get_error( ctx );
+ errdepth = X509_STORE_CTX_get_error_depth( ctx );
+
+ /*
+ * X509_get_*_name return pointers to the internal copies of
+ * those things requested. So do not free them.
+ */
+ subject = X509_get_subject_name( cert );
+ issuer = X509_get_issuer_name( cert );
+ /* X509_NAME_oneline, if passed a NULL buf, allocate memomry */
+ sname = X509_NAME_oneline( subject, NULL, 0 );
+ iname = X509_NAME_oneline( issuer, NULL, 0 );
+ if ( !ok ) certerr = (char *)X509_verify_cert_error_string( errnum );
+#ifdef HAVE_EBCDIC
+ if ( sname ) __etoa( sname );
+ if ( iname ) __etoa( iname );
+ if ( certerr ) {
+ certerr = LDAP_STRDUP( certerr );
+ __etoa( certerr );
+ }
+#endif
+ Debug( LDAP_DEBUG_TRACE,
+ "TLS certificate verification: depth: %d, err: %d, subject: %s,",
+ errdepth, errnum,
+ sname ? sname : "-unknown-" );
+ Debug( LDAP_DEBUG_TRACE, " issuer: %s\n", iname ? iname : "-unknown-", 0, 0 );
+ if ( !ok ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS certificate verification: Error, %s\n",
+ certerr, 0, 0 );
+ }
+ if ( sname )
+ OPENSSL_free ( sname );
+ if ( iname )
+ OPENSSL_free ( iname );
+#ifdef HAVE_EBCDIC
+ if ( certerr ) LDAP_FREE( certerr );
+#endif
+ return ok;
+}
+
+static int
+tlso_verify_ok( int ok, X509_STORE_CTX *ctx )
+{
+ (void) tlso_verify_cb( ok, ctx );
+ return 1;
+}
+
+/* Inspired by ERR_print_errors in OpenSSL */
+static void
+tlso_report_error( void )
+{
+ unsigned long l;
+ char buf[200];
+ const char *file;
+ int line;
+
+ while ( ( l = ERR_get_error_line( &file, &line ) ) != 0 ) {
+ ERR_error_string_n( l, buf, sizeof( buf ) );
+#ifdef HAVE_EBCDIC
+ if ( file ) {
+ file = LDAP_STRDUP( file );
+ __etoa( (char *)file );
+ }
+ __etoa( buf );
+#endif
+ Debug( LDAP_DEBUG_ANY, "TLS: %s %s:%d\n",
+ buf, file, line );
+#ifdef HAVE_EBCDIC
+ if ( file ) LDAP_FREE( (void *)file );
+#endif
+ }
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+static RSA *
+tlso_tmp_rsa_cb( SSL *ssl, int is_export, int key_length )
+{
+ RSA *tmp_rsa;
+ /* FIXME: Pregenerate the key on startup */
+ /* FIXME: Who frees the key? */
+#if OPENSSL_VERSION_NUMBER >= 0x00908000
+ BIGNUM *bn = BN_new();
+ tmp_rsa = NULL;
+ if ( bn ) {
+ if ( BN_set_word( bn, RSA_F4 )) {
+ tmp_rsa = RSA_new();
+ if ( tmp_rsa && !RSA_generate_key_ex( tmp_rsa, key_length, bn, NULL )) {
+ RSA_free( tmp_rsa );
+ tmp_rsa = NULL;
+ }
+ }
+ BN_free( bn );
+ }
+#else
+ tmp_rsa = RSA_generate_key( key_length, RSA_F4, NULL, NULL );
+#endif
+
+ if ( !tmp_rsa ) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: Failed to generate temporary %d-bit %s RSA key\n",
+ key_length, is_export ? "export" : "domestic", 0 );
+ }
+ return tmp_rsa;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 1.1 */
+
+static int
+tlso_seed_PRNG( const char *randfile )
+{
+#ifndef URANDOM_DEVICE
+ /* no /dev/urandom (or equiv) */
+ long total=0;
+ char buffer[MAXPATHLEN];
+
+ if (randfile == NULL) {
+ /* The seed file is $RANDFILE if defined, otherwise $HOME/.rnd.
+ * If $HOME is not set or buffer too small to hold the pathname,
+ * an error occurs. - From RAND_file_name() man page.
+ * The fact is that when $HOME is NULL, .rnd is used.
+ */
+ randfile = RAND_file_name( buffer, sizeof( buffer ) );
+ }
+#ifndef OPENSSL_NO_EGD
+ else if (RAND_egd(randfile) > 0) {
+ /* EGD socket */
+ return 0;
+ }
+#endif
+
+ if (randfile == NULL) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: Use configuration file or $RANDFILE to define seed PRNG\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ total = RAND_load_file(randfile, -1);
+
+ if (RAND_status() == 0) {
+ Debug( LDAP_DEBUG_ANY,
+ "TLS: PRNG not been seeded with enough data\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ /* assume if there was enough bits to seed that it's okay
+ * to write derived bits to the file
+ */
+ RAND_write_file(randfile);
+
+#endif
+
+ return 0;
+}
+
+
+tls_impl ldap_int_tls_impl = {
+ "OpenSSL",
+
+ tlso_init,
+ tlso_destroy,
+
+ tlso_ctx_new,
+ tlso_ctx_ref,
+ tlso_ctx_free,
+ tlso_ctx_init,
+
+ tlso_session_new,
+ tlso_session_connect,
+ tlso_session_accept,
+ tlso_session_upflags,
+ tlso_session_errmsg,
+ tlso_session_my_dn,
+ tlso_session_peer_dn,
+ tlso_session_chkhost,
+ tlso_session_strength,
+
+ &tlso_sbio,
+
+#ifdef LDAP_R_COMPILE
+ tlso_thr_init,
+#else
+ NULL,
+#endif
+
+ 0
+};
+
+#endif /* HAVE_OPENSSL */
diff --git a/libraries/libldap/turn.c b/libraries/libldap/turn.c
new file mode 100644
index 0000000..99c4274
--- /dev/null
+++ b/libraries/libldap/turn.c
@@ -0,0 +1,96 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was orignally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Turn Operation Request
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+int
+ldap_turn(
+ LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+#ifdef LDAP_EXOP_X_TURN
+ BerElement *turnvalber = NULL;
+ struct berval *turnvalp = NULL;
+ int rc;
+
+ turnvalber = ber_alloc_t( LBER_USE_DER );
+ if( mutual ) {
+ ber_printf( turnvalber, "{bs}", mutual, identifier );
+ } else {
+ ber_printf( turnvalber, "{s}", identifier );
+ }
+ ber_flatten( turnvalber, &turnvalp );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_X_TURN,
+ turnvalp, sctrls, cctrls, msgidp );
+ ber_free( turnvalber, 1 );
+ return rc;
+#else
+ return LDAP_CONTROL_NOT_FOUND;
+#endif
+}
+
+int
+ldap_turn_s(
+ LDAP *ld,
+ int mutual,
+ LDAP_CONST char* identifier,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+#ifdef LDAP_EXOP_X_TURN
+ BerElement *turnvalber = NULL;
+ struct berval *turnvalp = NULL;
+ int rc;
+
+ turnvalber = ber_alloc_t( LBER_USE_DER );
+ if( mutual ) {
+ ber_printf( turnvalber, "{bs}", 0xFF, identifier );
+ } else {
+ ber_printf( turnvalber, "{s}", identifier );
+ }
+ ber_flatten( turnvalber, &turnvalp );
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TURN,
+ turnvalp, sctrls, cctrls, NULL, NULL );
+ ber_free( turnvalber, 1 );
+ return rc;
+#else
+ return LDAP_CONTROL_NOT_FOUND;
+#endif
+}
+
diff --git a/libraries/libldap/txn.c b/libraries/libldap/txn.c
new file mode 100644
index 0000000..fe8400b
--- /dev/null
+++ b/libraries/libldap/txn.c
@@ -0,0 +1,155 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was orignally developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * LDAPv3 Transactions (draft-zeilenga-ldap-txn)
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_log.h"
+
+#ifdef LDAP_X_TXN
+int
+ldap_txn_start(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ return ldap_extended_operation( ld, LDAP_EXOP_X_TXN_START,
+ NULL, sctrls, cctrls, msgidp );
+}
+
+int
+ldap_txn_start_s(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ struct berval **txnid )
+{
+ assert( txnid != NULL );
+
+ return ldap_extended_operation_s( ld, LDAP_EXOP_X_TXN_START,
+ NULL, sctrls, cctrls, NULL, txnid );
+}
+
+int
+ldap_txn_end(
+ LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+ BerElement *txnber = NULL;
+ struct berval *txnval = NULL;
+
+ assert( txnid != NULL );
+
+ txnber = ber_alloc_t( LBER_USE_DER );
+
+ if( commit ) {
+ ber_printf( txnber, "{ON}", txnid );
+ } else {
+ ber_printf( txnber, "{bON}", commit, txnid );
+ }
+
+ ber_flatten( txnber, &txnval );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_X_TXN_END,
+ txnval, sctrls, cctrls, msgidp );
+
+ ber_free( txnber, 1 );
+ return rc;
+}
+
+int
+ldap_txn_end_s(
+ LDAP *ld,
+ int commit,
+ struct berval *txnid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *retidp )
+{
+ int rc;
+ BerElement *txnber = NULL;
+ struct berval *txnval = NULL;
+ struct berval *retdata = NULL;
+
+ if ( retidp != NULL ) *retidp = -1;
+
+ txnber = ber_alloc_t( LBER_USE_DER );
+
+ if( commit ) {
+ ber_printf( txnber, "{ON}", txnid );
+ } else {
+ ber_printf( txnber, "{bON}", commit, txnid );
+ }
+
+ ber_flatten( txnber, &txnval );
+
+ rc = ldap_extended_operation_s( ld, LDAP_EXOP_X_TXN_END,
+ txnval, sctrls, cctrls, NULL, &retdata );
+
+ ber_free( txnber, 1 );
+
+ /* parse retdata */
+ if( retdata != NULL ) {
+ BerElement *ber;
+ ber_tag_t tag;
+ ber_int_t retid;
+
+ if( retidp == NULL ) goto done;
+
+ ber = ber_init( retdata );
+
+ if( ber == NULL ) {
+ rc = ld->ld_errno = LDAP_NO_MEMORY;
+ goto done;
+ }
+
+ tag = ber_scanf( ber, "i", &retid );
+ ber_free( ber, 1 );
+
+ if ( tag != LBER_INTEGER ) {
+ rc = ld->ld_errno = LDAP_DECODING_ERROR;
+ goto done;
+ }
+
+ *retidp = (int) retid;
+
+done:
+ ber_bvfree( retdata );
+ }
+
+ return rc;
+}
+#endif
diff --git a/libraries/libldap/unbind.c b/libraries/libldap/unbind.c
new file mode 100644
index 0000000..11797c2
--- /dev/null
+++ b/libraries/libldap/unbind.c
@@ -0,0 +1,309 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1990 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* An Unbind Request looks like this:
+ *
+ * UnbindRequest ::= [APPLICATION 2] NULL
+ *
+ * and has no response. (Source: RFC 4511)
+ */
+
+int
+ldap_unbind_ext(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ /* check client controls */
+ rc = ldap_int_client_controls( ld, cctrls );
+ if( rc != LDAP_SUCCESS ) return rc;
+
+ return ldap_ld_free( ld, 1, sctrls, cctrls );
+}
+
+int
+ldap_unbind_ext_s(
+ LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ return ldap_unbind_ext( ld, sctrls, cctrls );
+}
+
+int
+ldap_unbind( LDAP *ld )
+{
+ Debug( LDAP_DEBUG_TRACE, "ldap_unbind\n", 0, 0, 0 );
+
+ return( ldap_unbind_ext( ld, NULL, NULL ) );
+}
+
+
+int
+ldap_ld_free(
+ LDAP *ld,
+ int close,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ LDAPMessage *lm, *next;
+ int err = LDAP_SUCCESS;
+
+ LDAP_MUTEX_LOCK( &ld->ld_ldcmutex );
+ /* Someone else is still using this ld. */
+ if (ld->ld_ldcrefcnt > 1) { /* but not last thread */
+ /* clean up self only */
+ ld->ld_ldcrefcnt--;
+ if ( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+ if ( ld->ld_referrals != NULL) {
+ LDAP_VFREE(ld->ld_referrals);
+ ld->ld_referrals = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+ LDAP_FREE( (char *) ld );
+ return( err );
+ }
+
+ /* This ld is the last thread. */
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldcmutex );
+
+ /* free LDAP structure and outstanding requests/responses */
+ LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+ while ( ld->ld_requests != NULL ) {
+ ldap_free_request( ld, ld->ld_requests );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
+
+ /* free and unbind from all open connections */
+ while ( ld->ld_conns != NULL ) {
+ ldap_free_connection( ld, ld->ld_conns, 1, close );
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
+ LDAP_MUTEX_LOCK( &ld->ld_res_mutex );
+ for ( lm = ld->ld_responses; lm != NULL; lm = next ) {
+ next = lm->lm_next;
+ ldap_msgfree( lm );
+ }
+
+ if ( ld->ld_abandoned != NULL ) {
+ LDAP_FREE( ld->ld_abandoned );
+ ld->ld_abandoned = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_res_mutex );
+
+ /* Should already be closed by ldap_free_connection which knows not to free
+ * this one */
+ ber_int_sb_destroy( ld->ld_sb );
+ LBER_FREE( ld->ld_sb );
+
+ LDAP_MUTEX_LOCK( &ld->ld_ldopts_mutex );
+
+ /* final close callbacks */
+ {
+ ldaplist *ll, *next;
+
+ for ( ll = ld->ld_options.ldo_conn_cbs; ll; ll = next ) {
+ ldap_conncb *cb = ll->ll_data;
+ next = ll->ll_next;
+ cb->lc_del( ld, NULL, cb );
+ LDAP_FREE( ll );
+ }
+ }
+
+ if ( ld->ld_error != NULL ) {
+ LDAP_FREE( ld->ld_error );
+ ld->ld_error = NULL;
+ }
+
+ if ( ld->ld_matched != NULL ) {
+ LDAP_FREE( ld->ld_matched );
+ ld->ld_matched = NULL;
+ }
+
+ if ( ld->ld_referrals != NULL) {
+ LDAP_VFREE(ld->ld_referrals);
+ ld->ld_referrals = NULL;
+ }
+
+ if ( ld->ld_selectinfo != NULL ) {
+ ldap_free_select_info( ld->ld_selectinfo );
+ ld->ld_selectinfo = NULL;
+ }
+
+ if ( ld->ld_options.ldo_defludp != NULL ) {
+ ldap_free_urllist( ld->ld_options.ldo_defludp );
+ ld->ld_options.ldo_defludp = NULL;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if ( ld->ld_options.ldo_peer != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_peer );
+ ld->ld_options.ldo_peer = NULL;
+ }
+
+ if ( ld->ld_options.ldo_cldapdn != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_cldapdn );
+ ld->ld_options.ldo_cldapdn = NULL;
+ }
+#endif
+
+#ifdef HAVE_CYRUS_SASL
+ if ( ld->ld_options.ldo_def_sasl_mech != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_mech );
+ ld->ld_options.ldo_def_sasl_mech = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_realm != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_realm );
+ ld->ld_options.ldo_def_sasl_realm = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_authcid != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authcid );
+ ld->ld_options.ldo_def_sasl_authcid = NULL;
+ }
+
+ if ( ld->ld_options.ldo_def_sasl_authzid != NULL ) {
+ LDAP_FREE( ld->ld_options.ldo_def_sasl_authzid );
+ ld->ld_options.ldo_def_sasl_authzid = NULL;
+ }
+#endif
+
+#ifdef HAVE_TLS
+ ldap_int_tls_destroy( &ld->ld_options );
+#endif
+
+ if ( ld->ld_options.ldo_sctrls != NULL ) {
+ ldap_controls_free( ld->ld_options.ldo_sctrls );
+ ld->ld_options.ldo_sctrls = NULL;
+ }
+
+ if ( ld->ld_options.ldo_cctrls != NULL ) {
+ ldap_controls_free( ld->ld_options.ldo_cctrls );
+ ld->ld_options.ldo_cctrls = NULL;
+ }
+ LDAP_MUTEX_UNLOCK( &ld->ld_ldopts_mutex );
+
+#ifdef LDAP_R_COMPILE
+ ldap_pvt_thread_mutex_destroy( &ld->ld_msgid_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_conn_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_req_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_res_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_abandon_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_ldopts_mutex );
+ ldap_pvt_thread_mutex_destroy( &ld->ld_ldcmutex );
+#endif
+#ifndef NDEBUG
+ LDAP_TRASH(ld);
+#endif
+ LDAP_FREE( (char *) ld->ldc );
+ LDAP_FREE( (char *) ld );
+
+ return( err );
+}
+
+int
+ldap_destroy( LDAP *ld )
+{
+ return ( ldap_ld_free( ld, 1, NULL, NULL ) );
+}
+
+int
+ldap_unbind_s( LDAP *ld )
+{
+ return( ldap_unbind_ext( ld, NULL, NULL ) );
+}
+
+/* FIXME: this function is called only by ldap_free_connection(),
+ * which, most of the times, is called with ld_req_mutex locked */
+int
+ldap_send_unbind(
+ LDAP *ld,
+ Sockbuf *sb,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ BerElement *ber;
+ ber_int_t id;
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_send_unbind\n", 0, 0, 0 );
+
+#ifdef LDAP_CONNECTIONLESS
+ if (LDAP_IS_UDP(ld))
+ return LDAP_SUCCESS;
+#endif
+ /* create a message to send */
+ if ( (ber = ldap_alloc_ber_with_options( ld )) == NULL ) {
+ return( ld->ld_errno );
+ }
+
+ LDAP_NEXT_MSGID(ld, id);
+
+ /* fill it in */
+ if ( ber_printf( ber, "{itn" /*}*/, id,
+ LDAP_REQ_UNBIND ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( ld->ld_errno );
+ }
+
+ /* Put Server Controls */
+ if( ldap_int_put_controls( ld, sctrls, ber ) != LDAP_SUCCESS ) {
+ ber_free( ber, 1 );
+ return ld->ld_errno;
+ }
+
+ if ( ber_printf( ber, /*{*/ "N}", LDAP_REQ_UNBIND ) == -1 ) {
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ ber_free( ber, 1 );
+ return( ld->ld_errno );
+ }
+
+ ld->ld_errno = LDAP_SUCCESS;
+ /* send the message */
+ if ( ber_flush2( sb, ber, LBER_FLUSH_FREE_ALWAYS ) == -1 ) {
+ ld->ld_errno = LDAP_SERVER_DOWN;
+ }
+
+ return( ld->ld_errno );
+}
diff --git a/libraries/libldap/url.c b/libraries/libldap/url.c
new file mode 100644
index 0000000..b39f70b
--- /dev/null
+++ b/libraries/libldap/url.c
@@ -0,0 +1,1622 @@
+/* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (c) 1996 Regents of the University of Michigan.
+ * All rights reserved.
+ */
+
+
+/*
+ * LDAP URLs look like this:
+ * ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
+ *
+ * where:
+ * attributes is a comma separated list
+ * scope is one of these three strings: base one sub (default=base)
+ * filter is an string-represented filter as in RFC 4515
+ *
+ * e.g., ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
+ *
+ * We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/* local functions */
+static const char* skip_url_prefix LDAP_P((
+ const char *url,
+ int *enclosedp,
+ const char **scheme ));
+
+int ldap_pvt_url_scheme2proto( const char *scheme )
+{
+ assert( scheme != NULL );
+
+ if( scheme == NULL ) {
+ return -1;
+ }
+
+ if( strcmp("ldap", scheme) == 0 ) {
+ return LDAP_PROTO_TCP;
+ }
+
+ if( strcmp("ldapi", scheme) == 0 ) {
+ return LDAP_PROTO_IPC;
+ }
+
+ if( strcmp("ldaps", scheme) == 0 ) {
+ return LDAP_PROTO_TCP;
+ }
+#ifdef LDAP_CONNECTIONLESS
+ if( strcmp("cldap", scheme) == 0 ) {
+ return LDAP_PROTO_UDP;
+ }
+#endif
+
+ return -1;
+}
+
+int ldap_pvt_url_scheme_port( const char *scheme, int port )
+{
+ assert( scheme != NULL );
+
+ if( port ) return port;
+ if( scheme == NULL ) return port;
+
+ if( strcmp("ldap", scheme) == 0 ) {
+ return LDAP_PORT;
+ }
+
+ if( strcmp("ldapi", scheme) == 0 ) {
+ return -1;
+ }
+
+ if( strcmp("ldaps", scheme) == 0 ) {
+ return LDAPS_PORT;
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ if( strcmp("cldap", scheme) == 0 ) {
+ return LDAP_PORT;
+ }
+#endif
+
+ return -1;
+}
+
+int
+ldap_pvt_url_scheme2tls( const char *scheme )
+{
+ assert( scheme != NULL );
+
+ if( scheme == NULL ) {
+ return -1;
+ }
+
+ return strcmp("ldaps", scheme) == 0;
+}
+
+int
+ldap_is_ldap_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ldap_is_ldaps_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "ldaps") == 0;
+}
+
+int
+ldap_is_ldapi_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "ldapi") == 0;
+}
+
+#ifdef LDAP_CONNECTIONLESS
+int
+ldap_is_ldapc_url( LDAP_CONST char *url )
+{
+ int enclosed;
+ const char * scheme;
+
+ if( url == NULL ) {
+ return 0;
+ }
+
+ if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
+ return 0;
+ }
+
+ return strcmp(scheme, "cldap") == 0;
+}
+#endif
+
+static const char*
+skip_url_prefix(
+ const char *url,
+ int *enclosedp,
+ const char **scheme )
+{
+ /*
+ * return non-zero if this looks like a LDAP URL; zero if not
+ * if non-zero returned, *urlp will be moved past "ldap://" part of URL
+ */
+ const char *p;
+
+ if ( url == NULL ) {
+ return( NULL );
+ }
+
+ p = url;
+
+ /* skip leading '<' (if any) */
+ if ( *p == '<' ) {
+ *enclosedp = 1;
+ ++p;
+ } else {
+ *enclosedp = 0;
+ }
+
+ /* skip leading "URL:" (if any) */
+ if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
+ p += LDAP_URL_URLCOLON_LEN;
+ }
+
+ /* check for "ldap://" prefix */
+ if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldap://" prefix and return success */
+ p += LDAP_URL_PREFIX_LEN;
+ *scheme = "ldap";
+ return( p );
+ }
+
+ /* check for "ldaps://" prefix */
+ if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldaps://" prefix and return success */
+ p += LDAPS_URL_PREFIX_LEN;
+ *scheme = "ldaps";
+ return( p );
+ }
+
+ /* check for "ldapi://" prefix */
+ if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "ldapi://" prefix and return success */
+ p += LDAPI_URL_PREFIX_LEN;
+ *scheme = "ldapi";
+ return( p );
+ }
+
+#ifdef LDAP_CONNECTIONLESS
+ /* check for "cldap://" prefix */
+ if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
+ /* skip over "cldap://" prefix and return success */
+ p += LDAPC_URL_PREFIX_LEN;
+ *scheme = "cldap";
+ return( p );
+ }
+#endif
+
+ return( NULL );
+}
+
+int
+ldap_pvt_scope2bv( int scope, struct berval *bv )
+{
+ switch ( scope ) {
+ case LDAP_SCOPE_BASE:
+ BER_BVSTR( bv, "base" );
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ BER_BVSTR( bv, "one" );
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ BER_BVSTR( bv, "sub" );
+ break;
+
+ case LDAP_SCOPE_SUBORDINATE:
+ BER_BVSTR( bv, "subordinate" );
+ break;
+
+ default:
+ return LDAP_OTHER;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+const char *
+ldap_pvt_scope2str( int scope )
+{
+ struct berval bv;
+
+ if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
+ return bv.bv_val;
+ }
+
+ return NULL;
+}
+
+int
+ldap_pvt_bv2scope( struct berval *bv )
+{
+ static struct {
+ struct berval bv;
+ int scope;
+ } v[] = {
+ { BER_BVC( "one" ), LDAP_SCOPE_ONELEVEL },
+ { BER_BVC( "onelevel" ), LDAP_SCOPE_ONELEVEL },
+ { BER_BVC( "base" ), LDAP_SCOPE_BASE },
+ { BER_BVC( "sub" ), LDAP_SCOPE_SUBTREE },
+ { BER_BVC( "subtree" ), LDAP_SCOPE_SUBTREE },
+ { BER_BVC( "subord" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVC( "children" ), LDAP_SCOPE_SUBORDINATE },
+ { BER_BVNULL, -1 }
+ };
+ int i;
+
+ for ( i = 0; v[ i ].scope != -1; i++ ) {
+ if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
+ return v[ i ].scope;
+ }
+ }
+
+ return( -1 );
+}
+
+int
+ldap_pvt_str2scope( const char *p )
+{
+ struct berval bv;
+
+ ber_str2bv( p, 0, 0, &bv );
+
+ return ldap_pvt_bv2scope( &bv );
+}
+
+static const char hex[] = "0123456789ABCDEF";
+
+#define URLESC_NONE 0x0000U
+#define URLESC_COMMA 0x0001U
+#define URLESC_SLASH 0x0002U
+
+static int
+hex_escape_len( const char *s, unsigned list )
+{
+ int len;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ for ( len = 0; s[0]; s++ ) {
+ switch ( s[0] ) {
+ /* RFC 2396: reserved */
+ case '?':
+ len += 3;
+ break;
+
+ case ',':
+ if ( list & URLESC_COMMA ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+
+ case '/':
+ if ( list & URLESC_SLASH ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+
+ case ';':
+ case ':':
+ case '@':
+ case '&':
+ case '=':
+ case '+':
+ case '$':
+
+ /* RFC 2396: unreserved mark */
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ len++;
+ break;
+
+ /* RFC 2396: unreserved alphanum */
+ default:
+ if ( !isalnum( (unsigned char) s[0] ) ) {
+ len += 3;
+ } else {
+ len++;
+ }
+ break;
+ }
+ }
+
+ return len;
+}
+
+static int
+hex_escape( char *buf, int len, const char *s, unsigned list )
+{
+ int i;
+ int pos;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
+ int escape = 0;
+
+ switch ( s[i] ) {
+ /* RFC 2396: reserved */
+ case '?':
+ escape = 1;
+ break;
+
+ case ',':
+ if ( list & URLESC_COMMA ) {
+ escape = 1;
+ }
+ break;
+
+ case '/':
+ if ( list & URLESC_SLASH ) {
+ escape = 1;
+ }
+ break;
+
+ case ';':
+ case ':':
+ case '@':
+ case '&':
+ case '=':
+ case '+':
+ case '$':
+
+ /* RFC 2396: unreserved mark */
+ case '-':
+ case '_':
+ case '.':
+ case '!':
+ case '~':
+ case '*':
+ case '\'':
+ case '(':
+ case ')':
+ break;
+
+ /* RFC 2396: unreserved alphanum */
+ default:
+ if ( !isalnum( (unsigned char) s[i] ) ) {
+ escape = 1;
+ }
+ break;
+ }
+
+ if ( escape ) {
+ buf[pos++] = '%';
+ buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
+ buf[pos++] = hex[ s[i] & 0x0f ];
+
+ } else {
+ buf[pos++] = s[i];
+ }
+ }
+
+ buf[pos] = '\0';
+
+ return pos;
+}
+
+static int
+hex_escape_len_list( char **s, unsigned flags )
+{
+ int len;
+ int i;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ len = 0;
+ for ( i = 0; s[i] != NULL; i++ ) {
+ if ( len ) {
+ len++;
+ }
+ len += hex_escape_len( s[i], flags );
+ }
+
+ return len;
+}
+
+static int
+hex_escape_list( char *buf, int len, char **s, unsigned flags )
+{
+ int pos;
+ int i;
+
+ if ( s == NULL ) {
+ return 0;
+ }
+
+ pos = 0;
+ for ( i = 0; s[i] != NULL; i++ ) {
+ int curlen;
+
+ if ( pos ) {
+ buf[pos++] = ',';
+ len--;
+ }
+ curlen = hex_escape( &buf[pos], len, s[i], flags );
+ len -= curlen;
+ pos += curlen;
+ }
+
+ return pos;
+}
+
+static int
+desc2str_len( LDAPURLDesc *u )
+{
+ int sep = 0;
+ int len = 0;
+ int is_ipc = 0;
+ struct berval scope;
+
+ if ( u == NULL || u->lud_scheme == NULL ) {
+ return -1;
+ }
+
+ if ( !strcmp( "ldapi", u->lud_scheme )) {
+ is_ipc = 1;
+ }
+
+ if ( u->lud_exts ) {
+ len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
+ if ( !sep ) {
+ sep = 5;
+ }
+ }
+
+ if ( u->lud_filter ) {
+ len += hex_escape_len( u->lud_filter, URLESC_NONE );
+ if ( !sep ) {
+ sep = 4;
+ }
+ }
+
+ if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
+ len += scope.bv_len;
+ if ( !sep ) {
+ sep = 3;
+ }
+ }
+
+ if ( u->lud_attrs ) {
+ len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
+ if ( !sep ) {
+ sep = 2;
+ }
+ }
+
+ if ( u->lud_dn && u->lud_dn[0] ) {
+ len += hex_escape_len( u->lud_dn, URLESC_NONE );
+ if ( !sep ) {
+ sep = 1;
+ }
+ };
+
+ len += sep;
+
+ if ( u->lud_port ) {
+ unsigned p = u->lud_port;
+ if ( p > 65535 )
+ return -1;
+
+ len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
+ }
+
+ if ( u->lud_host && u->lud_host[0] ) {
+ char *ptr;
+ len += hex_escape_len( u->lud_host, URLESC_SLASH );
+ if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
+ if ( strchr( ptr+1, ':' ))
+ len += 2; /* IPv6, [] */
+ }
+ }
+
+ len += strlen( u->lud_scheme ) + STRLENOF( "://" );
+
+ return len;
+}
+
+static int
+desc2str( LDAPURLDesc *u, char *s, int len )
+{
+ int i;
+ int sep = 0;
+ int sofar = 0;
+ int is_v6 = 0;
+ int is_ipc = 0;
+ struct berval scope = BER_BVNULL;
+ char *ptr;
+
+ if ( u == NULL ) {
+ return -1;
+ }
+
+ if ( s == NULL ) {
+ return -1;
+ }
+
+ if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
+ is_ipc = 1;
+ }
+
+ ldap_pvt_scope2bv( u->lud_scope, &scope );
+
+ if ( u->lud_exts ) {
+ sep = 5;
+ } else if ( u->lud_filter ) {
+ sep = 4;
+ } else if ( !BER_BVISEMPTY( &scope ) ) {
+ sep = 3;
+ } else if ( u->lud_attrs ) {
+ sep = 2;
+ } else if ( u->lud_dn && u->lud_dn[0] ) {
+ sep = 1;
+ }
+
+ if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
+ if ( strchr( ptr+1, ':' ))
+ is_v6 = 1;
+ }
+
+ if ( u->lud_port ) {
+ sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
+ is_v6 ? "[" : "",
+ u->lud_host ? u->lud_host : "",
+ is_v6 ? "]" : "",
+ u->lud_port );
+ len -= sofar;
+
+ } else {
+ sofar = sprintf( s, "%s://", u->lud_scheme );
+ len -= sofar;
+ if ( u->lud_host && u->lud_host[0] ) {
+ if ( is_v6 ) {
+ s[sofar++] = '[';
+ len--;
+ }
+ i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
+ sofar += i;
+ len -= i;
+ if ( is_v6 ) {
+ s[sofar++] = ']';
+ len--;
+ }
+ }
+ }
+
+ assert( len >= 0 );
+
+ if ( sep < 1 ) {
+ goto done;
+ }
+
+ s[sofar++] = '/';
+ len--;
+
+ assert( len >= 0 );
+
+ if ( u->lud_dn && u->lud_dn[0] ) {
+ i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+ }
+
+ if ( sep < 2 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+ if ( sep < 3 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ if ( !BER_BVISNULL( &scope ) ) {
+ strcpy( &s[sofar], scope.bv_val );
+ sofar += scope.bv_len;
+ len -= scope.bv_len;
+ }
+
+ assert( len >= 0 );
+
+ if ( sep < 4 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+ if ( sep < 5 ) {
+ goto done;
+ }
+ s[sofar++] = '?';
+ len--;
+
+ assert( len >= 0 );
+
+ i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
+ sofar += i;
+ len -= i;
+
+ assert( len >= 0 );
+
+done:
+ if ( len < 0 ) {
+ return -1;
+ }
+
+ return sofar;
+}
+
+char *
+ldap_url_desc2str( LDAPURLDesc *u )
+{
+ int len;
+ char *s;
+
+ if ( u == NULL ) {
+ return NULL;
+ }
+
+ len = desc2str_len( u );
+ if ( len < 0 ) {
+ return NULL;
+ }
+
+ /* allocate enough to hex escape everything -- overkill */
+ s = LDAP_MALLOC( len + 1 );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ if ( desc2str( u, s, len ) != len ) {
+ LDAP_FREE( s );
+ return NULL;
+ }
+
+ s[len] = '\0';
+
+ return s;
+}
+
+int
+ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
+{
+/*
+ * Pick apart the pieces of an LDAP URL.
+ */
+
+ LDAPURLDesc *ludp;
+ char *p, *q, *r;
+ int i, enclosed, proto, is_v6 = 0;
+ const char *scheme = NULL;
+ const char *url_tmp;
+ char *url;
+
+ int check_dn = 1;
+
+ if( url_in == NULL || ludpp == NULL ) {
+ return LDAP_URL_ERR_PARAM;
+ }
+
+#ifndef LDAP_INT_IN_KERNEL
+ /* Global options may not be created yet
+ * We can't test if the global options are initialized
+ * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
+ * the options and cause infinite recursion
+ */
+ Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
+#endif
+
+ *ludpp = NULL; /* pessimistic */
+
+ url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
+
+ if ( url_tmp == NULL ) {
+ return LDAP_URL_ERR_BADSCHEME;
+ }
+
+ assert( scheme != NULL );
+
+ proto = ldap_pvt_url_scheme2proto( scheme );
+ if ( proto == -1 ) {
+ return LDAP_URL_ERR_BADSCHEME;
+ }
+
+ /* make working copy of the remainder of the URL */
+ url = LDAP_STRDUP( url_tmp );
+ if ( url == NULL ) {
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if ( enclosed ) {
+ p = &url[strlen(url)-1];
+
+ if( *p != '>' ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_BADENCLOSURE;
+ }
+
+ *p = '\0';
+ }
+
+ /* allocate return struct */
+ ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
+
+ if ( ludp == NULL ) {
+ LDAP_FREE( url );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ ludp->lud_next = NULL;
+ ludp->lud_host = NULL;
+ ludp->lud_port = 0;
+ ludp->lud_dn = NULL;
+ ludp->lud_attrs = NULL;
+ ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
+ ludp->lud_filter = NULL;
+ ludp->lud_exts = NULL;
+
+ ludp->lud_scheme = LDAP_STRDUP( scheme );
+
+ if ( ludp->lud_scheme == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ /* scan forward for '/' that marks end of hostport and begin. of dn */
+ p = strchr( url, '/' );
+ q = NULL;
+
+ if( p != NULL ) {
+ /* terminate hostport; point to start of dn */
+ *p++ = '\0';
+ } else {
+ /* check for Novell kludge, see below */
+ p = strchr( url, '?' );
+ if ( p ) {
+ *p++ = '\0';
+ q = p;
+ p = NULL;
+ }
+ }
+
+ if ( proto != LDAP_PROTO_IPC ) {
+ /* IPv6 syntax with [ip address]:port */
+ if ( *url == '[' ) {
+ r = strchr( url, ']' );
+ if ( r == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ *r++ = '\0';
+ q = strchr( r, ':' );
+ if ( q && q != r ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ is_v6 = 1;
+ } else {
+ q = strchr( url, ':' );
+ }
+
+ if ( q != NULL ) {
+ char *next;
+
+ *q++ = '\0';
+ ldap_pvt_hex_unescape( q );
+
+ if( *q == '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ ludp->lud_port = strtol( q, &next, 10 );
+ if ( next == q || next[0] != '\0' ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+ /* check for Novell kludge */
+ if ( !p ) {
+ if ( *next != '\0' ) {
+ q = &next[1];
+ } else {
+ q = NULL;
+ }
+ }
+ }
+
+ if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
+ if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
+ ludp->lud_port = LDAPS_PORT;
+ } else {
+ ludp->lud_port = LDAP_PORT;
+ }
+ }
+ }
+
+ ldap_pvt_hex_unescape( url );
+
+ /* If [ip address]:port syntax, url is [ip and we skip the [ */
+ ludp->lud_host = LDAP_STRDUP( url + is_v6 );
+
+ if( ludp->lud_host == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
+ && ludp->lud_host != NULL
+ && *ludp->lud_host == '\0' )
+ {
+ LDAP_FREE( ludp->lud_host );
+ ludp->lud_host = NULL;
+ }
+
+ /*
+ * Kludge. ldap://111.222.333.444:389??cn=abc,o=company
+ *
+ * On early Novell releases, search references/referrals were returned
+ * in this format, i.e., the dn was kind of in the scope position,
+ * but the required slash is missing. The whole thing is illegal syntax,
+ * but we need to account for it. Fortunately it can't be confused with
+ * anything real.
+ */
+ if( (p == NULL) && (q != NULL) && (*q == '?') ) {
+ /* ? immediately followed by question */
+ q++;
+ if( *q != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( q );
+ ludp->lud_dn = LDAP_STRDUP( q );
+
+ } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+
+ } else {
+ check_dn = 0;
+ }
+
+ if ( check_dn && ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+
+ if( p == NULL ) {
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of dn */
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate dn part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse dn part */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_dn = LDAP_STRDUP( p );
+
+ } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
+ ludp->lud_dn = LDAP_STRDUP( "" );
+
+ } else {
+ check_dn = 0;
+ }
+
+ if( check_dn && ludp->lud_dn == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+
+ if( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of attributes */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate attributes part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse attributes */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_attrs = ldap_str2charray( p, "," );
+
+ if( ludp->lud_attrs == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADATTRS;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of scope */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the scope part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the scope */
+ ldap_pvt_hex_unescape( p );
+ ludp->lud_scope = ldap_pvt_str2scope( p );
+
+ if( ludp->lud_scope == -1 ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADSCOPE;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of filter */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* terminate the filter part */
+ *q++ = '\0';
+ }
+
+ if( *p != '\0' ) {
+ /* parse the filter */
+ ldap_pvt_hex_unescape( p );
+
+ if( ! *p ) {
+ /* missing filter */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADFILTER;
+ }
+
+ ludp->lud_filter = LDAP_STRDUP( p );
+
+ if( ludp->lud_filter == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_MEM;
+ }
+ }
+
+ if ( q == NULL ) {
+ /* no more */
+ LDAP_FREE( url );
+ *ludpp = ludp;
+ return LDAP_URL_SUCCESS;
+ }
+
+ /* scan forward for '?' that may marks end of extensions */
+ p = q;
+ q = strchr( p, '?' );
+
+ if( q != NULL ) {
+ /* extra '?' */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADURL;
+ }
+
+ /* parse the extensions */
+ ludp->lud_exts = ldap_str2charray( p, "," );
+
+ if( ludp->lud_exts == NULL ) {
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
+ ldap_pvt_hex_unescape( ludp->lud_exts[i] );
+
+ if( *ludp->lud_exts[i] == '!' ) {
+ /* count the number of critical extensions */
+ ludp->lud_crit_exts++;
+ }
+ }
+
+ if( i == 0 ) {
+ /* must have 1 or more */
+ LDAP_FREE( url );
+ ldap_free_urldesc( ludp );
+ return LDAP_URL_ERR_BADEXTS;
+ }
+
+ /* no more */
+ *ludpp = ludp;
+ LDAP_FREE( url );
+ return LDAP_URL_SUCCESS;
+}
+
+int
+ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
+{
+ return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC );
+}
+
+LDAPURLDesc *
+ldap_url_dup ( LDAPURLDesc *ludp )
+{
+ LDAPURLDesc *dest;
+
+ if ( ludp == NULL ) {
+ return NULL;
+ }
+
+ dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
+ if (dest == NULL)
+ return NULL;
+
+ *dest = *ludp;
+ dest->lud_scheme = NULL;
+ dest->lud_host = NULL;
+ dest->lud_dn = NULL;
+ dest->lud_filter = NULL;
+ dest->lud_attrs = NULL;
+ dest->lud_exts = NULL;
+ dest->lud_next = NULL;
+
+ if ( ludp->lud_scheme != NULL ) {
+ dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
+ if (dest->lud_scheme == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ dest->lud_host = LDAP_STRDUP( ludp->lud_host );
+ if (dest->lud_host == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_dn != NULL ) {
+ dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
+ if (dest->lud_dn == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_filter != NULL ) {
+ dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
+ if (dest->lud_filter == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
+ if (dest->lud_attrs == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
+ if (dest->lud_exts == NULL) {
+ ldap_free_urldesc(dest);
+ return NULL;
+ }
+ }
+
+ return dest;
+}
+
+LDAPURLDesc *
+ldap_url_duplist (LDAPURLDesc *ludlist)
+{
+ LDAPURLDesc *dest, *tail, *ludp, *newludp;
+
+ dest = NULL;
+ tail = NULL;
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ newludp = ldap_url_dup(ludp);
+ if (newludp == NULL) {
+ ldap_free_urllist(dest);
+ return NULL;
+ }
+ if (tail == NULL)
+ dest = newludp;
+ else
+ tail->lud_next = newludp;
+ tail = newludp;
+ }
+ return dest;
+}
+
+static int
+ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
+
+{
+ int i, rc;
+ LDAPURLDesc *ludp;
+ char **urls;
+
+ assert( ludlist != NULL );
+ assert( url != NULL );
+
+ *ludlist = NULL;
+
+ if ( sep == NULL ) {
+ sep = ", ";
+ }
+
+ urls = ldap_str2charray( url, sep );
+ if (urls == NULL)
+ return LDAP_URL_ERR_MEM;
+
+ /* count the URLs... */
+ for (i = 0; urls[i] != NULL; i++) ;
+ /* ...and put them in the "stack" backward */
+ while (--i >= 0) {
+ rc = ldap_url_parse_ext( urls[i], &ludp, flags );
+ if ( rc != 0 ) {
+ ldap_charray_free( urls );
+ ldap_free_urllist( *ludlist );
+ *ludlist = NULL;
+ return rc;
+ }
+ ludp->lud_next = *ludlist;
+ *ludlist = ludp;
+ }
+ ldap_charray_free( urls );
+ return LDAP_URL_SUCCESS;
+}
+
+int
+ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
+{
+ return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC );
+}
+
+int
+ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
+{
+ return ldap_url_parselist_int( ludlist, url, sep, flags );
+}
+
+int
+ldap_url_parsehosts(
+ LDAPURLDesc **ludlist,
+ const char *hosts,
+ int port )
+{
+ int i;
+ LDAPURLDesc *ludp;
+ char **specs, *p;
+
+ assert( ludlist != NULL );
+ assert( hosts != NULL );
+
+ *ludlist = NULL;
+
+ specs = ldap_str2charray(hosts, ", ");
+ if (specs == NULL)
+ return LDAP_NO_MEMORY;
+
+ /* count the URLs... */
+ for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
+
+ /* ...and put them in the "stack" backward */
+ while (--i >= 0) {
+ ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
+ if (ludp == NULL) {
+ ldap_charray_free(specs);
+ ldap_free_urllist(*ludlist);
+ *ludlist = NULL;
+ return LDAP_NO_MEMORY;
+ }
+ ludp->lud_port = port;
+ ludp->lud_host = specs[i];
+ specs[i] = NULL;
+ p = strchr(ludp->lud_host, ':');
+ if (p != NULL) {
+ /* more than one :, IPv6 address */
+ if ( strchr(p+1, ':') != NULL ) {
+ /* allow [address] and [address]:port */
+ if ( *ludp->lud_host == '[' ) {
+ p = LDAP_STRDUP(ludp->lud_host+1);
+ /* copied, make sure we free source later */
+ specs[i] = ludp->lud_host;
+ ludp->lud_host = p;
+ p = strchr( ludp->lud_host, ']' );
+ if ( p == NULL ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ *p++ = '\0';
+ if ( *p != ':' ) {
+ if ( *p != '\0' ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ p = NULL;
+ }
+ } else {
+ p = NULL;
+ }
+ }
+ if (p != NULL) {
+ char *next;
+
+ *p++ = 0;
+ ldap_pvt_hex_unescape(p);
+ ludp->lud_port = strtol( p, &next, 10 );
+ if ( next == p || next[0] != '\0' ) {
+ LDAP_FREE(ludp);
+ ldap_charray_free(specs);
+ return LDAP_PARAM_ERROR;
+ }
+ }
+ }
+ ldap_pvt_hex_unescape(ludp->lud_host);
+ ludp->lud_scheme = LDAP_STRDUP("ldap");
+ ludp->lud_next = *ludlist;
+ *ludlist = ludp;
+ }
+
+ /* this should be an array of NULLs now */
+ /* except entries starting with [ */
+ ldap_charray_free(specs);
+ return LDAP_SUCCESS;
+}
+
+char *
+ldap_url_list2hosts (LDAPURLDesc *ludlist)
+{
+ LDAPURLDesc *ludp;
+ int size;
+ char *s, *p, buf[32]; /* big enough to hold a long decimal # (overkill) */
+
+ if (ludlist == NULL)
+ return NULL;
+
+ /* figure out how big the string is */
+ size = 1; /* nul-term */
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ if ( ludp->lud_host == NULL ) continue;
+ size += strlen(ludp->lud_host) + 1; /* host and space */
+ if (strchr(ludp->lud_host, ':')) /* will add [ ] below */
+ size += 2;
+ if (ludp->lud_port != 0)
+ size += sprintf(buf, ":%d", ludp->lud_port);
+ }
+ s = LDAP_MALLOC(size);
+ if (s == NULL)
+ return NULL;
+
+ p = s;
+ for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
+ if ( ludp->lud_host == NULL ) continue;
+ if (strchr(ludp->lud_host, ':')) {
+ p += sprintf(p, "[%s]", ludp->lud_host);
+ } else {
+ strcpy(p, ludp->lud_host);
+ p += strlen(ludp->lud_host);
+ }
+ if (ludp->lud_port != 0)
+ p += sprintf(p, ":%d", ludp->lud_port);
+ *p++ = ' ';
+ }
+ if (p != s)
+ p--; /* nuke that extra space */
+ *p = '\0';
+ return s;
+}
+
+char *
+ldap_url_list2urls(
+ LDAPURLDesc *ludlist )
+{
+ LDAPURLDesc *ludp;
+ int size, sofar;
+ char *s;
+
+ if ( ludlist == NULL ) {
+ return NULL;
+ }
+
+ /* figure out how big the string is */
+ for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
+ int len = desc2str_len( ludp );
+ if ( len < 0 ) {
+ return NULL;
+ }
+ size += len + 1;
+ }
+
+ s = LDAP_MALLOC( size );
+
+ if ( s == NULL ) {
+ return NULL;
+ }
+
+ for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
+ int len;
+
+ len = desc2str( ludp, &s[sofar], size );
+
+ if ( len < 0 ) {
+ LDAP_FREE( s );
+ return NULL;
+ }
+
+ sofar += len;
+ size -= len;
+
+ s[sofar++] = ' ';
+ size--;
+
+ assert( size >= 0 );
+ }
+
+ s[sofar - 1] = '\0';
+
+ return s;
+}
+
+void
+ldap_free_urllist( LDAPURLDesc *ludlist )
+{
+ LDAPURLDesc *ludp, *next;
+
+ for (ludp = ludlist; ludp != NULL; ludp = next) {
+ next = ludp->lud_next;
+ ldap_free_urldesc(ludp);
+ }
+}
+
+void
+ldap_free_urldesc( LDAPURLDesc *ludp )
+{
+ if ( ludp == NULL ) {
+ return;
+ }
+
+ if ( ludp->lud_scheme != NULL ) {
+ LDAP_FREE( ludp->lud_scheme );
+ }
+
+ if ( ludp->lud_host != NULL ) {
+ LDAP_FREE( ludp->lud_host );
+ }
+
+ if ( ludp->lud_dn != NULL ) {
+ LDAP_FREE( ludp->lud_dn );
+ }
+
+ if ( ludp->lud_filter != NULL ) {
+ LDAP_FREE( ludp->lud_filter);
+ }
+
+ if ( ludp->lud_attrs != NULL ) {
+ LDAP_VFREE( ludp->lud_attrs );
+ }
+
+ if ( ludp->lud_exts != NULL ) {
+ LDAP_VFREE( ludp->lud_exts );
+ }
+
+ LDAP_FREE( ludp );
+}
+
+static int
+ldap_int_is_hexpair( char *s )
+{
+ int i;
+
+ for ( i = 0; i < 2; i++ ) {
+ if ( s[i] >= '0' && s[i] <= '9' ) {
+ continue;
+ }
+
+ if ( s[i] >= 'A' && s[i] <= 'F' ) {
+ continue;
+ }
+
+ if ( s[i] >= 'a' && s[i] <= 'f' ) {
+ continue;
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+ldap_int_unhex( int c )
+{
+ return( c >= '0' && c <= '9' ? c - '0'
+ : c >= 'A' && c <= 'F' ? c - 'A' + 10
+ : c - 'a' + 10 );
+}
+
+void
+ldap_pvt_hex_unescape( char *s )
+{
+ /*
+ * Remove URL hex escapes from s... done in place. The basic concept for
+ * this routine is borrowed from the WWW library HTUnEscape() routine.
+ */
+ char *p,
+ *save_s = s;
+
+ for ( p = s; *s != '\0'; ++s ) {
+ if ( *s == '%' ) {
+ /*
+ * FIXME: what if '%' is followed
+ * by non-hexpair chars?
+ */
+ if ( !ldap_int_is_hexpair( s + 1 ) ) {
+ p = save_s;
+ break;
+ }
+
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p = ldap_int_unhex( *s ) << 4;
+ if ( *++s == '\0' ) {
+ break;
+ }
+ *p++ += ldap_int_unhex( *s );
+ } else {
+ *p++ = *s;
+ }
+ }
+
+ *p = '\0';
+}
+
diff --git a/libraries/libldap/urltest.c b/libraries/libldap/urltest.c
new file mode 100644
index 0000000..1abfa57
--- /dev/null
+++ b/libraries/libldap/urltest.c
@@ -0,0 +1,128 @@
+/* urltest.c -- OpenLDAP URL API Test Program */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This program was initially developed by Pierangelo Masarati
+ * <ando@OpenLDAP.org> for inclusion in OpenLDAP Software.
+ */
+
+/*
+ * This program is designed to test the ldap_url_* functions
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+
+#include "ldap-int.h"
+
+#include "ldap_defaults.h"
+
+int
+main(int argc, char *argv[])
+{
+ const char *url,
+ *scope = NULL;
+ LDAPURLDesc *lud;
+ enum {
+ IS_LDAP = 0,
+ IS_LDAPS,
+ IS_LDAPI
+ } type = IS_LDAP;
+ int rc;
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "usage: urltest <url>\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ url = argv[ 1 ];
+
+ if ( ldap_is_ldaps_url( url ) ) {
+ fprintf( stdout, "LDAPS url\n" );
+ type = IS_LDAPS;
+
+ } else if ( ldap_is_ldapi_url( url ) ) {
+ fprintf( stdout, "LDAPI url\n" );
+ type = IS_LDAPI;
+
+ } else if ( ldap_is_ldap_url( url ) ) {
+ fprintf( stdout, "generic LDAP url\n" );
+
+ } else {
+ fprintf( stderr, "Need a valid LDAP url\n" );
+ exit( EXIT_FAILURE );
+ }
+
+ rc = ldap_url_parse( url, &lud );
+ if ( rc != LDAP_URL_SUCCESS ) {
+ fprintf( stderr, "ldap_url_parse(%s) failed (%d)\n", url, rc );
+ exit( EXIT_FAILURE );
+ }
+
+ fprintf( stdout, "PROTO: %s\n", lud->lud_scheme );
+ switch ( type ) {
+ case IS_LDAPI:
+ fprintf( stdout, "PATH: %s\n", lud->lud_host );
+ break;
+
+ default:
+ fprintf( stdout, "HOST: %s\n", lud->lud_host );
+ if ( lud->lud_port != 0 ) {
+ fprintf( stdout, "PORT: %d\n", lud->lud_port );
+ }
+ }
+
+ if ( lud->lud_dn && lud->lud_dn[ 0 ] ) {
+ fprintf( stdout, "DN: %s\n", lud->lud_dn );
+ }
+
+ if ( lud->lud_attrs ) {
+ int i;
+
+ fprintf( stdout, "ATTRS:\n" );
+ for ( i = 0; lud->lud_attrs[ i ]; i++ ) {
+ fprintf( stdout, "\t%s\n", lud->lud_attrs[ i ] );
+ }
+ }
+
+ scope = ldap_pvt_scope2str( lud->lud_scope );
+ if ( scope ) {
+ fprintf( stdout, "SCOPE: %s\n", scope );
+ }
+
+ if ( lud->lud_filter ) {
+ fprintf( stdout, "FILTER: %s\n", lud->lud_filter );
+ }
+
+ if ( lud->lud_exts ) {
+ int i;
+
+ fprintf( stdout, "EXTS:\n" );
+ for ( i = 0; lud->lud_exts[ i ]; i++ ) {
+ fprintf( stdout, "\t%s\n", lud->lud_exts[ i ] );
+ }
+ }
+
+ fprintf( stdout, "URL: %s\n", ldap_url_desc2str( lud ));
+
+ return EXIT_SUCCESS;
+}
diff --git a/libraries/libldap/utf-8-conv.c b/libraries/libldap/utf-8-conv.c
new file mode 100644
index 0000000..eb55509
--- /dev/null
+++ b/libraries/libldap/utf-8-conv.c
@@ -0,0 +1,485 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+/*
+ * UTF-8 Conversion Routines
+ *
+ * These routines convert between Wide Character and UTF-8,
+ * or between MultiByte and UTF-8 encodings.
+ *
+ * Both single character and string versions of the functions are provided.
+ * All functions return -1 if the character or string cannot be converted.
+ */
+
+#include "portable.h"
+
+#if SIZEOF_WCHAR_T >= 4
+/* These routines assume ( sizeof(wchar_t) >= 4 ) */
+
+#include <stdio.h>
+#include <ac/stdlib.h> /* For wctomb, wcstombs, mbtowc, mbstowcs */
+#include <ac/string.h>
+#include <ac/time.h> /* for time_t */
+
+#include "ldap-int.h"
+
+#include <ldap_utf8.h>
+
+static unsigned char mask[] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+
+/*-----------------------------------------------------------------------------
+ UTF-8 Format Summary
+
+ASCII chars 7 bits
+ 0xxxxxxx
+
+2-character UTF-8 sequence: 11 bits
+ 110xxxxx 10xxxxxx
+
+3-character UTF-8 16 bits
+ 1110xxxx 10xxxxxx 10xxxxxx
+
+4-char UTF-8 21 bits
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+5-char UTF-8 26 bits
+ 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+6-char UTF-8 31 bits
+ 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+
+Unicode address space (0 - 0x10FFFF) 21 bits
+ISO-10646 address space (0 - 0x7FFFFFFF) 31 bits
+
+Note: This code does not prevent UTF-8 sequences which are longer than
+ necessary from being decoded.
+*/
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 character to a wide char.
+ Return the length of the UTF-8 input character in bytes.
+*/
+int
+ldap_x_utf8_to_wc ( wchar_t *wchar, const char *utf8char )
+{
+ int utflen, i;
+ wchar_t ch;
+
+ if (utf8char == NULL) return -1;
+
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = LDAP_UTF8_CHARLEN2(utf8char, utflen);
+
+ if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1;
+
+ /* First byte minus length tag */
+ ch = (wchar_t)(utf8char[0] & mask[utflen]);
+
+ for(i=1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8char[i] & 0xc0) != 0x80) return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (wchar_t)(utf8char[i] & 0x3f);
+ }
+
+ if (wchar) *wchar = ch;
+
+ return utflen;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 string to a wide char string.
+ No more than 'count' wide chars will be written to the output buffer.
+ Return the size of the converted string in wide chars, excl null terminator.
+*/
+int
+ldap_x_utf8s_to_wcs ( wchar_t *wcstr, const char *utf8str, size_t count )
+{
+ size_t wclen = 0;
+ int utflen, i;
+ wchar_t ch;
+
+
+ /* If input ptr is NULL or empty... */
+ if (utf8str == NULL || !*utf8str) {
+ if ( wcstr )
+ *wcstr = 0;
+ return 0;
+ }
+
+ /* Examine next UTF-8 character. If output buffer is NULL, ignore count */
+ while ( *utf8str && (wcstr==NULL || wclen<count) ) {
+ /* Get UTF-8 sequence length from 1st byte */
+ utflen = LDAP_UTF8_CHARLEN2(utf8str, utflen);
+
+ if( utflen==0 || utflen > (int)LDAP_MAX_UTF8_LEN ) return -1;
+
+ /* First byte minus length tag */
+ ch = (wchar_t)(utf8str[0] & mask[utflen]);
+
+ for(i=1; i < utflen; i++) {
+ /* Subsequent bytes must start with 10 */
+ if ((utf8str[i] & 0xc0) != 0x80) return -1;
+
+ ch <<= 6; /* 6 bits of data in each subsequent byte */
+ ch |= (wchar_t)(utf8str[i] & 0x3f);
+ }
+
+ if (wcstr) wcstr[wclen] = ch;
+
+ utf8str += utflen; /* Move to next UTF-8 character */
+ wclen++; /* Count number of wide chars stored/required */
+ }
+
+ /* Add null terminator if there's room in the buffer. */
+ if (wcstr && wclen < count) wcstr[wclen] = 0;
+
+ return wclen;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert one wide char to a UTF-8 character.
+ Return the length of the converted UTF-8 character in bytes.
+ No more than 'count' bytes will be written to the output buffer.
+*/
+int
+ldap_x_wc_to_utf8 ( char *utf8char, wchar_t wchar, size_t count )
+{
+ int len=0;
+
+ if (utf8char == NULL) /* Just determine the required UTF-8 char length. */
+ { /* Ignore count */
+ if( wchar < 0 )
+ return -1;
+ if( wchar < 0x80 )
+ return 1;
+ if( wchar < 0x800 )
+ return 2;
+ if( wchar < 0x10000 )
+ return 3;
+ if( wchar < 0x200000 )
+ return 4;
+ if( wchar < 0x4000000 )
+ return 5;
+#if SIZEOF_WCHAR_T > 4
+ /* UL is not strictly needed by ANSI C */
+ if( wchar < (wchar_t)0x80000000UL )
+#endif /* SIZEOF_WCHAR_T > 4 */
+ return 6;
+ return -1;
+ }
+
+
+ if ( wchar < 0 ) { /* Invalid wide character */
+ len = -1;
+
+ } else if( wchar < 0x80 ) {
+ if (count >= 1) {
+ utf8char[len++] = (char)wchar;
+ }
+
+ } else if( wchar < 0x800 ) {
+ if (count >=2) {
+ utf8char[len++] = 0xc0 | ( wchar >> 6 );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x10000 ) {
+ if (count >= 3) {
+ utf8char[len++] = 0xe0 | ( wchar >> 12 );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x200000 ) {
+ if (count >= 4) {
+ utf8char[len++] = 0xf0 | ( wchar >> 18 );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else if( wchar < 0x4000000 ) {
+ if (count >= 5) {
+ utf8char[len++] = 0xf8 | ( wchar >> 24 );
+ utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+ } else
+#if SIZEOF_WCHAR_T > 4
+ /* UL is not strictly needed by ANSI C */
+ if( wchar < (wchar_t)0x80000000UL )
+#endif /* SIZEOF_WCHAR_T > 4 */
+ {
+ if (count >= 6) {
+ utf8char[len++] = 0xfc | ( wchar >> 30 );
+ utf8char[len++] = 0x80 | ( (wchar >> 24) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 18) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 12) & 0x3f );
+ utf8char[len++] = 0x80 | ( (wchar >> 6) & 0x3f );
+ utf8char[len++] = 0x80 | ( wchar & 0x3f );
+ }
+
+#if SIZEOF_WCHAR_T > 4
+ } else {
+ len = -1;
+#endif /* SIZEOF_WCHAR_T > 4 */
+ }
+
+ return len;
+
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert a wide char string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the # of bytes written to the output buffer, excl null terminator.
+*/
+int
+ldap_x_wcs_to_utf8s ( char *utf8str, const wchar_t *wcstr, size_t count )
+{
+ int len = 0;
+ int n;
+ char *p = utf8str;
+ wchar_t empty = 0; /* To avoid use of L"" construct */
+
+ if (wcstr == NULL) /* Treat input ptr NULL as an empty string */
+ wcstr = &empty;
+
+ if (utf8str == NULL) /* Just compute size of output, excl null */
+ {
+ while (*wcstr)
+ {
+ /* Get UTF-8 size of next wide char */
+ n = ldap_x_wc_to_utf8( NULL, *wcstr++, LDAP_MAX_UTF8_LEN);
+ if (n == -1)
+ return -1;
+ len += n;
+ }
+
+ return len;
+ }
+
+
+ /* Do the actual conversion. */
+
+ n = 1; /* In case of empty wcstr */
+ while (*wcstr)
+ {
+ n = ldap_x_wc_to_utf8( p, *wcstr++, count);
+
+ if (n <= 0) /* If encoding error (-1) or won't fit (0), quit */
+ break;
+
+ p += n;
+ count -= n; /* Space left in output buffer */
+ }
+
+ /* If not enough room for last character, pad remainder with null
+ so that return value = original count, indicating buffer full. */
+ if (n == 0)
+ {
+ while (count--)
+ *p++ = 0;
+ }
+
+ /* Add a null terminator if there's room. */
+ else if (count)
+ *p = 0;
+
+ if (n == -1) /* Conversion encountered invalid wide char. */
+ return -1;
+
+ /* Return the number of bytes written to output buffer, excl null. */
+ return (p - utf8str);
+}
+
+#ifdef ANDROID
+int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); }
+int mbtowc(wchar_t *pwc, const char *s, size_t n) { return mbrtowc(pwc, s, n, NULL); }
+#endif
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 character to a MultiByte character.
+ Return the size of the converted character in bytes.
+*/
+int
+ldap_x_utf8_to_mb ( char *mbchar, const char *utf8char,
+ int (*f_wctomb)(char *mbchar, wchar_t wchar) )
+{
+ wchar_t wchar;
+ int n;
+ char tmp[6]; /* Large enough for biggest multibyte char */
+
+ if (f_wctomb == NULL) /* If no conversion function was given... */
+ f_wctomb = wctomb; /* use the local ANSI C function */
+
+ /* First convert UTF-8 char to a wide char */
+ n = ldap_x_utf8_to_wc( &wchar, utf8char);
+
+ if (n == -1)
+ return -1; /* Invalid UTF-8 character */
+
+ if (mbchar == NULL)
+ n = f_wctomb( tmp, wchar );
+ else
+ n = f_wctomb( mbchar, wchar);
+
+ return n;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a UTF-8 string to a MultiByte string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the size of the converted string in bytes, excl null terminator.
+*/
+int
+ldap_x_utf8s_to_mbs ( char *mbstr, const char *utf8str, size_t count,
+ size_t (*f_wcstombs)(char *mbstr, const wchar_t *wcstr, size_t count) )
+{
+ wchar_t *wcs;
+ size_t wcsize;
+ int n;
+
+ if (f_wcstombs == NULL) /* If no conversion function was given... */
+ f_wcstombs = wcstombs; /* use the local ANSI C function */
+
+ if (utf8str == NULL || *utf8str == 0) /* NULL or empty input string */
+ {
+ if (mbstr)
+ *mbstr = 0;
+ return 0;
+ }
+
+/* Allocate memory for the maximum size wchar string that we could get. */
+ wcsize = strlen(utf8str) + 1;
+ wcs = (wchar_t *)LDAP_MALLOC(wcsize * sizeof(wchar_t));
+ if (wcs == NULL)
+ return -1; /* Memory allocation failure. */
+
+ /* First convert the UTF-8 string to a wide char string */
+ n = ldap_x_utf8s_to_wcs( wcs, utf8str, wcsize);
+
+ /* Then convert wide char string to multi-byte string */
+ if (n != -1)
+ {
+ n = f_wcstombs(mbstr, wcs, count);
+ }
+
+ LDAP_FREE(wcs);
+
+ return n;
+}
+
+/*-----------------------------------------------------------------------------
+ Convert a MultiByte character to a UTF-8 character.
+ 'mbsize' indicates the number of bytes of 'mbchar' to check.
+ Returns the number of bytes written to the output character.
+*/
+int
+ldap_x_mb_to_utf8 ( char *utf8char, const char *mbchar, size_t mbsize,
+ int (*f_mbtowc)(wchar_t *wchar, const char *mbchar, size_t count) )
+{
+ wchar_t wchar;
+ int n;
+
+ if (f_mbtowc == NULL) /* If no conversion function was given... */
+ f_mbtowc = mbtowc; /* use the local ANSI C function */
+
+ if (mbsize == 0) /* 0 is not valid. */
+ return -1;
+
+ if (mbchar == NULL || *mbchar == 0)
+ {
+ if (utf8char)
+ *utf8char = 0;
+ return 1;
+ }
+
+ /* First convert the MB char to a Wide Char */
+ n = f_mbtowc( &wchar, mbchar, mbsize);
+
+ if (n == -1)
+ return -1;
+
+ /* Convert the Wide Char to a UTF-8 character. */
+ n = ldap_x_wc_to_utf8( utf8char, wchar, LDAP_MAX_UTF8_LEN);
+
+ return n;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Convert a MultiByte string to a UTF-8 string.
+ No more than 'count' bytes will be written to the output buffer.
+ Return the size of the converted string in bytes, excl null terminator.
+*/
+int
+ldap_x_mbs_to_utf8s ( char *utf8str, const char *mbstr, size_t count,
+ size_t (*f_mbstowcs)(wchar_t *wcstr, const char *mbstr, size_t count) )
+{
+ wchar_t *wcs;
+ int n;
+ size_t wcsize;
+
+ if (mbstr == NULL) /* Treat NULL input string as an empty string */
+ mbstr = "";
+
+ if (f_mbstowcs == NULL) /* If no conversion function was given... */
+ f_mbstowcs = mbstowcs; /* use the local ANSI C function */
+
+ /* Allocate memory for the maximum size wchar string that we could get. */
+ wcsize = strlen(mbstr) + 1;
+ wcs = (wchar_t *)LDAP_MALLOC( wcsize * sizeof(wchar_t) );
+ if (wcs == NULL)
+ return -1;
+
+ /* First convert multi-byte string to a wide char string */
+ n = f_mbstowcs(wcs, mbstr, wcsize);
+
+ /* Convert wide char string to UTF-8 string */
+ if (n != -1)
+ {
+ n = ldap_x_wcs_to_utf8s( utf8str, wcs, count);
+ }
+
+ LDAP_FREE(wcs);
+
+ return n;
+}
+
+#endif /* SIZEOF_WCHAR_T >= 4 */
diff --git a/libraries/libldap/utf-8.c b/libraries/libldap/utf-8.c
new file mode 100644
index 0000000..7a3431a
--- /dev/null
+++ b/libraries/libldap/utf-8.c
@@ -0,0 +1,562 @@
+/* utf-8.c -- Basic UTF-8 routines */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Basic UTF-8 routines
+ *
+ * These routines are "dumb". Though they understand UTF-8,
+ * they don't grok Unicode. That is, they can push bits,
+ * but don't have a clue what the bits represent. That's
+ * good enough for use with the LDAP Client SDK.
+ *
+ * These routines are not optimized.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap_utf8.h"
+
+#include "ldap-int.h"
+#include "ldap_defaults.h"
+
+/*
+ * return the number of bytes required to hold the
+ * NULL-terminated UTF-8 string NOT INCLUDING the
+ * termination.
+ */
+ber_len_t ldap_utf8_bytes( const char * p )
+{
+ ber_len_t bytes;
+
+ for( bytes=0; p[bytes]; bytes++ ) {
+ /* EMPTY */ ;
+ }
+
+ return bytes;
+}
+
+ber_len_t ldap_utf8_chars( const char * p )
+{
+ /* could be optimized and could check for invalid sequences */
+ ber_len_t chars=0;
+
+ for( ; *p ; LDAP_UTF8_INCR(p) ) {
+ chars++;
+ }
+
+ return chars;
+}
+
+/* return offset to next character */
+int ldap_utf8_offset( const char * p )
+{
+ return LDAP_UTF8_NEXT(p) - p;
+}
+
+/*
+ * Returns length indicated by first byte.
+ */
+const char ldap_utf8_lentab[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 0, 0 };
+
+int ldap_utf8_charlen( const char * p )
+{
+ if (!(*p & 0x80))
+ return 1;
+
+ return ldap_utf8_lentab[*(const unsigned char *)p ^ 0x80];
+}
+
+/*
+ * Make sure the UTF-8 char used the shortest possible encoding
+ * returns charlen if valid, 0 if not.
+ *
+ * Here are the valid UTF-8 encodings, taken from RFC 2279 page 4.
+ * The table is slightly modified from that of the RFC.
+ *
+ * UCS-4 range (hex) UTF-8 sequence (binary)
+ * 0000 0000-0000 007F 0.......
+ * 0000 0080-0000 07FF 110++++. 10......
+ * 0000 0800-0000 FFFF 1110++++ 10+..... 10......
+ * 0001 0000-001F FFFF 11110+++ 10++.... 10...... 10......
+ * 0020 0000-03FF FFFF 111110++ 10+++... 10...... 10...... 10......
+ * 0400 0000-7FFF FFFF 1111110+ 10++++.. 10...... 10...... 10...... 10......
+ *
+ * The '.' bits are "don't cares". When validating a UTF-8 sequence,
+ * at least one of the '+' bits must be set, otherwise the character
+ * should have been encoded in fewer octets. Note that in the two-octet
+ * case, only the first octet needs to be validated, and this is done
+ * in the ldap_utf8_lentab[] above.
+ */
+
+/* mask of required bits in second octet */
+#undef c
+#define c const char
+c ldap_utf8_mintab[] = {
+ (c)0x20, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x30, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80, (c)0x80,
+ (c)0x38, (c)0x80, (c)0x80, (c)0x80, (c)0x3c, (c)0x80, (c)0x00, (c)0x00 };
+#undef c
+
+int ldap_utf8_charlen2( const char * p )
+{
+ int i = LDAP_UTF8_CHARLEN( p );
+
+ if ( i > 2 ) {
+ if ( !( ldap_utf8_mintab[*p & 0x1f] & p[1] ) )
+ i = 0;
+ }
+ return i;
+}
+
+/* conv UTF-8 to UCS-4, useful for comparisons */
+ldap_ucs4_t ldap_x_utf8_to_ucs4( const char * p )
+{
+ const unsigned char *c = (const unsigned char *) p;
+ ldap_ucs4_t ch;
+ int len, i;
+ static unsigned char mask[] = {
+ 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
+
+ len = LDAP_UTF8_CHARLEN2(p, len);
+
+ if( len == 0 ) return LDAP_UCS4_INVALID;
+
+ ch = c[0] & mask[len];
+
+ for(i=1; i < len; i++) {
+ if ((c[i] & 0xc0) != 0x80) {
+ return LDAP_UCS4_INVALID;
+ }
+
+ ch <<= 6;
+ ch |= c[i] & 0x3f;
+ }
+
+ return ch;
+}
+
+/* conv UCS-4 to UTF-8, not used */
+int ldap_x_ucs4_to_utf8( ldap_ucs4_t c, char *buf )
+{
+ int len=0;
+ unsigned char* p = (unsigned char *) buf;
+
+ /* not a valid Unicode character */
+ if ( c < 0 ) return 0;
+
+ /* Just return length, don't convert */
+ if(buf == NULL) {
+ if( c < 0x80 ) return 1;
+ else if( c < 0x800 ) return 2;
+ else if( c < 0x10000 ) return 3;
+ else if( c < 0x200000 ) return 4;
+ else if( c < 0x4000000 ) return 5;
+ else return 6;
+ }
+
+ if( c < 0x80 ) {
+ p[len++] = c;
+
+ } else if( c < 0x800 ) {
+ p[len++] = 0xc0 | ( c >> 6 );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x10000 ) {
+ p[len++] = 0xe0 | ( c >> 12 );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x200000 ) {
+ p[len++] = 0xf0 | ( c >> 18 );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else if( c < 0x4000000 ) {
+ p[len++] = 0xf8 | ( c >> 24 );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+
+ } else /* if( c < 0x80000000 ) */ {
+ p[len++] = 0xfc | ( c >> 30 );
+ p[len++] = 0x80 | ( (c >> 24) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 18) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 12) & 0x3f );
+ p[len++] = 0x80 | ( (c >> 6) & 0x3f );
+ p[len++] = 0x80 | ( c & 0x3f );
+ }
+
+ return len;
+}
+
+#define LDAP_UCS_UTF8LEN(c) \
+ c < 0 ? 0 : (c < 0x80 ? 1 : (c < 0x800 ? 2 : (c < 0x10000 ? 3 : \
+ (c < 0x200000 ? 4 : (c < 0x4000000 ? 5 : 6)))))
+
+/* Convert a string to UTF-8 format. The input string is expected to
+ * have characters of 1, 2, or 4 octets (in network byte order)
+ * corresponding to the ASN.1 T61STRING, BMPSTRING, and UNIVERSALSTRING
+ * types respectively. (Here T61STRING just means that there is one
+ * octet per character and characters may use the high bit of the octet.
+ * The characters are assumed to use ISO mappings, no provision is made
+ * for converting from T.61 coding rules to Unicode.)
+ */
+
+int
+ldap_ucs_to_utf8s( struct berval *ucs, int csize, struct berval *utf8s )
+{
+ unsigned char *in, *end;
+ char *ptr;
+ ldap_ucs4_t u;
+ int i, l = 0;
+
+ utf8s->bv_val = NULL;
+ utf8s->bv_len = 0;
+
+ in = (unsigned char *)ucs->bv_val;
+
+ /* Make sure we stop at an even multiple of csize */
+ end = in + ( ucs->bv_len & ~(csize-1) );
+
+ for (; in < end; ) {
+ u = *in++;
+ if (csize > 1) {
+ u <<= 8;
+ u |= *in++;
+ }
+ if (csize > 2) {
+ u <<= 8;
+ u |= *in++;
+ u <<= 8;
+ u |= *in++;
+ }
+ i = LDAP_UCS_UTF8LEN(u);
+ if (i == 0)
+ return LDAP_INVALID_SYNTAX;
+ l += i;
+ }
+
+ utf8s->bv_val = LDAP_MALLOC( l+1 );
+ if (utf8s->bv_val == NULL)
+ return LDAP_NO_MEMORY;
+ utf8s->bv_len = l;
+
+ ptr = utf8s->bv_val;
+ for (in = (unsigned char *)ucs->bv_val; in < end; ) {
+ u = *in++;
+ if (csize > 1) {
+ u <<= 8;
+ u |= *in++;
+ }
+ if (csize > 2) {
+ u <<= 8;
+ u |= *in++;
+ u <<= 8;
+ u |= *in++;
+ }
+ ptr += ldap_x_ucs4_to_utf8(u, ptr);
+ }
+ *ptr = '\0';
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Advance to the next UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char* ldap_utf8_next( const char * p )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ if( LDAP_UTF8_ISASCII(u) ) {
+ return (char *) &p[1];
+ }
+
+ for( i=1; i<6; i++ ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Advance to the previous UTF-8 character
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+char* ldap_utf8_prev( const char * p )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) p;
+
+ for( i=-1; i>-6 ; i-- ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return (char *) &p[i];
+ }
+ }
+
+ return (char *) &p[i];
+}
+
+/*
+ * Copy one UTF-8 character from src to dst returning
+ * number of bytes copied.
+ *
+ * Ignores length of multibyte character, instead rely on
+ * continuation markers to find start of next character.
+ * This allows for "resyncing" of when invalid characters
+ * are provided provided the start of the next character
+ * is appears within the 6 bytes examined.
+ */
+int ldap_utf8_copy( char* dst, const char *src )
+{
+ int i;
+ const unsigned char *u = (const unsigned char *) src;
+
+ dst[0] = src[0];
+
+ if( LDAP_UTF8_ISASCII(u) ) {
+ return 1;
+ }
+
+ for( i=1; i<6; i++ ) {
+ if ( ( u[i] & 0xc0 ) != 0x80 ) {
+ return i;
+ }
+ dst[i] = src[i];
+ }
+
+ return i;
+}
+
+#ifndef UTF8_ALPHA_CTYPE
+/*
+ * UTF-8 ctype routines
+ * Only deals with characters < 0x80 (ie: US-ASCII)
+ */
+
+int ldap_utf8_isascii( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+ return LDAP_ASCII(c);
+}
+
+int ldap_utf8_isdigit( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_DIGIT( c );
+}
+
+int ldap_utf8_isxdigit( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_HEX(c);
+}
+
+int ldap_utf8_isspace( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\v':
+ case '\f':
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * These are not needed by the C SDK and are
+ * not "good enough" for general use.
+ */
+int ldap_utf8_isalpha( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_ALPHA(c);
+}
+
+int ldap_utf8_isalnum( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_ALNUM(c);
+}
+
+int ldap_utf8_islower( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_LOWER(c);
+}
+
+int ldap_utf8_isupper( const char * p )
+{
+ unsigned c = * (const unsigned char *) p;
+
+ if(!LDAP_ASCII(c)) return 0;
+
+ return LDAP_UPPER(c);
+}
+#endif
+
+
+/*
+ * UTF-8 string routines
+ */
+
+/* like strchr() */
+char * (ldap_utf8_strchr)( const char *str, const char *chr )
+{
+ for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) {
+ if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( chr ) ) {
+ return (char *) str;
+ }
+ }
+
+ return NULL;
+}
+
+/* like strcspn() but returns number of bytes, not characters */
+ber_len_t (ldap_utf8_strcspn)( const char *str, const char *set )
+{
+ const char *cstr;
+ const char *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) {
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ return cstr - str;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strspn() but returns number of bytes, not characters */
+ber_len_t (ldap_utf8_strspn)( const char *str, const char *set )
+{
+ const char *cstr;
+ const char *cset;
+
+ for( cstr = str; *cstr != '\0'; LDAP_UTF8_INCR(cstr) ) {
+ for( cset = set; ; LDAP_UTF8_INCR(cset) ) {
+ if( *cset == '\0' ) {
+ return cstr - str;
+ }
+
+ if( ldap_x_utf8_to_ucs4( cstr ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ break;
+ }
+ }
+ }
+
+ return cstr - str;
+}
+
+/* like strpbrk(), replaces strchr() as well */
+char *(ldap_utf8_strpbrk)( const char *str, const char *set )
+{
+ for( ; *str != '\0'; LDAP_UTF8_INCR(str) ) {
+ const char *cset;
+
+ for( cset = set; *cset != '\0'; LDAP_UTF8_INCR(cset) ) {
+ if( ldap_x_utf8_to_ucs4( str ) == ldap_x_utf8_to_ucs4( cset ) ) {
+ return (char *) str;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* like strtok_r(), not strtok() */
+char *(ldap_utf8_strtok)(char *str, const char *sep, char **last)
+{
+ char *begin;
+ char *end;
+
+ if( last == NULL ) return NULL;
+
+ begin = str ? str : *last;
+
+ begin += ldap_utf8_strspn( begin, sep );
+
+ if( *begin == '\0' ) {
+ *last = NULL;
+ return NULL;
+ }
+
+ end = &begin[ ldap_utf8_strcspn( begin, sep ) ];
+
+ if( *end != '\0' ) {
+ char *next = LDAP_UTF8_NEXT( end );
+ *end = '\0';
+ end = next;
+ }
+
+ *last = end;
+ return begin;
+}
diff --git a/libraries/libldap/util-int.c b/libraries/libldap/util-int.c
new file mode 100644
index 0000000..d4c0dfa
--- /dev/null
+++ b/libraries/libldap/util-int.c
@@ -0,0 +1,923 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * Portions Copyright 1998 A. Hartgers.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Bart Hartgers for inclusion in
+ * OpenLDAP Software.
+ */
+
+/*
+ * util-int.c Various functions to replace missing threadsafe ones.
+ * Without the real *_r funcs, things will
+ * work, but might not be threadsafe.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include "ldap-int.h"
+
+#ifndef h_errno
+/* newer systems declare this in <netdb.h> for you, older ones don't.
+ * harmless to declare it again (unless defined by a macro).
+ */
+extern int h_errno;
+#endif
+
+#ifdef HAVE_HSTRERROR
+# define HSTRERROR(e) hstrerror(e)
+#else
+# define HSTRERROR(e) hp_strerror(e)
+#endif
+
+#ifndef LDAP_R_COMPILE
+# undef HAVE_REENTRANT_FUNCTIONS
+# undef HAVE_CTIME_R
+# undef HAVE_GETHOSTBYNAME_R
+# undef HAVE_GETHOSTBYADDR_R
+
+#else
+# include <ldap_pvt_thread.h>
+ ldap_pvt_thread_mutex_t ldap_int_resolv_mutex;
+ ldap_pvt_thread_mutex_t ldap_int_hostname_mutex;
+ static ldap_pvt_thread_mutex_t ldap_int_gettime_mutex;
+
+# if (defined( HAVE_CTIME_R ) || defined( HAVE_REENTRANT_FUNCTIONS)) \
+ && defined( CTIME_R_NARGS )
+# define USE_CTIME_R
+# else
+ static ldap_pvt_thread_mutex_t ldap_int_ctime_mutex;
+# endif
+
+/* USE_GMTIME_R and USE_LOCALTIME_R defined in ldap_pvt.h */
+
+#ifdef LDAP_DEVEL
+ /* to be released with 2.5 */
+#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R )
+ /* we use the same mutex for gmtime(3) and localtime(3)
+ * because implementations may use the same buffer
+ * for both functions */
+ static ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex;
+#endif
+#else /* ! LDAP_DEVEL */
+ ldap_pvt_thread_mutex_t ldap_int_gmtime_mutex;
+#endif /* ! LDAP_DEVEL */
+
+# if defined(HAVE_GETHOSTBYNAME_R) && \
+ (GETHOSTBYNAME_R_NARGS < 5) || (6 < GETHOSTBYNAME_R_NARGS)
+ /* Don't know how to handle this version, pretend it's not there */
+# undef HAVE_GETHOSTBYNAME_R
+# endif
+# if defined(HAVE_GETHOSTBYADDR_R) && \
+ (GETHOSTBYADDR_R_NARGS < 7) || (8 < GETHOSTBYADDR_R_NARGS)
+ /* Don't know how to handle this version, pretend it's not there */
+# undef HAVE_GETHOSTBYADDR_R
+# endif
+#endif /* LDAP_R_COMPILE */
+
+char *ldap_pvt_ctime( const time_t *tp, char *buf )
+{
+#ifdef USE_CTIME_R
+# if (CTIME_R_NARGS > 3) || (CTIME_R_NARGS < 2)
+# error "CTIME_R_NARGS should be 2 or 3"
+# elif CTIME_R_NARGS > 2 && defined(CTIME_R_RETURNS_INT)
+ return( ctime_r(tp,buf,26) < 0 ? 0 : buf );
+# elif CTIME_R_NARGS > 2
+ return ctime_r(tp,buf,26);
+# else
+ return ctime_r(tp,buf);
+# endif
+
+#else
+
+ LDAP_MUTEX_LOCK( &ldap_int_ctime_mutex );
+ AC_MEMCPY( buf, ctime(tp), 26 );
+ LDAP_MUTEX_UNLOCK( &ldap_int_ctime_mutex );
+
+ return buf;
+#endif
+}
+
+#if !defined( USE_GMTIME_R ) || !defined( USE_LOCALTIME_R )
+int
+ldap_pvt_gmtime_lock( void )
+{
+# ifndef LDAP_R_COMPILE
+ return 0;
+# else /* LDAP_R_COMPILE */
+ return ldap_pvt_thread_mutex_lock( &ldap_int_gmtime_mutex );
+# endif /* LDAP_R_COMPILE */
+}
+
+int
+ldap_pvt_gmtime_unlock( void )
+{
+# ifndef LDAP_R_COMPILE
+ return 0;
+# else /* LDAP_R_COMPILE */
+ return ldap_pvt_thread_mutex_unlock( &ldap_int_gmtime_mutex );
+# endif /* LDAP_R_COMPILE */
+}
+#endif /* !USE_GMTIME_R || !USE_LOCALTIME_R */
+
+#ifndef USE_GMTIME_R
+struct tm *
+ldap_pvt_gmtime( const time_t *timep, struct tm *result )
+{
+ struct tm *tm_ptr;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex );
+ tm_ptr = gmtime( timep );
+ if ( tm_ptr == NULL ) {
+ result = NULL;
+
+ } else {
+ *result = *tm_ptr;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex );
+
+ return result;
+}
+#endif /* !USE_GMTIME_R */
+
+#ifndef USE_LOCALTIME_R
+struct tm *
+ldap_pvt_localtime( const time_t *timep, struct tm *result )
+{
+ struct tm *tm_ptr;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gmtime_mutex );
+ tm_ptr = localtime( timep );
+ if ( tm_ptr == NULL ) {
+ result = NULL;
+
+ } else {
+ *result = *tm_ptr;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gmtime_mutex );
+
+ return result;
+}
+#endif /* !USE_LOCALTIME_R */
+
+static int _ldap_pvt_gt_subs;
+
+#ifdef _WIN32
+/* Windows SYSTEMTIME only has 10 millisecond resolution, so we
+ * also need to use a high resolution timer to get microseconds.
+ * This is pretty clunky.
+ */
+static LARGE_INTEGER _ldap_pvt_gt_freq;
+static LARGE_INTEGER _ldap_pvt_gt_prev;
+static int _ldap_pvt_gt_offset;
+
+#define SEC_TO_UNIX_EPOCH 11644473600LL
+#define TICKS_PER_SECOND 10000000
+
+static int
+ldap_pvt_gettimeusec(int *sec)
+{
+ LARGE_INTEGER count;
+
+ QueryPerformanceCounter( &count );
+
+ /* It shouldn't ever go backwards, but multiple CPUs might
+ * be able to hit in the same tick.
+ */
+ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex );
+ /* We assume Windows has at least a vague idea of
+ * when a second begins. So we align our microsecond count
+ * with the Windows millisecond count using this offset.
+ * We retain the submillisecond portion of our own count.
+ *
+ * Note - this also assumes that the relationship between
+ * the PerformanceCounter and SystemTime stays constant;
+ * that assumption breaks if the SystemTime is adjusted by
+ * an external action.
+ */
+ if ( !_ldap_pvt_gt_freq.QuadPart ) {
+ LARGE_INTEGER c2;
+ ULARGE_INTEGER ut;
+ FILETIME ft0, ft1;
+ long long t;
+ int usec;
+
+ /* Initialize our offset */
+ QueryPerformanceFrequency( &_ldap_pvt_gt_freq );
+
+ /* Wait for a tick of the system time: 10-15ms */
+ GetSystemTimeAsFileTime( &ft0 );
+ do {
+ GetSystemTimeAsFileTime( &ft1 );
+ } while ( ft1.dwLowDateTime == ft0.dwLowDateTime );
+
+ ut.LowPart = ft1.dwLowDateTime;
+ ut.HighPart = ft1.dwHighDateTime;
+ QueryPerformanceCounter( &c2 );
+
+ /* get second and fraction portion of counter */
+ t = c2.QuadPart % (_ldap_pvt_gt_freq.QuadPart*10);
+
+ /* convert to microseconds */
+ t *= 1000000;
+ usec = t / _ldap_pvt_gt_freq.QuadPart;
+
+ ut.QuadPart /= 10;
+ ut.QuadPart %= 10000000;
+ _ldap_pvt_gt_offset = usec - ut.QuadPart;
+ count = c2;
+ }
+ if ( count.QuadPart <= _ldap_pvt_gt_prev.QuadPart ) {
+ _ldap_pvt_gt_subs++;
+ } else {
+ _ldap_pvt_gt_subs = 0;
+ _ldap_pvt_gt_prev = count;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex );
+
+ /* convert to microseconds */
+ count.QuadPart %= _ldap_pvt_gt_freq.QuadPart*10;
+ count.QuadPart *= 1000000;
+ count.QuadPart /= _ldap_pvt_gt_freq.QuadPart;
+ count.QuadPart -= _ldap_pvt_gt_offset;
+
+ /* We've extracted the 1s and microseconds.
+ * The 1sec digit is used to detect wraparound in microsecnds.
+ */
+ if (count.QuadPart < 0)
+ count.QuadPart += 10000000;
+ else if (count.QuadPart >= 10000000)
+ count.QuadPart -= 10000000;
+
+ *sec = count.QuadPart / 1000000;
+ return count.QuadPart % 1000000;
+}
+
+
+/* emulate POSIX gettimeofday */
+int
+ldap_pvt_gettimeofday( struct timeval *tv, void *unused )
+{
+ FILETIME ft;
+ ULARGE_INTEGER ut;
+ int sec, sec0;
+
+ GetSystemTimeAsFileTime( &ft );
+ ut.LowPart = ft.dwLowDateTime;
+ ut.HighPart = ft.dwHighDateTime;
+
+ /* convert to usec */
+ ut.QuadPart /= (TICKS_PER_SECOND / 1000000);
+
+ tv->tv_usec = ldap_pvt_gettimeusec(&sec);
+ tv->tv_sec = ut.QuadPart / 1000000 - SEC_TO_UNIX_EPOCH;
+
+ /* check for carry from microseconds */
+ sec0 = tv->tv_sec % 10;
+ if (sec0 < sec || (sec0 == 9 && !sec))
+ tv->tv_sec++;
+
+ return 0;
+}
+
+/* return a broken out time, with microseconds
+ */
+void
+ldap_pvt_gettime( struct lutil_tm *tm )
+{
+ SYSTEMTIME st;
+ int sec, sec0;
+ static const char daysPerMonth[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+ GetSystemTime( &st );
+ tm->tm_usec = ldap_pvt_gettimeusec(&sec);
+ tm->tm_usub = _ldap_pvt_gt_subs;
+
+ /* any difference larger than microseconds is
+ * already reflected in st
+ */
+ tm->tm_sec = st.wSecond;
+ tm->tm_min = st.wMinute;
+ tm->tm_hour = st.wHour;
+ tm->tm_mday = st.wDay;
+ tm->tm_mon = st.wMonth - 1;
+ tm->tm_year = st.wYear - 1900;
+
+ /* check for carry from microseconds */
+ sec0 = tm->tm_sec % 10;
+ if (sec0 < sec || (sec0 == 9 && !sec)) {
+ tm->tm_sec++;
+ /* FIXME: we don't handle leap seconds */
+ if (tm->tm_sec > 59) {
+ tm->tm_sec = 0;
+ tm->tm_min++;
+ if (tm->tm_min > 59) {
+ tm->tm_min = 0;
+ tm->tm_hour++;
+ if (tm->tm_hour > 23) {
+ int days = daysPerMonth[tm->tm_mon];
+ tm->tm_hour = 0;
+ tm->tm_mday++;
+
+ /* if it's February of a leap year,
+ * add 1 day to this month
+ */
+ if (tm->tm_mon == 1 &&
+ ((!(st.wYear % 4) && (st.wYear % 100)) ||
+ !(st.wYear % 400)))
+ days++;
+
+ if (tm->tm_mday > days) {
+ tm->tm_mday = 1;
+ tm->tm_mon++;
+ if (tm->tm_mon > 11) {
+ tm->tm_mon = 0;
+ tm->tm_year++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#else
+
+static struct timeval _ldap_pvt_gt_prevTv;
+
+void
+ldap_pvt_gettime( struct lutil_tm *ltm )
+{
+ struct timeval tv;
+
+ struct tm tm;
+ time_t t;
+
+ gettimeofday( &tv, NULL );
+ t = tv.tv_sec;
+
+ LDAP_MUTEX_LOCK( &ldap_int_gettime_mutex );
+ if ( tv.tv_sec < _ldap_pvt_gt_prevTv.tv_sec
+ || ( tv.tv_sec == _ldap_pvt_gt_prevTv.tv_sec
+ && tv.tv_usec <= _ldap_pvt_gt_prevTv.tv_usec )) {
+ _ldap_pvt_gt_subs++;
+ } else {
+ _ldap_pvt_gt_subs = 0;
+ _ldap_pvt_gt_prevTv = tv;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_gettime_mutex );
+
+ ltm->tm_usub = _ldap_pvt_gt_subs;
+
+ ldap_pvt_gmtime( &t, &tm );
+
+ ltm->tm_sec = tm.tm_sec;
+ ltm->tm_min = tm.tm_min;
+ ltm->tm_hour = tm.tm_hour;
+ ltm->tm_mday = tm.tm_mday;
+ ltm->tm_mon = tm.tm_mon;
+ ltm->tm_year = tm.tm_year;
+ ltm->tm_usec = tv.tv_usec;
+}
+#endif
+
+size_t
+ldap_pvt_csnstr(char *buf, size_t len, unsigned int replica, unsigned int mod)
+{
+ struct lutil_tm tm;
+ int n;
+
+ ldap_pvt_gettime( &tm );
+
+ n = snprintf( buf, len,
+ "%4d%02d%02d%02d%02d%02d.%06dZ#%06x#%03x#%06x",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec, tm.tm_usec, tm.tm_usub, replica, mod );
+
+ if( n < 0 ) return 0;
+ return ( (size_t) n < len ) ? n : 0;
+}
+
+#define BUFSTART (1024-32)
+#define BUFMAX (32*1024-32)
+
+#if defined(LDAP_R_COMPILE)
+static char *safe_realloc( char **buf, int len );
+
+#if !(defined(HAVE_GETHOSTBYNAME_R) && defined(HAVE_GETHOSTBYADDR_R))
+static int copy_hostent( struct hostent *res,
+ char **buf, struct hostent * src );
+#endif
+#endif
+
+int ldap_pvt_gethostbyname_a(
+ const char *name,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr )
+{
+#if defined( HAVE_GETHOSTBYNAME_R )
+
+# define NEED_SAFE_REALLOC 1
+ int r=-1;
+ int buflen=BUFSTART;
+ *buf = NULL;
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( buf, buflen )==NULL)
+ return r;
+
+#if (GETHOSTBYNAME_R_NARGS < 6)
+ *result=gethostbyname_r( name, resbuf, *buf, buflen, herrno_ptr );
+ r = (*result == NULL) ? -1 : 0;
+#else
+ r = gethostbyname_r( name, resbuf, *buf,
+ buflen, result, herrno_ptr );
+#endif
+
+ Debug( LDAP_DEBUG_TRACE, "ldap_pvt_gethostbyname_a: host=%s, r=%d\n",
+ name, r, 0 );
+
+#ifdef NETDB_INTERNAL
+ if ((r<0) &&
+ (*herrno_ptr==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ return r;
+ }
+ return -1;
+#elif defined( LDAP_R_COMPILE )
+# define NEED_COPY_HOSTENT
+ struct hostent *he;
+ int retval;
+ *buf = NULL;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+
+ he = gethostbyname( name );
+
+ if (he==NULL) {
+ *herrno_ptr = h_errno;
+ retval = -1;
+ } else if (copy_hostent( resbuf, buf, he )<0) {
+ *herrno_ptr = -1;
+ retval = -1;
+ } else {
+ *result = resbuf;
+ retval = 0;
+ }
+
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+ return retval;
+#else
+ *buf = NULL;
+ *result = gethostbyname( name );
+
+ if (*result!=NULL) {
+ return 0;
+ }
+
+ *herrno_ptr = h_errno;
+
+ return -1;
+#endif
+}
+
+#if !defined( HAVE_GETNAMEINFO ) && !defined( HAVE_HSTRERROR )
+static const char *
+hp_strerror( int err )
+{
+ switch (err) {
+ case HOST_NOT_FOUND: return _("Host not found (authoritative)");
+ case TRY_AGAIN: return _("Host not found (server fail?)");
+ case NO_RECOVERY: return _("Non-recoverable failure");
+ case NO_DATA: return _("No data of requested type");
+#ifdef NETDB_INTERNAL
+ case NETDB_INTERNAL: return STRERROR( errno );
+#endif
+ }
+ return _("Unknown resolver error");
+}
+#endif
+
+int ldap_pvt_get_hname(
+ const struct sockaddr *sa,
+ int len,
+ char *name,
+ int namelen,
+ char **err )
+{
+ int rc;
+#if defined( HAVE_GETNAMEINFO )
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ rc = getnameinfo( sa, len, name, namelen, NULL, 0, 0 );
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+ if ( rc ) *err = (char *)AC_GAI_STRERROR( rc );
+ return rc;
+
+#else /* !HAVE_GETNAMEINFO */
+ char *addr;
+ int alen;
+ struct hostent *hp = NULL;
+#ifdef HAVE_GETHOSTBYADDR_R
+ struct hostent hb;
+ int buflen=BUFSTART, h_errno;
+ char *buf=NULL;
+#endif
+
+#ifdef LDAP_PF_INET6
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin = (struct sockaddr_in6 *)sa;
+ addr = (char *)&sin->sin6_addr;
+ alen = sizeof(sin->sin6_addr);
+ } else
+#endif
+ if (sa->sa_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+ addr = (char *)&sin->sin_addr;
+ alen = sizeof(sin->sin_addr);
+ } else {
+ rc = NO_RECOVERY;
+ *err = (char *)HSTRERROR( rc );
+ return rc;
+ }
+#if defined( HAVE_GETHOSTBYADDR_R )
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( &buf, buflen )==NULL) {
+ *err = (char *)STRERROR( ENOMEM );
+ return ENOMEM;
+ }
+#if (GETHOSTBYADDR_R_NARGS < 8)
+ hp=gethostbyaddr_r( addr, alen, sa->sa_family,
+ &hb, buf, buflen, &h_errno );
+ rc = (hp == NULL) ? -1 : 0;
+#else
+ rc = gethostbyaddr_r( addr, alen, sa->sa_family,
+ &hb, buf, buflen,
+ &hp, &h_errno );
+#endif
+#ifdef NETDB_INTERNAL
+ if ((rc<0) &&
+ (h_errno==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ break;
+ }
+ if (hp) {
+ strncpy( name, hp->h_name, namelen );
+ } else {
+ *err = (char *)HSTRERROR( h_errno );
+ }
+ LDAP_FREE(buf);
+#else /* HAVE_GETHOSTBYADDR_R */
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ hp = gethostbyaddr( addr, alen, sa->sa_family );
+ if (hp) {
+ strncpy( name, hp->h_name, namelen );
+ rc = 0;
+ } else {
+ rc = h_errno;
+ *err = (char *)HSTRERROR( h_errno );
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+#endif /* !HAVE_GETHOSTBYADDR_R */
+ return rc;
+#endif /* !HAVE_GETNAMEINFO */
+}
+
+int ldap_pvt_gethostbyaddr_a(
+ const char *addr,
+ int len,
+ int type,
+ struct hostent *resbuf,
+ char **buf,
+ struct hostent **result,
+ int *herrno_ptr )
+{
+#if defined( HAVE_GETHOSTBYADDR_R )
+
+# undef NEED_SAFE_REALLOC
+# define NEED_SAFE_REALLOC
+ int r=-1;
+ int buflen=BUFSTART;
+ *buf = NULL;
+ for(;buflen<BUFMAX;) {
+ if (safe_realloc( buf, buflen )==NULL)
+ return r;
+#if (GETHOSTBYADDR_R_NARGS < 8)
+ *result=gethostbyaddr_r( addr, len, type,
+ resbuf, *buf, buflen, herrno_ptr );
+ r = (*result == NULL) ? -1 : 0;
+#else
+ r = gethostbyaddr_r( addr, len, type,
+ resbuf, *buf, buflen,
+ result, herrno_ptr );
+#endif
+
+#ifdef NETDB_INTERNAL
+ if ((r<0) &&
+ (*herrno_ptr==NETDB_INTERNAL) &&
+ (errno==ERANGE))
+ {
+ buflen*=2;
+ continue;
+ }
+#endif
+ return r;
+ }
+ return -1;
+#elif defined( LDAP_R_COMPILE )
+# undef NEED_COPY_HOSTENT
+# define NEED_COPY_HOSTENT
+ struct hostent *he;
+ int retval;
+ *buf = NULL;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ he = gethostbyaddr( addr, len, type );
+
+ if (he==NULL) {
+ *herrno_ptr = h_errno;
+ retval = -1;
+ } else if (copy_hostent( resbuf, buf, he )<0) {
+ *herrno_ptr = -1;
+ retval = -1;
+ } else {
+ *result = resbuf;
+ retval = 0;
+ }
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+
+ return retval;
+
+#else /* gethostbyaddr() */
+ *buf = NULL;
+ *result = gethostbyaddr( addr, len, type );
+
+ if (*result!=NULL) {
+ return 0;
+ }
+ return -1;
+#endif
+}
+/*
+ * ldap_int_utils_init() should be called before any other function.
+ */
+
+void ldap_int_utils_init( void )
+{
+ static int done=0;
+ if (done)
+ return;
+ done=1;
+
+#ifdef LDAP_R_COMPILE
+#if !defined( USE_CTIME_R ) && !defined( HAVE_REENTRANT_FUNCTIONS )
+ ldap_pvt_thread_mutex_init( &ldap_int_ctime_mutex );
+#endif
+#if !defined( USE_GMTIME_R ) && !defined( USE_LOCALTIME_R )
+ ldap_pvt_thread_mutex_init( &ldap_int_gmtime_mutex );
+#endif
+ ldap_pvt_thread_mutex_init( &ldap_int_resolv_mutex );
+
+ ldap_pvt_thread_mutex_init( &ldap_int_hostname_mutex );
+
+ ldap_pvt_thread_mutex_init( &ldap_int_gettime_mutex );
+
+#ifdef HAVE_GSSAPI
+ ldap_pvt_thread_mutex_init( &ldap_int_gssapi_mutex );
+#endif
+#endif
+
+ /* call other module init functions here... */
+}
+
+#if defined( NEED_COPY_HOSTENT )
+# undef NEED_SAFE_REALLOC
+#define NEED_SAFE_REALLOC
+
+static char *cpy_aliases(
+ char ***tgtio,
+ char *buf,
+ char **src )
+{
+ int len;
+ char **tgt=*tgtio;
+ for( ; (*src) ; src++ ) {
+ len = strlen( *src ) + 1;
+ AC_MEMCPY( buf, *src, len );
+ *tgt++=buf;
+ buf+=len;
+ }
+ *tgtio=tgt;
+ return buf;
+}
+
+static char *cpy_addresses(
+ char ***tgtio,
+ char *buf,
+ char **src,
+ int len )
+{
+ char **tgt=*tgtio;
+ for( ; (*src) ; src++ ) {
+ AC_MEMCPY( buf, *src, len );
+ *tgt++=buf;
+ buf+=len;
+ }
+ *tgtio=tgt;
+ return buf;
+}
+
+static int copy_hostent(
+ struct hostent *res,
+ char **buf,
+ struct hostent * src )
+{
+ char **p;
+ char **tp;
+ char *tbuf;
+ int name_len;
+ int n_alias=0;
+ int total_alias_len=0;
+ int n_addr=0;
+ int total_addr_len=0;
+ int total_len;
+
+ /* calculate the size needed for the buffer */
+ name_len = strlen( src->h_name ) + 1;
+
+ if( src->h_aliases != NULL ) {
+ for( p = src->h_aliases; (*p) != NULL; p++ ) {
+ total_alias_len += strlen( *p ) + 1;
+ n_alias++;
+ }
+ }
+
+ if( src->h_addr_list != NULL ) {
+ for( p = src->h_addr_list; (*p) != NULL; p++ ) {
+ n_addr++;
+ }
+ total_addr_len = n_addr * src->h_length;
+ }
+
+ total_len = (n_alias + n_addr + 2) * sizeof( char * ) +
+ total_addr_len + total_alias_len + name_len;
+
+ if (safe_realloc( buf, total_len )) {
+ tp = (char **) *buf;
+ tbuf = *buf + (n_alias + n_addr + 2) * sizeof( char * );
+ AC_MEMCPY( res, src, sizeof( struct hostent ) );
+ /* first the name... */
+ AC_MEMCPY( tbuf, src->h_name, name_len );
+ res->h_name = tbuf; tbuf+=name_len;
+ /* now the aliases */
+ res->h_aliases = tp;
+ if ( src->h_aliases != NULL ) {
+ tbuf = cpy_aliases( &tp, tbuf, src->h_aliases );
+ }
+ *tp++=NULL;
+ /* finally the addresses */
+ res->h_addr_list = tp;
+ if ( src->h_addr_list != NULL ) {
+ tbuf = cpy_addresses( &tp, tbuf, src->h_addr_list, src->h_length );
+ }
+ *tp++=NULL;
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+#if defined( NEED_SAFE_REALLOC )
+static char *safe_realloc( char **buf, int len )
+{
+ char *tmpbuf;
+ tmpbuf = LDAP_REALLOC( *buf, len );
+ if (tmpbuf) {
+ *buf=tmpbuf;
+ }
+ return tmpbuf;
+}
+#endif
+
+char * ldap_pvt_get_fqdn( char *name )
+{
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints, *res;
+#else
+ char *ha_buf;
+ struct hostent *hp, he_buf;
+ int local_h_errno;
+#endif
+ int rc;
+ char *fqdn, hostbuf[MAXHOSTNAMELEN+1];
+
+ if( name == NULL ) {
+ if( gethostname( hostbuf, MAXHOSTNAMELEN ) == 0 ) {
+ hostbuf[MAXHOSTNAMELEN] = '\0';
+ name = hostbuf;
+ } else {
+ name = "localhost";
+ }
+ }
+
+#ifdef HAVE_GETADDRINFO
+ memset( &hints, 0, sizeof( hints ));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_CANONNAME;
+
+ LDAP_MUTEX_LOCK( &ldap_int_resolv_mutex );
+ rc = getaddrinfo( name, NULL, &hints, &res );
+ LDAP_MUTEX_UNLOCK( &ldap_int_resolv_mutex );
+ if ( rc == 0 && res->ai_canonname ) {
+ fqdn = LDAP_STRDUP( res->ai_canonname );
+ } else {
+ fqdn = LDAP_STRDUP( name );
+ }
+ if ( rc == 0 )
+ freeaddrinfo( res );
+#else
+ rc = ldap_pvt_gethostbyname_a( name,
+ &he_buf, &ha_buf, &hp, &local_h_errno );
+
+ if( rc < 0 || hp == NULL || hp->h_name == NULL ) {
+ fqdn = LDAP_STRDUP( name );
+ } else {
+ fqdn = LDAP_STRDUP( hp->h_name );
+ }
+
+ LDAP_FREE( ha_buf );
+#endif
+ return fqdn;
+}
+
+#if ( defined( HAVE_GETADDRINFO ) || defined( HAVE_GETNAMEINFO ) ) \
+ && !defined( HAVE_GAI_STRERROR )
+char *ldap_pvt_gai_strerror (int code) {
+ static struct {
+ int code;
+ const char *msg;
+ } values[] = {
+#ifdef EAI_ADDRFAMILY
+ { EAI_ADDRFAMILY, N_("Address family for hostname not supported") },
+#endif
+ { EAI_AGAIN, N_("Temporary failure in name resolution") },
+ { EAI_BADFLAGS, N_("Bad value for ai_flags") },
+ { EAI_FAIL, N_("Non-recoverable failure in name resolution") },
+ { EAI_FAMILY, N_("ai_family not supported") },
+ { EAI_MEMORY, N_("Memory allocation failure") },
+#ifdef EAI_NODATA
+ { EAI_NODATA, N_("No address associated with hostname") },
+#endif
+ { EAI_NONAME, N_("Name or service not known") },
+ { EAI_SERVICE, N_("Servname not supported for ai_socktype") },
+ { EAI_SOCKTYPE, N_("ai_socktype not supported") },
+#ifdef EAI_SYSTEM
+ { EAI_SYSTEM, N_("System error") },
+#endif
+ { 0, NULL }
+ };
+
+ int i;
+
+ for ( i = 0; values[i].msg != NULL; i++ ) {
+ if ( values[i].code == code ) {
+ return (char *) _(values[i].msg);
+ }
+ }
+
+ return _("Unknown error");
+}
+#endif
diff --git a/libraries/libldap/vlvctrl.c b/libraries/libldap/vlvctrl.c
new file mode 100644
index 0000000..6357e61
--- /dev/null
+++ b/libraries/libldap/vlvctrl.c
@@ -0,0 +1,361 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* Portions Copyright (C) 1999, 2000 Novell, Inc. All Rights Reserved.
+ *
+ * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND
+ * TREATIES. USE, MODIFICATION, AND REDISTRIBUTION OF THIS WORK IS SUBJECT
+ * TO VERSION 2.0.1 OF THE OPENLDAP PUBLIC LICENSE, A COPY OF WHICH IS
+ * AVAILABLE AT HTTP://WWW.OPENLDAP.ORG/LICENSE.HTML OR IN THE FILE "LICENSE"
+ * IN THE TOP-LEVEL DIRECTORY OF THE DISTRIBUTION. ANY USE OR EXPLOITATION
+ * OF THIS WORK OTHER THAN AS AUTHORIZED IN VERSION 2.0.1 OF THE OPENLDAP
+ * PUBLIC LICENSE, OR OTHER PRIOR WRITTEN CONSENT FROM NOVELL, COULD SUBJECT
+ * THE PERPETRATOR TO CRIMINAL AND CIVIL LIABILITY.
+ *---
+ * Note: A verbatim copy of version 2.0.1 of the OpenLDAP Public License
+ * can be found in the file "build/LICENSE-2.0.1" in this distribution
+ * of OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+#define LDAP_VLVBYINDEX_IDENTIFIER 0xa0L
+#define LDAP_VLVBYVALUE_IDENTIFIER 0x81L
+#define LDAP_VLVCONTEXT_IDENTIFIER 0x04L
+
+
+/*---
+ ldap_create_vlv_control
+
+ Create and encode the Virtual List View control.
+
+ ld (IN) An LDAP session handle.
+
+ vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents
+ are used to construct the value of the control
+ that is created.
+
+ value (OUT) A struct berval that contains the value to be assigned to the ldctl_value member
+ of an LDAPControl structure that contains the
+ VirtualListViewRequest control.
+ The bv_val member of the berval structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_memfree().
+
+
+ Ber encoding
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byoffset [0] SEQUENCE, {
+ offset INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ [1] greaterThanOrEqual assertionValue }
+ contextID OCTET STRING OPTIONAL }
+
+
+ Note: The first time the VLV control is created, the ldvlv_context
+ field of the LDAPVLVInfo structure should be set to NULL.
+ The context obtained from calling ldap_parse_vlv_control()
+ should be used as the context in the next ldap_create_vlv_control
+ call.
+
+ ---*/
+
+int
+ldap_create_vlv_control_value(
+ LDAP *ld,
+ LDAPVLVInfo *vlvinfop,
+ struct berval *value )
+{
+ ber_tag_t tag;
+ BerElement *ber;
+
+ if ( ld == NULL || vlvinfop == NULL || value == NULL ) {
+ if ( ld )
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return LDAP_PARAM_ERROR;
+ }
+
+ assert( LDAP_VALID( ld ) );
+
+ value->bv_val = NULL;
+ value->bv_len = 0;
+ ld->ld_errno = LDAP_SUCCESS;
+
+ ber = ldap_alloc_ber_with_options( ld );
+ if ( ber == NULL ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return ld->ld_errno;
+ }
+
+ tag = ber_printf( ber, "{ii" /*}*/,
+ vlvinfop->ldvlv_before_count,
+ vlvinfop->ldvlv_after_count );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( vlvinfop->ldvlv_attrvalue == NULL ) {
+ tag = ber_printf( ber, "t{iiN}",
+ LDAP_VLVBYINDEX_IDENTIFIER,
+ vlvinfop->ldvlv_offset,
+ vlvinfop->ldvlv_count );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ } else {
+ tag = ber_printf( ber, "tO",
+ LDAP_VLVBYVALUE_IDENTIFIER,
+ vlvinfop->ldvlv_attrvalue );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ if ( vlvinfop->ldvlv_context ) {
+ tag = ber_printf( ber, "tO",
+ LDAP_VLVCONTEXT_IDENTIFIER,
+ vlvinfop->ldvlv_context );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+ }
+
+ tag = ber_printf( ber, /*{*/ "N}" );
+ if ( tag == LBER_ERROR ) {
+ goto error_return;
+ }
+
+ if ( ber_flatten2( ber, value, 1 ) == -1 ) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ }
+
+ if ( 0 ) {
+error_return:;
+ ld->ld_errno = LDAP_ENCODING_ERROR;
+ }
+
+ if ( ber != NULL ) {
+ ber_free( ber, 1 );
+ }
+
+ return ld->ld_errno;
+}
+
+/*---
+ ldap_create_vlv_control
+
+ Create and encode the Virtual List View control.
+
+ ld (IN) An LDAP session handle.
+
+ vlvinfop (IN) The address of an LDAPVLVInfo structure whose contents
+ are used to construct the value of the control
+ that is created.
+
+ ctrlp (OUT) A result parameter that will be assigned the address
+ of an LDAPControl structure that contains the
+ VirtualListViewRequest control created by this function.
+ The memory occupied by the LDAPControl structure
+ SHOULD be freed when it is no longer in use by
+ calling ldap_control_free().
+
+
+ Ber encoding
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byoffset [0] SEQUENCE, {
+ offset INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ [1] greaterThanOrEqual assertionValue }
+ contextID OCTET STRING OPTIONAL }
+
+
+ Note: The first time the VLV control is created, the ldvlv_context
+ field of the LDAPVLVInfo structure should be set to NULL.
+ The context obtained from calling ldap_parse_vlv_control()
+ should be used as the context in the next ldap_create_vlv_control
+ call.
+
+ ---*/
+
+int
+ldap_create_vlv_control(
+ LDAP *ld,
+ LDAPVLVInfo *vlvinfop,
+ LDAPControl **ctrlp )
+{
+ struct berval value;
+
+ if ( ctrlp == NULL ) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return ld->ld_errno;
+ }
+
+ ld->ld_errno = ldap_create_vlv_control_value( ld, vlvinfop, &value );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+
+ ld->ld_errno = ldap_control_create( LDAP_CONTROL_VLVREQUEST,
+ 1, &value, 0, ctrlp );
+ if ( ld->ld_errno != LDAP_SUCCESS ) {
+ LDAP_FREE( value.bv_val );
+ }
+ }
+
+ return ld->ld_errno;
+}
+
+
+/*---
+ ldap_parse_vlvresponse_control
+
+ Decode the Virtual List View control return information.
+
+ ld (IN) An LDAP session handle.
+
+ ctrl (IN) The address of the LDAPControl structure.
+
+ target_posp (OUT) This result parameter is filled in with the list
+ index of the target entry. If this parameter is
+ NULL, the target position is not returned.
+
+ list_countp (OUT) This result parameter is filled in with the server's
+ estimate of the size of the list. If this parameter
+ is NULL, the size is not returned.
+
+ contextp (OUT) This result parameter is filled in with the address
+ of a struct berval that contains the server-
+ generated context identifier if one was returned by
+ the server. If the server did not return a context
+ identifier, this parameter will be set to NULL, even
+ if an error occured.
+ The returned context SHOULD be used in the next call
+ to create a VLV sort control. The struct berval
+ returned SHOULD be disposed of by calling ber_bvfree()
+ when it is no longer needed. If NULL is passed for
+ contextp, the context identifier is not returned.
+
+ errcodep (OUT) This result parameter is filled in with the VLV
+ result code. If this parameter is NULL, the result
+ code is not returned.
+
+
+ Ber encoding
+
+ VirtualListViewResponse ::= SEQUENCE {
+ targetPosition INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt),
+ virtualListViewResult ENUMERATED {
+ success (0),
+ operatonsError (1),
+ unwillingToPerform (53),
+ insufficientAccessRights (50),
+ busy (51),
+ timeLimitExceeded (3),
+ adminLimitExceeded (11),
+ sortControlMissing (60),
+ offsetRangeError (61),
+ other (80) },
+ contextID OCTET STRING OPTIONAL }
+
+---*/
+
+int
+ldap_parse_vlvresponse_control(
+ LDAP *ld,
+ LDAPControl *ctrl,
+ ber_int_t *target_posp,
+ ber_int_t *list_countp,
+ struct berval **contextp,
+ ber_int_t *errcodep )
+{
+ BerElement *ber;
+ ber_int_t pos, count, err;
+ ber_tag_t tag, berTag;
+ ber_len_t berLen;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+
+ if (contextp) {
+ *contextp = NULL; /* Make sure we return a NULL if error occurs. */
+ }
+
+ if (ctrl == NULL) {
+ ld->ld_errno = LDAP_PARAM_ERROR;
+ return(ld->ld_errno);
+ }
+
+ if (strcmp(LDAP_CONTROL_VLVRESPONSE, ctrl->ldctl_oid) != 0) {
+ /* Not VLV Response control */
+ ld->ld_errno = LDAP_CONTROL_NOT_FOUND;
+ return(ld->ld_errno);
+ }
+
+ /* Create a BerElement from the berval returned in the control. */
+ ber = ber_init(&ctrl->ldctl_value);
+
+ if (ber == NULL) {
+ ld->ld_errno = LDAP_NO_MEMORY;
+ return(ld->ld_errno);
+ }
+
+ /* Extract the data returned in the control. */
+ tag = ber_scanf(ber, "{iie" /*}*/, &pos, &count, &err);
+
+ if( tag == LBER_ERROR) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+
+
+ /* Since the context is the last item encoded, if caller doesn't want
+ it returned, don't decode it. */
+ if (contextp) {
+ if (LDAP_VLVCONTEXT_IDENTIFIER == ber_peek_tag(ber, &berLen)) {
+ tag = ber_scanf(ber, "tO", &berTag, contextp);
+
+ if( tag == LBER_ERROR) {
+ ber_free(ber, 1);
+ ld->ld_errno = LDAP_DECODING_ERROR;
+ return(ld->ld_errno);
+ }
+ }
+ }
+
+ ber_free(ber, 1);
+
+ /* Return data to the caller for items that were requested. */
+ if (target_posp) *target_posp = pos;
+ if (list_countp) *list_countp = count;
+ if (errcodep) *errcodep = err;
+
+ ld->ld_errno = LDAP_SUCCESS;
+ return(ld->ld_errno);
+}
diff --git a/libraries/libldap/whoami.c b/libraries/libldap/whoami.c
new file mode 100644
index 0000000..a14cadb
--- /dev/null
+++ b/libraries/libldap/whoami.c
@@ -0,0 +1,102 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This program was orignally developed by Kurt D. Zeilenga for inclusion in
+ * OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+/*
+ * LDAP Who Am I? (Extended) Operation <draft-zeilenga-ldap-authzid-xx.txt>
+ */
+
+int ldap_parse_whoami(
+ LDAP *ld,
+ LDAPMessage *res,
+ struct berval **authzid )
+{
+ int rc;
+ char *retoid = NULL;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( res != NULL );
+ assert( authzid != NULL );
+
+ *authzid = NULL;
+
+ rc = ldap_parse_extended_result( ld, res, &retoid, authzid, 0 );
+
+ if( rc != LDAP_SUCCESS ) {
+ ldap_perror( ld, "ldap_parse_whoami" );
+ return rc;
+ }
+
+ ber_memfree( retoid );
+ return rc;
+}
+
+int
+ldap_whoami( LDAP *ld,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls,
+ int *msgidp )
+{
+ int rc;
+
+ assert( ld != NULL );
+ assert( LDAP_VALID( ld ) );
+ assert( msgidp != NULL );
+
+ rc = ldap_extended_operation( ld, LDAP_EXOP_WHO_AM_I,
+ NULL, sctrls, cctrls, msgidp );
+
+ return rc;
+}
+
+int
+ldap_whoami_s(
+ LDAP *ld,
+ struct berval **authzid,
+ LDAPControl **sctrls,
+ LDAPControl **cctrls )
+{
+ int rc;
+ int msgid;
+ LDAPMessage *res;
+
+ rc = ldap_whoami( ld, sctrls, cctrls, &msgid );
+ if ( rc != LDAP_SUCCESS ) return rc;
+
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *) NULL, &res ) == -1 || !res ) {
+ return ld->ld_errno;
+ }
+
+ rc = ldap_parse_whoami( ld, res, authzid );
+ if( rc != LDAP_SUCCESS ) {
+ ldap_msgfree( res );
+ return rc;
+ }
+
+ return( ldap_result2error( ld, res, 1 ) );
+}
diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in
new file mode 100644
index 0000000..85eff3d
--- /dev/null
+++ b/libraries/libldap_r/Makefile.in
@@ -0,0 +1,88 @@
+# Makefile.in for LDAP -lldap
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2021 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+LIBRARY = libldap_r.la
+
+PROGRAMS = apitest ltest
+
+XXDIR = $(srcdir)/../libldap
+XXSRCS = apitest.c test.c \
+ bind.c open.c result.c error.c compare.c search.c \
+ controls.c messages.c references.c extended.c cyrus.c \
+ modify.c add.c modrdn.c delete.c abandon.c \
+ sasl.c gssapi.c sbind.c unbind.c cancel.c \
+ filter.c free.c sort.c passwd.c whoami.c \
+ getdn.c getentry.c getattr.c getvalues.c addentry.c \
+ request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
+ init.c options.c print.c string.c util-int.c schema.c \
+ charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
+ tls2.c tls_o.c tls_g.c tls_m.c \
+ turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
+ assertion.c deref.c ldif.c fetch.c
+SRCS = threads.c rdwr.c rmutex.c tpool.c rq.c \
+ thr_posix.c thr_cthreads.c thr_thr.c thr_nt.c \
+ thr_pth.c thr_stub.c thr_debug.c
+OBJS = threads.lo rdwr.lo rmutex.lo tpool.lo rq.lo \
+ thr_posix.lo thr_cthreads.lo thr_thr.lo thr_nt.lo \
+ thr_pth.lo thr_stub.lo thr_debug.lo \
+ bind.lo open.lo result.lo error.lo compare.lo search.lo \
+ controls.lo messages.lo references.lo extended.lo cyrus.lo \
+ modify.lo add.lo modrdn.lo delete.lo abandon.lo \
+ sasl.lo gssapi.lo sbind.lo unbind.lo cancel.lo \
+ filter.lo free.lo sort.lo passwd.lo whoami.lo \
+ getdn.lo getentry.lo getattr.lo getvalues.lo addentry.lo \
+ request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
+ init.lo options.lo print.lo string.lo util-int.lo schema.lo \
+ charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
+ tls2.lo tls_o.lo tls_g.lo tls_m.lo \
+ turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
+ assertion.lo deref.lo ldif.lo fetch.lo
+
+LDAP_INCDIR= ../../include
+LDAP_LIBDIR= ../../libraries
+
+LIB_DEFS = -DLDAP_LIBRARY
+
+XDEFS = -DLDAP_R_COMPILE -I$(XXDIR)
+XLIBS = $(LIBRARY) $(LDAP_LIBLBER_LA) $(LDAP_LIBLUTIL_A)
+XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
+XXXLIBS = $(LTHREAD_LIBS)
+NT_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS)
+UNIX_LINK_LIBS = $(LDAP_LIBLBER_LA) $(AC_LIBS) $(SECURITY_LIBS) $(LTHREAD_LIBS)
+
+.links : Makefile
+ @for i in $(XXSRCS); do \
+ $(RM) $$i ; \
+ $(LN_S) $(XXDIR)/$$i . ; \
+ done
+ touch .links
+
+$(XXSRCS) : .links
+
+clean-local: FORCE
+ @$(RM) .links
+
+depend-common: .links
+
+apitest: $(XLIBS) apitest.o
+ $(LTLINK) -o $@ apitest.o $(LIBS)
+ltest: $(XLIBS) test.o
+ $(LTLINK) -o $@ test.o $(LIBS)
+
+install-local: $(CFFILES) FORCE
+ -$(MKDIR) $(DESTDIR)$(libdir)
+ $(LTINSTALL) $(INSTALLFLAGS) -m 644 $(LIBRARY) $(DESTDIR)$(libdir)
+ $(LTFINISH) $(DESTDIR)$(libdir)
+
diff --git a/libraries/libldap_r/ldap_thr_debug.h b/libraries/libldap_r/ldap_thr_debug.h
new file mode 100644
index 0000000..6bf9314
--- /dev/null
+++ b/libraries/libldap_r/ldap_thr_debug.h
@@ -0,0 +1,191 @@
+/* ldap_thr_debug.h - preprocessor magic for LDAP_THREAD_DEBUG */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#ifdef LDAP_THREAD_DEBUG
+
+/*
+ * libldap_r .c files should include this file after ldap_pvt_thread.h,
+ * with the appropriate LDAP_THREAD*_IMPLEMENTATION macro(s) defined.
+ */
+
+#ifndef _LDAP_PVT_THREAD_H
+#error "ldap_pvt_thread.h" must be included before "ldap_thr_debug.h"
+#endif
+
+/*
+ * Support for thr_debug.c:
+ *
+ * thr_debug.c defines ldap_pvt_thread_* as wrappers around the real
+ * ldap_pvt_thread_* implementation, which this file renames to
+ * ldap_int_thread_*.
+ *
+ * Implementation:
+ *
+ * This file re#defines selected ldap_pvt_thread_* names to
+ * ldap_int_thread_*, which will be used from wrappers in thr_debug.c.
+ * Two ldap_int_*() calls are redirected to call ldap_debug_*(): These
+ * are wrappers around the originals, whose definitions are not renamed.
+ * This file then #includes ldap_pvt_thread.h to declare the renamed
+ * functions/types. If #included from thr_debug.c it finally #undefines
+ * the macros again.
+ *
+ * include/ldap_pvt_thread.h declares the typedefs ldap_pvt_thread*_t as
+ * either wrapper types ldap_debug_thread*_t or their usual definitions
+ * ldap_int_thread*_t, depending on the LDAP_THREAD_DEBUG_WRAP option.
+ * When defining the underlying implementation, this file then redirects
+ * the type names back to the original ldap_int_thread*_t types.
+ * include/ldap_<int,pvt>_thread.h also do some thr_debug magic.
+ *
+ * So,
+ * libldap_r/<not thr_debug.c> thus define ldap_int_thread_*() instead
+ * of ldap_pvt_thread_*().
+ * thr_debug.c defines the ldap_pvt_*() and ldap_debug_*() functions.
+ * In thread.c, ldap_pvt_thread_<initialize/destroy>() will call
+ * ldap_debug_thread_*() instead of ldap_int_thread_*().
+ * In tpool.c, ldap_int_thread_pool_shutdown() has explicit thr_debug.c
+ * support which treats ldap_pvt_thread_pool_destroy() the same way.
+ */
+
+#ifndef LDAP_THREAD_IMPLEMENTATION /* for first part of threads.c */
+#define ldap_int_thread_initialize ldap_debug_thread_initialize
+#define ldap_int_thread_destroy ldap_debug_thread_destroy
+#else /* LDAP_THREAD_IMPLEMENTATION -- for thr_*.c and end of threads.c */
+#undef ldap_int_thread_initialize
+#undef ldap_int_thread_destroy
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_mutex_t ldap_int_thread_mutex_t
+#define ldap_pvt_thread_cond_t ldap_int_thread_cond_t
+#endif
+#define ldap_pvt_thread_sleep ldap_int_thread_sleep
+#define ldap_pvt_thread_get_concurrency ldap_int_thread_get_concurrency
+#define ldap_pvt_thread_set_concurrency ldap_int_thread_set_concurrency
+#define ldap_pvt_thread_create ldap_int_thread_create
+#define ldap_pvt_thread_exit ldap_int_thread_exit
+#define ldap_pvt_thread_join ldap_int_thread_join
+#define ldap_pvt_thread_kill ldap_int_thread_kill
+#define ldap_pvt_thread_yield ldap_int_thread_yield
+#define ldap_pvt_thread_cond_init ldap_int_thread_cond_init
+#define ldap_pvt_thread_cond_destroy ldap_int_thread_cond_destroy
+#define ldap_pvt_thread_cond_signal ldap_int_thread_cond_signal
+#define ldap_pvt_thread_cond_broadcast ldap_int_thread_cond_broadcast
+#define ldap_pvt_thread_cond_wait ldap_int_thread_cond_wait
+#define ldap_pvt_thread_mutex_init ldap_int_thread_mutex_init
+#define ldap_pvt_thread_mutex_destroy ldap_int_thread_mutex_destroy
+#define ldap_pvt_thread_mutex_lock ldap_int_thread_mutex_lock
+#define ldap_pvt_thread_mutex_trylock ldap_int_thread_mutex_trylock
+#define ldap_pvt_thread_mutex_unlock ldap_int_thread_mutex_unlock
+#define ldap_pvt_thread_self ldap_int_thread_self
+#endif /* LDAP_THREAD_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_RDWR_IMPLEMENTATION /* rdwr.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_rdwr_t ldap_int_thread_rdwr_t
+#endif
+#define ldap_pvt_thread_rdwr_init ldap_int_thread_rdwr_init
+#define ldap_pvt_thread_rdwr_destroy ldap_int_thread_rdwr_destroy
+#define ldap_pvt_thread_rdwr_rlock ldap_int_thread_rdwr_rlock
+#define ldap_pvt_thread_rdwr_rtrylock ldap_int_thread_rdwr_rtrylock
+#define ldap_pvt_thread_rdwr_runlock ldap_int_thread_rdwr_runlock
+#define ldap_pvt_thread_rdwr_wlock ldap_int_thread_rdwr_wlock
+#define ldap_pvt_thread_rdwr_wtrylock ldap_int_thread_rdwr_wtrylock
+#define ldap_pvt_thread_rdwr_wunlock ldap_int_thread_rdwr_wunlock
+#define ldap_pvt_thread_rdwr_readers ldap_int_thread_rdwr_readers
+#define ldap_pvt_thread_rdwr_writers ldap_int_thread_rdwr_writers
+#define ldap_pvt_thread_rdwr_active ldap_int_thread_rdwr_active
+#endif /* LDAP_THREAD_RDWR_IMPLEMENTATION */
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c, thr_stub.c, thr_debug.c */
+#ifdef LDAP_THREAD_DEBUG_WRAP /* see ldap_pvt_thread.h */
+#define ldap_pvt_thread_pool_t ldap_int_thread_pool_t
+#endif
+#define ldap_pvt_thread_pool_init ldap_int_thread_pool_init
+#define ldap_pvt_thread_pool_submit ldap_int_thread_pool_submit
+#define ldap_pvt_thread_pool_maxthreads ldap_int_thread_pool_maxthreads
+#define ldap_pvt_thread_pool_backload ldap_int_thread_pool_backload
+#define ldap_pvt_thread_pool_pause ldap_int_thread_pool_pause
+#define ldap_pvt_thread_pool_resume ldap_int_thread_pool_resume
+#define ldap_pvt_thread_pool_destroy ldap_int_thread_pool_destroy
+#define ldap_pvt_thread_pool_getkey ldap_int_thread_pool_getkey
+#define ldap_pvt_thread_pool_setkey ldap_int_thread_pool_setkey
+#define ldap_pvt_thread_pool_purgekey ldap_int_thread_pool_purgekey
+#define ldap_pvt_thread_pool_context ldap_int_thread_pool_context
+#define ldap_pvt_thread_pool_context_reset ldap_int_thread_pool_context_reset
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#undef _LDAP_PVT_THREAD_H
+#include "ldap_pvt_thread.h"
+
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION /* tpool.c */
+/*
+ * tpool.c:ldap_int_thread_pool_shutdown() needs this. Could not
+ * use it for ldap_pvt_thread.h above because of its use of LDAP_P().
+ */
+#undef ldap_pvt_thread_pool_destroy
+#define ldap_pvt_thread_pool_destroy(p,r) ldap_int_thread_pool_destroy(p,r)
+#endif
+
+#ifdef LDAP_THREAD_DEBUG_IMPLEMENTATION /* thr_debug.c */
+#undef ldap_pvt_thread_mutex_t
+#undef ldap_pvt_thread_cond_t
+#undef ldap_pvt_thread_sleep
+#undef ldap_pvt_thread_get_concurrency
+#undef ldap_pvt_thread_set_concurrency
+#undef ldap_pvt_thread_create
+#undef ldap_pvt_thread_exit
+#undef ldap_pvt_thread_join
+#undef ldap_pvt_thread_kill
+#undef ldap_pvt_thread_yield
+#undef ldap_pvt_thread_cond_init
+#undef ldap_pvt_thread_cond_destroy
+#undef ldap_pvt_thread_cond_signal
+#undef ldap_pvt_thread_cond_broadcast
+#undef ldap_pvt_thread_cond_wait
+#undef ldap_pvt_thread_mutex_init
+#undef ldap_pvt_thread_mutex_destroy
+#undef ldap_pvt_thread_mutex_lock
+#undef ldap_pvt_thread_mutex_trylock
+#undef ldap_pvt_thread_mutex_unlock
+#undef ldap_pvt_thread_self
+/* LDAP_THREAD_RDWR_IMPLEMENTATION: */
+#undef ldap_pvt_thread_rdwr_t
+#undef ldap_pvt_thread_rdwr_init
+#undef ldap_pvt_thread_rdwr_destroy
+#undef ldap_pvt_thread_rdwr_rlock
+#undef ldap_pvt_thread_rdwr_rtrylock
+#undef ldap_pvt_thread_rdwr_runlock
+#undef ldap_pvt_thread_rdwr_wlock
+#undef ldap_pvt_thread_rdwr_wtrylock
+#undef ldap_pvt_thread_rdwr_wunlock
+#undef ldap_pvt_thread_rdwr_readers
+#undef ldap_pvt_thread_rdwr_writers
+#undef ldap_pvt_thread_rdwr_active
+/* LDAP_THREAD_POOL_IMPLEMENTATION: */
+#undef ldap_pvt_thread_pool_t
+#undef ldap_pvt_thread_pool_init
+#undef ldap_pvt_thread_pool_submit
+#undef ldap_pvt_thread_pool_maxthreads
+#undef ldap_pvt_thread_pool_backload
+#undef ldap_pvt_thread_pool_pause
+#undef ldap_pvt_thread_pool_resume
+#undef ldap_pvt_thread_pool_destroy
+#undef ldap_pvt_thread_pool_getkey
+#undef ldap_pvt_thread_pool_setkey
+#undef ldap_pvt_thread_pool_purgekey
+#undef ldap_pvt_thread_pool_context
+#undef ldap_pvt_thread_pool_context_reset
+#endif /* LDAP_THREAD_DEBUG_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */
diff --git a/libraries/libldap_r/rdwr.c b/libraries/libldap_r/rdwr.c
new file mode 100644
index 0000000..424e83e
--- /dev/null
+++ b/libraries/libldap_r/rdwr.c
@@ -0,0 +1,458 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Kurt D. Zeilenga for inclusion
+ * in OpenLDAP Software. Additional significant contributors include:
+ * Stuart Lynne
+ */
+
+/*
+ * This is an improved implementation of Reader/Writer locks does
+ * not protect writers from starvation. That is, if a writer is
+ * currently waiting on a reader, any new reader will get
+ * the lock before the writer.
+ *
+ * Does not support cancellation nor does any status checking.
+ */
+/* Adapted from publically available examples for:
+ * "Programming with Posix Threads"
+ * by David R Butenhof, Addison-Wesley
+ * http://cseng.aw.com/bookpage.taf?ISBN=0-201-63392-2
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*
+ * implementations that provide their own compatible
+ * reader/writer locks define LDAP_THREAD_HAVE_RDWR
+ * in ldap_pvt_thread.h
+ */
+#ifndef LDAP_THREAD_HAVE_RDWR
+
+struct ldap_int_thread_rdwr_s {
+ ldap_pvt_thread_mutex_t ltrw_mutex;
+ ldap_pvt_thread_cond_t ltrw_read; /* wait for read */
+ ldap_pvt_thread_cond_t ltrw_write; /* wait for write */
+ int ltrw_valid;
+#define LDAP_PVT_THREAD_RDWR_VALID 0x0bad
+ int ltrw_r_active;
+ int ltrw_w_active;
+ int ltrw_r_wait;
+ int ltrw_w_wait;
+#ifdef LDAP_RDWR_DEBUG
+ /* keep track of who has these locks */
+#define MAX_READERS 32
+ int ltrw_more_readers; /* Set if ltrw_readers[] is incomplete */
+ ldap_pvt_thread_t ltrw_readers[MAX_READERS];
+ ldap_pvt_thread_t ltrw_writer;
+#endif
+};
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+
+ rw = (struct ldap_int_thread_rdwr_s *) LDAP_CALLOC( 1,
+ sizeof( struct ldap_int_thread_rdwr_s ) );
+ if ( !rw )
+ return LDAP_NO_MEMORY;
+
+ /* we should check return results */
+ ldap_pvt_thread_mutex_init( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_init( &rw->ltrw_read );
+ ldap_pvt_thread_cond_init( &rw->ltrw_write );
+
+ rw->ltrw_valid = LDAP_PVT_THREAD_RDWR_VALID;
+
+ *rwlock = rw;
+ return 0;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ /* active threads? */
+ if( rw->ltrw_r_active > 0 || rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ /* waiting threads? */
+ if( rw->ltrw_r_wait > 0 || rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ rw->ltrw_valid = 0;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ ldap_pvt_thread_mutex_destroy( &rw->ltrw_mutex );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_read );
+ ldap_pvt_thread_cond_destroy( &rw->ltrw_write );
+
+ LDAP_FREE(rw);
+ *rwlock = NULL;
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0 ) {
+ /* writer is active */
+
+ rw->ltrw_r_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_read, &rw->ltrw_mutex );
+ } while( rw->ltrw_w_active > 0 );
+
+ rw->ltrw_r_wait--;
+ assert( rw->ltrw_r_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if( rw->ltrw_w_active > 0) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ if( rw->ltrw_r_active < MAX_READERS )
+ rw->ltrw_readers[rw->ltrw_r_active] = ldap_pvt_thread_self();
+ else
+ rw->ltrw_more_readers = 1;
+#endif
+ rw->ltrw_r_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_r_active--;
+#ifdef LDAP_RDWR_DEBUG
+ /* Remove us from the list of readers */
+ {
+ ldap_pvt_thread_t self = ldap_pvt_thread_self();
+ int i, j;
+ for( i = j = rw->ltrw_r_active; i >= 0; i--) {
+ if (rw->ltrw_readers[i] == self) {
+ rw->ltrw_readers[i] = rw->ltrw_readers[j];
+ rw->ltrw_readers[j] = 0;
+ break;
+ }
+ }
+ if( !rw->ltrw_more_readers )
+ assert( i >= 0 );
+ else if( j == 0 )
+ rw->ltrw_more_readers = 0;
+ }
+#endif
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_active == 0 && rw->ltrw_w_wait > 0 ) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ rw->ltrw_w_wait++;
+
+ do {
+ ldap_pvt_thread_cond_wait(
+ &rw->ltrw_write, &rw->ltrw_mutex );
+ } while ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 );
+
+ rw->ltrw_w_wait--;
+ assert( rw->ltrw_w_wait >= 0 );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if ( rw->ltrw_w_active > 0 || rw->ltrw_r_active > 0 ) {
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ rw->ltrw_writer = ldap_pvt_thread_self();
+#endif
+ rw->ltrw_w_active++;
+
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+
+ if( rw->ltrw_valid != LDAP_PVT_THREAD_RDWR_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rw->ltrw_mutex );
+
+ rw->ltrw_w_active--;
+
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ if (rw->ltrw_r_wait > 0) {
+ ldap_pvt_thread_cond_broadcast( &rw->ltrw_read );
+
+ } else if (rw->ltrw_w_wait > 0) {
+ ldap_pvt_thread_cond_signal( &rw->ltrw_write );
+ }
+
+#ifdef LDAP_RDWR_DEBUG
+ assert( rw->ltrw_writer == ldap_pvt_thread_self() );
+ rw->ltrw_writer = 0;
+#endif
+ ldap_pvt_thread_mutex_unlock( &rw->ltrw_mutex );
+
+ return 0;
+}
+
+#ifdef LDAP_RDWR_DEBUG
+
+/* just for testing,
+ * return 0 if false, suitable for assert(ldap_pvt_thread_rdwr_Xchk(rdwr))
+ *
+ * Currently they don't check if the calling thread is the one
+ * that has the lock, just that there is a reader or writer.
+ *
+ * Basically sufficent for testing that places that should have
+ * a lock are caught.
+ */
+
+int ldap_pvt_thread_rdwr_readers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_r_active );
+}
+
+int ldap_pvt_thread_rdwr_writers(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return( rw->ltrw_w_active );
+}
+
+int ldap_pvt_thread_rdwr_active(ldap_pvt_thread_rdwr_t *rwlock)
+{
+ struct ldap_int_thread_rdwr_s *rw;
+
+ assert( rwlock != NULL );
+ rw = *rwlock;
+
+ assert( rw != NULL );
+ assert( rw->ltrw_valid == LDAP_PVT_THREAD_RDWR_VALID );
+ assert( rw->ltrw_w_active >= 0 );
+ assert( rw->ltrw_w_wait >= 0 );
+ assert( rw->ltrw_r_active >= 0 );
+ assert( rw->ltrw_r_wait >= 0 );
+
+ return(ldap_pvt_thread_rdwr_readers(rwlock) +
+ ldap_pvt_thread_rdwr_writers(rwlock));
+}
+
+#endif /* LDAP_RDWR_DEBUG */
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
diff --git a/libraries/libldap_r/rmutex.c b/libraries/libldap_r/rmutex.c
new file mode 100644
index 0000000..6099a2a
--- /dev/null
+++ b/libraries/libldap_r/rmutex.c
@@ -0,0 +1,219 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2006-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software.
+ */
+
+/*
+ * This is an implementation of recursive mutexes.
+ */
+
+#include "portable.h"
+
+#include <ac/stdlib.h>
+
+#include <ac/errno.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+
+struct ldap_int_thread_rmutex_s {
+ ldap_pvt_thread_mutex_t ltrm_mutex;
+ ldap_pvt_thread_cond_t ltrm_cond;
+ ldap_pvt_thread_t ltrm_owner;
+ int ltrm_valid;
+#define LDAP_PVT_THREAD_RMUTEX_VALID 0x0cdb
+ int ltrm_depth;
+ int ltrm_waits;
+};
+
+static const ldap_pvt_thread_t tid_zero;
+
+int
+ldap_pvt_thread_rmutex_init( ldap_pvt_thread_rmutex_t *rmutex )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+
+ rm = (struct ldap_int_thread_rmutex_s *) LDAP_CALLOC( 1,
+ sizeof( struct ldap_int_thread_rmutex_s ) );
+ if ( !rm )
+ return LDAP_NO_MEMORY;
+
+ /* we should check return results */
+ ldap_pvt_thread_mutex_init( &rm->ltrm_mutex );
+ ldap_pvt_thread_cond_init( &rm->ltrm_cond );
+
+ rm->ltrm_valid = LDAP_PVT_THREAD_RMUTEX_VALID;
+
+ *rmutex = rm;
+ return 0;
+}
+
+int
+ldap_pvt_thread_rmutex_destroy( ldap_pvt_thread_rmutex_t *rmutex )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ /* in use? */
+ if( rm->ltrm_depth > 0 || rm->ltrm_waits > 0 ) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+
+ rm->ltrm_valid = 0;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ ldap_pvt_thread_mutex_destroy( &rm->ltrm_mutex );
+ ldap_pvt_thread_cond_destroy( &rm->ltrm_cond );
+
+ LDAP_FREE(rm);
+ *rmutex = NULL;
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_lock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if( rm->ltrm_depth > 0 ) {
+ /* already locked */
+ if ( !ldap_pvt_thread_equal( rm->ltrm_owner, owner )) {
+ rm->ltrm_waits++;
+ do {
+ ldap_pvt_thread_cond_wait( &rm->ltrm_cond,
+ &rm->ltrm_mutex );
+ } while( rm->ltrm_depth > 0 );
+
+ rm->ltrm_waits--;
+ assert( rm->ltrm_waits >= 0 );
+ rm->ltrm_owner = owner;
+ }
+ } else {
+ rm->ltrm_owner = owner;
+ }
+
+ rm->ltrm_depth++;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_trylock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if( rm->ltrm_depth > 0 ) {
+ if ( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EBUSY;
+ }
+ } else {
+ rm->ltrm_owner = owner;
+ }
+
+ rm->ltrm_depth++;
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
+int ldap_pvt_thread_rmutex_unlock( ldap_pvt_thread_rmutex_t *rmutex,
+ ldap_pvt_thread_t owner )
+{
+ struct ldap_int_thread_rmutex_s *rm;
+
+ assert( rmutex != NULL );
+ rm = *rmutex;
+
+ assert( rm != NULL );
+ assert( rm->ltrm_valid == LDAP_PVT_THREAD_RMUTEX_VALID );
+
+ if( rm->ltrm_valid != LDAP_PVT_THREAD_RMUTEX_VALID )
+ return LDAP_PVT_THREAD_EINVAL;
+
+ ldap_pvt_thread_mutex_lock( &rm->ltrm_mutex );
+
+ if( !ldap_pvt_thread_equal( owner, rm->ltrm_owner )) {
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+ return LDAP_PVT_THREAD_EINVAL;
+ }
+
+ rm->ltrm_depth--;
+ if ( !rm->ltrm_depth )
+ rm->ltrm_owner = tid_zero;
+
+ assert( rm->ltrm_depth >= 0 );
+ assert( rm->ltrm_waits >= 0 );
+
+ if ( !rm->ltrm_depth && rm->ltrm_waits ) {
+ ldap_pvt_thread_cond_signal( &rm->ltrm_cond );
+ }
+
+ ldap_pvt_thread_mutex_unlock( &rm->ltrm_mutex );
+
+ return 0;
+}
+
diff --git a/libraries/libldap_r/rq.c b/libraries/libldap_r/rq.c
new file mode 100644
index 0000000..5857cc6
--- /dev/null
+++ b/libraries/libldap_r/rq.c
@@ -0,0 +1,221 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2003-2021 The OpenLDAP Foundation.
+ * Portions Copyright 2003 IBM Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Jong Hyuk Choi for inclusion
+ * in OpenLDAP Software.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h"
+#include "ldap_queue.h"
+#include "ldap_rq.h"
+
+struct re_s *
+ldap_pvt_runqueue_insert(
+ struct runqueue_s* rq,
+ time_t interval,
+ ldap_pvt_thread_start_t *routine,
+ void *arg,
+ char *tname,
+ char *tspec
+)
+{
+ struct re_s* entry;
+
+ entry = (struct re_s *) LDAP_CALLOC( 1, sizeof( struct re_s ));
+ if ( entry ) {
+ entry->interval.tv_sec = interval;
+ entry->interval.tv_usec = 0;
+ entry->next_sched.tv_sec = time( NULL );
+ entry->next_sched.tv_usec = 0;
+ entry->routine = routine;
+ entry->arg = arg;
+ entry->tname = tname;
+ entry->tspec = tspec;
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ }
+ return entry;
+}
+
+struct re_s *
+ldap_pvt_runqueue_find(
+ struct runqueue_s *rq,
+ ldap_pvt_thread_start_t *routine,
+ void *arg
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->routine == routine && e->arg == arg )
+ return e;
+ }
+ return NULL;
+}
+
+void
+ldap_pvt_runqueue_remove(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry)
+ break;
+ }
+
+ assert( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ LDAP_FREE( entry );
+}
+
+struct re_s*
+ldap_pvt_runqueue_next_sched(
+ struct runqueue_s* rq,
+ struct timeval* next_run
+)
+{
+ struct re_s* entry;
+
+ entry = LDAP_STAILQ_FIRST( &rq->task_list );
+ if ( entry == NULL || entry->next_sched.tv_sec == 0 ) {
+ return NULL;
+ } else {
+ *next_run = entry->next_sched;
+ return entry;
+ }
+}
+
+void
+ldap_pvt_runqueue_runtask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_INSERT_TAIL( &rq->run_list, entry, rnext );
+}
+
+void
+ldap_pvt_runqueue_stoptask(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ LDAP_STAILQ_REMOVE( &rq->run_list, entry, re_s, rnext );
+}
+
+int
+ldap_pvt_runqueue_isrunning(
+ struct runqueue_s* rq,
+ struct re_s* entry
+)
+{
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->run_list, rnext ) {
+ if ( e == entry ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+ldap_pvt_runqueue_resched(
+ struct runqueue_s* rq,
+ struct re_s* entry,
+ int defer
+)
+{
+ struct re_s* prev;
+ struct re_s* e;
+
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e == entry )
+ break;
+ }
+
+ assert ( e == entry );
+
+ LDAP_STAILQ_REMOVE( &rq->task_list, entry, re_s, tnext );
+
+ if ( !defer ) {
+ entry->next_sched.tv_sec = time( NULL ) + entry->interval.tv_sec;
+ } else {
+ entry->next_sched.tv_sec = 0;
+ }
+
+ if ( LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else if ( entry->next_sched.tv_sec == 0 ) {
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ } else {
+ prev = NULL;
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ } else if ( e->next_sched.tv_sec > entry->next_sched.tv_sec ) {
+ if ( prev == NULL ) {
+ LDAP_STAILQ_INSERT_HEAD( &rq->task_list, entry, tnext );
+ } else {
+ LDAP_STAILQ_INSERT_AFTER( &rq->task_list, prev, entry, tnext );
+ }
+ return;
+ }
+ prev = e;
+ }
+ LDAP_STAILQ_INSERT_TAIL( &rq->task_list, entry, tnext );
+ }
+}
+
+int
+ldap_pvt_runqueue_persistent_backload(
+ struct runqueue_s* rq
+)
+{
+ struct re_s* e;
+ int count = 0;
+
+ ldap_pvt_thread_mutex_lock( &rq->rq_mutex );
+ if ( !LDAP_STAILQ_EMPTY( &rq->task_list )) {
+ LDAP_STAILQ_FOREACH( e, &rq->task_list, tnext ) {
+ if ( e->next_sched.tv_sec == 0 )
+ count++;
+ }
+ }
+ ldap_pvt_thread_mutex_unlock( &rq->rq_mutex );
+ return count;
+}
+
diff --git a/libraries/libldap_r/thr_cthreads.c b/libraries/libldap_r/thr_cthreads.c
new file mode 100644
index 0000000..6cc4789
--- /dev/null
+++ b/libraries/libldap_r/thr_cthreads.c
@@ -0,0 +1,180 @@
+/* thr_cthreads.c - wrapper for mach cthreads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* This work was initially developed by Luke Howard for inclusion
+ * in U-MICH LDAP 3.3.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_MACH_CTHREADS )
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *), void *arg)
+{
+ *thread = cthread_fork( (cthread_fn_t) start_routine, arg);
+ return ( *thread == NULL ? -1 : 0 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ cthread_exit( (any_t) retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ void *status;
+ status = (void *) cthread_join ( thread );
+ if (thread_return != NULL)
+ {
+ *thread_return = status;
+ }
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ cthread_yield();
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ condition_init( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ condition_clear( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ condition_signal( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ condition_broadcast( cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ condition_wait( cond, mutex );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_init( mutex );
+ mutex->name = NULL;
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_clear( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_lock( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ mutex_unlock( mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return mutex_try_lock( mutex );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return cthread_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return cthread_keycreate( key );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return cthread_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return cthread_getspecific( key, data );
+}
+
+#endif /* HAVE_MACH_CTHREADS */
diff --git a/libraries/libldap_r/thr_debug.c b/libraries/libldap_r/thr_debug.c
new file mode 100644
index 0000000..24ac74b
--- /dev/null
+++ b/libraries/libldap_r/thr_debug.c
@@ -0,0 +1,1292 @@
+/* thr_debug.c - wrapper around the chosen thread wrapper, for debugging. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+/*
+ * This package provides several types of thread operation debugging:
+ *
+ * - Check the results of operations on threads, mutexes, condition
+ * variables and read/write locks. Also check some thread pool
+ * operations, but not those for which failure can happen in normal
+ * slapd operation.
+ *
+ * - Wrap those types except threads and pools in structs with state
+ * information, and check that on all operations:
+ *
+ * + Check that the resources are initialized and are only used at
+ * their original address (i.e. not realloced or copied).
+ *
+ * + Check the owner (thread ID) on mutex operations.
+ *
+ * + Optionally allocate a reference to a byte of dummy memory.
+ * This lets malloc debuggers see some incorrect use as memory
+ * leaks, access to freed memory, etc.
+ *
+ * - Print an error message and by default abort() upon errors.
+ *
+ * - Print a count of leaked thread resources after cleanup.
+ *
+ * Compile-time (./configure) setup: Macros defined in CPPFLAGS.
+ *
+ * LDAP_THREAD_DEBUG or LDAP_THREAD_DEBUG=2
+ * Enables debugging, but value & 2 turns off type wrapping.
+ *
+ * LDAP_UINTPTR_T=integer type to hold pointers, preferably unsigned.
+ * Used by dummy memory option "scramble". Default = unsigned long.
+ *
+ * LDAP_DEBUG_THREAD_NONE = initializer for a "no thread" thread ID.
+ *
+ * In addition, you may need to set up an implementation-specific way
+ * to enable whatever error checking your thread library provides.
+ * Currently only implemented for Posix threads (pthreads), where
+ * you may need to define LDAP_INT_THREAD_MUTEXATTR. The default
+ * is PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_ERRORCHECK_NP for
+ * Linux threads. See pthread_mutexattr_settype(3).
+ *
+ * Run-time configuration:
+ *
+ * Memory debugging tools:
+ * Tools that report uninitialized memory accesses should disable
+ * such warnings about the function debug_already_initialized().
+ * Alternatively, include "noreinit" (below) in $LDAP_THREAD_DEBUG.
+ *
+ * Environment variable $LDAP_THREAD_DEBUG:
+ * The variable may contain a comma- or space-separated option list.
+ * Options:
+ * off - Disable this package. (It still slows things down).
+ * tracethreads - Report create/join/exit/kill of threads.
+ * noabort - Do not abort() on errors.
+ * noerror - Do not report errors. Implies noabort.
+ * nocount - Do not report counts of unreleased resources.
+ * nosync - Disable tests that use synchronizaion and thus
+ * clearly affect thread scheduling:
+ * Implies nocount, and cancels threadID if that is set.
+ * Note that if you turn on tracethreads or malloc
+ * debugging, these also use library calls which may
+ * affect thread scheduling (fprintf and malloc).
+ * The following options do not apply if type wrapping is disabled:
+ * nomem - Do not check memory operations.
+ * Implies noreinit,noalloc.
+ * noreinit - Do not catch reinitialization of existing resources.
+ * (That test accesses uninitialized memory).
+ * threadID - Trace thread IDs. Currently mostly useless.
+ * Malloc debugging -- allocate dummy memory for initialized
+ * resources, so malloc debuggers will report them as memory leaks:
+ * noalloc - Default. Do not allocate dummy memory.
+ * alloc - Store a pointer to dummy memory. However, leak
+ * detectors might not catch unreleased resources in
+ * global variables.
+ * scramble - Store bitwise complement of dummy memory pointer.
+ * That never escapes memory leak detectors -
+ * but detection while the program is running will
+ * report active resources as leaks. Do not
+ * use this if a garbage collector is in use:-)
+ * adjptr - Point to end of dummy memory.
+ * Purify reports these as "potential leaks" (PLK).
+ * I have not checked other malloc debuggers.
+ */
+
+#include "portable.h"
+
+#if defined( LDAP_THREAD_DEBUG )
+
+#include <stdio.h>
+#include <ac/errno.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_DEBUG_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* Get the underlying implementation */
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+#undef LDAP_THREAD_DEBUG_THREAD_ID
+#elif !defined LDAP_THREAD_DEBUG_THREAD_ID
+#define LDAP_THREAD_DEBUG_THREAD_ID 1
+#endif
+
+/* Use native malloc - the OpenLDAP wrappers may defeat malloc debuggers */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+
+
+/* Options from environment variable $LDAP_THREAD_DEBUG */
+enum { Count_no = 0, Count_yes, Count_reported, Count_reported_more };
+static int count = Count_yes;
+#ifdef LDAP_THREAD_DEBUG_WRAP
+enum { Wrap_noalloc, Wrap_alloc, Wrap_scramble, Wrap_adjptr };
+static int wraptype = Wrap_noalloc, wrap_offset, unwrap_offset;
+static int nomem, noreinit;
+#endif
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+static int threadID;
+#else
+enum { threadID = 0 };
+#endif
+static int nodebug, noabort, noerror, nosync, tracethreads;
+static int wrap_threads;
+static int options_done;
+
+
+/* ldap_pvt_thread_initialize() called, ldap_pvt_thread_destroy() not called */
+static int threading_enabled;
+
+
+/* Resource counts */
+enum {
+ Idx_unexited_thread, Idx_unjoined_thread, Idx_locked_mutex,
+ Idx_mutex, Idx_cond, Idx_rdwr, Idx_tpool, Idx_max
+};
+static int resource_counts[Idx_max];
+static const char *const resource_names[] = {
+ "unexited threads", "unjoined threads", "locked mutexes",
+ "mutexes", "conds", "rdwrs", "thread pools"
+};
+static ldap_int_thread_mutex_t resource_mutexes[Idx_max];
+
+
+/* Hide pointers from malloc debuggers. */
+#define SCRAMBLE(ptr) (~(LDAP_UINTPTR_T) (ptr))
+#define UNSCRAMBLE_usagep(num) ((ldap_debug_usage_info_t *) ~(num))
+#define UNSCRAMBLE_dummyp(num) ((unsigned char *) ~(num))
+
+
+#define WARN(var, msg) (warn (__FILE__, __LINE__, (msg), #var, (var)))
+#define WARN_IF(rc, msg) {if (rc) warn (__FILE__, __LINE__, (msg), #rc, (rc));}
+
+#define ERROR(var, msg) { \
+ if (!noerror) { \
+ errmsg(__FILE__, __LINE__, (msg), #var, (var)); \
+ if( !noabort ) abort(); \
+ } \
+}
+
+#define ERROR_IF(rc, msg) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+
+#ifdef LDAP_THREAD_DEBUG_WRAP
+#define MEMERROR_IF(rc, msg, mem_act) { \
+ if (!noerror) { \
+ int rc_ = (rc); \
+ if (rc_) { \
+ errmsg(__FILE__, __LINE__, (msg), #rc, rc_); \
+ if( wraptype != Wrap_noalloc ) { mem_act; } \
+ if( !noabort ) abort(); \
+ } \
+ } \
+}
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+#if 0
+static void
+warn( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s warning: %s\n"
+ : "%s:%d: %s warning: %s is %d\n"),
+ file, line, msg, var, val );
+}
+#endif
+
+static void
+errmsg( const char *file, int line, const char *msg, const char *var, int val )
+{
+ fprintf( stderr,
+ (strpbrk( var, "!=" )
+ ? "%s:%d: %s error: %s\n"
+ : "%s:%d: %s error: %s is %d\n"),
+ file, line, msg, var, val );
+}
+
+static void
+count_resource_leaks( void )
+{
+ int i, j;
+ char errbuf[200];
+ if( count == Count_yes ) {
+ count = Count_reported;
+#if 0 /* Could break if there are still threads after atexit */
+ for( i = j = 0; i < Idx_max; i++ )
+ j |= ldap_int_thread_mutex_destroy( &resource_mutexes[i] );
+ WARN_IF( j, "ldap_debug_thread_destroy:mutexes" );
+#endif
+ for( i = j = 0; i < Idx_max; i++ )
+ if( resource_counts[i] )
+ j += sprintf( errbuf + j, ", %d %s",
+ resource_counts[i], resource_names[i] );
+ if( j )
+ fprintf( stderr, "== thr_debug: Leaked%s. ==\n", errbuf + 1 );
+ }
+}
+
+static void
+get_options( void )
+{
+ static const struct option_info_s {
+ const char *name;
+ int *var, val;
+ } option_info[] = {
+ { "off", &nodebug, 1 },
+ { "noabort", &noabort, 1 },
+ { "noerror", &noerror, 1 },
+ { "nocount", &count, Count_no },
+ { "nosync", &nosync, 1 },
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ { "threadID", &threadID, 1 },
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ { "nomem", &nomem, 1 },
+ { "noreinit", &noreinit, 1 },
+ { "noalloc", &wraptype, Wrap_noalloc },
+ { "alloc", &wraptype, Wrap_alloc },
+ { "adjptr", &wraptype, Wrap_adjptr },
+ { "scramble", &wraptype, Wrap_scramble },
+#endif
+ { "tracethreads", &tracethreads, 1 },
+ { NULL, NULL, 0 }
+ };
+ const char *s = getenv( "LDAP_THREAD_DEBUG" );
+ if( s != NULL ) {
+ while( *(s += strspn( s, ", \t\r\n" )) != '\0' ) {
+ size_t optlen = strcspn( s, ", \t\r\n" );
+ const struct option_info_s *oi = option_info;
+ while( oi->name &&
+ (strncasecmp( oi->name, s, optlen ) || oi->name[optlen]) )
+ oi++;
+ if( oi->name )
+ *oi->var = oi->val;
+ else
+ fprintf( stderr,
+ "== thr_debug: Unknown $%s option '%.*s' ==\n",
+ "LDAP_THREAD_DEBUG", (int) optlen, s );
+ s += optlen;
+ }
+ }
+ if( nodebug ) {
+ tracethreads = 0;
+ nosync = noerror = 1;
+ }
+ if( nosync )
+ count = Count_no;
+ if( noerror )
+ noabort = 1;
+#if LDAP_THREAD_DEBUG_THREAD_ID +0
+ if( nosync )
+ threadID = 0;
+#endif
+#ifdef LDAP_THREAD_DEBUG_WRAP
+ if( noerror )
+ nomem = 1;
+ if( !nomem ) {
+ static const ldap_debug_usage_info_t usage;
+ if( sizeof(LDAP_UINTPTR_T) < sizeof(unsigned char *)
+ || sizeof(LDAP_UINTPTR_T) < sizeof(ldap_debug_usage_info_t *)
+ || UNSCRAMBLE_usagep( SCRAMBLE( &usage ) ) != &usage
+ || UNSCRAMBLE_dummyp( SCRAMBLE( (unsigned char *) 0 ) ) )
+ {
+ fputs( "== thr_debug: Memory checks unsupported, "
+ "adding nomem to $LDAP_THREAD_DEBUG ==\n", stderr );
+ nomem = 1;
+ }
+ }
+ if( nomem ) {
+ noreinit = 1;
+ wraptype = Wrap_noalloc;
+ }
+ unwrap_offset = -(wrap_offset = (wraptype == Wrap_adjptr));
+#endif
+ wrap_threads = (tracethreads || threadID || count);
+ options_done = 1;
+}
+
+
+#ifndef LDAP_THREAD_DEBUG_WRAP
+
+#define WRAPPED(ptr) (ptr)
+#define GET_OWNER(ptr) 0
+#define SET_OWNER(ptr, thread) ((void) 0)
+#define RESET_OWNER(ptr) ((void) 0)
+#define ASSERT_OWNER(ptr, msg) ((void) 0)
+#define ASSERT_NO_OWNER(ptr, msg) ((void) 0)
+
+#define init_usage(ptr, msg) ((void) 0)
+#define check_usage(ptr, msg) ((void) 0)
+#define destroy_usage(ptr) ((void) 0)
+
+#else /* LDAP_THREAD_DEBUG_WRAP */
+
+/* Specialize this if the initializer is not appropriate. */
+/* The ASSERT_NO_OWNER() definition may also need an override. */
+#ifndef LDAP_DEBUG_THREAD_NONE
+#define LDAP_DEBUG_THREAD_NONE { -1 } /* "no thread" ldap_int_thread_t value */
+#endif
+
+static const ldap_int_thread_t ldap_debug_thread_none = LDAP_DEBUG_THREAD_NONE;
+
+#define THREAD_MUTEX_OWNER(mutex) \
+ ldap_int_thread_equal( (mutex)->owner, ldap_int_thread_self() )
+
+void
+ldap_debug_thread_assert_mutex_owner(
+ const char *file,
+ int line,
+ const char *msg,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ if( !(noerror || THREAD_MUTEX_OWNER( mutex )) ) {
+ errmsg( file, line, msg, "ASSERT_MUTEX_OWNER", 0 );
+ if( !noabort ) abort();
+ }
+}
+
+#define WRAPPED(ptr) (&(ptr)->wrapped)
+#define GET_OWNER(ptr) ((ptr)->owner)
+#define SET_OWNER(ptr, thread) ((ptr)->owner = (thread))
+#define RESET_OWNER(ptr) ((ptr)->owner = ldap_debug_thread_none)
+#define ASSERT_OWNER(ptr, msg) ERROR_IF( !THREAD_MUTEX_OWNER( ptr ), msg )
+#ifndef ASSERT_NO_OWNER
+#define ASSERT_NO_OWNER(ptr, msg) ERROR_IF( \
+ !ldap_int_thread_equal( (ptr)->owner, ldap_debug_thread_none ), msg )
+#endif
+
+/* Try to provoke memory access error (for malloc debuggers) */
+#define PEEK(mem) {if (-*(volatile const unsigned char *)(mem)) debug_noop();}
+
+static void debug_noop( void );
+static int debug_already_initialized( const ldap_debug_usage_info_t *usage );
+
+/* Name used for clearer error message */
+#define IS_COPY_OR_MOVED(usage) ((usage)->self != SCRAMBLE( usage ))
+
+#define DUMMY_ADDR(usage) \
+ (wraptype == Wrap_scramble \
+ ? UNSCRAMBLE_dummyp( (usage)->mem.num ) \
+ : (usage)->mem.ptr + unwrap_offset)
+
+/* Mark resource as initialized */
+static void
+init_usage( ldap_debug_usage_info_t *usage, const char *msg )
+{
+ if( !options_done )
+ get_options();
+ if( !nomem ) {
+ if( !noreinit ) {
+ MEMERROR_IF( debug_already_initialized( usage ), msg, {
+ /* Provoke malloc debuggers */
+ unsigned char *dummy = DUMMY_ADDR( usage );
+ PEEK( dummy );
+ free( dummy );
+ free( dummy );
+ } );
+ }
+ if( wraptype != Wrap_noalloc ) {
+ unsigned char *dummy = malloc( 1 );
+ assert( dummy != NULL );
+ if( wraptype == Wrap_scramble ) {
+ usage->mem.num = SCRAMBLE( dummy );
+ /* Verify that ptr<->integer casts work on this host */
+ assert( UNSCRAMBLE_dummyp( usage->mem.num ) == dummy );
+ } else {
+ usage->mem.ptr = dummy + wrap_offset;
+ }
+ }
+ } else {
+ /* Unused, but set for readability in debugger */
+ usage->mem.ptr = NULL;
+ }
+ usage->self = SCRAMBLE( usage ); /* If nomem, only for debugger */
+ usage->magic = ldap_debug_magic;
+ usage->state = ldap_debug_state_inited;
+}
+
+/* Check that resource is initialized and not copied/realloced */
+static void
+check_usage( const ldap_debug_usage_info_t *usage, const char *msg )
+{
+ enum { Is_destroyed = 1 }; /* Name used for clearer error message */
+
+ if( usage->magic != ldap_debug_magic ) {
+ ERROR( usage->magic, msg );
+ return;
+ }
+ switch( usage->state ) {
+ case ldap_debug_state_destroyed:
+ MEMERROR_IF( Is_destroyed, msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ } );
+ break;
+ default:
+ ERROR( usage->state, msg );
+ break;
+ case ldap_debug_state_inited:
+ if( !nomem ) {
+ MEMERROR_IF( IS_COPY_OR_MOVED( usage ), msg, {
+ PEEK( DUMMY_ADDR( usage ) );
+ PEEK( UNSCRAMBLE_usagep( usage->self ) );
+ } );
+ }
+ break;
+ }
+}
+
+/* Mark resource as destroyed. */
+/* Does not check for errors, call check_usage()/init_usage() first. */
+static void
+destroy_usage( ldap_debug_usage_info_t *usage )
+{
+ if( usage->state == ldap_debug_state_inited ) {
+ if( wraptype != Wrap_noalloc ) {
+ free( DUMMY_ADDR( usage ) );
+ /* Do not reset the DUMMY_ADDR, leave it for malloc debuggers
+ * in case the resource is used after it is freed. */
+ }
+ usage->state = ldap_debug_state_destroyed;
+ }
+}
+
+/* Define these after they are used, so they are hopefully not inlined */
+
+static void
+debug_noop( void )
+{
+}
+
+/*
+ * Valid programs access uninitialized memory here unless "noreinit".
+ *
+ * Returns true if the resource is initialized and not copied/realloced.
+ */
+LDAP_GCCATTR((noinline))
+static int
+debug_already_initialized( const ldap_debug_usage_info_t *usage )
+{
+ /*
+ * 'ret' keeps the Valgrind warning "Conditional jump or move
+ * depends on uninitialised value(s)" _inside_ this function.
+ */
+ volatile int ret = 0;
+ if( usage->state == ldap_debug_state_inited )
+ if( !IS_COPY_OR_MOVED( usage ) )
+ if( usage->magic == ldap_debug_magic )
+ ret = 1;
+ return ret;
+}
+
+#endif /* LDAP_THREAD_DEBUG_WRAP */
+
+
+#if !(LDAP_THREAD_DEBUG_THREAD_ID +0)
+
+typedef void ldap_debug_thread_t;
+#define init_thread_info() {}
+#define with_thread_info_lock(statements) { statements; }
+#define thread_info_detached(t) 0
+#define add_thread_info(msg, thr, det) ((void) 0)
+#define remove_thread_info(tinfo, msg) ((void) 0)
+#define get_thread_info(thread, msg) NULL
+
+#else /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+/*
+ * Thread ID tracking. Currently acieves little.
+ * Should be either expanded or deleted.
+ */
+
+/*
+ * Array of threads. Used instead of making ldap_pvt_thread_t a wrapper
+ * around ldap_int_thread_t, which would slow down ldap_pvt_thread_self().
+ */
+typedef struct {
+ ldap_pvt_thread_t wrapped;
+ ldap_debug_usage_info_t usage;
+ int detached;
+ int idx;
+} ldap_debug_thread_t;
+
+static ldap_debug_thread_t **thread_info;
+static unsigned int thread_info_size, thread_info_used;
+static ldap_int_thread_mutex_t thread_info_mutex;
+
+#define init_thread_info() { \
+ if( threadID ) { \
+ int mutex_init_rc = ldap_int_thread_mutex_init( &thread_info_mutex ); \
+ assert( mutex_init_rc == 0 ); \
+ } \
+}
+
+#define with_thread_info_lock(statements) { \
+ int rc_wtl_ = ldap_int_thread_mutex_lock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+ { statements; } \
+ rc_wtl_ = ldap_int_thread_mutex_unlock( &thread_info_mutex ); \
+ assert( rc_wtl_ == 0 ); \
+}
+
+#define thread_info_detached(t) ((t)->detached)
+
+static void
+add_thread_info(
+ const char *msg,
+ const ldap_pvt_thread_t *thread,
+ int detached )
+{
+ ldap_debug_thread_t *t;
+
+ if( thread_info_used >= thread_info_size ) {
+ unsigned int more = thread_info_size + 8;
+ unsigned int new_size = thread_info_size + more;
+
+ t = calloc( more, sizeof(ldap_debug_thread_t) );
+ assert( t != NULL );
+ thread_info = realloc( thread_info, new_size * sizeof(*thread_info) );
+ assert( thread_info != NULL );
+ do {
+ t->idx = thread_info_size;
+ thread_info[thread_info_size++] = t++;
+ } while( thread_info_size < new_size );
+ }
+
+ t = thread_info[thread_info_used];
+ init_usage( &t->usage, msg );
+ t->wrapped = *thread;
+ t->detached = detached;
+ thread_info_used++;
+}
+
+static void
+remove_thread_info( ldap_debug_thread_t *t, const char *msg )
+{
+ ldap_debug_thread_t *last;
+ int idx;
+ check_usage( &t->usage, msg );
+ destroy_usage( &t->usage );
+ idx = t->idx;
+ assert( thread_info[idx] == t );
+ last = thread_info[--thread_info_used];
+ assert( last->idx == thread_info_used );
+ (thread_info[idx] = last)->idx = idx;
+ (thread_info[thread_info_used] = t )->idx = thread_info_used;
+}
+
+static ldap_debug_thread_t *
+get_thread_info( ldap_pvt_thread_t thread, const char *msg )
+{
+ unsigned int i;
+ ldap_debug_thread_t *t;
+ for( i = 0; i < thread_info_used; i++ ) {
+ if( ldap_pvt_thread_equal( thread, thread_info[i]->wrapped ) )
+ break;
+ }
+ ERROR_IF( i == thread_info_used, msg );
+ t = thread_info[i];
+ check_usage( &t->usage, msg );
+ return t;
+}
+
+#endif /* LDAP_THREAD_DEBUG_THREAD_ID */
+
+
+static char *
+thread_name( char *buf, int bufsize, ldap_pvt_thread_t thread )
+{
+ int i;
+ --bufsize;
+ if( bufsize > 2*sizeof(thread) )
+ bufsize = 2*sizeof(thread);
+ for( i = 0; i < bufsize; i += 2 )
+ snprintf( buf+i, 3, "%02x", ((unsigned char *)&thread)[i/2] );
+ return buf;
+}
+
+
+/* Add <adjust> (+/-1) to resource count <which> unless "nocount". */
+static void
+adjust_count( int which, int adjust )
+{
+ int rc;
+ switch( count ) {
+ case Count_no:
+ break;
+ case Count_yes:
+ rc = ldap_int_thread_mutex_lock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ resource_counts[which] += adjust;
+ rc = ldap_int_thread_mutex_unlock( &resource_mutexes[which] );
+ assert( rc == 0 );
+ break;
+ case Count_reported:
+ fputs( "== thr_debug: More thread activity after exit ==\n", stderr );
+ count = Count_reported_more;
+ /* FALL THROUGH */
+ case Count_reported_more:
+ /* Not used, but result might be inspected with debugger */
+ /* (Hopefully threading is disabled by now...) */
+ resource_counts[which] += adjust;
+ break;
+ }
+}
+
+
+/* Wrappers for LDAP_THREAD_IMPLEMENTATION: */
+
+/* Used instead of ldap_int_thread_initialize by ldap_pvt_thread_initialize */
+int
+ldap_debug_thread_initialize( void )
+{
+ int i, rc, rc2;
+ if( !options_done )
+ get_options();
+ ERROR_IF( threading_enabled, "ldap_debug_thread_initialize" );
+ threading_enabled = 1;
+ rc = ldap_int_thread_initialize();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_initialize:threads" );
+ threading_enabled = 0;
+ } else {
+ init_thread_info();
+ if( count != Count_no ) {
+ for( i = rc2 = 0; i < Idx_max; i++ )
+ rc2 |= ldap_int_thread_mutex_init( &resource_mutexes[i] );
+ assert( rc2 == 0 );
+ /* FIXME: Only for static libldap_r as in init.c? If so, why? */
+ atexit( count_resource_leaks );
+ }
+ }
+ return rc;
+}
+
+/* Used instead of ldap_int_thread_destroy by ldap_pvt_thread_destroy */
+int
+ldap_debug_thread_destroy( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_debug_thread_destroy" );
+ /* sleep(1) -- need to wait for thread pool to finish? */
+ rc = ldap_int_thread_destroy();
+ if( rc ) {
+ ERROR( rc, "ldap_debug_thread_destroy:threads" );
+ } else {
+ threading_enabled = 0;
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_set_concurrency( int n )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_set_concurrency" );
+ rc = ldap_int_thread_set_concurrency( n );
+ ERROR_IF( rc, "ldap_pvt_thread_set_concurrency" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_get_concurrency( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_get_concurrency" );
+ rc = ldap_int_thread_get_concurrency();
+ ERROR_IF( rc, "ldap_pvt_thread_get_concurrency" );
+ return rc;
+}
+
+unsigned int
+ldap_pvt_thread_sleep( unsigned int interval )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_sleep" );
+ rc = ldap_int_thread_sleep( interval );
+ ERROR_IF( rc, "ldap_pvt_thread_sleep" );
+ return 0;
+}
+
+static void
+thread_exiting( const char *how, const char *msg )
+{
+ ldap_pvt_thread_t thread;
+#if 0 /* Detached threads may exit after ldap_debug_thread_destroy(). */
+ ERROR_IF( !threading_enabled, msg );
+#endif
+ thread = ldap_pvt_thread_self();
+ if( tracethreads ) {
+ char buf[40];
+ fprintf( stderr, "== thr_debug: %s thread %s ==\n",
+ how, thread_name( buf, sizeof(buf), thread ) );
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ ldap_debug_thread_t *t = get_thread_info( thread, msg );
+ if( thread_info_detached( t ) )
+ remove_thread_info( t, msg );
+ });
+ }
+ adjust_count( Idx_unexited_thread, -1 );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thread_exiting( "Exiting", "ldap_pvt_thread_exit" );
+ ldap_int_thread_exit( retval );
+}
+
+typedef struct {
+ void *(*start_routine)( void * );
+ void *arg;
+} ldap_debug_thread_call_t;
+
+static void *
+ldap_debug_thread_wrapper( void *arg )
+{
+ void *ret;
+ ldap_debug_thread_call_t call = *(ldap_debug_thread_call_t *)arg;
+ free( arg );
+ ret = call.start_routine( call.arg );
+ thread_exiting( "Returning from", "ldap_debug_thread_wrapper" );
+ return ret;
+}
+
+int
+ldap_pvt_thread_create(
+ ldap_pvt_thread_t *thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_create" );
+
+ if( wrap_threads ) {
+ ldap_debug_thread_call_t *call = malloc(
+ sizeof( ldap_debug_thread_call_t ) );
+ assert( call != NULL );
+ call->start_routine = start_routine;
+ call->arg = arg;
+ start_routine = ldap_debug_thread_wrapper;
+ arg = call;
+ }
+ if( threadID ) {
+ with_thread_info_lock({
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ if( rc == 0 )
+ add_thread_info( "ldap_pvt_thread_create", thread, detach );
+ });
+ } else {
+ rc = ldap_int_thread_create( thread, detach, start_routine, arg );
+ }
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_create" );
+ if( wrap_threads )
+ free( arg );
+ } else {
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Created thread %s%s from thread %s ==\n",
+ thread_name( buf, sizeof(buf), *thread ),
+ detach ? " (detached)" : "",
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ adjust_count( Idx_unexited_thread, +1 );
+ if( !detach )
+ adjust_count( Idx_unjoined_thread, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ int rc;
+ ldap_debug_thread_t *t = NULL;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_join" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr, "== thr_debug: Joining thread %s in thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ),
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ if( threadID )
+ with_thread_info_lock( {
+ t = get_thread_info( thread, "ldap_pvt_thread_join" );
+ ERROR_IF( thread_info_detached( t ), "ldap_pvt_thread_join" );
+ } );
+ rc = ldap_int_thread_join( thread, thread_return );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_join" );
+ } else {
+ if( threadID )
+ with_thread_info_lock(
+ remove_thread_info( t, "ldap_pvt_thread_join" ) );
+ adjust_count( Idx_unjoined_thread, -1 );
+ }
+
+ return rc;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_kill" );
+ if( tracethreads ) {
+ char buf[40], buf2[40];
+ fprintf( stderr,
+ "== thr_debug: Killing thread %s (sig %i) from thread %s ==\n",
+ thread_name( buf, sizeof(buf), thread ), signo,
+ thread_name( buf2, sizeof(buf2), ldap_pvt_thread_self() ) );
+ }
+ rc = ldap_int_thread_kill( thread, signo );
+ ERROR_IF( rc, "ldap_pvt_thread_kill" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_yield" );
+ rc = ldap_int_thread_yield();
+ ERROR_IF( rc, "ldap_pvt_thread_yield" );
+ return rc;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_self" );
+#endif
+ return ldap_int_thread_self();
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ init_usage( &cond->usage, "ldap_pvt_thread_cond_init" );
+ rc = ldap_int_thread_cond_init( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_init" );
+ destroy_usage( &cond->usage );
+ } else {
+ adjust_count( Idx_cond, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_destroy" );
+ rc = ldap_int_thread_cond_destroy( WRAPPED( cond ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_cond_destroy" );
+ } else {
+ destroy_usage( &cond->usage );
+ adjust_count( Idx_cond, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_signal" );
+ rc = ldap_int_thread_cond_signal( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_signal" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ int rc;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_broadcast" );
+ rc = ldap_int_thread_cond_broadcast( WRAPPED( cond ) );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_broadcast" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_cond_wait(
+ ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ ldap_int_thread_t owner;
+ check_usage( &cond->usage, "ldap_pvt_thread_cond_wait:cond" );
+ check_usage( &mutex->usage, "ldap_pvt_thread_cond_wait:mutex" );
+ adjust_count( Idx_locked_mutex, -1 );
+ owner = GET_OWNER( mutex );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ RESET_OWNER( mutex );
+ rc = ldap_int_thread_cond_wait( WRAPPED( cond ), WRAPPED( mutex ) );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_cond_wait" );
+ SET_OWNER( mutex, rc ? owner : ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ ERROR_IF( rc, "ldap_pvt_thread_cond_wait" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ init_usage( &mutex->usage, "ldap_pvt_thread_mutex_init" );
+ rc = ldap_int_thread_mutex_init( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_init" );
+ destroy_usage( &mutex->usage );
+ } else {
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_destroy" );
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_destroy" );
+ rc = ldap_int_thread_mutex_destroy( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_mutex_destroy" );
+ } else {
+ destroy_usage( &mutex->usage );
+ RESET_OWNER( mutex );
+ adjust_count( Idx_mutex, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_lock" );
+ rc = ldap_int_thread_mutex_lock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_lock" );
+ } else {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_lock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_trylock" );
+ rc = ldap_int_thread_mutex_trylock( WRAPPED( mutex ) );
+ if( rc == 0 ) {
+ ASSERT_NO_OWNER( mutex, "ldap_pvt_thread_mutex_trylock" );
+ SET_OWNER( mutex, ldap_int_thread_self() );
+ adjust_count( Idx_locked_mutex, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ int rc;
+ check_usage( &mutex->usage, "ldap_pvt_thread_mutex_unlock" );
+ ASSERT_OWNER( mutex, "ldap_pvt_thread_mutex_unlock" );
+ RESET_OWNER( mutex ); /* Breaks if this thread did not own the mutex */
+ rc = ldap_int_thread_mutex_unlock( WRAPPED( mutex ) );
+ if( rc ) {
+ ERROR_IF( rc, "ldap_pvt_thread_mutex_unlock" );
+ } else {
+ adjust_count( Idx_locked_mutex, -1 );
+ }
+ return rc;
+}
+
+
+/* Wrappers for LDAP_THREAD_RDWR_IMPLEMENTATION: */
+
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ init_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_init" );
+ rc = ldap_int_thread_rdwr_init( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_init" );
+ destroy_usage( &rwlock->usage );
+ } else {
+ adjust_count( Idx_rdwr, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_destroy" );
+ rc = ldap_int_thread_rdwr_destroy( WRAPPED( rwlock ) );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_rdwr_destroy" );
+ } else {
+ destroy_usage( &rwlock->usage );
+ adjust_count( Idx_rdwr, -1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rlock" );
+ rc = ldap_int_thread_rdwr_rlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_rlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_rtrylock" );
+ return ldap_int_thread_rdwr_rtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_runlock" );
+ rc = ldap_int_thread_rdwr_runlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_runlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wlock" );
+ rc = ldap_int_thread_rdwr_wlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wlock" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wtrylock" );
+ return ldap_int_thread_rdwr_wtrylock( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ int rc;
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_wunlock" );
+ rc = ldap_int_thread_rdwr_wunlock( WRAPPED( rwlock ) );
+ ERROR_IF( rc, "ldap_pvt_thread_rdwr_wunlock" );
+ return rc;
+}
+
+#if defined(LDAP_RDWR_DEBUG) && !defined(LDAP_THREAD_HAVE_RDWR)
+
+int
+ldap_pvt_thread_rdwr_readers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_readers" );
+ return ldap_int_thread_rdwr_readers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_writers( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_writers" );
+ return ldap_int_thread_rdwr_writers( WRAPPED( rwlock ) );
+}
+
+int
+ldap_pvt_thread_rdwr_active( ldap_pvt_thread_rdwr_t *rwlock )
+{
+ check_usage( &rwlock->usage, "ldap_pvt_thread_rdwr_active" );
+ return ldap_int_thread_rdwr_active( WRAPPED( rwlock ) );
+}
+
+#endif /* LDAP_RDWR_DEBUG && !LDAP_THREAD_HAVE_RDWR */
+
+
+/* Some wrappers for LDAP_THREAD_POOL_IMPLEMENTATION: */
+#ifdef LDAP_THREAD_POOL_IMPLEMENTATION
+
+int
+ldap_pvt_thread_pool_init(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ int rc;
+ if( !options_done )
+ get_options();
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_init" );
+ rc = ldap_int_thread_pool_init( tpool, max_threads, max_pending );
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_init" );
+ } else {
+ adjust_count( Idx_tpool, +1 );
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_submit(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_submit" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_submit( tpool, start_routine, arg );
+ if( has_pool )
+ ERROR_IF( rc, "ldap_pvt_thread_pool_submit" );
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_maxthreads" );
+ return ldap_int_thread_pool_maxthreads( tpool, max_threads );
+}
+
+int
+ldap_pvt_thread_pool_backload( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_backload" );
+ return ldap_int_thread_pool_backload( tpool );
+}
+
+int
+ldap_pvt_thread_pool_destroy( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ int rc, has_pool;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_destroy" );
+ has_pool = (tpool && *tpool);
+ rc = ldap_int_thread_pool_destroy( tpool, run_pending );
+ if( has_pool ) {
+ if( rc ) {
+ ERROR( rc, "ldap_pvt_thread_pool_destroy" );
+ } else {
+ adjust_count( Idx_tpool, -1 );
+ }
+ }
+ return rc;
+}
+
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_pause" );
+ return ldap_int_thread_pool_pause( tpool );
+}
+
+int
+ldap_pvt_thread_pool_resume( ldap_pvt_thread_pool_t *tpool )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_resume" );
+ return ldap_int_thread_pool_resume( tpool );
+}
+
+int
+ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_getkey" );
+#endif
+ return ldap_int_thread_pool_getkey( xctx, key, data, kfree );
+}
+
+int
+ldap_pvt_thread_pool_setkey(
+ void *xctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ int rc;
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_setkey" );
+ rc = ldap_int_thread_pool_setkey(
+ xctx, key, data, kfree, olddatap, oldkfreep );
+ ERROR_IF( rc, "ldap_pvt_thread_pool_setkey" );
+ return rc;
+}
+
+void
+ldap_pvt_thread_pool_purgekey( void *key )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_purgekey" );
+ ldap_int_thread_pool_purgekey( key );
+}
+
+void *
+ldap_pvt_thread_pool_context( void )
+{
+#if 0 /* Function is used by ch_free() via slap_sl_contxt() in slapd */
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context" );
+#endif
+ return ldap_int_thread_pool_context();
+}
+
+void
+ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ERROR_IF( !threading_enabled, "ldap_pvt_thread_pool_context_reset" );
+ ldap_int_thread_pool_context_reset( vctx );
+}
+
+#endif /* LDAP_THREAD_POOL_IMPLEMENTATION */
+
+#endif /* LDAP_THREAD_DEBUG */
diff --git a/libraries/libldap_r/thr_nt.c b/libraries/libldap_r/thr_nt.c
new file mode 100644
index 0000000..1de232d
--- /dev/null
+++ b/libraries/libldap_r/thr_nt.c
@@ -0,0 +1,245 @@
+/* thr_nt.c - wrapper around NT threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_NT_THREADS )
+
+#define _WIN32_WINNT 0x0400
+#include <windows.h>
+#include <process.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+typedef struct ldap_int_thread_s {
+ long tid;
+ HANDLE thd;
+} ldap_int_thread_s;
+
+#ifndef NT_MAX_THREADS
+#define NT_MAX_THREADS 1024
+#endif
+
+static ldap_int_thread_s tids[NT_MAX_THREADS];
+static int ntids;
+
+
+/* mingw compiler very sensitive about getting prototypes right */
+typedef unsigned __stdcall thrfunc_t(void *);
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+int
+ldap_int_mutex_firstcreate( ldap_int_thread_mutex_t *mutex )
+{
+ if ( *mutex == NULL ) {
+ HANDLE p = CreateMutex( NULL, 0, NULL );
+ if ( InterlockedCompareExchangePointer((PVOID*)mutex, (PVOID)p, NULL) != NULL)
+ CloseHandle( p );
+ }
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ unsigned tid;
+ HANDLE thd;
+ int rc = -1;
+
+ thd = (HANDLE) _beginthreadex(NULL, LDAP_PVT_THREAD_STACK_SIZE, (thrfunc_t *) start_routine,
+ arg, 0, &tid);
+
+ if ( thd ) {
+ *thread = (ldap_pvt_thread_t) tid;
+ tids[ntids].tid = tid;
+ tids[ntids].thd = thd;
+ ntids++;
+ rc = 0;
+ }
+ return rc;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ _endthread( );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ DWORD status;
+ int i;
+
+ for (i=0; i<ntids; i++) {
+ if ( tids[i].tid == thread )
+ break;
+ }
+ if ( i > ntids ) return -1;
+
+ status = WaitForSingleObject( tids[i].thd, INFINITE );
+ for (; i<ntids; i++) {
+ tids[i] = tids[i+1];
+ }
+ ntids--;
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ Sleep( 0 );
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ *cond = CreateEvent( NULL, FALSE, FALSE, NULL );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ CloseHandle( *cv );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ SignalObjectAndWait( *mutex, *cond, INFINITE, FALSE );
+ WaitForSingleObject( *mutex, INFINITE );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ while ( WaitForSingleObject( *cond, 0 ) == WAIT_TIMEOUT )
+ SetEvent( *cond );
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ *mutex = CreateMutex( NULL, 0, NULL );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ CloseHandle( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mutex, INFINITE );
+ return status == WAIT_FAILED ? -1 : 0;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ ReleaseMutex( *mutex );
+ return ( 0 );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ DWORD status;
+ status = WaitForSingleObject( *mp, 0 );
+ return status == WAIT_FAILED || status == WAIT_TIMEOUT
+ ? -1 : 0;
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return GetCurrentThreadId();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *keyp )
+{
+ DWORD key = TlsAlloc();
+ if ( key != TLS_OUT_OF_INDEXES ) {
+ *keyp = key;
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ /* TlsFree returns 0 on failure */
+ return( TlsFree( key ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return ( TlsSetValue( key, data ) == 0 );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ void *ptr = TlsGetValue( key );
+ *data = ptr;
+ return( ptr ? GetLastError() : 0 );
+}
+
+#endif
diff --git a/libraries/libldap_r/thr_posix.c b/libraries/libldap_r/thr_posix.c
new file mode 100644
index 0000000..e4b4357
--- /dev/null
+++ b/libraries/libldap_r/thr_posix.c
@@ -0,0 +1,388 @@
+/* thr_posix.c - wrapper around posix and posixish thread implementations. */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_PTHREADS )
+
+#include <ac/errno.h>
+
+#ifdef REPLACE_BROKEN_YIELD
+#ifndef HAVE_NANOSLEEP
+#include <ac/socket.h>
+#endif
+#include <ac/time.h>
+#endif
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+#include <signal.h> /* For pthread_kill() */
+
+#if HAVE_PTHREADS < 6
+# define LDAP_INT_THREAD_ATTR_DEFAULT pthread_attr_default
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT pthread_condattr_default
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT pthread_mutexattr_default
+#else
+# define LDAP_INT_THREAD_ATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_CONDATTR_DEFAULT NULL
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT NULL
+#endif
+
+#ifdef LDAP_THREAD_DEBUG
+# if defined LDAP_INT_THREAD_MUTEXATTR /* May be defined in CPPFLAGS */
+# elif defined HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads hack */
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK_NP
+# else
+# define LDAP_INT_THREAD_MUTEXATTR PTHREAD_MUTEX_ERRORCHECK
+# endif
+static pthread_mutexattr_t mutex_attr;
+# undef LDAP_INT_THREAD_MUTEXATTR_DEFAULT
+# define LDAP_INT_THREAD_MUTEXATTR_DEFAULT &mutex_attr
+#endif
+
+#if HAVE_PTHREADS < 7
+#define ERRVAL(val) ((val) < 0 ? errno : 0)
+#else
+#define ERRVAL(val) (val)
+#endif
+
+int
+ldap_int_thread_initialize( void )
+{
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_init( &mutex_attr );
+ pthread_mutexattr_settype( &mutex_attr, LDAP_INT_THREAD_MUTEXATTR );
+#endif
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+#ifdef HAVE_PTHREAD_KILL_OTHER_THREADS_NP
+ /* LinuxThreads: kill clones */
+ pthread_kill_other_threads_np();
+#endif
+#ifdef LDAP_INT_THREAD_MUTEXATTR
+ pthread_mutexattr_destroy( &mutex_attr );
+#endif
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+#ifdef HAVE_PTHREAD_SETCONCURRENCY
+ return pthread_setconcurrency( n );
+#elif defined(HAVE_THR_SETCONCURRENCY)
+ return thr_setconcurrency( n );
+#else
+ return 0;
+#endif
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+#ifdef HAVE_PTHREAD_GETCONCURRENCY
+ return pthread_getconcurrency();
+#elif defined(HAVE_THR_GETCONCURRENCY)
+ return thr_getconcurrency();
+#else
+ return 0;
+#endif
+}
+#endif
+
+/* detachstate appeared in Draft 6, but without manifest constants.
+ * in Draft 7 they were called PTHREAD_CREATE_UNDETACHED and ...DETACHED.
+ * in Draft 8 on, ...UNDETACHED became ...JOINABLE.
+ */
+#ifndef PTHREAD_CREATE_JOINABLE
+#ifdef PTHREAD_CREATE_UNDETACHED
+#define PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED
+#else
+#define PTHREAD_CREATE_JOINABLE 0
+#endif
+#endif
+
+#ifndef PTHREAD_CREATE_DETACHED
+#define PTHREAD_CREATE_DETACHED 1
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void * ),
+ void *arg)
+{
+ int rtn;
+ pthread_attr_t attr;
+
+/* Always create the thread attrs, so we can set stacksize if we need to */
+#if HAVE_PTHREADS > 5
+ pthread_attr_init(&attr);
+#else
+ pthread_attr_create(&attr);
+#endif
+
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ /* this should be tunable */
+ pthread_attr_setstacksize( &attr, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+
+#if HAVE_PTHREADS > 5
+ detach = detach ? PTHREAD_CREATE_DETACHED : PTHREAD_CREATE_JOINABLE;
+#if HAVE_PTHREADS == 6
+ pthread_attr_setdetachstate(&attr, &detach);
+#else
+ pthread_attr_setdetachstate(&attr, detach);
+#endif
+#endif
+
+#if HAVE_PTHREADS < 5
+ rtn = pthread_create( thread, attr, start_routine, arg );
+#else
+ rtn = pthread_create( thread, &attr, start_routine, arg );
+#endif
+
+#if HAVE_PTHREADS > 5
+ pthread_attr_destroy(&attr);
+#else
+ pthread_attr_delete(&attr);
+ if( detach ) {
+ pthread_detach( thread );
+ }
+#endif
+
+#if HAVE_PTHREADS < 7
+ if ( rtn < 0 ) rtn = errno;
+#endif
+ return rtn;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pthread_exit( retval );
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+#if HAVE_PTHREADS < 7
+ void *dummy;
+ if (thread_return==NULL)
+ thread_return=&dummy;
+#endif
+ return ERRVAL( pthread_join( thread, thread_return ) );
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+#if defined(HAVE_PTHREAD_KILL) && HAVE_PTHREADS > 4
+ /* MacOS 10.1 is detected as v10 but has no pthread_kill() */
+ return ERRVAL( pthread_kill( thread, signo ) );
+#else
+ /* pthread package with DCE */
+ if (kill( getpid(), signo )<0)
+ return errno;
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+#ifdef REPLACE_BROKEN_YIELD
+#ifdef HAVE_NANOSLEEP
+ struct timespec t = { 0, 0 };
+ nanosleep(&t, NULL);
+#else
+ struct timeval tv = {0,0};
+ select( 0, NULL, NULL, NULL, &tv );
+#endif
+ return 0;
+
+#elif defined(HAVE_THR_YIELD)
+ thr_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 10
+ return sched_yield();
+
+#elif defined(_POSIX_THREAD_IS_GNU_PTH)
+ sched_yield();
+ return 0;
+
+#elif HAVE_PTHREADS == 6
+ pthread_yield(NULL);
+ return 0;
+
+#else
+ pthread_yield();
+ return 0;
+#endif
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_init(
+ cond, LDAP_INT_THREAD_CONDATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_destroy( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return ERRVAL( pthread_cond_broadcast( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_init(
+ mutex, LDAP_INT_THREAD_MUTEXATTR_DEFAULT ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_trylock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return ERRVAL( pthread_mutex_unlock( mutex ) );
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_self( void )
+{
+ return pthread_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pthread_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pthread_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pthread_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pthread_getspecific( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+#ifdef HAVE_PTHREAD_RWLOCK_DESTROY
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_init( rw, NULL ) );
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_destroy( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_rdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_tryrdlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_wrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_trywrlock( rw ) );
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return ERRVAL( pthread_rwlock_unlock( rw ) );
+}
+
+#endif /* HAVE_PTHREAD_RWLOCK_DESTROY */
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_PTHREADS */
+
diff --git a/libraries/libldap_r/thr_pth.c b/libraries/libldap_r/thr_pth.c
new file mode 100644
index 0000000..13377a0
--- /dev/null
+++ b/libraries/libldap_r/thr_pth.c
@@ -0,0 +1,231 @@
+/* thr_pth.c - wrappers around GNU Pth */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_GNU_PTH )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_RDWR_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+#include <errno.h>
+
+/*******************
+ * *
+ * GNU Pth Threads *
+ * *
+ *******************/
+
+static pth_attr_t detach_attr;
+static pth_attr_t joined_attr;
+
+int
+ldap_int_thread_initialize( void )
+{
+ if( !pth_init() ) {
+ return -1;
+ }
+ detach_attr = pth_attr_new();
+ joined_attr = pth_attr_new();
+#ifdef LDAP_PVT_THREAD_SET_STACK_SIZE
+ pth_attr_set( joined_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+ pth_attr_set( detach_attr, PTH_ATTR_STACK_SIZE, LDAP_PVT_THREAD_STACK_SIZE );
+#endif
+ return pth_attr_set( detach_attr, PTH_ATTR_JOINABLE, FALSE );
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ pth_attr_destroy(detach_attr);
+ pth_kill();
+ return 0;
+}
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ *thread = pth_spawn( detach ? detach_attr : joined_attr,
+ start_routine, arg );
+
+ return *thread == NULL ? errno : 0;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ pth_exit( retval );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ return pth_join( thread, thread_return ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return pth_raise( thread, signo ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ return pth_yield(NULL) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_init( cond ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 0 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return( pth_cond_notify( cond, 1 ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_cond_await( cond, mutex, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_init( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 0, NULL ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_release( mutex ) ? 0 : errno );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( pth_mutex_acquire( mutex, 1, NULL ) ? 0 : errno );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return pth_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return pth_key_create( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return pth_key_delete( key );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return pth_key_setdata( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ *data = pth_key_getdata( key );
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_RDWR
+int
+ldap_pvt_thread_rdwr_init( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_init( rw ) ? 0 : errno;
+}
+
+int
+ldap_pvt_thread_rdwr_destroy( ldap_pvt_thread_rdwr_t *rw )
+{
+ return 0;
+}
+
+int ldap_pvt_thread_rdwr_rlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_rtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RD, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_runlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 0, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wtrylock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_acquire( rw, PTH_RWLOCK_RW, 1, NULL ) ? 0 : errno;
+}
+
+int ldap_pvt_thread_rdwr_wunlock( ldap_pvt_thread_rdwr_t *rw )
+{
+ return pth_rwlock_release( rw ) ? 0 : errno;
+}
+
+#endif /* LDAP_THREAD_HAVE_RDWR */
+#endif /* HAVE_GNU_PTH */
diff --git a/libraries/libldap_r/thr_stub.c b/libraries/libldap_r/thr_stub.c
new file mode 100644
index 0000000..05357d9
--- /dev/null
+++ b/libraries/libldap_r/thr_stub.c
@@ -0,0 +1,305 @@
+/* thr_stub.c - stubs for the threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( NO_THREADS )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/***********************************************************************
+ * *
+ * no threads package defined for this system - fake ok returns from *
+ * all threads routines (making it single-threaded). *
+ * *
+ ***********************************************************************/
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+static void* ldap_int_status = NULL;
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)(void *),
+ void *arg)
+{
+ if( ! detach ) ldap_int_status = NULL;
+ start_routine( arg );
+ return 0;
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ if( retval != NULL ) {
+ ldap_int_status = retval;
+ }
+ return;
+}
+
+int
+ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **status )
+{
+ if(status != NULL) *status = ldap_int_status;
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cond )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return 0;
+}
+
+/*
+ * NO_THREADS requires a separate tpool implementation since
+ * generic ldap_pvt_thread_pool_wrapper loops forever.
+ */
+int
+ldap_pvt_thread_pool_init (
+ ldap_pvt_thread_pool_t *pool_out,
+ int max_concurrency, int max_pending )
+{
+ *pool_out = (ldap_pvt_thread_pool_t) 0;
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_submit (
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ (start_routine)(NULL, arg);
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_retract (
+ ldap_pvt_thread_pool_t *pool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_maxthreads ( ldap_pvt_thread_pool_t *tpool, int max_threads )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_query( ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_pool_param_t param, void *value )
+{
+ *(int *)value = -1;
+ return(-1);
+}
+
+int
+ldap_pvt_thread_pool_backload (
+ ldap_pvt_thread_pool_t *pool )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_pool_destroy (
+ ldap_pvt_thread_pool_t *pool, int run_pending )
+{
+ return(0);
+}
+
+void
+ldap_pvt_thread_pool_idle ( ldap_pvt_thread_pool_t *pool )
+{
+ return;
+}
+
+void
+ldap_pvt_thread_pool_unidle ( ldap_pvt_thread_pool_t *pool )
+{
+ return;
+}
+
+int ldap_pvt_thread_pool_getkey (
+ void *ctx, void *key, void **data, ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_setkey (
+ void *ctx, void *key,
+ void *data, ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap, ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ if ( olddatap ) *olddatap = NULL;
+ if ( oldkfreep ) *oldkfreep = 0;
+ return(0);
+}
+
+void ldap_pvt_thread_pool_purgekey( void *key )
+{
+}
+
+int ldap_pvt_thread_pool_pause (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_resume (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+int ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool )
+{
+ return(0);
+}
+
+void *ldap_pvt_thread_pool_context( )
+{
+ return(NULL);
+}
+
+void ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return(0);
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return(0);
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_pool_tid( void *vctx )
+{
+
+ return(0);
+}
+
+#endif /* NO_THREADS */
diff --git a/libraries/libldap_r/thr_thr.c b/libraries/libldap_r/thr_thr.c
new file mode 100644
index 0000000..dfb41a6
--- /dev/null
+++ b/libraries/libldap_r/thr_thr.c
@@ -0,0 +1,186 @@
+/* thr_thr.c - wrappers around solaris threads */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#if defined( HAVE_THR )
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+/*******************
+ * *
+ * Solaris Threads *
+ * *
+ *******************/
+
+int
+ldap_int_thread_initialize( void )
+{
+ return 0;
+}
+
+int
+ldap_int_thread_destroy( void )
+{
+ return 0;
+}
+
+#ifdef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency(int n)
+{
+ return thr_setconcurrency( n );
+}
+#endif
+
+#ifdef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency(void)
+{
+ return thr_getconcurrency();
+}
+#endif
+
+int
+ldap_pvt_thread_create( ldap_pvt_thread_t * thread,
+ int detach,
+ void *(*start_routine)( void *),
+ void *arg)
+{
+ return( thr_create( NULL, LDAP_PVT_THREAD_STACK_SIZE, start_routine,
+ arg, detach ? THR_DETACHED : 0, thread ) );
+}
+
+void
+ldap_pvt_thread_exit( void *retval )
+{
+ thr_exit( NULL );
+}
+
+int ldap_pvt_thread_join( ldap_pvt_thread_t thread, void **thread_return )
+{
+ thr_join( thread, NULL, thread_return );
+ return 0;
+}
+
+int
+ldap_pvt_thread_kill( ldap_pvt_thread_t thread, int signo )
+{
+ thr_kill( thread, signo );
+ return 0;
+}
+
+int
+ldap_pvt_thread_yield( void )
+{
+ thr_yield();
+ return 0;
+}
+
+int
+ldap_pvt_thread_cond_init( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_init( cond, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_cond_signal( ldap_pvt_thread_cond_t *cond )
+{
+ return( cond_signal( cond ) );
+}
+
+int
+ldap_pvt_thread_cond_broadcast( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_broadcast( cv ) );
+}
+
+int
+ldap_pvt_thread_cond_wait( ldap_pvt_thread_cond_t *cond,
+ ldap_pvt_thread_mutex_t *mutex )
+{
+ return( cond_wait( cond, mutex ) );
+}
+
+int
+ldap_pvt_thread_cond_destroy( ldap_pvt_thread_cond_t *cv )
+{
+ return( cond_destroy( cv ) );
+}
+
+int
+ldap_pvt_thread_mutex_init( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_init( mutex, USYNC_THREAD, NULL ) );
+}
+
+int
+ldap_pvt_thread_mutex_destroy( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_destroy( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_lock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_lock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_unlock( ldap_pvt_thread_mutex_t *mutex )
+{
+ return( mutex_unlock( mutex ) );
+}
+
+int
+ldap_pvt_thread_mutex_trylock( ldap_pvt_thread_mutex_t *mp )
+{
+ return( mutex_trylock( mp ) );
+}
+
+ldap_pvt_thread_t
+ldap_pvt_thread_self( void )
+{
+ return thr_self();
+}
+
+int
+ldap_pvt_thread_key_create( ldap_pvt_thread_key_t *key )
+{
+ return thr_keycreate( key, NULL );
+}
+
+int
+ldap_pvt_thread_key_destroy( ldap_pvt_thread_key_t key )
+{
+ return( 0 );
+}
+
+int
+ldap_pvt_thread_key_setdata( ldap_pvt_thread_key_t key, void *data )
+{
+ return thr_setspecific( key, data );
+}
+
+int
+ldap_pvt_thread_key_getdata( ldap_pvt_thread_key_t key, void **data )
+{
+ return thr_getspecific( key, data );
+}
+
+#endif /* HAVE_THR */
diff --git a/libraries/libldap_r/threads.c b/libraries/libldap_r/threads.c
new file mode 100644
index 0000000..ff9ed8f
--- /dev/null
+++ b/libraries/libldap_r/threads.c
@@ -0,0 +1,113 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_thr_debug.h" /* May redirect thread initialize/destroy calls */
+
+
+/*
+ * Common LDAP thread routines
+ * see thr_*.c for implementation specific routines
+ * see rdwr.c for generic reader/writer lock implementation
+ * see tpool.c for generic thread pool implementation
+ */
+
+
+int ldap_pvt_thread_initialize( void )
+{
+ int rc;
+ static int init = 0;
+ ldap_pvt_thread_rmutex_t rm;
+ ldap_pvt_thread_t tid;
+
+ /* we only get one shot at this */
+ if( init++ ) return -1;
+
+ rc = ldap_int_thread_initialize();
+ if( rc ) return rc;
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ rc = ldap_int_thread_pool_startup();
+ if( rc ) return rc;
+#endif
+
+ /* kludge to pull symbol definitions in */
+ ldap_pvt_thread_rmutex_init( &rm );
+ tid = ldap_pvt_thread_self();
+ ldap_pvt_thread_rmutex_lock( &rm, tid );
+ ldap_pvt_thread_rmutex_trylock( &rm, tid );
+ ldap_pvt_thread_rmutex_unlock( &rm, tid );
+ ldap_pvt_thread_rmutex_unlock( &rm, tid );
+ ldap_pvt_thread_rmutex_destroy( &rm );
+
+ return 0;
+}
+
+int ldap_pvt_thread_destroy( void )
+{
+#ifndef LDAP_THREAD_HAVE_TPOOL
+ (void) ldap_int_thread_pool_shutdown();
+#endif
+ return ldap_int_thread_destroy();
+}
+
+
+/*
+ * Default implementations of some LDAP thread routines
+ */
+
+#define LDAP_THREAD_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename the symbols defined below */
+
+
+#ifndef LDAP_THREAD_HAVE_GETCONCURRENCY
+int
+ldap_pvt_thread_get_concurrency ( void )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SETCONCURRENCY
+int
+ldap_pvt_thread_set_concurrency ( int concurrency )
+{
+ return 1;
+}
+#endif
+
+#ifndef LDAP_THREAD_HAVE_SLEEP
+/*
+ * Here we assume we have fully preemptive threads and that sleep()
+ * does the right thing.
+ */
+unsigned int
+ldap_pvt_thread_sleep(
+ unsigned int interval
+)
+{
+ sleep( interval );
+ return 0;
+}
+#endif
diff --git a/libraries/libldap_r/tpool.c b/libraries/libldap_r/tpool.c
new file mode 100644
index 0000000..17cee3b
--- /dev/null
+++ b/libraries/libldap_r/tpool.c
@@ -0,0 +1,1032 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/signal.h>
+#include <ac/stdarg.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/errno.h>
+
+#include "ldap-int.h"
+#include "ldap_pvt_thread.h" /* Get the thread interface */
+#include "ldap_queue.h"
+#define LDAP_THREAD_POOL_IMPLEMENTATION
+#include "ldap_thr_debug.h" /* May rename symbols defined below */
+
+#ifndef LDAP_THREAD_HAVE_TPOOL
+
+/* Thread-specific key with data and optional free function */
+typedef struct ldap_int_tpool_key_s {
+ void *ltk_key;
+ void *ltk_data;
+ ldap_pvt_thread_pool_keyfree_t *ltk_free;
+} ldap_int_tpool_key_t;
+
+/* Max number of thread-specific keys we store per thread.
+ * We don't expect to use many...
+ */
+#define MAXKEYS 32
+
+/* Max number of threads */
+#define LDAP_MAXTHR 1024 /* must be a power of 2 */
+
+/* (Theoretical) max number of pending requests */
+#define MAX_PENDING (INT_MAX/2) /* INT_MAX - (room to avoid overflow) */
+
+/* pool->ltp_pause values */
+enum { NOT_PAUSED = 0, WANT_PAUSE = 1, PAUSED = 2 };
+
+/* Context: thread ID and thread-specific key/data pairs */
+typedef struct ldap_int_thread_userctx_s {
+ ldap_pvt_thread_t ltu_id;
+ ldap_int_tpool_key_t ltu_key[MAXKEYS];
+} ldap_int_thread_userctx_t;
+
+
+/* Simple {thread ID -> context} hash table; key=ctx->ltu_id.
+ * Protected by ldap_pvt_thread_pool_mutex except during pauses,
+ * when it is read-only (used by pool_purgekey and pool_context).
+ * Protected by tpool->ltp_mutex during pauses.
+ */
+static struct {
+ ldap_int_thread_userctx_t *ctx;
+ /* ctx is valid when not NULL or DELETED_THREAD_CTX */
+# define DELETED_THREAD_CTX (&ldap_int_main_thrctx + 1) /* dummy addr */
+} thread_keys[LDAP_MAXTHR];
+
+#define TID_HASH(tid, hash) do { \
+ unsigned const char *ptr_ = (unsigned const char *)&(tid); \
+ unsigned i_; \
+ for (i_ = 0, (hash) = ptr_[0]; ++i_ < sizeof(tid);) \
+ (hash) += ((hash) << 5) ^ ptr_[i_]; \
+} while(0)
+
+
+/* Task for a thread to perform */
+typedef struct ldap_int_thread_task_s {
+ union {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_task_s) q;
+ LDAP_SLIST_ENTRY(ldap_int_thread_task_s) l;
+ } ltt_next;
+ ldap_pvt_thread_start_t *ltt_start_routine;
+ void *ltt_arg;
+} ldap_int_thread_task_t;
+
+typedef LDAP_STAILQ_HEAD(tcq, ldap_int_thread_task_s) ldap_int_tpool_plist_t;
+
+struct ldap_int_thread_pool_s {
+ LDAP_STAILQ_ENTRY(ldap_int_thread_pool_s) ltp_next;
+
+ /* protect members below, and protect thread_keys[] during pauses */
+ ldap_pvt_thread_mutex_t ltp_mutex;
+
+ /* not paused and something to do for pool_<wrapper/pause/destroy>() */
+ ldap_pvt_thread_cond_t ltp_cond;
+
+ /* ltp_active_count <= 1 && ltp_pause */
+ ldap_pvt_thread_cond_t ltp_pcond;
+
+ /* ltp_pause == 0 ? &ltp_pending_list : &empty_pending_list,
+ * maintaned to reduce work for pool_wrapper()
+ */
+ ldap_int_tpool_plist_t *ltp_work_list;
+
+ /* pending tasks, and unused task objects */
+ ldap_int_tpool_plist_t ltp_pending_list;
+ LDAP_SLIST_HEAD(tcl, ldap_int_thread_task_s) ltp_free_list;
+
+ /* The pool is finishing, waiting for its threads to close.
+ * They close when ltp_pending_list is done. pool_submit()
+ * rejects new tasks. ltp_max_pending = -(its old value).
+ */
+ int ltp_finishing;
+
+ /* Some active task needs to be the sole active task.
+ * Atomic variable so ldap_pvt_thread_pool_pausing() can read it.
+ * Note: Pauses adjust ltp_<open_count/vary_open_count/work_list>,
+ * so pool_<submit/wrapper>() mostly can avoid testing ltp_pause.
+ */
+ volatile sig_atomic_t ltp_pause;
+
+ /* Max number of threads in pool, or 0 for default (LDAP_MAXTHR) */
+ int ltp_max_count;
+
+ /* Max pending + paused + idle tasks, negated when ltp_finishing */
+ int ltp_max_pending;
+
+ int ltp_pending_count; /* Pending + paused + idle tasks */
+ int ltp_active_count; /* Active, not paused/idle tasks */
+ int ltp_open_count; /* Number of threads, negated when ltp_pause */
+ int ltp_starting; /* Currenlty starting threads */
+
+ /* >0 if paused or we may open a thread, <0 if we should close a thread.
+ * Updated when ltp_<finishing/pause/max_count/open_count> change.
+ * Maintained to reduce the time ltp_mutex must be locked in
+ * ldap_pvt_thread_pool_<submit/wrapper>().
+ */
+ int ltp_vary_open_count;
+# define SET_VARY_OPEN_COUNT(pool) \
+ ((pool)->ltp_vary_open_count = \
+ (pool)->ltp_pause ? 1 : \
+ (pool)->ltp_finishing ? -1 : \
+ ((pool)->ltp_max_count ? (pool)->ltp_max_count : LDAP_MAXTHR) \
+ - (pool)->ltp_open_count)
+};
+
+static ldap_int_tpool_plist_t empty_pending_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(empty_pending_list);
+
+static int ldap_int_has_thread_pool = 0;
+static LDAP_STAILQ_HEAD(tpq, ldap_int_thread_pool_s)
+ ldap_int_thread_pool_list =
+ LDAP_STAILQ_HEAD_INITIALIZER(ldap_int_thread_pool_list);
+
+static ldap_pvt_thread_mutex_t ldap_pvt_thread_pool_mutex;
+
+static void *ldap_int_thread_pool_wrapper( void *pool );
+
+static ldap_pvt_thread_key_t ldap_tpool_key;
+
+/* Context of the main thread */
+static ldap_int_thread_userctx_t ldap_int_main_thrctx;
+
+int
+ldap_int_thread_pool_startup ( void )
+{
+ ldap_int_main_thrctx.ltu_id = ldap_pvt_thread_self();
+ ldap_pvt_thread_key_create( &ldap_tpool_key );
+ return ldap_pvt_thread_mutex_init(&ldap_pvt_thread_pool_mutex);
+}
+
+int
+ldap_int_thread_pool_shutdown ( void )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ while ((pool = LDAP_STAILQ_FIRST(&ldap_int_thread_pool_list)) != NULL) {
+ (ldap_pvt_thread_pool_destroy)(&pool, 0); /* ignore thr_debug macro */
+ }
+ ldap_pvt_thread_mutex_destroy(&ldap_pvt_thread_pool_mutex);
+ ldap_pvt_thread_key_destroy( ldap_tpool_key );
+ return(0);
+}
+
+
+/* Create a thread pool */
+int
+ldap_pvt_thread_pool_init (
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads,
+ int max_pending )
+{
+ ldap_pvt_thread_pool_t pool;
+ int rc;
+
+ /* multiple pools are currently not supported (ITS#4943) */
+ assert(!ldap_int_has_thread_pool);
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+ if (! (1 <= max_pending && max_pending <= MAX_PENDING))
+ max_pending = MAX_PENDING;
+
+ *tpool = NULL;
+ pool = (ldap_pvt_thread_pool_t) LDAP_CALLOC(1,
+ sizeof(struct ldap_int_thread_pool_s));
+
+ if (pool == NULL) return(-1);
+
+ rc = ldap_pvt_thread_mutex_init(&pool->ltp_mutex);
+ if (rc != 0) {
+fail1:
+ LDAP_FREE(pool);
+ return(rc);
+ }
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_cond);
+ if (rc != 0) {
+fail2:
+ ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
+ goto fail1;
+ }
+ rc = ldap_pvt_thread_cond_init(&pool->ltp_pcond);
+ if (rc != 0) {
+ ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
+ goto fail2;
+ }
+
+ ldap_int_has_thread_pool = 1;
+
+ pool->ltp_max_count = max_threads;
+ SET_VARY_OPEN_COUNT(pool);
+ pool->ltp_max_pending = max_pending;
+
+ LDAP_STAILQ_INIT(&pool->ltp_pending_list);
+ pool->ltp_work_list = &pool->ltp_pending_list;
+ LDAP_SLIST_INIT(&pool->ltp_free_list);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_INSERT_TAIL(&ldap_int_thread_pool_list, pool, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ /* Start no threads just yet. That can break if the process forks
+ * later, as slapd does in order to daemonize. On at least POSIX,
+ * only the forking thread would survive in the child. Yet fork()
+ * can't unlock/clean up other threads' locks and data structures,
+ * unless pthread_atfork() handlers have been set up to do so.
+ */
+
+ *tpool = pool;
+ return(0);
+}
+
+
+/* Submit a task to be performed by the thread pool */
+int
+ldap_pvt_thread_pool_submit (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ struct ldap_int_thread_pool_s *pool;
+ ldap_int_thread_task_t *task;
+ ldap_pvt_thread_t thr;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ if (pool->ltp_pending_count >= pool->ltp_max_pending)
+ goto failed;
+
+ task = LDAP_SLIST_FIRST(&pool->ltp_free_list);
+ if (task) {
+ LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l);
+ } else {
+ task = (ldap_int_thread_task_t *) LDAP_MALLOC(sizeof(*task));
+ if (task == NULL)
+ goto failed;
+ }
+
+ task->ltt_start_routine = start_routine;
+ task->ltt_arg = arg;
+
+ pool->ltp_pending_count++;
+ LDAP_STAILQ_INSERT_TAIL(&pool->ltp_pending_list, task, ltt_next.q);
+
+ /* true if ltp_pause != 0 or we should open (create) a thread */
+ if (pool->ltp_vary_open_count > 0 &&
+ pool->ltp_open_count < pool->ltp_active_count+pool->ltp_pending_count)
+ {
+ if (pool->ltp_pause)
+ goto done;
+
+ pool->ltp_starting++;
+ pool->ltp_open_count++;
+ SET_VARY_OPEN_COUNT(pool);
+
+ if (0 != ldap_pvt_thread_create(
+ &thr, 1, ldap_int_thread_pool_wrapper, pool))
+ {
+ /* couldn't create thread. back out of
+ * ltp_open_count and check for even worse things.
+ */
+ pool->ltp_starting--;
+ pool->ltp_open_count--;
+ SET_VARY_OPEN_COUNT(pool);
+
+ if (pool->ltp_open_count == 0) {
+ /* no open threads at all?!?
+ */
+ ldap_int_thread_task_t *ptr;
+
+ /* let pool_destroy know there are no more threads */
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ LDAP_STAILQ_FOREACH(ptr, &pool->ltp_pending_list, ltt_next.q)
+ if (ptr == task) break;
+ if (ptr == task) {
+ /* no open threads, task not handled, so
+ * back out of ltp_pending_count, free the task,
+ * report the error.
+ */
+ pool->ltp_pending_count--;
+ LDAP_STAILQ_REMOVE(&pool->ltp_pending_list, task,
+ ldap_int_thread_task_s, ltt_next.q);
+ LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task,
+ ltt_next.l);
+ goto failed;
+ }
+ }
+ /* there is another open thread, so this
+ * task will be handled eventually.
+ */
+ }
+ }
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ done:
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+
+ failed:
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(-1);
+}
+
+static void *
+no_task( void *ctx, void *arg )
+{
+ return NULL;
+}
+
+/* Cancel a pending task that was previously submitted.
+ * Return 1 if the task was successfully cancelled, 0 if
+ * not found, -1 for invalid parameters
+ */
+int
+ldap_pvt_thread_pool_retract (
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_start_t *start_routine, void *arg )
+{
+ struct ldap_int_thread_pool_s *pool;
+ ldap_int_thread_task_t *task;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ LDAP_STAILQ_FOREACH(task, &pool->ltp_pending_list, ltt_next.q)
+ if (task->ltt_start_routine == start_routine &&
+ task->ltt_arg == arg) {
+ /* Could LDAP_STAILQ_REMOVE the task, but that
+ * walks ltp_pending_list again to find it.
+ */
+ task->ltt_start_routine = no_task;
+ task->ltt_arg = NULL;
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return task != NULL;
+}
+
+/* Set max #threads. value <= 0 means max supported #threads (LDAP_MAXTHR) */
+int
+ldap_pvt_thread_pool_maxthreads(
+ ldap_pvt_thread_pool_t *tpool,
+ int max_threads )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ if (! (0 <= max_threads && max_threads <= LDAP_MAXTHR))
+ max_threads = 0;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pool->ltp_max_count = max_threads;
+ SET_VARY_OPEN_COUNT(pool);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+}
+
+/* Inspect the pool */
+int
+ldap_pvt_thread_pool_query(
+ ldap_pvt_thread_pool_t *tpool,
+ ldap_pvt_thread_pool_param_t param,
+ void *value )
+{
+ struct ldap_int_thread_pool_s *pool;
+ int count = -1;
+
+ if ( tpool == NULL || value == NULL ) {
+ return -1;
+ }
+
+ pool = *tpool;
+
+ if ( pool == NULL ) {
+ return 0;
+ }
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ switch ( param ) {
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX:
+ count = pool->ltp_max_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_MAX_PENDING:
+ count = pool->ltp_max_pending;
+ if (count < 0)
+ count = -count;
+ if (count == MAX_PENDING)
+ count = 0;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_OPEN:
+ count = pool->ltp_open_count;
+ if (count < 0)
+ count = -count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STARTING:
+ count = pool->ltp_starting;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE:
+ count = pool->ltp_active_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PAUSING:
+ count = (pool->ltp_pause != 0);
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING:
+ count = pool->ltp_pending_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD:
+ count = pool->ltp_pending_count + pool->ltp_active_count;
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_ACTIVE_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_PENDING_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD_MAX:
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_STATE:
+ *((char **)value) =
+ pool->ltp_pause ? "pausing" :
+ !pool->ltp_finishing ? "running" :
+ pool->ltp_pending_count ? "finishing" : "stopping";
+ break;
+
+ case LDAP_PVT_THREAD_POOL_PARAM_UNKNOWN:
+ break;
+ }
+ ldap_pvt_thread_mutex_unlock( &pool->ltp_mutex );
+
+ if ( count > -1 ) {
+ *((int *)value) = count;
+ }
+
+ return ( count == -1 ? -1 : 0 );
+}
+
+/*
+ * true if pool is pausing; does not lock any mutex to check.
+ * 0 if not pause, 1 if pause, -1 if error or no pool.
+ */
+int
+ldap_pvt_thread_pool_pausing( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc = -1;
+ struct ldap_int_thread_pool_s *pool;
+
+ if ( tpool != NULL && (pool = *tpool) != NULL ) {
+ rc = (pool->ltp_pause != 0);
+ }
+
+ return rc;
+}
+
+/*
+ * wrapper for ldap_pvt_thread_pool_query(), left around
+ * for backwards compatibility
+ */
+int
+ldap_pvt_thread_pool_backload ( ldap_pvt_thread_pool_t *tpool )
+{
+ int rc, count;
+
+ rc = ldap_pvt_thread_pool_query( tpool,
+ LDAP_PVT_THREAD_POOL_PARAM_BACKLOAD, (void *)&count );
+
+ if ( rc == 0 ) {
+ return count;
+ }
+
+ return rc;
+}
+
+/* Destroy the pool after making its threads finish */
+int
+ldap_pvt_thread_pool_destroy ( ldap_pvt_thread_pool_t *tpool, int run_pending )
+{
+ struct ldap_int_thread_pool_s *pool, *pptr;
+ ldap_int_thread_task_t *task;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ LDAP_STAILQ_FOREACH(pptr, &ldap_int_thread_pool_list, ltp_next)
+ if (pptr == pool) break;
+ if (pptr == pool)
+ LDAP_STAILQ_REMOVE(&ldap_int_thread_pool_list, pool,
+ ldap_int_thread_pool_s, ltp_next);
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ if (pool != pptr) return(-1);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pool->ltp_finishing = 1;
+ SET_VARY_OPEN_COUNT(pool);
+ if (pool->ltp_max_pending > 0)
+ pool->ltp_max_pending = -pool->ltp_max_pending;
+
+ if (!run_pending) {
+ while ((task = LDAP_STAILQ_FIRST(&pool->ltp_pending_list)) != NULL) {
+ LDAP_STAILQ_REMOVE_HEAD(&pool->ltp_pending_list, ltt_next.q);
+ LDAP_FREE(task);
+ }
+ pool->ltp_pending_count = 0;
+ }
+
+ while (pool->ltp_open_count) {
+ if (!pool->ltp_pause)
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ }
+
+ while ((task = LDAP_SLIST_FIRST(&pool->ltp_free_list)) != NULL)
+ {
+ LDAP_SLIST_REMOVE_HEAD(&pool->ltp_free_list, ltt_next.l);
+ LDAP_FREE(task);
+ }
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ ldap_pvt_thread_cond_destroy(&pool->ltp_pcond);
+ ldap_pvt_thread_cond_destroy(&pool->ltp_cond);
+ ldap_pvt_thread_mutex_destroy(&pool->ltp_mutex);
+ LDAP_FREE(pool);
+ *tpool = NULL;
+ ldap_int_has_thread_pool = 0;
+ return(0);
+}
+
+/* Thread loop. Accept and handle submitted tasks. */
+static void *
+ldap_int_thread_pool_wrapper (
+ void *xpool )
+{
+ struct ldap_int_thread_pool_s *pool = xpool;
+ ldap_int_thread_task_t *task;
+ ldap_int_tpool_plist_t *work_list;
+ ldap_int_thread_userctx_t ctx, *kctx;
+ unsigned i, keyslot, hash;
+
+ assert(pool != NULL);
+
+ for ( i=0; i<MAXKEYS; i++ ) {
+ ctx.ltu_key[i].ltk_key = NULL;
+ }
+
+ ctx.ltu_id = ldap_pvt_thread_self();
+ TID_HASH(ctx.ltu_id, hash);
+
+ ldap_pvt_thread_key_setdata( ldap_tpool_key, &ctx );
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ /* thread_keys[] is read-only when paused */
+ while (pool->ltp_pause)
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+
+ /* find a key slot to give this thread ID and store a
+ * pointer to our keys there; start at the thread ID
+ * itself (mod LDAP_MAXTHR) and look for an empty slot.
+ */
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ for (keyslot = hash & (LDAP_MAXTHR-1);
+ (kctx = thread_keys[keyslot].ctx) && kctx != DELETED_THREAD_CTX;
+ keyslot = (keyslot+1) & (LDAP_MAXTHR-1));
+ thread_keys[keyslot].ctx = &ctx;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ pool->ltp_starting--;
+ pool->ltp_active_count++;
+
+ for (;;) {
+ work_list = pool->ltp_work_list; /* help the compiler a bit */
+ task = LDAP_STAILQ_FIRST(work_list);
+ if (task == NULL) { /* paused or no pending tasks */
+ if (--(pool->ltp_active_count) < 2) {
+ /* Notify pool_pause it is the sole active thread. */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+
+ do {
+ if (pool->ltp_vary_open_count < 0) {
+ /* Not paused, and either finishing or too many
+ * threads running (can happen if ltp_max_count
+ * was reduced). Let this thread die.
+ */
+ goto done;
+ }
+
+ /* We could check an idle timer here, and let the
+ * thread die if it has been inactive for a while.
+ * Only die if there are other open threads (i.e.,
+ * always have at least one thread open).
+ * The check should be like this:
+ * if (pool->ltp_open_count>1 && pool->ltp_starting==0)
+ * check timer, wait if ltp_pause, leave thread;
+ *
+ * Just use pthread_cond_timedwait() if we want to
+ * check idle time.
+ */
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+
+ work_list = pool->ltp_work_list;
+ task = LDAP_STAILQ_FIRST(work_list);
+ } while (task == NULL);
+
+ pool->ltp_active_count++;
+ }
+
+ LDAP_STAILQ_REMOVE_HEAD(work_list, ltt_next.q);
+ pool->ltp_pending_count--;
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ task->ltt_start_routine(&ctx, task->ltt_arg);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+ LDAP_SLIST_INSERT_HEAD(&pool->ltp_free_list, task, ltt_next.l);
+ }
+ done:
+
+ assert(!pool->ltp_pause); /* thread_keys writable, ltp_open_count >= 0 */
+
+ /* The ltp_mutex lock protects ctx->ltu_key from pool_purgekey()
+ * during this call, since it prevents new pauses. */
+ ldap_pvt_thread_pool_context_reset(&ctx);
+
+ ldap_pvt_thread_mutex_lock(&ldap_pvt_thread_pool_mutex);
+ thread_keys[keyslot].ctx = DELETED_THREAD_CTX;
+ ldap_pvt_thread_mutex_unlock(&ldap_pvt_thread_pool_mutex);
+
+ pool->ltp_open_count--;
+ SET_VARY_OPEN_COUNT(pool);
+ /* let pool_destroy know we're all done */
+ if (pool->ltp_open_count == 0)
+ ldap_pvt_thread_cond_signal(&pool->ltp_cond);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+
+ ldap_pvt_thread_exit(NULL);
+ return(NULL);
+}
+
+/* Arguments > ltp_pause to handle_pause(,PAUSE_ARG()). arg=PAUSE_ARG
+ * ensures (arg-ltp_pause) sets GO_* at need and keeps DO_PAUSE/GO_*.
+ */
+#define GO_IDLE 8
+#define GO_UNIDLE 16
+#define CHECK_PAUSE 32 /* if ltp_pause: GO_IDLE; wait; GO_UNIDLE */
+#define DO_PAUSE 64 /* CHECK_PAUSE; pause the pool */
+#define PAUSE_ARG(a) \
+ ((a) | ((a) & (GO_IDLE|GO_UNIDLE) ? GO_IDLE-1 : CHECK_PAUSE))
+
+static int
+handle_pause( ldap_pvt_thread_pool_t *tpool, int pause_type )
+{
+ struct ldap_int_thread_pool_s *pool;
+ int ret = 0, pause, max_ltp_pause;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ if (pause_type == CHECK_PAUSE && !pool->ltp_pause)
+ return(0);
+
+ /* Let pool_unidle() ignore requests for new pauses */
+ max_ltp_pause = pause_type==PAUSE_ARG(GO_UNIDLE) ? WANT_PAUSE : NOT_PAUSED;
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ pause = pool->ltp_pause; /* NOT_PAUSED, WANT_PAUSE or PAUSED */
+
+ /* If ltp_pause and not GO_IDLE|GO_UNIDLE: Set GO_IDLE,GO_UNIDLE */
+ pause_type -= pause;
+
+ if (pause_type & GO_IDLE) {
+ pool->ltp_pending_count++;
+ pool->ltp_active_count--;
+ if (pause && pool->ltp_active_count < 2) {
+ /* Tell the task waiting to DO_PAUSE it can proceed */
+ ldap_pvt_thread_cond_signal(&pool->ltp_pcond);
+ }
+ }
+
+ if (pause_type & GO_UNIDLE) {
+ /* Wait out pause if any, then cancel GO_IDLE */
+ if (pause > max_ltp_pause) {
+ ret = 1;
+ do {
+ ldap_pvt_thread_cond_wait(&pool->ltp_cond, &pool->ltp_mutex);
+ } while (pool->ltp_pause > max_ltp_pause);
+ }
+ pool->ltp_pending_count--;
+ pool->ltp_active_count++;
+ }
+
+ if (pause_type & DO_PAUSE) {
+ /* Tell everyone else to pause or finish, then await that */
+ ret = 0;
+ assert(!pool->ltp_pause);
+ pool->ltp_pause = WANT_PAUSE;
+ /* Let ldap_pvt_thread_pool_submit() through to its ltp_pause test,
+ * and do not finish threads in ldap_pvt_thread_pool_wrapper() */
+ pool->ltp_open_count = -pool->ltp_open_count;
+ SET_VARY_OPEN_COUNT(pool);
+ /* Hide pending tasks from ldap_pvt_thread_pool_wrapper() */
+ pool->ltp_work_list = &empty_pending_list;
+ /* Wait for this task to become the sole active task */
+ while (pool->ltp_active_count > 1) {
+ ldap_pvt_thread_cond_wait(&pool->ltp_pcond, &pool->ltp_mutex);
+ }
+ assert(pool->ltp_pause == WANT_PAUSE);
+ pool->ltp_pause = PAUSED;
+ }
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(ret);
+}
+
+/* Consider this task idle: It will not block pool_pause() in other tasks. */
+void
+ldap_pvt_thread_pool_idle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_IDLE));
+}
+
+/* Cancel pool_idle(). If the pool is paused, wait it out first. */
+void
+ldap_pvt_thread_pool_unidle( ldap_pvt_thread_pool_t *tpool )
+{
+ handle_pause(tpool, PAUSE_ARG(GO_UNIDLE));
+}
+
+/*
+ * If a pause was requested, wait for it. If several threads
+ * are waiting to pause, let through one or more pauses.
+ * The calling task must be active, not idle.
+ * Return 1 if we waited, 0 if not, -1 at parameter error.
+ */
+int
+ldap_pvt_thread_pool_pausecheck( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(CHECK_PAUSE));
+}
+
+/*
+ * Pause the pool. The calling task must be active, not idle.
+ * Return when all other tasks are paused or idle.
+ */
+int
+ldap_pvt_thread_pool_pause( ldap_pvt_thread_pool_t *tpool )
+{
+ return handle_pause(tpool, PAUSE_ARG(DO_PAUSE));
+}
+
+/* End a pause */
+int
+ldap_pvt_thread_pool_resume (
+ ldap_pvt_thread_pool_t *tpool )
+{
+ struct ldap_int_thread_pool_s *pool;
+
+ if (tpool == NULL)
+ return(-1);
+
+ pool = *tpool;
+
+ if (pool == NULL)
+ return(0);
+
+ ldap_pvt_thread_mutex_lock(&pool->ltp_mutex);
+
+ assert(pool->ltp_pause == PAUSED);
+ pool->ltp_pause = 0;
+ if (pool->ltp_open_count <= 0) /* true when paused, but be paranoid */
+ pool->ltp_open_count = -pool->ltp_open_count;
+ SET_VARY_OPEN_COUNT(pool);
+ pool->ltp_work_list = &pool->ltp_pending_list;
+
+ ldap_pvt_thread_cond_broadcast(&pool->ltp_cond);
+
+ ldap_pvt_thread_mutex_unlock(&pool->ltp_mutex);
+ return(0);
+}
+
+/*
+ * Get the key's data and optionally free function in the given context.
+ */
+int ldap_pvt_thread_pool_getkey(
+ void *xctx,
+ void *key,
+ void **data,
+ ldap_pvt_thread_pool_keyfree_t **kfree )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i;
+
+ if ( !ctx || !key || !data ) return EINVAL;
+
+ for ( i=0; i<MAXKEYS && ctx->ltu_key[i].ltk_key; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ *data = ctx->ltu_key[i].ltk_data;
+ if ( kfree ) *kfree = ctx->ltu_key[i].ltk_free;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static void
+clear_key_idx( ldap_int_thread_userctx_t *ctx, int i )
+{
+ for ( ; i < MAXKEYS-1 && ctx->ltu_key[i+1].ltk_key; i++ )
+ ctx->ltu_key[i] = ctx->ltu_key[i+1];
+ ctx->ltu_key[i].ltk_key = NULL;
+}
+
+/*
+ * Set or remove data for the key in the given context.
+ * key can be any unique pointer.
+ * kfree() is an optional function to free the data (but not the key):
+ * pool_context_reset() and pool_purgekey() call kfree(key, data),
+ * but pool_setkey() does not. For pool_setkey() it is the caller's
+ * responsibility to free any existing data with the same key.
+ * kfree() must not call functions taking a tpool argument.
+ */
+int ldap_pvt_thread_pool_setkey(
+ void *xctx,
+ void *key,
+ void *data,
+ ldap_pvt_thread_pool_keyfree_t *kfree,
+ void **olddatap,
+ ldap_pvt_thread_pool_keyfree_t **oldkfreep )
+{
+ ldap_int_thread_userctx_t *ctx = xctx;
+ int i, found;
+
+ if ( !ctx || !key ) return EINVAL;
+
+ for ( i=found=0; i<MAXKEYS; i++ ) {
+ if ( ctx->ltu_key[i].ltk_key == key ) {
+ found = 1;
+ break;
+ } else if ( !ctx->ltu_key[i].ltk_key ) {
+ break;
+ }
+ }
+
+ if ( olddatap ) {
+ if ( found ) {
+ *olddatap = ctx->ltu_key[i].ltk_data;
+ } else {
+ *olddatap = NULL;
+ }
+ }
+
+ if ( oldkfreep ) {
+ if ( found ) {
+ *oldkfreep = ctx->ltu_key[i].ltk_free;
+ } else {
+ *oldkfreep = 0;
+ }
+ }
+
+ if ( data || kfree ) {
+ if ( i>=MAXKEYS )
+ return ENOMEM;
+ ctx->ltu_key[i].ltk_key = key;
+ ctx->ltu_key[i].ltk_data = data;
+ ctx->ltu_key[i].ltk_free = kfree;
+ } else if ( found ) {
+ clear_key_idx( ctx, i );
+ }
+
+ return 0;
+}
+
+/* Free all elements with this key, no matter which thread they're in.
+ * May only be called while the pool is paused.
+ */
+void ldap_pvt_thread_pool_purgekey( void *key )
+{
+ int i, j;
+ ldap_int_thread_userctx_t *ctx;
+
+ assert ( key != NULL );
+
+ for ( i=0; i<LDAP_MAXTHR; i++ ) {
+ ctx = thread_keys[i].ctx;
+ if ( ctx && ctx != DELETED_THREAD_CTX ) {
+ for ( j=0; j<MAXKEYS && ctx->ltu_key[j].ltk_key; j++ ) {
+ if ( ctx->ltu_key[j].ltk_key == key ) {
+ if (ctx->ltu_key[j].ltk_free)
+ ctx->ltu_key[j].ltk_free( ctx->ltu_key[j].ltk_key,
+ ctx->ltu_key[j].ltk_data );
+ clear_key_idx( ctx, j );
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Find the context of the current thread.
+ * This is necessary if the caller does not have access to the
+ * thread context handle (for example, a slapd plugin calling
+ * slapi_search_internal()). No doubt it is more efficient
+ * for the application to keep track of the thread context
+ * handles itself.
+ */
+void *ldap_pvt_thread_pool_context( )
+{
+ void *ctx = NULL;
+
+ ldap_pvt_thread_key_getdata( ldap_tpool_key, &ctx );
+ return ctx ? ctx : (void *) &ldap_int_main_thrctx;
+}
+
+/*
+ * Free the context's keys.
+ * Must not call functions taking a tpool argument (because this
+ * thread already holds ltp_mutex when called from pool_wrapper()).
+ */
+void ldap_pvt_thread_pool_context_reset( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+ int i;
+
+ for ( i=MAXKEYS-1; i>=0; i--) {
+ if ( !ctx->ltu_key[i].ltk_key )
+ continue;
+ if ( ctx->ltu_key[i].ltk_free )
+ ctx->ltu_key[i].ltk_free( ctx->ltu_key[i].ltk_key,
+ ctx->ltu_key[i].ltk_data );
+ ctx->ltu_key[i].ltk_key = NULL;
+ }
+}
+
+ldap_pvt_thread_t ldap_pvt_thread_pool_tid( void *vctx )
+{
+ ldap_int_thread_userctx_t *ctx = vctx;
+
+ return ctx->ltu_id;
+}
+#endif /* LDAP_THREAD_HAVE_TPOOL */