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