On Tue, Feb 28, 2012 at 8:25 PM, Hallvard B Furuseth <h.b.furuseth@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