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.
512 lines
13 KiB
512 lines
13 KiB
/*
|
|
* LDAP module - Configuration file parser
|
|
*
|
|
* Copyright (C) 2001-2003 FhG FOKUS
|
|
* Copyright (C) 2004,2005 Free Software Foundation, Inc.
|
|
* Copyright (C) 2005,2006 iptelorg GmbH
|
|
*
|
|
* This file is part of ser, a free SIP server.
|
|
*
|
|
* SER 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.
|
|
*
|
|
* SER 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 "ld_cfg.h"
|
|
#include "ld_mod.h"
|
|
#include "ld_uri.h"
|
|
|
|
#include "../../cfg_parser.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../dprint.h"
|
|
#include "../../trim.h"
|
|
#include "../../ut.h"
|
|
#include "../../resolve.h"
|
|
|
|
#include <ldap.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <libgen.h>
|
|
|
|
|
|
enum section_type {
|
|
LDAP_CON_SECTION = 0,
|
|
LDAP_TABLE_SECTION
|
|
};
|
|
|
|
static struct ld_cfg* cfg = NULL;
|
|
|
|
static struct ld_con_info* con = NULL;
|
|
|
|
|
|
void ld_cfg_free(void)
|
|
{
|
|
struct ld_con_info* c;
|
|
struct ld_cfg* ptr;
|
|
int i;
|
|
|
|
while (cfg) {
|
|
ptr = cfg;
|
|
cfg = cfg->next;
|
|
|
|
if (ptr->table.s) pkg_free(ptr->table.s);
|
|
if (ptr->base.s) pkg_free(ptr->base.s);
|
|
if (ptr->filter.s) pkg_free(ptr->filter.s);
|
|
|
|
for(i = 0; i < ptr->n; i++) {
|
|
if (ptr->field[i].s) pkg_free(ptr->field[i].s);
|
|
if (ptr->attr[i].s) pkg_free(ptr->attr[i].s);
|
|
}
|
|
if (ptr->field) pkg_free(ptr->field);
|
|
if (ptr->attr) pkg_free(ptr->attr);
|
|
if (ptr->syntax) pkg_free(ptr->syntax);
|
|
}
|
|
|
|
while (con) {
|
|
c = con;
|
|
con = con->next;
|
|
|
|
if (c->id.s) pkg_free(c->id.s);
|
|
if (c->host.s) pkg_free(c->host.s);
|
|
if (c->username.s) pkg_free(c->username.s);
|
|
if (c->password.s) pkg_free(c->password.s);
|
|
|
|
pkg_free(c);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
static int parse_field_map(void* param, cfg_parser_t* st, unsigned int flags)
|
|
{
|
|
int ret;
|
|
cfg_token_t t;
|
|
void* ptr;
|
|
static cfg_option_t syntaxes[] = {
|
|
{"GeneralizedTime", .val = LD_SYNTAX_GENTIME},
|
|
{"Integer", .val = LD_SYNTAX_INT },
|
|
{"BitString", .val = LD_SYNTAX_BIT },
|
|
{"Boolean", .val = LD_SYNTAX_BOOL },
|
|
{"String", .val = LD_SYNTAX_STRING },
|
|
{"Binary", .val = LD_SYNTAX_BIN },
|
|
{"Float", .val = LD_SYNTAX_FLOAT },
|
|
{0}
|
|
};
|
|
|
|
cfg_option_t* syntax;
|
|
|
|
if (cfg_eat_equal(st, flags)) return -1;
|
|
|
|
if (!(ptr = pkg_realloc(cfg->field, sizeof(str) * (cfg->n + 1)))) {
|
|
ERR("ldap:%s:%d:%d Out of memory\n", st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
cfg->field = (str*)ptr;
|
|
cfg->field[cfg->n].s = NULL;
|
|
|
|
if (!(ptr = pkg_realloc(cfg->attr, sizeof(str) * (cfg->n + 1)))) {
|
|
ERR("ldap:%s:%d:%d: Out of memory\n", st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
cfg->attr = (str*)ptr;
|
|
cfg->attr[cfg->n].s = NULL;
|
|
|
|
if (!(ptr = pkg_realloc(cfg->syntax, sizeof(enum ld_syntax)*(cfg->n+1)))) {
|
|
ERR("ldap:%s:%d:%d: Out of memory\n", st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
cfg->syntax = (enum ld_syntax*)ptr;
|
|
cfg->syntax[cfg->n] = LD_SYNTAX_STRING;
|
|
|
|
cfg->n++;
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: Database field name expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
if (t.type != CFG_TOKEN_ALPHA) {
|
|
ERR("ldap:%s:%d:%d: Invalid field name format %d:'%.*s'\n",
|
|
st->file, t.start.line, t.start.col,
|
|
t.type, STR_FMT(&t.val));
|
|
return -1;
|
|
}
|
|
|
|
if ((cfg->field[cfg->n - 1].s = as_asciiz(&t.val)) == NULL) {
|
|
ERR("ldap:%s:%d:%d: Out of memory\n", st->file,
|
|
t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
cfg->field[cfg->n - 1].len = t.val.len;
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: Delimiter ':' missing\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
if (t.type != ':') {
|
|
ERR("ldap:%s:%d:%d: Syntax error, ':' expected\n",
|
|
st->file, t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: LDAP Attribute syntax or name expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
if (t.type == '(') {
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: LDAP Attribute Syntax expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
if (t.type != CFG_TOKEN_ALPHA) {
|
|
ERR("ldap:%s:%d:%d: Invalid LDAP attribute syntax format %d:'%.*s'\n",
|
|
st->file, t.start.line, t.start.col,
|
|
t.type, STR_FMT(&t.val));
|
|
return -1;
|
|
}
|
|
|
|
if ((syntax = cfg_lookup_token(syntaxes, &t.val)) == NULL) {
|
|
ERR("ldap:%s:%d:%d: Invalid syntaxt value '%.*s'\n",
|
|
st->file, t.start.line, t.start.col, STR_FMT(&t.val));
|
|
return -1;
|
|
}
|
|
cfg->syntax[cfg->n - 1] = syntax->val;
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: Closing ')' missing in attribute syntax\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
if (t.type != ')') {
|
|
ERR("ldap:%s:%d:%d: Syntax error, ')' expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("ldap:%s:%d:%d: LDAP Attribute name expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (t.type != CFG_TOKEN_ALPHA) {
|
|
ERR("ldap:%s:%d:%d: Invalid LDAP attribute name format %d:'%.*s'\n",
|
|
st->file, t.start.line, t.start.col, t.type, STR_FMT(&t.val));
|
|
return -1;
|
|
}
|
|
|
|
if ((cfg->attr[cfg->n - 1].s = as_asciiz(&t.val)) == NULL) {
|
|
ERR("ldap:%s:%d:%d: Out of memory\n",
|
|
st->file, t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
cfg->attr[cfg->n - 1].len = t.val.len;
|
|
|
|
if (cfg_eat_eol(st, flags)) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static cfg_option_t scope_values[] = {
|
|
{"base", .val = LDAP_SCOPE_BASE },
|
|
{"onelevel", .val = LDAP_SCOPE_ONELEVEL},
|
|
{"one", .val = LDAP_SCOPE_ONELEVEL},
|
|
{"subtree", .val = LDAP_SCOPE_SUBTREE },
|
|
{"sub", .val = LDAP_SCOPE_SUBTREE },
|
|
#if defined HAVE_SCOPE_CHILDREN
|
|
{"children", .val = LDAP_SCOPE_CHILDREN},
|
|
#endif
|
|
{0}
|
|
};
|
|
|
|
|
|
static cfg_option_t deref_values[] = {
|
|
{"never", .val = LDAP_DEREF_NEVER }, /* default, 0x00 */
|
|
{"searching", .val = LDAP_DEREF_SEARCHING},
|
|
{"finding", .val = LDAP_DEREF_FINDING },
|
|
{"always", .val = LDAP_DEREF_ALWAYS },
|
|
{0}
|
|
};
|
|
|
|
static cfg_option_t ldap_tab_options[] = {
|
|
{"scope", .param = scope_values, .f = cfg_parse_enum_opt},
|
|
{"field_map", .f = parse_field_map},
|
|
{"filter", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"base", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"timelimit", .f = cfg_parse_int_opt},
|
|
{"sizelimit", .f = cfg_parse_int_opt},
|
|
{"chase_references", .param = deref_values, .f = cfg_parse_enum_opt},
|
|
{"chase_referrals", .f = cfg_parse_bool_opt},
|
|
{0}
|
|
};
|
|
|
|
|
|
static cfg_option_t auth_values[] = {
|
|
{"none", .val = LDAP_AUTHMECH_NONE},
|
|
{"simple", .val = LDAP_AUTHMECH_SIMPLE},
|
|
{"digest-md5", .val = LDAP_AUTHMECH_DIGESTMD5},
|
|
{"external", .val = LDAP_AUTHMECH_EXTERNAL},
|
|
{0}
|
|
};
|
|
|
|
|
|
static cfg_option_t ldap_con_options[] = {
|
|
{"host", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"port", .f = cfg_parse_int_opt},
|
|
{"username", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"password", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"authtype", .param = auth_values, .f = cfg_parse_enum_opt},
|
|
{"tls", .f = cfg_parse_bool_opt},
|
|
{"ca_list", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{"require_certificate", .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
|
|
{0}
|
|
};
|
|
|
|
|
|
static cfg_option_t section_types[] = {
|
|
{"connection", .val = LDAP_CON_SECTION},
|
|
{"con", .val = LDAP_CON_SECTION},
|
|
{"table", .val = LDAP_TABLE_SECTION},
|
|
{0}
|
|
};
|
|
|
|
|
|
static int parse_section(void* param, cfg_parser_t* st, unsigned int flags)
|
|
{
|
|
cfg_token_t t;
|
|
int ret, type, i;
|
|
cfg_option_t* opt;
|
|
str* id = NULL;
|
|
struct ld_cfg* tab;
|
|
struct ld_con_info* cinfo;
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("%s:%d:%d: Section type missing\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
if (t.type != CFG_TOKEN_ALPHA ||
|
|
((opt = cfg_lookup_token(section_types, &t.val)) == NULL)) {
|
|
ERR("%s:%d:%d: Invalid section type %d:'%.*s'\n",
|
|
st->file, t.start.line, t.start.col, t.type, STR_FMT(&t.val));
|
|
return -1;
|
|
}
|
|
type = opt->val;
|
|
|
|
if (type == LDAP_TABLE_SECTION) {
|
|
if ((tab = pkg_malloc(sizeof(*tab))) == NULL) {
|
|
ERR("ldap:%s:%d: Out of memory\n", st->file, st->line);
|
|
return -1;
|
|
}
|
|
memset(tab, '\0', sizeof(*tab));
|
|
tab->next = cfg;
|
|
cfg = tab;
|
|
|
|
cfg_set_options(st, ldap_tab_options);
|
|
ldap_tab_options[2].param = &cfg->filter;
|
|
ldap_tab_options[3].param = &cfg->base;
|
|
for(i = 0; scope_values[i].name; i++) {
|
|
scope_values[i].param = &cfg->scope;
|
|
}
|
|
ldap_tab_options[4].param = &cfg->timelimit;
|
|
ldap_tab_options[5].param = &cfg->sizelimit;
|
|
for(i = 0; deref_values[i].name; i++) {
|
|
deref_values[i].param = &cfg->chase_references;
|
|
}
|
|
ldap_tab_options[7].param = &cfg->chase_referrals;
|
|
} else if (type == LDAP_CON_SECTION) {
|
|
if ((cinfo = pkg_malloc(sizeof(*cinfo))) == NULL) {
|
|
ERR("ldap:%s:%d: Out of memory\n", st->file, st->line);
|
|
return -1;
|
|
}
|
|
memset(cinfo, '\0', sizeof(*cinfo));
|
|
cinfo->next = con;
|
|
con = cinfo;
|
|
|
|
cfg_set_options(st, ldap_con_options);
|
|
ldap_con_options[0].param = &con->host;
|
|
ldap_con_options[1].param = &con->port;
|
|
ldap_con_options[2].param = &con->username;
|
|
ldap_con_options[3].param = &con->password;
|
|
for(i = 0; auth_values[i].name; i++) {
|
|
auth_values[i].param = &con->authmech;
|
|
}
|
|
ldap_con_options[5].param = &con->tls;
|
|
ldap_con_options[6].param = &con->ca_list;
|
|
ldap_con_options[7].param = &con->req_cert;
|
|
} else {
|
|
BUG("%s:%d:%d: Unsupported section type %c\n",
|
|
st->file, t.start.line, t.start.col, t.type);
|
|
return -1;
|
|
}
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("%s:%d:%d: Delimiter ':' expected.\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
if (type == LDAP_TABLE_SECTION) {
|
|
id = &cfg->table;
|
|
} else if (type == LDAP_CON_SECTION) {
|
|
id = &con->id;
|
|
} else {
|
|
BUG("%s:%d:%d: Invalid section type %d\n", st->file,
|
|
st->line, st->col, type);
|
|
}
|
|
|
|
ret = cfg_parse_str(id, st, CFG_STR_PKGMEM);
|
|
if (ret < 0) return -1;
|
|
if (ret > 0) {
|
|
ERR("%s:%d:%d: Section identifier expected\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
|
|
ret = cfg_get_token(&t, st, 0);
|
|
if (ret < 0) return ret;
|
|
if (ret > 0) {
|
|
ERR("%s:%d:%d: Missing closing ']'.\n",
|
|
st->file, st->line, st->col);
|
|
return -1;
|
|
}
|
|
if (t. type != ']') {
|
|
ERR("%s:%d:%d: Syntax error, ']' expected.\n",
|
|
st->file, t.start.line, t.start.col);
|
|
return -1;
|
|
}
|
|
|
|
if (cfg_eat_eol(st, flags)) return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
struct ld_cfg* ld_find_cfg(str* table)
|
|
{
|
|
struct ld_cfg* ptr;
|
|
|
|
ptr = cfg;
|
|
while(ptr) {
|
|
if (ptr->table.len == table->len &&
|
|
!strncmp(ptr->table.s, table->s, table->len))
|
|
return ptr;
|
|
ptr = ptr->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
char* ld_find_attr_name(enum ld_syntax* syntax, struct ld_cfg* cfg, char* fld_name)
|
|
{
|
|
int i;
|
|
|
|
for(i = 0; i < cfg->n; i++) {
|
|
if (!strcmp(fld_name, cfg->field[i].s)) {
|
|
*syntax = cfg->syntax[i];
|
|
return cfg->attr[i].s;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
struct ld_con_info* ld_find_conn_info(str* conn_id)
|
|
{
|
|
struct ld_con_info* ptr;
|
|
|
|
ptr = con;
|
|
while(ptr) {
|
|
if (ptr->id.len == conn_id->len &&
|
|
!memcmp(ptr->id.s, conn_id->s, conn_id->len)) {
|
|
return ptr;
|
|
}
|
|
ptr = ptr->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static int ld_cfg_validity_check(struct ld_cfg *cfg)
|
|
{
|
|
struct ld_cfg *pcfg;
|
|
|
|
for (pcfg = cfg; pcfg; pcfg = pcfg->next) {
|
|
if (pcfg->sizelimit < 0 || pcfg->sizelimit > LD_MAXINT) {
|
|
ERR("ldap: invalid sizelimit (%d) specified\n", pcfg->sizelimit);
|
|
return -1;
|
|
}
|
|
if (pcfg->timelimit < 0 || pcfg->timelimit > LD_MAXINT) {
|
|
ERR("ldap: invalid timelimit (%d) specified\n", pcfg->timelimit);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int ld_load_cfg(str* filename)
|
|
{
|
|
cfg_parser_t* parser;
|
|
cfg = NULL;
|
|
|
|
if ((parser = cfg_parser_init(0, filename)) == NULL) {
|
|
ERR("ldap: Error while initializing configuration file parser.\n");
|
|
return -1;
|
|
}
|
|
|
|
cfg_section_parser(parser, parse_section, NULL);
|
|
|
|
if (sr_cfg_parse(parser)) {
|
|
if (cfg == NULL) {
|
|
ERR("ldap: A table name (i.e. [table_name]) is missing in the "
|
|
"configuration file.\n");
|
|
}
|
|
cfg_parser_close(parser);
|
|
ld_cfg_free();
|
|
return -1;
|
|
}
|
|
cfg_parser_close(parser);
|
|
|
|
if (ld_cfg_validity_check(cfg)) {
|
|
ld_cfg_free();
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|