mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
494 lines
9.4 KiB
494 lines
9.4 KiB
/*
|
|
* Kamailio LDAP Module
|
|
*
|
|
* Copyright (C) 2007 University of North Carolina
|
|
*
|
|
* Original author: Christian Schlatter, cs@unc.edu
|
|
*
|
|
*
|
|
* This file is part of Kamailio, a free SIP server.
|
|
*
|
|
* Kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* Kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "../../ut.h"
|
|
|
|
#include <ldap.h>
|
|
|
|
#include "ldap_api_fn.h"
|
|
#include "api.h"
|
|
#include "ldap_connect.h"
|
|
#include "ldap_escape.h"
|
|
#include "ld_session.h"
|
|
|
|
static LDAP* last_ldap_handle = NULL;
|
|
static LDAPMessage* last_ldap_result_holder = NULL;
|
|
static LDAPMessage* last_ldap_result = NULL;
|
|
|
|
int get_connected_ldap_session(
|
|
char* _lds_name,
|
|
struct ld_session** _lds);
|
|
int lds_search(char* _lds_name,
|
|
char* _dn,
|
|
int _scope,
|
|
char* _filter,
|
|
char** _attrs,
|
|
struct timeval* _search_timeout,
|
|
int* _ld_result_count,
|
|
int* _ld_error);
|
|
|
|
|
|
int load_ldap(ldap_api_t *api)
|
|
{
|
|
if (api == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
api->ldap_params_search = ldap_params_search;
|
|
api->ldap_url_search = ldap_url_search;
|
|
api->ldap_result_attr_vals = ldap_get_attr_vals;
|
|
api->ldap_value_free_len = ldap_value_free_len;
|
|
api->ldap_result_next = ldap_inc_result_pointer;
|
|
api->ldap_str2scope = ldap_str2scope;
|
|
api->ldap_rfc4515_escape = ldap_rfc4515_escape;
|
|
api->get_ldap_handle = get_ldap_handle;
|
|
api->get_last_ldap_result = get_last_ldap_result;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int get_ldap_handle(char* _lds_name, LDAP** _ldap_handle)
|
|
{
|
|
int rc;
|
|
struct ld_session* lds;
|
|
|
|
rc = get_connected_ldap_session(_lds_name, &lds);
|
|
if (rc == 0)
|
|
{
|
|
*_ldap_handle = lds->handle;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int get_connected_ldap_session(char* _lds_name, struct ld_session** _lds)
|
|
{
|
|
/*
|
|
* get ld session
|
|
*/
|
|
if ((*_lds = get_ld_session(_lds_name)) == NULL)
|
|
{
|
|
LM_ERR("[%s]: ldap_session not found\n", _lds_name);
|
|
return -1;
|
|
}
|
|
|
|
/* try to reconnect if ldap session handle is NULL */
|
|
if ((*_lds)->handle == NULL)
|
|
{
|
|
if (ldap_reconnect(_lds_name) == 0)
|
|
{
|
|
if ((*_lds = get_ld_session(_lds_name)) == NULL)
|
|
{
|
|
LM_ERR("[%s]: ldap_session not found\n", _lds_name);
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (last_ldap_result_holder != NULL)
|
|
{
|
|
ldap_msgfree(last_ldap_result_holder);
|
|
last_ldap_result_holder = NULL;
|
|
last_ldap_result = NULL;
|
|
}
|
|
ldap_disconnect(_lds_name);
|
|
LM_ERR("[%s]: reconnect failed\n", _lds_name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* free old last_ldap_result */
|
|
/*
|
|
* this is done now in lds_search
|
|
*
|
|
|
|
if (last_ldap_result != NULL) {
|
|
ldap_msgfree(last_ldap_result);
|
|
last_ldap_result = NULL;
|
|
}
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
void get_last_ldap_result(LDAP** _last_ldap_handle, LDAPMessage** _last_ldap_result)
|
|
{
|
|
*_last_ldap_handle = last_ldap_handle;
|
|
*_last_ldap_result = last_ldap_result;
|
|
}
|
|
|
|
int ldap_params_search(
|
|
int* _ld_result_count,
|
|
char* _lds_name,
|
|
char* _dn,
|
|
int _scope,
|
|
char** _attrs,
|
|
char* _filter,
|
|
...)
|
|
{
|
|
int rc;
|
|
static char filter_str[LDAP_MAX_FILTER_LEN];
|
|
char *filter_ptr = NULL;
|
|
va_list filter_vars;
|
|
|
|
/*
|
|
* check _scope
|
|
*/
|
|
switch (_scope)
|
|
{
|
|
case LDAP_SCOPE_ONELEVEL:
|
|
case LDAP_SCOPE_BASE:
|
|
case LDAP_SCOPE_SUBTREE:
|
|
break;
|
|
default:
|
|
LM_ERR("[%s]: invalid scope argument [%d]\n", _lds_name, _scope);
|
|
return -1;
|
|
}
|
|
|
|
if (_filter) {
|
|
/*
|
|
* vsnprintf
|
|
*/
|
|
va_start(filter_vars, _filter);
|
|
rc = vsnprintf(filter_str, (size_t)LDAP_MAX_FILTER_LEN, _filter,
|
|
filter_vars);
|
|
va_end(filter_vars);
|
|
|
|
if (rc >= LDAP_MAX_FILTER_LEN)
|
|
{
|
|
LM_ERR( "[%s]: filter string too long (len [%d], max len [%d])\n",
|
|
_lds_name,
|
|
rc,
|
|
LDAP_MAX_FILTER_LEN);
|
|
return -1;
|
|
}
|
|
else if (rc < 0)
|
|
{
|
|
LM_ERR("vsnprintf failed\n");
|
|
return -1;
|
|
}
|
|
filter_ptr = filter_str;
|
|
}
|
|
|
|
/*
|
|
* ldap search
|
|
*/
|
|
if (lds_search(_lds_name,
|
|
_dn,
|
|
_scope,
|
|
filter_ptr,
|
|
_attrs,
|
|
NULL,
|
|
_ld_result_count,
|
|
&rc)
|
|
!= 0)
|
|
{
|
|
/* try again if LDAP API ERROR */
|
|
if (LDAP_API_ERROR(rc) &&
|
|
(lds_search(_lds_name,
|
|
_dn,
|
|
_scope,
|
|
filter_str,
|
|
_attrs,
|
|
NULL,
|
|
_ld_result_count,
|
|
&rc) != 0))
|
|
{
|
|
LM_ERR( "[%s]: LDAP search (dn [%s], scope [%d],"
|
|
" filter [%s]) failed: %s\n",
|
|
_lds_name,
|
|
_dn,
|
|
_scope,
|
|
filter_str,
|
|
ldap_err2string(rc));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
LM_DBG( "[%s]: [%d] LDAP entries found\n",
|
|
_lds_name,
|
|
*_ld_result_count);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ldap_url_search(
|
|
char* _ldap_url,
|
|
int* _ld_result_count)
|
|
{
|
|
LDAPURLDesc *ludp;
|
|
int rc;
|
|
|
|
if (ldap_url_parse(_ldap_url, &ludp) != 0) {
|
|
LM_ERR("invalid LDAP URL [%s]\n", ZSW(_ldap_url));
|
|
if (ludp != NULL) {
|
|
ldap_free_urldesc(ludp);
|
|
}
|
|
return -2;
|
|
}
|
|
if (ludp->lud_host == NULL)
|
|
{
|
|
LM_ERR( "no ldap session name found in ldap URL [%s]\n",
|
|
ZSW(_ldap_url));
|
|
return -2;
|
|
}
|
|
|
|
|
|
LM_DBG( "LDAP URL parsed into session_name"
|
|
" [%s], base [%s], scope [%d], filter [%s]\n",
|
|
ZSW(ludp->lud_host),
|
|
ZSW(ludp->lud_dn),
|
|
ludp->lud_scope,
|
|
ZSW(ludp->lud_filter));
|
|
|
|
rc = ldap_params_search(_ld_result_count,
|
|
ludp->lud_host,
|
|
ludp->lud_dn,
|
|
ludp->lud_scope,
|
|
ludp->lud_attrs,
|
|
ludp->lud_filter);
|
|
ldap_free_urldesc(ludp);
|
|
return rc;
|
|
}
|
|
|
|
|
|
int ldap_inc_result_pointer(void)
|
|
{
|
|
LDAPMessage *next_result = NULL;
|
|
|
|
/*
|
|
* check for last_ldap_result
|
|
*/
|
|
if (last_ldap_result == NULL) {
|
|
LM_ERR("last_ldap_result == NULL\n");
|
|
return -1;
|
|
}
|
|
if (last_ldap_handle == NULL)
|
|
{
|
|
LM_ERR("last_ldap_handle == NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* get next LDAP result pointer
|
|
*/
|
|
if ((next_result = ldap_next_entry(last_ldap_handle, last_ldap_result))
|
|
== NULL)
|
|
{
|
|
/* no more LDAP entries */
|
|
return 1;
|
|
}
|
|
last_ldap_result = next_result;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ldap_get_attr_vals(str *_attr_name, struct berval ***_vals)
|
|
{
|
|
BerElement *ber;
|
|
char *a;
|
|
|
|
/*
|
|
* check for last_ldap_result
|
|
*/
|
|
if (last_ldap_result == NULL) {
|
|
LM_ERR("last_ldap_result == NULL\n");
|
|
return -1;
|
|
}
|
|
if (last_ldap_handle == NULL)
|
|
{
|
|
LM_ERR("last_ldap_handle == NULL\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* search for attribute named _attr_name
|
|
*/
|
|
*_vals = NULL;
|
|
for (a = ldap_first_attribute(last_ldap_handle,
|
|
last_ldap_result,
|
|
&ber);
|
|
a != NULL;
|
|
a = ldap_next_attribute(last_ldap_handle,
|
|
last_ldap_result,
|
|
ber))
|
|
{
|
|
if (strncmp(a, _attr_name->s, _attr_name->len) == 0) {
|
|
*_vals = ldap_get_values_len(
|
|
last_ldap_handle,
|
|
last_ldap_result,
|
|
a);
|
|
ldap_memfree(a);
|
|
break;
|
|
}
|
|
ldap_memfree(a);
|
|
}
|
|
|
|
if (ber != NULL) {
|
|
ber_free(ber, 0);
|
|
}
|
|
|
|
if (*_vals != NULL)
|
|
{
|
|
return 0;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
int ldap_str2scope(char* scope_str)
|
|
{
|
|
if ( strcasecmp( scope_str, "one" ) == 0 ) {
|
|
return LDAP_SCOPE_ONELEVEL;
|
|
|
|
} else if ( strcasecmp( scope_str, "onelevel" ) == 0 ) {
|
|
return LDAP_SCOPE_ONELEVEL;
|
|
|
|
} else if ( strcasecmp( scope_str, "base" ) == 0 ) {
|
|
return LDAP_SCOPE_BASE;
|
|
|
|
} else if ( strcasecmp( scope_str, "sub" ) == 0 ) {
|
|
return LDAP_SCOPE_SUBTREE;
|
|
|
|
} else if ( strcasecmp( scope_str, "subtree" ) == 0 ) {
|
|
return LDAP_SCOPE_SUBTREE;
|
|
};
|
|
|
|
return( -1 );
|
|
}
|
|
/*
|
|
* sets last_ldap_result and last_ldap_handle
|
|
*/
|
|
int lds_search(
|
|
char* _lds_name,
|
|
char* _dn,
|
|
int _scope,
|
|
char* _filter,
|
|
char** _attrs,
|
|
struct timeval* _search_timeout,
|
|
int* _ld_result_count,
|
|
int* _ld_error)
|
|
{
|
|
struct ld_session* lds;
|
|
#ifdef LDAP_PERF
|
|
struct timeval before_search = { 0, 0 }, after_search = { 0, 0 };
|
|
#endif
|
|
|
|
/*
|
|
* get ld_handle
|
|
*/
|
|
if (get_connected_ldap_session(_lds_name, &lds) != 0)
|
|
{
|
|
LM_ERR("[%s]: couldn't get ldap session\n", _lds_name);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* free last_ldap_result
|
|
*/
|
|
if (last_ldap_result_holder != NULL) {
|
|
ldap_msgfree(last_ldap_result_holder);
|
|
last_ldap_result_holder = NULL;
|
|
last_ldap_result = NULL;
|
|
}
|
|
|
|
|
|
LM_DBG( "[%s]: performing LDAP search: dn [%s],"
|
|
" scope [%d], filter [%s], client_timeout [%d] usecs\n",
|
|
_lds_name,
|
|
_dn,
|
|
_scope,
|
|
_filter,
|
|
(int)(lds->client_search_timeout.tv_sec * 1000000
|
|
+ lds->client_search_timeout.tv_usec));
|
|
|
|
#ifdef LDAP_PERF
|
|
gettimeofday(&before_search, NULL);
|
|
#endif
|
|
|
|
/*
|
|
* perform ldap search
|
|
*/
|
|
*_ld_error = ldap_search_ext_s(
|
|
lds->handle,
|
|
_dn,
|
|
_scope,
|
|
_filter,
|
|
_attrs,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&lds->client_search_timeout,
|
|
0,
|
|
&last_ldap_result_holder);
|
|
|
|
#ifdef LDAP_PERF
|
|
gettimeofday(&after_search, NULL);
|
|
|
|
LM_INFO("[%s]: LDAP search took [%d] usecs\n",
|
|
_lds_name,
|
|
(int)((after_search.tv_sec * 1000000 + after_search.tv_usec)
|
|
- (before_search.tv_sec * 1000000 + before_search.tv_usec)));
|
|
#endif
|
|
|
|
if (*_ld_error != LDAP_SUCCESS)
|
|
{
|
|
if (last_ldap_result_holder != NULL)
|
|
{
|
|
ldap_msgfree(last_ldap_result_holder);
|
|
last_ldap_result_holder = NULL;
|
|
}
|
|
|
|
if (LDAP_API_ERROR(*_ld_error))
|
|
{
|
|
ldap_disconnect(_lds_name);
|
|
}
|
|
|
|
LM_DBG( "[%s]: ldap_search_ext_st failed: %s\n",
|
|
_lds_name,
|
|
ldap_err2string(*_ld_error));
|
|
return -1;
|
|
}
|
|
|
|
last_ldap_handle = lds->handle;
|
|
*_ld_result_count = ldap_count_entries(lds->handle, last_ldap_result_holder);
|
|
if (*_ld_result_count < 0)
|
|
{
|
|
LM_DBG("[%s]: ldap_count_entries failed\n", _lds_name);
|
|
return -1;
|
|
}
|
|
|
|
last_ldap_result = last_ldap_result_holder;
|
|
|
|
return 0;
|
|
}
|
|
|