2 * Asterisk -- A telephony toolkit for Linux.
4 * Copyright (C) 2005, Oxymium sarl
5 * Manuel Guesdon <mguesdon@oxymium.net> - LDAP RealTime Driver Author/Adaptor
7 * Copyright (C) 2007, Digium, Inc.
8 * Russell Bryant <russell@digium.com>
10 * See http://www.asterisk.org for more information about
11 * the Asterisk project. Please do not directly contact
12 * any of the maintainers of this project for assistance;
13 * the project provides a web site, mailing lists and IRC
14 * channels for your use.
16 * This program is free software, distributed under the terms of
17 * the GNU General Public License Version 2. See the LICENSE file
18 * at the top of the source tree.
24 * \brief ldap plugin for portable configuration engine (ARA)
26 * \author Mark Spencer <markster@digium.com>
27 * \author Manuel Guesdon
28 * \author Carl-Einar Thorner <cthorner@voicerd.com>
29 * \author Russell Bryant <russell@digium.com>
31 * \arg http://www.openldap.org
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/channel.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/config.h"
51 #include "asterisk/module.h"
52 #include "asterisk/lock.h"
53 #include "asterisk/options.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/utils.h"
56 #include "asterisk/strings.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/linkedlists.h"
60 #define RES_CONFIG_LDAP_CONF "res_ldap.conf"
62 AST_MUTEX_DEFINE_STATIC(ldap_lock);
64 static LDAP *ldapConn;
66 static char user[512];
68 static char basedn[512];
69 static int version = 3;
70 static time_t connect_time;
72 static int parse_config(void);
73 static int ldap_reconnect(void);
74 static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
76 struct category_and_metric {
79 const char *variable_name;
80 const char *variable_value;
81 int var_metric; /*!< For organizing variables (particularly includes and switch statments) within a context */
84 /*! \brief Table configuration */
85 struct ldap_table_config {
86 char *table_name; /*!< table name */
87 char *additional_filter; /*!< additional filter */
88 struct ast_variable *attributes; /*!< attribute names conversion */
89 struct ast_variable *delimiters; /*!< the current delimiter is semicolon, so we are not using this variable */
90 AST_LIST_ENTRY(ldap_table_config) entry;
93 /*! \brief Should be locked before using it */
94 static AST_LIST_HEAD_NOLOCK_STATIC(table_configs, ldap_table_config);
95 static struct ldap_table_config *base_table_config;
96 static struct ldap_table_config *static_table_config;
98 static struct ast_cli_entry ldap_cli[] = {
99 AST_CLI_DEFINE(realtime_ldap_status, "Shows connection information for the LDAP RealTime driver"),
102 /*! \brief Create a new table_config */
103 static struct ldap_table_config *table_config_new(const char *table_name)
105 struct ldap_table_config *p;
107 if (!(p = ast_calloc(1, sizeof(*p))))
111 if (!(p->table_name = ast_strdup(table_name))) {
120 /*! \brief Find a table_config - Should be locked before using it
121 * \note This function assumes ldap_lock to be locked. */
122 static struct ldap_table_config *table_config_for_table_name(const char *table_name)
124 struct ldap_table_config *c = NULL;
126 AST_LIST_TRAVERSE(&table_configs, c, entry) {
127 if (!strcmp(c->table_name, table_name))
134 /*! \brief Find variable by name */
135 static struct ast_variable *variable_named(struct ast_variable *var, const char *name)
137 for (; var; var = var->next) {
138 if (!strcasecmp(name, var->name))
145 /*! \brief for the semicolon delimiter
146 \param somestr - pointer to a string
148 \return number of occurances of the delimiter(semicolon)
150 static int semicolon_count_str(const char *somestr)
154 for (; *somestr; somestr++) {
162 /* takes a linked list of \a ast_variable variables, finds the one with the name variable_value
163 * and returns the number of semicolons in the value for that \a ast_variable
165 static int semicolon_count_var(struct ast_variable *var)
167 struct ast_variable *var_value = variable_named(var, "variable_value");
172 ast_debug(1, "LINE(%d) semicolon_count_var: %s\n", __LINE__, var_value->value);
174 return semicolon_count_str(var_value->value);
177 /*! \brief add attribute to table config - Should be locked before using it */
178 static void ldap_table_config_add_attribute(struct ldap_table_config *table_config,
179 const char *attribute_name, const char *attribute_value)
181 struct ast_variable *var;
183 if (ast_strlen_zero(attribute_name) || ast_strlen_zero(attribute_value))
186 if (!(var = ast_variable_new(attribute_name, attribute_value, table_config->table_name)))
189 if (table_config->attributes)
190 var->next = table_config->attributes;
191 table_config->attributes = var;
194 /*! \brief Free table_config
195 * \note assumes ldap_lock to be locked */
196 static void table_configs_free(void)
198 struct ldap_table_config *c;
200 while ((c = AST_LIST_REMOVE_HEAD(&table_configs, entry))) {
203 if (c->additional_filter)
204 free(c->additional_filter);
206 ast_variables_destroy(c->attributes);
210 base_table_config = NULL;
211 static_table_config = NULL;
214 /*! \brief Convert variable name to ldap attribute name - Should be locked before using it */
215 static const char *convert_attribute_name_to_ldap(struct ldap_table_config *table_config,
216 const char *attribute_name)
219 struct ldap_table_config *configs[] = { table_config, base_table_config };
221 for (i = 0; i < ARRAY_LEN(configs); i++) {
222 struct ast_variable *attribute;
227 attribute = configs[i]->attributes;
228 for (; attribute; attribute = attribute->next) {
229 if (!strcasecmp(attribute_name, attribute->name))
230 return attribute->value;
234 return attribute_name;
237 /*! \brief Convert ldap attribute name to variable name - Should be locked before using it */
238 static const char *convert_attribute_name_from_ldap(struct ldap_table_config *table_config,
239 const char *attribute_name)
242 struct ldap_table_config *configs[] = { table_config, base_table_config };
244 for (i = 0; i < ARRAY_LEN(configs); i++) {
245 struct ast_variable *attribute;
250 attribute = configs[i]->attributes;
251 for (; attribute; attribute = attribute->next) {
252 if (strcasecmp(attribute_name, attribute->value) == 0)
253 return attribute->name;
257 return attribute_name;
260 /*! \brief Get variables from ldap entry attributes - Should be locked before using it
261 * \return a linked list of ast_variable variables.
263 static struct ast_variable *realtime_ldap_entry_to_var(struct ldap_table_config *table_config,
264 LDAPMessage *ldap_entry)
266 BerElement *ber = NULL;
267 struct ast_variable *var = NULL;
268 struct ast_variable *prev = NULL;
269 int is_delimited = 0;
271 char *ldap_attribute_name;
272 struct berval *value;
275 ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
277 while (ldap_attribute_name) {
278 struct berval **values = NULL;
279 const char *attribute_name = convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
280 int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
282 values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name); /* these are freed at the end */
284 struct berval **v = values;
288 ast_debug(2, "LINE(%d) attribute_name: %s LDAP value: %s\n", __LINE__, attribute_name, value->bv_val);
289 if (is_realmed_password_attribute) {
290 if (!strncasecmp(value->bv_val, "{md5}", 5))
293 value->bv_val = NULL;
294 ast_debug(2, "md5: %s\n", value->bv_val);
297 /* ok, so looping through all delimited values except the last one (not, last character is not delimited...) */
301 while (!ast_strlen_zero(value->bv_val + i)) {
302 if (value->bv_val[i] == ';'){
303 value->bv_val[i] = '\0';
305 prev->next = ast_variable_new(attribute_name, &value->bv_val[pos], table_config->table_name);
310 prev = var = ast_variable_new(attribute_name, &value->bv_val[pos], table_config->table_name);
317 /* for the last delimited value or if the value is not delimited: */
319 prev->next = ast_variable_new(attribute_name, &value->bv_val[pos], table_config->table_name);
324 prev = var = ast_variable_new(attribute_name, &value->bv_val[pos], table_config->table_name);
329 ldap_value_free_len(values);
331 ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
338 /*! \brief Get variables from ldap entry attributes - Should be locked before using it
340 * The results are freed outside this function so is the \a vars array.
342 * \return \a vars - an array of ast_variable variables terminated with a null.
344 static struct ast_variable **realtime_ldap_result_to_vars(struct ldap_table_config *table_config,
345 LDAPMessage *ldap_result, unsigned int *entries_count_ptr)
347 struct ast_variable **vars;
351 LDAPMessage *ldap_entry = NULL;
352 BerElement *ber = NULL;
353 struct ast_variable *var = NULL;
354 struct ast_variable *prev = NULL;
355 int is_delimited = 0;
356 char *delim_value = NULL;
357 int delim_tot_count = 0;
360 /* First find the total count */
361 ldap_entry = ldap_first_entry(ldapConn, ldap_result);
363 for (tot_count = 0; ldap_entry; tot_count++){
364 tot_count += semicolon_count_var(realtime_ldap_entry_to_var(table_config, ldap_entry));
365 ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
368 if (entries_count_ptr)
369 *entries_count_ptr = tot_count;
370 /* Now that we have the total count we allocate space and create the variables
371 * Remember that each element in vars is a linked list that points to realtime variable.
372 * If the we are dealing with a static realtime variable we create a new element in the \a vars array for each delimited
373 * value in \a variable_value; otherwise, we keep \a vars static and increase the length of the linked list of variables in the array element.
374 * This memory must be freed outside of this function. */
375 vars = ast_calloc(sizeof(struct ast_variable *), tot_count + 1);
377 ldap_entry = ldap_first_entry(ldapConn, ldap_result);
381 /* For each static realtime variable we may create several entries in the \a vars array if it's delimited */
382 for (entry_index = 0; ldap_entry; ) {
388 do { /* while delim_count */
390 /* Starting new static var */
391 char *ldap_attribute_name = ldap_first_attribute(ldapConn, ldap_entry, &ber);
392 struct berval *value;
393 while (ldap_attribute_name) {
395 const char *attribute_name =
396 convert_attribute_name_from_ldap(table_config, ldap_attribute_name);
397 int is_realmed_password_attribute = strcasecmp(attribute_name, "md5secret") == 0;
398 struct berval **values = NULL;
400 values = ldap_get_values_len(ldapConn, ldap_entry, ldap_attribute_name);
402 struct berval **v = values;
406 if (is_realmed_password_attribute) {
407 if (strncasecmp(value->bv_val, "{md5}", 5) == 0)
410 value->bv_val = NULL;
411 ast_debug(2, "md5: %s\n", value->bv_val);
414 if (delim_value == NULL
415 && !is_realmed_password_attribute
416 && (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0)) {
418 delim_value = ast_strdup(value->bv_val);
420 if ((delim_tot_count = semicolon_count_str(delim_value)) > 0) {
421 ast_debug(4, "LINE(%d) is delimited %d times: %s\n", __LINE__, delim_tot_count, delim_value);
426 if (is_delimited != 0
427 && !is_realmed_password_attribute
428 && (static_table_config != table_config || strcmp(attribute_name, "variable_value") == 0) ){
429 /* for non-Static RealTime, first */
432 while (!ast_strlen_zero(value->bv_val + i)) {
433 ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
434 if (delim_value[i] == ';') {
435 delim_value[i] = '\0';
437 ast_debug(2, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
440 prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
445 prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
449 if (static_table_config == table_config) {
455 if (ast_strlen_zero(value->bv_val + i)) {
456 ast_debug(4, "LINE(%d) DELIM pos: %d i: %d delim_count: %d\n", __LINE__, pos, i, delim_count);
457 /* Last delimited value */
458 ast_debug(4, "LINE(%d) DELIM - attribute_name: %s value: %s pos: %d\n", __LINE__, attribute_name, &delim_value[pos], pos);
460 prev->next = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
465 prev = var = ast_variable_new(attribute_name, &delim_value[pos], table_config->table_name);
467 /* Remembering to free memory */
474 ast_debug(4, "LINE(%d) DELIM pos: %d i: %d\n", __LINE__, pos, i);
481 ast_debug(2, "LINE(%d) attribute_name: %s value: %s\n", __LINE__, attribute_name, value->bv_val);
484 prev->next = ast_variable_new(attribute_name, value->bv_val, table_config->table_name);
489 prev = var = ast_variable_new(attribute_name, value->bv_val, table_config->table_name);
495 ldap_value_free_len(values);
497 ldap_attribute_name = ldap_next_attribute(ldapConn, ldap_entry, ber);
498 } /*!< while (ldap_attribute_name) */
500 if (static_table_config == table_config) {
501 if (option_debug > 2) {
502 const struct ast_variable *tmpdebug = variable_named(var, "variable_name");
503 const struct ast_variable *tmpdebug2 = variable_named(var, "variable_value");
504 if (tmpdebug && tmpdebug2) {
505 ast_debug(3, "LINE(%d) Added to vars - %s = %s\n", __LINE__, tmpdebug->value, tmpdebug2->value);
508 vars[entry_index++] = var;
513 } while (delim_count <= delim_tot_count && static_table_config == table_config);
515 if (static_table_config != table_config) {
516 ast_debug(3, "LINE(%d) Added to vars - non static\n", __LINE__);
518 vars[entry_index++] = var;
521 ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
522 } /*!< end for loop over ldap_entry */
528 static int is_ldap_connect_error(int err)
530 return (err == LDAP_SERVER_DOWN
531 || err == LDAP_TIMEOUT || err == LDAP_CONNECT_ERROR);
534 /*! \brief Get LDAP entry by dn and return attributes as variables - Should be locked before using it
535 This is used for setting the default values of an object(i.e., with accountBaseDN)
537 static struct ast_variable *ldap_loadentry(struct ldap_table_config *table_config,
541 ast_log(LOG_ERROR, "No table config\n");
544 struct ast_variable **vars = NULL;
545 struct ast_variable *var = NULL;
547 LDAPMessage *ldap_result = NULL;
550 ast_debug(2, "ldap_loadentry dn=%s\n", dn);
553 result = ldap_search_ext_s(ldapConn, dn, LDAP_SCOPE_BASE,
554 "(objectclass=*)", NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, &ldap_result);
555 if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
557 "Failed to query database. Try %d/3\n",
561 usleep(500000L * tries);
563 ldap_unbind_ext_s(ldapConn, NULL, NULL);
566 if (!ldap_reconnect())
570 } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
572 if (result != LDAP_SUCCESS) {
574 "Failed to query database. Check debug for more info.\n");
575 ast_debug(2, "dn=%s\n", dn);
576 ast_debug(2, "Query Failed because: %s\n",
577 ldap_err2string(result));
578 ast_mutex_unlock(&ldap_lock);
582 unsigned int *entries_count_ptr = NULL; /*!< not using this */
583 if ((num_entry = ldap_count_entries(ldapConn, ldap_result)) > 0) {
584 ast_debug(3, "num_entry: %d\n", num_entry);
586 vars = realtime_ldap_result_to_vars(table_config, ldap_result, entries_count_ptr);
588 ast_log(LOG_WARNING, "More than one entry for dn=%s. Take only 1st one\n", dn);
590 ast_log(LOG_WARNING, "Could not find any entry dn=%s.\n", dn);
593 ldap_msgfree(ldap_result);
595 /* Chopping \a vars down to one variable */
597 struct ast_variable **p = vars;
601 ast_variables_destroy(var);
604 vars = ast_realloc(vars, sizeof(struct ast_variable *));
613 /*! \brief caller should free returned pointer */
614 static char *substituted(struct ast_channel *channel, const char *string)
616 #define MAXRESULT 2048
617 char *ret_string = NULL;
619 if (!ast_strlen_zero(string)) {
620 ret_string = ast_calloc(1, MAXRESULT);
621 pbx_substitute_variables_helper(channel, string, ret_string, MAXRESULT - 1);
623 ast_debug(2, "substituted: string: '%s' => '%s' \n",
628 /*! \brief caller should free returned pointer */
629 static char *cleaned_basedn(struct ast_channel *channel, const char *basedn)
631 char *cbasedn = NULL;
634 cbasedn = substituted(channel, basedn);
635 if (*cbasedn == '"') {
637 if (!ast_strlen_zero(cbasedn)) {
638 int len = strlen(cbasedn);
639 if (cbasedn[len - 1] == '"')
640 cbasedn[len - 1] = '\0';
651 ast_debug(2, "basedn: '%s' => '%s' \n", basedn, cbasedn);
655 /*! \brief Replace \<search\> by \<by\> in string. No check is done on string allocated size ! */
656 static int replace_string_in_string(char *string, const char *search, const char *by)
658 int search_len = strlen(search);
659 int by_len = strlen(by);
661 char *p = strstr(string, search);
665 if (by_len == search_len)
666 memcpy(p, by, by_len);
668 memmove(p + by_len, p + search_len,
669 strlen(p + search_len) + 1);
670 memcpy(p, by, by_len);
672 p = strstr(p + by_len, search);
678 /*! \brief Append a name=value filter string. The filter string can grow. */
679 static void append_var_and_value_to_filter(struct ast_str **filter,
680 struct ldap_table_config *table_config,
681 const char *name, const char *value)
683 char *new_name = NULL;
684 char *new_value = NULL;
685 char *like_pos = strstr(name, " LIKE");
687 ast_debug(2, "name='%s' value='%s'\n", name, value);
690 int len = like_pos - name;
691 name = new_name = ast_strdupa(name);
692 new_name[len] = '\0';
693 value = new_value = ast_strdupa(value);
694 replace_string_in_string(new_value, "\\_", "_");
695 replace_string_in_string(new_value, "%", "*");
698 name = convert_attribute_name_to_ldap(table_config, name);
700 ast_str_append(filter, 0, "(%s=%s)", name, value);
703 /*! \brief LDAP base function
704 * \return a null terminated array of ast_variable (one per entry) or NULL if no entry is found or if an error occured
705 * caller should free the returned array and ast_variables
706 * \param entries_count_ptr is a pointer to found entries count (can be NULL)
707 * \param basedn is the base DN
708 * \param table_name is the table_name (used dor attribute convertion and additional filter)
709 * \param ap contains null terminated list of pairs name/value
711 static struct ast_variable **realtime_ldap_base_ap(unsigned int *entries_count_ptr,
712 const char *basedn, const char *table_name, va_list ap)
714 struct ast_variable **vars = NULL;
715 const char *newparam = NULL;
716 const char *newval = NULL;
717 struct ldap_table_config *table_config = NULL;
718 char *clean_basedn = cleaned_basedn(NULL, basedn);
719 struct ast_str *filter = NULL;
722 LDAPMessage *ldap_result = NULL;
725 ast_log(LOG_WARNING, "No table_name specified.\n");
726 ast_free(clean_basedn);
730 if (!(filter = ast_str_create(80))) {
731 ast_free(clean_basedn);
735 /* Get the first parameter and first value in our list of passed paramater/value pairs */
736 newparam = va_arg(ap, const char *);
737 newval = va_arg(ap, const char *);
739 if (!newparam || !newval) {
740 ast_log(LOG_WARNING, "Realtime retrieval requires at least 1 parameter"
741 " and 1 value to search on.\n");
743 ast_free(clean_basedn);
747 ast_mutex_lock(&ldap_lock);
749 /* We now have our complete statement; Lets connect to the server and execute it. */
750 if (!ldap_reconnect()) {
751 ast_mutex_unlock(&ldap_lock);
753 ast_free(clean_basedn);
757 table_config = table_config_for_table_name(table_name);
759 ast_log(LOG_WARNING, "No table named '%s'.\n", table_name);
760 ast_mutex_unlock(&ldap_lock);
762 ast_free(clean_basedn);
766 ast_str_append(&filter, 0, "(&");
768 if (table_config && table_config->additional_filter)
769 ast_str_append(&filter, 0, "%s", table_config->additional_filter);
770 if (table_config != base_table_config && base_table_config &&
771 base_table_config->additional_filter) {
772 ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
775 /* Create the first part of the query using the first parameter/value pairs we just extracted */
776 /* If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */
778 append_var_and_value_to_filter(&filter, table_config, newparam, newval);
779 while ((newparam = va_arg(ap, const char *))) {
780 newval = va_arg(ap, const char *);
781 append_var_and_value_to_filter(&filter, table_config, newparam, newval);
783 ast_str_append(&filter, 0, ")");
786 /* freeing ldap_result further down */
787 result = ldap_search_ext_s(ldapConn, clean_basedn,
788 LDAP_SCOPE_SUBTREE, filter->str, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
790 if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
791 ast_log(LOG_DEBUG, "Failed to query database. Try %d/10\n",
796 ldap_unbind_ext_s(ldapConn, NULL, NULL);
799 if (!ldap_reconnect())
803 } while (result != LDAP_SUCCESS && tries < 10 && is_ldap_connect_error(result));
805 if (result != LDAP_SUCCESS) {
806 ast_log(LOG_WARNING, "Failed to query database. Check debug for more info.\n");
807 ast_log(LOG_WARNING, "Query: %s\n", filter->str);
808 ast_log(LOG_WARNING, "Query Failed because: %s\n", ldap_err2string(result));
810 /* this is where we create the variables from the search result
811 * freeing this \a vars outside this function */
812 if (ldap_count_entries(ldapConn, ldap_result) > 0) {
813 /* is this a static var or some other? they are handled different for delimited values */
814 vars = realtime_ldap_result_to_vars(table_config, ldap_result, entries_count_ptr);
816 ast_log(LOG_WARNING, "Could not find any entry matching %s in base dn %s.\n",
817 filter->str, clean_basedn);
820 ldap_msgfree(ldap_result);
822 /* TODO: get the default variables from the accountBaseDN, not implemented with delimited values */
824 struct ast_variable **p = vars;
826 struct ast_variable *append_var = NULL;
827 struct ast_variable *tmp = *p;
829 if (strcasecmp(tmp->name, "accountBaseDN") == 0) {
830 /* Get the variable to compare with for the defaults */
831 struct ast_variable *base_var = ldap_loadentry(table_config, tmp->value);
834 struct ast_variable *next = base_var->next;
835 struct ast_variable *test_var = *p;
836 int base_var_found = 0;
838 /* run throught the default values and fill it inn if it is missing */
840 if (strcasecmp(test_var->name, base_var->name) == 0) {
844 test_var = test_var->next;
846 if (base_var_found) {
847 base_var->next = NULL;
848 ast_variables_destroy(base_var);
852 base_var->next = append_var;
854 base_var->next = NULL;
855 append_var = base_var;
860 if (!tmp->next && append_var) {
861 tmp->next = append_var;
875 ast_free(clean_basedn);
877 ast_mutex_unlock(&ldap_lock);
882 /*! \brief same as realtime_ldap_base_ap but take variable arguments count list */
883 static struct ast_variable **realtime_ldap_base(unsigned int *entries_count_ptr,
884 const char *basedn, const char *table_name, ...)
886 struct ast_variable **vars = NULL;
889 va_start(ap, table_name);
890 vars = realtime_ldap_base_ap(entries_count_ptr, basedn, table_name, ap);
896 /*! \brief See Asterisk doc
898 * For Realtime Dynamic(i.e., switch, queues, and directory) -- I think
900 static struct ast_variable *realtime_ldap(const char *basedn,
901 const char *table_name, va_list ap)
903 struct ast_variable **vars = realtime_ldap_base_ap(NULL, basedn, table_name, ap);
904 struct ast_variable *var = NULL;
907 struct ast_variable *last_var = NULL;
908 struct ast_variable **p = vars;
911 while (last_var->next)
912 last_var = last_var->next;
925 /*! \brief See Asterisk doc
927 * this function will be called for the switch statment if no match is found with the realtime_ldap function(i.e. it is a failover);
928 * however, the ast_load_realtime wil match on wildcharacters also depending on what the mode is set to
929 * this is an area of asterisk that could do with a lot of modification
930 * I think this function returns Realtime dynamic objects
932 static struct ast_config *realtime_multi_ldap(const char *basedn,
933 const char *table_name, va_list ap)
935 struct ast_variable **vars =
936 realtime_ldap_base_ap(NULL, basedn, table_name, ap);
937 struct ast_config *cfg = NULL;
940 cfg = ast_config_new();
942 ast_log(LOG_WARNING, "Out of memory!\n");
944 struct ast_variable **p = vars;
947 struct ast_category *cat = NULL;
948 cat = ast_category_new("", table_name, -1);
950 ast_log(LOG_WARNING, "Out of memory!\n");
953 struct ast_variable *var = *p;
955 struct ast_variable *next = var->next;
957 ast_variable_append(cat, var);
961 ast_category_append(cfg, cat);
972 * \brief Sorting alogrithm for qsort to find the order of the variables \a a and \a b
973 * \param a pointer to category_and_metric struct
974 * \param b pointer to category_and_metric struct
976 * \retval -1 for if b is greater
977 * \retval 0 zero for equal
978 * \retval 1 if a is greater
980 static int compare_categories(const void *a, const void *b)
982 const struct category_and_metric *as = a;
983 const struct category_and_metric *bs = b;
985 if (as->metric < bs->metric)
987 else if (as->metric > bs->metric)
989 else if (as->metric == bs->metric && strcmp(as->name, bs->name) != 0)
990 return strcmp(as->name, bs->name);
992 /* if the metric and the category name is the same, we check the variable metric */
993 if (as->var_metric < bs->var_metric)
995 else if (as->var_metric > bs->var_metric)
1001 /*! \brief See Asterisk doc
1003 * This is for Static Realtime (again: I think...)
1005 * load the configuration stuff for the .conf files
1006 * called on a reload
1008 static struct ast_config *config_ldap(const char *basedn, const char *table_name,
1009 const char *file, struct ast_config *cfg, struct ast_flags config_flags, const char *sugg_incl, const char *who_asked)
1011 unsigned int vars_count = 0;
1012 struct ast_variable **vars;
1014 struct ast_variable *new_v = NULL;
1015 struct ast_category *cur_cat = NULL;
1016 const char *last_category = NULL;
1017 int last_category_metric = 0;
1018 struct category_and_metric *categories;
1019 struct ast_variable **p;
1021 if (ast_strlen_zero(file) || !strcasecmp(file, RES_CONFIG_LDAP_CONF)) {
1022 ast_log(LOG_WARNING, "Cannot configure myself.\n");
1026 vars = realtime_ldap_base(&vars_count, basedn, table_name, "filename",
1027 file, "commented", "FALSE", NULL);
1030 ast_log(LOG_WARNING, "Could not find config '%s' in database.\n", file);
1034 /*!\note Since the items come back in random order, they need to be sorted
1035 * first, and since the data could easily exceed stack size, this is
1036 * allocated from the heap.
1038 if (!(categories = ast_calloc(sizeof(*categories), vars_count)))
1041 for (vars_count = 0, p = vars; *p; p++) {
1042 struct ast_variable *category = variable_named(*p, "category");
1043 struct ast_variable *cat_metric = variable_named(*p, "cat_metric");
1044 struct ast_variable *var_name = variable_named(*p, "variable_name");
1045 struct ast_variable *var_val = variable_named(*p, "variable_value");
1046 struct ast_variable *var_metric = variable_named(*p, "var_metric");
1047 struct ast_variable *dn = variable_named(*p, "dn");
1049 ast_debug(1, "category: %s\n", category->value);
1050 ast_debug(1, "var_name: %s\n", var_name->value);
1051 ast_debug(1, "var_val: %s\n", var_val->value);
1052 ast_debug(1, "cat_metric: %s\n", cat_metric->value);
1056 "No category name in entry '%s' for file '%s'.\n",
1057 (dn ? dn->value : "?"), file);
1058 } else if (!cat_metric) {
1060 "No category metric in entry '%s'(category: %s) for file '%s'.\n",
1061 (dn ? dn->value : "?"), category->value, file);
1062 } else if (!var_metric) {
1064 "No variable metric in entry '%s'(category: %s) for file '%s'.\n",
1065 (dn ? dn->value : "?"), category->value, file);
1066 } else if (!var_name) {
1068 "No variable name in entry '%s' (category: %s metric: %s) for file '%s'.\n",
1069 (dn ? dn->value : "?"), category->value,
1070 cat_metric->value, file);
1071 } else if (!var_val) {
1073 "No variable value in entry '%s' (category: %s metric: %s variable: %s) for file '%s'.\n",
1074 (dn ? dn->value : "?"), category->value,
1075 cat_metric->value, var_name->value, file);
1077 categories[vars_count].name = category->value;
1078 categories[vars_count].metric = atoi(cat_metric->value);
1079 categories[vars_count].variable_name = var_name->value;
1080 categories[vars_count].variable_value = var_val->value;
1081 categories[vars_count].var_metric = atoi(var_metric->value);
1086 qsort(categories, vars_count, sizeof(*categories), compare_categories);
1088 for (i = 0; i < vars_count; i++) {
1089 if (!strcmp(categories[i].variable_name, "#include")) {
1090 struct ast_flags config_flags = { 0 };
1091 if (!ast_config_internal_load(categories[i].variable_value, cfg, config_flags, "", who_asked))
1096 if (!last_category || strcmp(last_category, categories[i].name) ||
1097 last_category_metric != categories[i].metric) {
1098 cur_cat = ast_category_new(categories[i].name, table_name, -1);
1101 last_category = categories[i].name;
1102 last_category_metric = categories[i].metric;
1103 ast_category_append(cfg, cur_cat);
1106 if (!(new_v = ast_variable_new(categories[i].variable_name, categories[i].variable_value, table_name)))
1109 ast_variable_append(cur_cat, new_v);
1118 /* \brief Function to update a set of values in ldap
1121 static int update_ldap(const char *basedn, const char *table_name, const char *attribute,
1122 const char *lookup, va_list ap)
1125 LDAPMessage *ldap_entry = NULL;
1126 LDAPMod **ldap_mods;
1127 const char *newparam = NULL;
1128 const char *newval = NULL;
1130 int num_entries = 0;
1134 struct ldap_table_config *table_config = NULL;
1135 char *clean_basedn = NULL;
1136 struct ast_str *filter = NULL;
1139 LDAPMessage *ldap_result = NULL;
1142 ast_log(LOG_WARNING, "No table_name specified.\n");
1146 if (!(filter = ast_str_create(80)))
1149 if (!attribute || !lookup) {
1150 ast_log(LOG_WARNING,
1151 "LINE(%d): search parameters are empty.\n", __LINE__);
1154 ast_mutex_lock(&ldap_lock);
1156 /* We now have our complete statement; Lets connect to the server and execute it. */
1157 if (!ldap_reconnect()) {
1158 ast_mutex_unlock(&ldap_lock);
1162 table_config = table_config_for_table_name(table_name);
1163 if (!table_config) {
1164 ast_log(LOG_WARNING, "No table named '%s'.\n", table_name);
1165 ast_mutex_unlock(&ldap_lock);
1169 clean_basedn = cleaned_basedn(NULL, basedn);
1171 /* Create the filter with the table additional filter and the parameter/value pairs we were given */
1172 ast_str_append(&filter, 0, "(&");
1173 if (table_config && table_config->additional_filter) {
1174 ast_str_append(&filter, 0, "%s", table_config->additional_filter);
1176 if (table_config != base_table_config && base_table_config
1177 && base_table_config->additional_filter) {
1178 ast_str_append(&filter, 0, "%s", base_table_config->additional_filter);
1180 append_var_and_value_to_filter(&filter, table_config, attribute, lookup);
1181 ast_str_append(&filter, 0, ")");
1183 /* Create the modification array with the parameter/value pairs we were given,
1184 * if there are several parameters with the same name, we collect them into
1185 * one parameter/value pair and delimit them with a semicolon */
1186 newparam = va_arg(ap, const char *);
1187 newparam = convert_attribute_name_to_ldap(table_config, newparam);
1188 newval = va_arg(ap, const char *);
1189 if (!newparam || !newval) {
1190 ast_log(LOG_WARNING,
1191 "LINE(%d): need at least one paramter to modify.\n", __LINE__);
1195 mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */
1196 ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size);
1197 ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod));
1199 ldap_mods[0]->mod_op = LDAP_MOD_REPLACE;
1200 ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
1201 strcpy(ldap_mods[0]->mod_type, newparam);
1203 ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2);
1204 ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
1205 strcpy(ldap_mods[0]->mod_values[0], newval);
1207 while ((newparam = va_arg(ap, const char *))) {
1208 newparam = convert_attribute_name_to_ldap(table_config, newparam);
1209 newval = va_arg(ap, const char *);
1212 for (i = 0; i < mods_size - 1; i++) {
1213 if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) {
1214 /* We have the parameter allready, adding the value as a semicolon delimited value */
1215 ldap_mods[i]->mod_values[0] = ast_realloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(newval) + 2));
1216 strcat(ldap_mods[i]->mod_values[0], ";");
1217 strcat(ldap_mods[i]->mod_values[0], newval);
1223 /* create new mod */
1226 ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size);
1227 ldap_mods[mods_size - 1] = NULL;
1228 ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod));
1230 ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE;
1232 ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1);
1233 strcpy(ldap_mods[mods_size - 2]->mod_type, newparam);
1235 ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2);
1236 ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1);
1237 strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval);
1240 /* freeing ldap_mods further down */
1243 /* freeing ldap_result further down */
1244 result = ldap_search_ext_s(ldapConn, clean_basedn,
1245 LDAP_SCOPE_SUBTREE, filter->str, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT,
1247 if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) {
1248 ast_log(LOG_WARNING, "Failed to query database. Try %d/3\n",
1252 usleep(500000L * tries);
1254 ldap_unbind_ext_s(ldapConn, NULL, NULL);
1257 if (!ldap_reconnect())
1261 } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result));
1263 if (result != LDAP_SUCCESS) {
1264 ast_log(LOG_WARNING, "Failed to query directory. Check debug for more info.\n");
1265 ast_log(LOG_WARNING, "Query: %s\n", filter->str);
1266 ast_log(LOG_WARNING, "Query Failed because: %s\n",
1267 ldap_err2string(result));
1269 ast_mutex_unlock(&ldap_lock);
1274 ldap_msgfree(ldap_result);
1275 ldap_mods_free(ldap_mods, 0);
1278 /* Ready to update */
1279 if ((num_entries = ldap_count_entries(ldapConn, ldap_result)) > 0) {
1280 ast_debug(3, "LINE(%d) Modifying %s=%s hits: %d\n", __LINE__, attribute, lookup, num_entries);
1281 for (i = 0; option_debug > 2 && i < mods_size - 1; i++)
1282 ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]);
1284 ldap_entry = ldap_first_entry(ldapConn, ldap_result);
1286 for (i = 0; ldap_entry; i++) {
1287 dn = ldap_get_dn(ldapConn, ldap_entry);
1288 if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS)
1289 ast_log(LOG_ERROR, "Couldn't modify dn:%s because %s", dn, ldap_err2string(error));
1291 ldap_entry = ldap_next_entry(ldapConn, ldap_entry);
1295 ast_mutex_unlock(&ldap_lock);
1300 ldap_msgfree(ldap_result);
1301 ldap_mods_free(ldap_mods, 0);
1305 static struct ast_config_engine ldap_engine = {
1307 .load_func = config_ldap,
1308 .realtime_func = realtime_ldap,
1309 .realtime_multi_func = realtime_multi_ldap,
1310 .update_func = update_ldap
1313 static int load_module(void)
1315 if (parse_config() < 0) {
1316 ast_log(LOG_NOTICE, "Cannot load LDAP RealTime driver.\n");
1320 ast_mutex_lock(&ldap_lock);
1322 if (!ldap_reconnect())
1323 ast_log(LOG_WARNING, "Couldn't establish connection. Check debug.\n");
1325 ast_config_engine_register(&ldap_engine);
1326 ast_verb(1, "LDAP RealTime driver loaded.\n");
1327 ast_cli_register_multiple(ldap_cli, sizeof(ldap_cli) / sizeof(struct ast_cli_entry));
1329 ast_mutex_unlock(&ldap_lock);
1334 static int unload_module(void)
1336 /* Aquire control before doing anything to the module itself. */
1337 ast_mutex_lock(&ldap_lock);
1339 table_configs_free();
1342 ldap_unbind_ext_s(ldapConn, NULL, NULL);
1345 ast_cli_unregister_multiple(ldap_cli, sizeof(ldap_cli) / sizeof(struct ast_cli_entry));
1346 ast_config_engine_deregister(&ldap_engine);
1347 ast_verb(1, "LDAP RealTime unloaded.\n");
1349 /* Unlock so something else can destroy the lock. */
1350 ast_mutex_unlock(&ldap_lock);
1355 static int reload(void)
1357 /* Aquire control before doing anything to the module itself. */
1358 ast_mutex_lock(&ldap_lock);
1361 ldap_unbind_ext_s(ldapConn, NULL, NULL);
1365 if (parse_config() < 0) {
1366 ast_log(LOG_NOTICE, "Cannot reload LDAP RealTime driver.\n");
1367 ast_mutex_unlock(&ldap_lock);
1371 if (!ldap_reconnect())
1372 ast_log(LOG_WARNING, "Couldn't establish connection. Check debug.\n");
1374 ast_verb(2, "LDAP RealTime reloaded.\n");
1376 /* Done reloading. Release lock so others can now use driver. */
1377 ast_mutex_unlock(&ldap_lock);
1382 int parse_config(void)
1384 struct ast_config *config;
1385 struct ast_flags config_flags = {0};
1386 const char *s, *host;
1388 char *category_name = NULL;
1390 config = ast_config_load(RES_CONFIG_LDAP_CONF, config_flags);
1393 ast_log(LOG_WARNING, "Cannot load configuration %s\n", RES_CONFIG_LDAP_CONF);
1397 if (!(s = ast_variable_retrieve(config, "_general", "user"))) {
1398 ast_log(LOG_WARNING, "No directory user found, anonymous binding as default.\n");
1401 ast_copy_string(user, s, sizeof(user));
1403 if (!(s = ast_variable_retrieve(config, "_general", "pass"))) {
1404 ast_log(LOG_WARNING, "No directory password found, using 'asterisk' as default.\n");
1405 ast_copy_string(pass, "asterisk", sizeof(pass) - 1);
1407 ast_copy_string(pass, s, sizeof(pass));
1409 /* URL is preferred, use host and port if not found */
1410 if ((s = ast_variable_retrieve(config, "_general", "url"))) {
1411 ast_copy_string(url, s, sizeof(url));
1412 } else if ((host = ast_variable_retrieve(config, "_general", "host"))) {
1413 if (!(s = ast_variable_retrieve(config, "_general", "port")) || sscanf(s, "%d", &port) != 1) {
1414 ast_log(LOG_NOTICE, "No directory port found, using 389 as default.\n");
1418 snprintf(url, sizeof(url), "ldap://%s:%d", host, port);
1420 ast_log(LOG_ERROR, "No directory URL or host found.\n");
1421 ast_config_destroy(config);
1425 if (!(s = ast_variable_retrieve(config, "_general", "basedn"))) {
1426 ast_log(LOG_ERROR, "No LDAP base dn found, using 'asterisk' as default.\n");
1429 ast_copy_string(basedn, s, sizeof(basedn));
1431 if (!(s = ast_variable_retrieve(config, "_general", "version")) || !(s = ast_variable_retrieve(config, "_general", "protocol"))) {
1432 ast_log(LOG_NOTICE, "No explicit LDAP version found, using 3 as default.\n");
1434 } else if (sscanf(s, "%d", &version) != 1 || version < 1 || version > 6) {
1435 ast_log(LOG_WARNING, "Invalid LDAP version '%s', using 3 as default.\n", s);
1439 table_configs_free();
1441 while ((category_name = ast_category_browse(config, category_name))) {
1442 int is_general = (strcasecmp(category_name, "_general") == 0);
1443 int is_config = (strcasecmp(category_name, "config") == 0); /*!< using the [config] context for Static RealTime */
1444 struct ast_variable *var = ast_variable_browse(config, category_name);
1447 struct ldap_table_config *table_config =
1448 table_config_for_table_name(category_name);
1449 if (!table_config) {
1450 table_config = table_config_new(category_name);
1451 AST_LIST_INSERT_HEAD(&table_configs, table_config, entry);
1453 base_table_config = table_config;
1455 static_table_config = table_config;
1457 for (; var; var = var->next) {
1458 if (!strcasecmp(var->name, "additionalFilter"))
1459 table_config->additional_filter = strdup(var->value);
1461 ldap_table_config_add_attribute(table_config, var->name, var->value);
1466 ast_config_destroy(config);
1471 /*! \note ldap_lock should have been locked before calling this function. */
1472 static int ldap_reconnect(void)
1474 int bind_result = 0;
1478 ast_debug(2, "Everything seems fine.\n");
1482 if (ast_strlen_zero(url)) {
1483 ast_log(LOG_ERROR, "Not enough parameters to connect to ldap database\n");
1487 if (LDAP_SUCCESS != ldap_initialize(&ldapConn, url)) {
1488 ast_log(LOG_ERROR, "Failed to init ldap connection to '%s'. Check debug for more info.\n", url);
1492 if (LDAP_OPT_SUCCESS != ldap_set_option(ldapConn, LDAP_OPT_PROTOCOL_VERSION, &version)) {
1493 ast_log(LOG_WARNING, "Unable to set LDAP protocol version to %d, falling back to default.\n", version);
1496 if (!ast_strlen_zero(user)) {
1497 ast_debug(2, "bind to '%s' as user '%s'\n", url, user);
1498 cred.bv_val = (char *) pass;
1499 cred.bv_len = strlen(pass);
1500 bind_result = ldap_sasl_bind_s(ldapConn, user, LDAP_SASL_SIMPLE, &cred, NULL, NULL, NULL);
1502 ast_debug(2, "bind %s anonymously\n", url);
1503 bind_result = ldap_sasl_bind_s(ldapConn, NULL, LDAP_SASL_SIMPLE, NULL, NULL, NULL, NULL);
1505 if (bind_result == LDAP_SUCCESS) {
1506 ast_debug(2, "Successfully connected to database.\n");
1507 connect_time = time(NULL);
1510 ast_log(LOG_WARNING, "bind failed: %s\n", ldap_err2string(bind_result));
1511 ldap_unbind_ext_s(ldapConn, NULL, NULL);
1517 static char *realtime_ldap_status(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1519 char status[256], status2[100] = "";
1520 int ctime = time(NULL) - connect_time;
1524 e->command = "realtime ldap status";
1526 "Usage: realtime ldap status\n"
1527 " Shows connection information for the LDAP RealTime driver\n";
1536 if (!ast_strlen_zero(url))
1537 snprintf(status, sizeof(status), "Connected to '%s', baseDN %s", url, basedn);
1539 if (!ast_strlen_zero(user))
1540 snprintf(status2, sizeof(status2), " with username %s", user);
1542 if (ctime > 31536000) {
1543 ast_cli(a->fd, "%s%s for %d years, %d days, %d hours, %d minutes, %d seconds.\n",
1544 status, status2, ctime / 31536000,
1545 (ctime % 31536000) / 86400, (ctime % 86400) / 3600,
1546 (ctime % 3600) / 60, ctime % 60);
1547 } else if (ctime > 86400) {
1548 ast_cli(a->fd, "%s%s for %d days, %d hours, %d minutes, %d seconds.\n",
1549 status, status2, ctime / 86400, (ctime % 86400) / 3600,
1550 (ctime % 3600) / 60, ctime % 60);
1551 } else if (ctime > 3600) {
1552 ast_cli(a->fd, "%s%s for %d hours, %d minutes, %d seconds.\n",
1553 status, status2, ctime / 3600, (ctime % 3600) / 60,
1555 } else if (ctime > 60) {
1556 ast_cli(a->fd, "%s%s for %d minutes, %d seconds.\n", status, status2,
1557 ctime / 60, ctime % 60);
1559 ast_cli(a->fd, "%s%s for %d seconds.\n", status, status2, ctime);
1565 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "LDAP realtime interface",
1566 .load = load_module,
1567 .unload = unload_module,