On Tue, Feb 28, 2012 at 8:25 PM, Hallvard B Furuseth <
h.b.furuseth(a)usit.uio.no> wrote:
The essential parts here are creating the LDAP* with
ldap_initialize() or whatever, and ldap_start_tls_s().
Note that ldap_unbind() is misnamed, it should have been
called ldap_destroy(). It does send an unbind, but the more important
part is that it destroys the LDAP*.
Got it. Thanks for your reminding, Hallvard.
Don't know where it says that. Options with LDAP* NULL are global,
but global options are copied to newly created LDAP*s. Changing
a global option has no effect on an existing LDAP*, unless the
option itself really is global - if any option is. I suppose
some SASL or TLS opts might be, if some SASL/TLS library does
not support per-connection options.
Try this:
/* Usage: <program> [ serverhost [ 2nd arg prevents unbind/reinit ]] */
#include <assert.h>
#include <ldap.h>
#include <stdio.h>
int main(int argc, char **argv) {
int i, r, flags[] = { LDAP_OPT_X_TLS_NEVER, LDAP_OPT_X_TLS_DEMAND };
LDAP *ld;
i = LDAP_VERSION3;
r = ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &i);
assert(r == LDAP_OPT_SUCCESS);
for (i = 0; i < 2; i++) {
r = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &flags[i]);
assert(r == LDAP_OPT_SUCCESS);
if (i < argc-2)
continue;
r = ldap_initialize(&ld, argv[1]);
assert(r == LDAP_SUCCESS);
r = ldap_start_tls_s(ld, NULL, NULL);
printf("#%d => %s%s", i+1, ldap_err2string(r), i ? "\n" :
", ");
ldap_unbind_ext_s(ld, NULL, NULL);
}
return 0;
}
./testprog servername
./testprog servername x
Thanks a lot for your testing example. Based on your code, I did some minor
customization to test the cert options:
#include <assert.h>
#include <ldap.h>
#include <stdio.h>
int main() {
int i, r, flags[] = { LDAP_OPT_X_TLS_NEVER, LDAP_OPT_X_TLS_DEMAND };
LDAP *ld;
const char *username = "cn=test,cn=Users,dc=my,dc=server,dc=com";
const char *password = "test";
struct berval password_ber = { 0, NULL };
int msgid = 0;
LDAPMessage *result = NULL;
LDAPControl **ctrls = NULL;
int err = 0;
char *matched = NULL;
char *info = NULL;
char **refs = NULL;
i = LDAP_VERSION3;
r = ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &i);
assert(r == LDAP_OPT_SUCCESS);
for (i = 0; i < 2; i++) {
r = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, &flags[i]);
assert(r == LDAP_OPT_SUCCESS);
r = ldap_initialize(&ld, "ldaps://my.server.com:3269");
assert(r == LDAP_SUCCESS && ld != NULL);
password_ber.bv_val = ber_strdup(password);
password_ber.bv_len = strlen(password_ber.bv_val);
r = ldap_sasl_bind(ld, username, LDAP_SASL_SIMPLE, &password_ber,
NULL, NULL, &msgid);
printf("ldap_sasl_bind(): #%d => return status is %s\n", i+1,
ldap_err2string(r));
printf("ldap_sasl_bind(): #%d => msgid is %d\n", i+1, msgid);
assert(msgid != -1);
r = ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &result);
assert(r != -1);
r = ldap_parse_result(ld, result, &err, &matched, &info, &refs,
&ctrls, 1);
printf("ldap_parse_result(): #%d => return status is %s\n", i+1,
ldap_err2string(r));
printf("ldap_parse_result(): #%d => error is %s\n", i+1,
ldap_err2string(err));
if (ctrls)
ldap_controls_free(ctrls);
if (password_ber.bv_val)
ber_memfree(password_ber.bv_val);
if (matched)
ber_memfree(matched);
if (info)
ber_memfree(info);
if (refs)
ber_memvfree((void **)refs);
if (ld)
ldap_unbind_ext(ld, NULL, NULL);
}
return 0;
}
The output is:
ldap_sasl_bind(): #1 => return status is Success
ldap_sasl_bind(): #1 => msgid is 1
ldap_parse_result(): #1 => return status is Success
ldap_parse_result(): #1 => error is Success
ldap_sasl_bind(): #2 => return status is Success
ldap_sasl_bind(): #2 => msgid is 1
ldap_parse_result(): #2 => return status is Success
ldap_parse_result(): #2 => error is Success
So, it does seem that the value for the cert option
LDAP_OPT_X_TLS_REQUIRE_CERT has a global effect. It seems to be per
process, instead of per binding.
Is there a way to set this option on a per binding basis, so that different
bindings can have different choices on the cert requirement?
Thanks,
Qiang