[Date Prev][Date Next] [Chronological] [Thread] [Top]

test program for ldap_int_sasl_init issue



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;
}