I'm looking into a Debian bug report: https://bugs.debian.org/860947
ldap_int_sasl_init says:
/* XXX not threadsafe */
static int sasl_initialized = 0;
I'm not yet sure whether it's the cause of that report, but it does seem
to be problematic. I'm seeing spurious "no such mechanism" results
and/or crashes in mutiple environments performing SASL binds in
parallel:
- slapd with multiple syncrepl clients all using SASL binds,
- the ldclt tool from the 389-ds project, and
- the test program I'm about to describe below.
I wrote a test program for this issue, and I'd like to ask the list to
check the code before I submit an ITS, in case I've made a mistake that
renders my test invalid.
gcc -g sasltest.c -pthread -llber -lldap -lsasl2 -DSASL
As I understand it, -lldap_r should not be needed here since each thread
creates its own LDAP instance. (It doesn't seem to change the results,
anyway.)
Built without -DSASL, it uses simple binds, and appears to work fine,
but again, I'm not confident my code is correct.
Thanks!
Ryan
#include <assert.h>
#include <ldap.h>
#include <pthread.h>
#ifdef SASL
#include <sasl/sasl.h>
#endif
#include <stdio.h>
#define THREADS 10
#define BINDS 5
#define URI "ldap://"
#define BINDDN "cn=admin,dc=example,dc=com"
#define USER "admin"
#define PASS "admin"
static void check(int rc) {
if (rc == LDAP_SUCCESS) {
return;
}
char *err = ldap_err2string(rc);
fprintf(stderr, "rc = %d (%s)\n", rc, err);
}
#ifdef SASL
static int sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *in) {
sasl_interact_t *interact = in;
while (interact->id != SASL_CB_LIST_END) {
switch (interact->id) {
case SASL_CB_AUTHNAME:
interact->result = USER;
interact->len = strlen(USER);
break;
case SASL_CB_USER:
interact->result = "";
interact->len = 0;
break;
case SASL_CB_PASS:
interact->result = PASS;
interact->len = strlen(PASS);
break;
default:
assert(0);
}
interact++;
}
return 0;
}
#endif
static void *bind_thread(void *unused) {
LDAP *ld;
int rc;
int i;
int version = 3;
rc = ldap_initialize(&ld, URI);
check(rc); assert(rc == LDAP_SUCCESS);
rc = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
check(rc); assert(rc == LDAP_SUCCESS);
for (i = 0; i < BINDS; i++) {
#ifdef SASL
rc = ldap_sasl_interactive_bind_s(ld, NULL, "DIGEST-MD5", NULL, NULL, LDAP_SASL_QUIET, sasl_interact, NULL);
#else
rc = ldap_sasl_bind_s(ld, BINDDN, LDAP_SASL_SIMPLE, ber_bvstr(PASS), NULL, NULL, NULL);
#endif
check(rc); assert(rc == LDAP_SUCCESS);
}
rc = ldap_unbind_ext_s(ld, NULL, NULL);
check(rc); assert(rc == LDAP_SUCCESS);
return NULL;
}
int main (int argc, char *argv[]) {
pthread_t threads[THREADS];
int rc;
int i;
int debug;
/* give libldap a chance to do global init */
ldap_get_option(NULL, LDAP_OPT_DEBUG_LEVEL, &debug);
for (i = 0; i < THREADS; i++) {
rc = pthread_create(&threads[i], NULL, bind_thread, NULL);
assert(!rc);
}
for (i = 0; i < THREADS; i++) {
rc = pthread_join(threads[i], NULL);
assert (!rc);
}
pthread_exit(NULL);
return 0;
}