Commit fd407a9f authored by Guillaume BIENKOWSKI's avatar Guillaume BIENKOWSKI
Browse files

Update LDAP provider to handle connection asynchronously.

This is done through a thread which is launched to keep the UI responsive.

Also configured the default server to "localhost" so that misconfiguration
doesn't query example.com...
parent 6d989a42
......@@ -18,6 +18,7 @@
#include "private.h"
#include "lpconfig.h"
#include "contact_providers_priv.h"
#include "mediastreamer2/mscommon.h"
#include <belle-sip/dict.h>
#ifdef BUILD_LDAP
......@@ -28,12 +29,6 @@
#define MAX_RUNNING_REQUESTS 10
#define FILTER_MAX_SIZE 512
typedef enum {
ANONYMOUS,
PLAIN,
SASL
} LDAPAuthMethod;
struct LDAPFriendData {
char* name;
char* sip;
......@@ -49,16 +44,19 @@ struct _LinphoneLDAPContactProvider
uint req_count;
// bind transaction
int bind_msgid;
const char* auth_mechanism;
bool_t connected;
ms_thread_t bind_thread;
// config
int use_tls;
LDAPAuthMethod auth_method;
const char* auth_method;
const char* username;
const char* password;
const char* server;
const char* bind_dn;
const char* sasl_authname;
const char* sasl_realm;
const char* base_object;
const char* sip_attr;
......@@ -70,6 +68,7 @@ struct _LinphoneLDAPContactProvider
int timeout;
int deref_aliases;
int max_results;
};
struct _LinphoneLDAPContactSearch
......@@ -92,7 +91,6 @@ LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPConta
{
LinphoneLDAPContactSearch* search = belle_sip_object_new(LinphoneLDAPContactSearch);
LinphoneContactSearch* base = LINPHONE_CONTACT_SEARCH(search);
struct timeval timeout = { cp->timeout, 0 };
linphone_contact_search_init(base, predicate, cb, cb_data);
......@@ -102,27 +100,6 @@ LinphoneLDAPContactSearch* linphone_ldap_contact_search_create(LinphoneLDAPConta
snprintf(search->filter, FILTER_MAX_SIZE-1, cp->filter, predicate);
search->filter[FILTER_MAX_SIZE-1] = 0;
ms_message("Calling ldap_search_ext with predicate '%s' on base %s", search->filter, cp->base_object);
int ret = ldap_search_ext(search->ld,
cp->base_object, // base from which to start
LDAP_SCOPE_SUBTREE,
search->filter, // search predicate
cp->attributes, // which attributes to get
0, // 0 = get attrs AND value, 1 = get attrs only
NULL,
NULL,
&timeout, // server timeout for the search
cp->max_results,// max result number
&search->msgid );
if( ret != LDAP_SUCCESS ){
ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
belle_sip_object_unref(search);
return NULL;
} else {
ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", search, search->msgid);
}
return search;
}
......@@ -136,7 +113,6 @@ unsigned int linphone_ldap_contact_search_result_count(LinphoneLDAPContactSearch
return obj->found_count;
}
static void linphone_ldap_contact_search_destroy( LinphoneLDAPContactSearch* obj )
{
//ms_message("~LinphoneLDAPContactSearch(%p)", obj);
......@@ -163,31 +139,7 @@ static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContact
static void linphone_ldap_contact_provider_conf_destroy(LinphoneLDAPContactProvider* obj );
static bool_t linphone_ldap_contact_provider_iterate(void *data);
static int linphone_ldap_contact_provider_bind_interact(LDAP *ld, unsigned flags, void *defaults, void *sasl_interact);
/* Authentication methods */
struct AuthMethodDescription{
LDAPAuthMethod method;
const char* description;
};
static struct AuthMethodDescription ldap_auth_method_description[] = {
{ANONYMOUS, "anonymous"},
{PLAIN, "plain"},
{SASL, "sasl"},
{0, NULL}
};
static LDAPAuthMethod linphone_ldap_contact_provider_auth_method( const char* description )
{
struct AuthMethodDescription* desc = ldap_auth_method_description;
while( desc && desc->description ){
if( strcmp(description, desc->description) == 0)
return desc->method;
desc++;
}
return ANONYMOUS;
}
static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req);
static void linphone_ldap_contact_provider_destroy_request_cb(void *req)
{
......@@ -210,38 +162,6 @@ static void linphone_ldap_contact_provider_destroy( LinphoneLDAPContactProvider*
linphone_ldap_contact_provider_conf_destroy(obj);
}
static int linphone_ldap_contact_provider_parse_bind_results( LinphoneLDAPContactProvider* obj, LDAPMessage* results )
{
int ret;
if( obj->auth_method == ANONYMOUS ) {
ms_message("ANONYMOUS BIND OK");
ret = LDAP_SUCCESS;
} else {
ms_message("Advanced BIND follow-up");
ret = ldap_sasl_interactive_bind(obj->ld,
NULL, // dn, should be NULL
"DIGEST-MD5", // TODO: use defined auth
NULL,NULL, // server and client controls
LDAP_SASL_QUIET, // never prompt, only use callback
linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed
obj, // private data
results, // result, to pass later on when a ldap_result() comes
&obj->auth_mechanism,
&obj->bind_msgid );
if( ret != LDAP_SUCCESS){
ms_error("ldap_parse_sasl_bind_result failed(%d)", ret);
}
}
if( ret == LDAP_SUCCESS ){
obj->connected = TRUE;
obj->bind_msgid = 0;
}
return ret;
}
static int linphone_ldap_contact_provider_complete_contact( LinphoneLDAPContactProvider* obj, struct LDAPFriendData* lf, const char* attr_name, const char* attr_value)
{
if( strcmp(attr_name, obj->name_attr ) == 0 ){
......@@ -335,7 +255,7 @@ static void linphone_ldap_contact_provider_handle_search_result( LinphoneLDAPCon
static bool_t linphone_ldap_contact_provider_iterate(void *data)
{
LinphoneLDAPContactProvider* obj = LINPHONE_LDAP_CONTACT_PROVIDER(data);
if( obj->ld && ((obj->req_count > 0) || (obj->bind_msgid != 0) )){
if( obj->ld && obj->connected && (obj->req_count > 0) ){
// never block
struct timeval timeout = {0,0};
......@@ -346,19 +266,14 @@ static bool_t linphone_ldap_contact_provider_iterate(void *data)
switch( ret ){
case -1:
{
ms_warning("Error in ldap_result : returned -1 (req_count %d, bind_msgid %d): %s", obj->req_count, obj->bind_msgid, ldap_err2string(errno));
ms_warning("Error in ldap_result : returned -1 (req_count %d): %s", obj->req_count, ldap_err2string(errno));
break;
}
case 0: break; // nothing to do
case LDAP_RES_BIND:
{
ms_message("iterate: LDAP_RES_BIND");
if( ldap_msgid( results ) != obj->bind_msgid ) {
ms_error("Bad msgid");
} else {
linphone_ldap_contact_provider_parse_bind_results( obj, results );
}
ms_error("iterate: unexpected LDAP_RES_BIND");
break;
}
case LDAP_RES_EXTENDED:
......@@ -389,6 +304,20 @@ static bool_t linphone_ldap_contact_provider_iterate(void *data)
if( results )
ldap_msgfree(results);
}
if( obj->ld && obj->connected ){
// check for pending searches
uint i;
for( i=0; i<obj->req_count; i++){
LinphoneLDAPContactSearch* search = (LinphoneLDAPContactSearch*)ms_list_nth_data( obj->requests, i );
if( search && search->msgid == 0){
ms_message("Found pending search %p (for %s), launching...", search, search->filter);
linphone_ldap_contact_provider_perform_search(obj, search);
}
}
}
return TRUE;
}
......@@ -410,6 +339,9 @@ static char* required_config_keys[] = {
"auth_method",
"username",
"password",
"bind_dn",
"sasl_authname",
"sasl_realm",
// search
"base_object",
......@@ -455,24 +387,25 @@ static void linphone_ldap_contact_provider_loadconfig(LinphoneLDAPContactProvide
// clone new config into the dictionary
obj->config = linphone_dictionary_ref(linphone_dictionary_clone(dict));
#if 0 // until sasl auth is set up, force anonymous auth.
linphone_dictionary_set_string(obj->config, "auth_method", "ANONYMOUS");
#endif
obj->use_tls = linphone_dictionary_get_int(obj->config, "use_tls", 0);
obj->timeout = linphone_dictionary_get_int(obj->config, "timeout", 10);
obj->deref_aliases = linphone_dictionary_get_int(obj->config, "deref_aliases", 0);
obj->max_results = linphone_dictionary_get_int(obj->config, "max_results", 50);
obj->auth_method = linphone_dictionary_get_string(obj->config, "auth_method", "ANONYMOUS");
obj->username = linphone_dictionary_get_string(obj->config, "username", "");
obj->password = linphone_dictionary_get_string(obj->config, "password", "");
obj->bind_dn = linphone_dictionary_get_string(obj->config, "bind_dn", "");
obj->base_object = linphone_dictionary_get_string(obj->config, "base_object", "dc=example,dc=com");
obj->server = linphone_dictionary_get_string(obj->config, "server", "ldap://192.168.0.230:10389");
obj->server = linphone_dictionary_get_string(obj->config, "server", "ldap://localhost");
obj->filter = linphone_dictionary_get_string(obj->config, "filter", "uid=*%s*");
obj->name_attr = linphone_dictionary_get_string(obj->config, "name_attribute", "givenName");
obj->sip_attr = linphone_dictionary_get_string(obj->config, "sip_attribute", "mobile");
/*
* Get authentication method
*/
obj->auth_method = linphone_ldap_contact_provider_auth_method(
linphone_dictionary_get_string(obj->config, "auth_method", "anonymous")
);
obj->sasl_authname = linphone_dictionary_get_string(obj->config, "sasl_authname", "");
obj->sasl_realm = linphone_dictionary_get_string(obj->config, "sasl_realm", "");
/*
* parse the attributes list
......@@ -520,20 +453,23 @@ static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
switch( interact->id ) {
case SASL_CB_GETREALM:
ms_message("* SASL_CB_GETREALM");
dflt=NULL;
ms_message("* SASL_CB_GETREALM -> %s", obj->sasl_realm);
dflt = obj->sasl_realm;
break;
case SASL_CB_USER:
case SASL_CB_AUTHNAME:
ms_message("* SASL_CB_AUTHNAME -> %s", obj->username);
dflt=obj->username;
ms_message("* SASL_CB_AUTHNAME -> %s", obj->sasl_authname);
dflt = obj->sasl_authname;
break;
case SASL_CB_USER:
ms_message("* SASL_CB_USER -> %s", obj->username);
dflt = obj->username;
break;
case SASL_CB_PASS:
ms_message("* SASL_CB_PASS -> %s", obj->password);
dflt=obj->password;
ms_message("* SASL_CB_PASS (hidden)");
dflt = obj->password;
break;
default:
ms_message("my_sasl_interact asked for unknown %lx\n",interact->id);
ms_message("SASL interact asked for unknown id %lx\n",interact->id);
}
interact->result = (dflt && *dflt) ? dflt : (const char*)"";
interact->len = strlen( (const char*)interact->result );
......@@ -543,32 +479,42 @@ static int linphone_ldap_contact_provider_bind_interact(LDAP *ld,
return LDAP_SUCCESS;
}
static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
static void* ldap_bind_thread_func( void*arg)
{
LinphoneLDAPContactProvider* obj = linphone_ldap_contact_provider_ref(arg);
const char* auth_mechanism = obj->auth_method;
int ret;
const char* auth_mechanism = linphone_dictionary_get_string(obj->config, "auth_method", "anonymous");
LDAPAuthMethod method = obj->auth_method;
if( method == ANONYMOUS ){
// for anonymous authentication, use a simple sasl_bind
struct berval creds = {strlen(obj->password), ms_strdup(obj->password)};
ret = ldap_sasl_bind(obj->ld, obj->base_object, NULL, &creds, NULL, NULL, &obj->bind_msgid);
if(creds.bv_val) ms_free(creds.bv_val);
} else {
ret = ldap_sasl_interactive_bind(obj->ld,
NULL, // dn, should be NULL
"SIMPLE",//"DIGEST-MD5",
NULL,NULL, // server and client controls
LDAP_SASL_QUIET, // never prompt, only use callback
linphone_ldap_contact_provider_bind_interact, // callback to call when info is needed
obj, // private data
NULL, // result, to pass later on when a ldap_result() comes
&obj->auth_mechanism,
&obj->bind_msgid );
if( (strcmp(auth_mechanism, "ANONYMOUS") == 0) || (strcmp(auth_mechanism, "SIMPLE") == 0) )
{
struct berval passwd = { strlen(obj->password), ms_strdup(obj->password)};
auth_mechanism = LDAP_SASL_SIMPLE;
ret = ldap_sasl_bind_s(obj->ld,
obj->bind_dn,
auth_mechanism,
&passwd,
NULL,
NULL,
NULL);
ms_free(passwd.bv_val);
}
if( ret == LDAP_SUCCESS || ret == LDAP_SASL_BIND_IN_PROGRESS ) {
if( ret == LDAP_SASL_BIND_IN_PROGRESS) ms_message("BIND_IN_PROGRESS");
ms_message("LDAP bind request sent, auth: %s, msgid %x", obj->auth_mechanism?obj->auth_mechanism:"-", obj->bind_msgid);
else
{
ms_message("LDAP interactive bind");
ret = ldap_sasl_interactive_bind_s(obj->ld,
obj->bind_dn,
auth_mechanism,
NULL,NULL,
LDAP_SASL_QUIET,
linphone_ldap_contact_provider_bind_interact,
obj);
}
if( ret == LDAP_SUCCESS ) {
ms_message("LDAP bind OK");
obj->connected = 1;
} else {
int err;
ldap_get_option(obj->ld, LDAP_OPT_RESULT_CODE, &err);
......@@ -576,6 +522,15 @@ static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj
ret, err, ldap_err2string(err), auth_mechanism );
}
obj->bind_thread = 0;
linphone_ldap_contact_provider_unref(obj);
return (void*)0;
}
static int linphone_ldap_contact_provider_bind( LinphoneLDAPContactProvider* obj )
{
// perform the bind in an alternate thread, so that we don't stall the main loop
ms_thread_create(&obj->bind_thread, NULL, ldap_bind_thread_func, obj);
return 0;
}
......@@ -619,7 +574,7 @@ LinphoneLDAPContactProvider*linphone_ldap_contact_provider_create(LinphoneCore*
} else {
// prevents blocking calls to bind() when the server is invalid, but this is not working for now..
// see bug https://bugzilla.mozilla.org/show_bug.cgi?id=79509
ldap_set_option( obj->ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
//ldap_set_option( obj->ld, LDAP_OPT_CONNECT_ASYNC, LDAP_OPT_ON);
// register our hook into iterate so that LDAP can do its magic asynchronously.
linphone_core_add_iterate_hook(lc, linphone_ldap_contact_provider_iterate, obj);
......@@ -678,22 +633,67 @@ static unsigned int linphone_ldap_contact_provider_cancel_search(LinphoneContact
return ret;
}
static int linphone_ldap_contact_provider_perform_search( LinphoneLDAPContactProvider* obj, LinphoneLDAPContactSearch* req)
{
int ret = -1;
struct timeval timeout = { obj->timeout, 0 };
if( req->msgid == 0 ){
ms_message ( "Calling ldap_search_ext with predicate '%s' on base %s", req->filter, obj->base_object );
ret = ldap_search_ext(obj->ld,
obj->base_object,// base from which to start
LDAP_SCOPE_SUBTREE,
req->filter, // search predicate
obj->attributes, // which attributes to get
0, // 0 = get attrs AND value, 1 = get attrs only
NULL,
NULL,
&timeout, // server timeout for the search
obj->max_results,// max result number
&req->msgid );
if( ret != LDAP_SUCCESS ){
ms_error("Error ldap_search_ext returned %d (%s)", ret, ldap_err2string(ret));
} else {
ms_message("LinphoneLDAPContactSearch created @%p : msgid %d", req, req->msgid);
}
} else {
ms_warning( "LDAP Search already performed for %s, msgid %d", req->filter, req->msgid);
}
return ret;
}
static LinphoneLDAPContactSearch* linphone_ldap_contact_provider_begin_search ( LinphoneLDAPContactProvider* obj,
const char* predicate,
ContactSearchCallback cb,
void* cb_data )
{
bool_t connected = obj->connected;
// if we're not yet connected, bind
if( !obj->connected ) linphone_ldap_contact_provider_bind(obj);
if( !connected ) {
if( !obj->bind_thread ) linphone_ldap_contact_provider_bind(obj);
}
LinphoneLDAPContactSearch* request = linphone_ldap_contact_search_create ( obj, predicate, cb, cb_data );
LinphoneLDAPContactSearch* request = linphone_ldap_contact_search_create( obj, predicate, cb, cb_data );
if ( request != NULL ) {
if( connected ){
int ret = linphone_ldap_contact_provider_perform_search(obj, request);
ms_message ( "Created search %d for '%s', msgid %d, @%p", obj->req_count, predicate, request->msgid, request );
if( ret != LDAP_SUCCESS ){
belle_sip_object_unref(request);
request = NULL;
}
} else {
ms_message("Delayed search, wait for connection");
}
obj->requests = ms_list_append ( obj->requests, request );
if( request != NULL ) {
obj->requests = ms_list_append ( obj->requests, request );
obj->req_count++;
}
return request;
}
......@@ -708,13 +708,10 @@ static int linphone_ldap_contact_provider_marshal(LinphoneLDAPContactProvider* o
error = belle_sip_snprintf(buff, buff_size, offset, "req_count:%d,\n", obj->req_count);
if(error!= BELLE_SIP_OK) return error;
error = belle_sip_snprintf(buff, buff_size, offset, "bind_msgid:%d,\n", obj->bind_msgid);
if(error!= BELLE_SIP_OK) return error;
error = belle_sip_snprintf(buff, buff_size, offset,
"CONFIG:\n"
"tls: %d \n"
"auth: %d \n"
"auth: %s \n"
"user: %s \n"
"pass: %s \n"
"server: %s \n"
......
......@@ -76,7 +76,7 @@ static void linphone_gtk_ldap_load_settings(GtkWidget* param)
entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_server"));
gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"server", "ldap://example.com") );
gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"server", "ldap://localhost") );
entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_username"));
gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"username", "") );
......@@ -86,7 +86,7 @@ static void linphone_gtk_ldap_load_settings(GtkWidget* param)
// TODO
// GtkComboBox* cbox = GTK_COMBO_BOX(linphone_gtk_get_widget(pb,"ldap_auth_method"));
// gtk_combo_box_set_active(entry, linphone_dictionary_get_string(ldap_conf,"auth_method", "anonymous") );
// gtk_combo_box_set_active(entry, linphone_dictionary_get_string(ldap_conf,"auth_method", "ANONYMOUS") );
entry = GTK_ENTRY(linphone_gtk_get_widget(pb,"ldap_base_object"));
gtk_entry_set_text(entry, linphone_dictionary_get_string(ldap_conf,"base_object", "dc=example,dc=com") );
......@@ -113,6 +113,9 @@ static void linphone_gtk_ldap_load_settings(GtkWidget* param)
spin = GTK_SPIN_BUTTON(linphone_gtk_get_widget(pb,"ldap_timeout"));
gtk_spin_button_set_value(spin, linphone_dictionary_get_int(ldap_conf,"timeout", 10) );
// TODO: add missing LDAP components
// TODO: move this to an external box
}
void linphone_gtk_ldap_reset(GtkWidget *tabmgr)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment