modules/db2_ldap/ld_cfg.c
29c53ed0
 /*
  * 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
9cd18ef9
  * with this program; if not, write to the Free Software Foundation, Inc.,
9e1ff448
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29c53ed0
  */
 
 #include "ld_cfg.h"
 #include "ld_mod.h"
7c7fabe9
 #include "ld_uri.h"
29c53ed0
 
 #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>
 
 
e977e0c7
 enum section_type {
 	LDAP_CON_SECTION = 0,
 	LDAP_TABLE_SECTION
 };
 
 static struct ld_cfg* cfg = NULL;
 
 static struct ld_con_info* con = NULL;
29c53ed0
 
 
 void ld_cfg_free(void)
 {
e977e0c7
 	struct ld_con_info* c;
29c53ed0
 	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);
 	}
e977e0c7
 
 	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);
9cd18ef9
 		if (c->username.s) pkg_free(c->username.s);
 		if (c->password.s) pkg_free(c->password.s);
 
e977e0c7
 		pkg_free(c);
 	}
 
29c53ed0
 }
 
 
 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;
 
e977e0c7
 	if (cfg_eat_equal(st, flags)) return -1;
29c53ed0
 
 	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;
9cd18ef9
 
29c53ed0
 	cfg->n++;
 
 	ret = cfg_get_token(&t, st, 0);
 	if (ret < 0) return -1;
e977e0c7
 	if (ret > 0) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Database field name expected\n",
29c53ed0
 		    st->file, st->line, st->col);
 		return -1;
 	}
 
 	if (t.type != CFG_TOKEN_ALPHA) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Invalid field name format %d:'%.*s'\n",
29c53ed0
 		    st->file, t.start.line, t.start.col,
 		    t.type, STR_FMT(&t.val));
 		return -1;
 	}
9cd18ef9
 
29c53ed0
 	if ((cfg->field[cfg->n - 1].s = as_asciiz(&t.val)) == NULL) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Out of memory\n", st->file,
29c53ed0
 			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;
e977e0c7
 	if (ret > 0) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Delimiter ':' missing\n",
29c53ed0
 		    st->file, st->line, st->col);
 		return -1;
 	}
 	if (t.type != ':') {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Syntax error, ':' expected\n",
29c53ed0
 		    st->file, t.start.line, t.start.col);
 		return -1;
 	}
 
 	ret = cfg_get_token(&t, st, 0);
 	if (ret < 0) return -1;
e977e0c7
 	if (ret > 0) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: LDAP Attribute syntax or name expected\n",
29c53ed0
 		    st->file, st->line, st->col);
 		return -1;
 	}
 
 	if (t.type == '(') {
 		ret = cfg_get_token(&t, st, 0);
 		if (ret < 0) return -1;
e977e0c7
 		if (ret > 0) {
9cd18ef9
 			ERR("ldap:%s:%d:%d: LDAP Attribute Syntax expected\n",
29c53ed0
 				st->file, st->line, st->col);
 			return -1;
 		}
 		if (t.type != CFG_TOKEN_ALPHA) {
9cd18ef9
 			ERR("ldap:%s:%d:%d: Invalid LDAP attribute syntax format %d:'%.*s'\n",
29c53ed0
 				st->file, t.start.line, t.start.col,
 				t.type, STR_FMT(&t.val));
 			return -1;
 		}
9cd18ef9
 
29c53ed0
 		if ((syntax = cfg_lookup_token(syntaxes, &t.val)) == NULL) {
9cd18ef9
 			ERR("ldap:%s:%d:%d: Invalid syntaxt value '%.*s'\n",
29c53ed0
 				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;
e977e0c7
 		if (ret > 0) {
9cd18ef9
 			ERR("ldap:%s:%d:%d: Closing ')' missing in attribute syntax\n",
29c53ed0
 				st->file, st->line, st->col);
 			return -1;
 		}
 
 		if (t.type != ')') {
9cd18ef9
 			ERR("ldap:%s:%d:%d: Syntax error, ')' expected\n",
29c53ed0
 				st->file, st->line, st->col);
 			return -1;
 		}
 
 		ret = cfg_get_token(&t, st, 0);
 		if (ret < 0) return -1;
e977e0c7
 		if (ret > 0) {
9cd18ef9
 			ERR("ldap:%s:%d:%d: LDAP Attribute name expected\n",
29c53ed0
 				st->file, st->line, st->col);
 			return -1;
 		}
 	}
9cd18ef9
 
29c53ed0
 	if (t.type != CFG_TOKEN_ALPHA) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Invalid LDAP attribute name format %d:'%.*s'\n",
29c53ed0
 		    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) {
9cd18ef9
 		ERR("ldap:%s:%d:%d: Out of memory\n",
29c53ed0
 		    st->file, t.start.line, t.start.col);
 		return -1;
 	}
 	cfg->attr[cfg->n - 1].len = t.val.len;
e977e0c7
 
 	if (cfg_eat_eol(st, flags)) return -1;
29c53ed0
 	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}
 };
 
 
8a5e170a
 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}
 };
 
e977e0c7
 static cfg_option_t ldap_tab_options[] = {
 	{"scope",     .param = scope_values, .f = cfg_parse_enum_opt},
29c53ed0
 	{"field_map", .f = parse_field_map},
e977e0c7
 	{"filter",    .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
 	{"base",      .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
cbba2c20
 	{"timelimit", .f = cfg_parse_int_opt},
 	{"sizelimit", .f = cfg_parse_int_opt},
8a5e170a
 	{"chase_references",  .param = deref_values, .f = cfg_parse_enum_opt},
 	{"chase_referrals",   .f = cfg_parse_bool_opt},
29c53ed0
 	{0}
 };
 
 
7c7fabe9
 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}
 };
 
 
e977e0c7
 static cfg_option_t ldap_con_options[] = {
ba8702db
 	{"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},
8a5e170a
 	{"tls",			    .f = cfg_parse_bool_opt},
ba8702db
 	{"ca_list",  		    .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
8a5e170a
 	{"require_certificate",     .f = cfg_parse_str_opt, .flags = CFG_STR_PKGMEM},
e977e0c7
 	{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)
29c53ed0
 {
e977e0c7
 	cfg_token_t t;
 	int ret, type, i;
 	cfg_option_t* opt;
9cd18ef9
 	str* id = NULL;
e977e0c7
 	struct ld_cfg* tab;
 	struct ld_con_info* cinfo;
 
 	ret = cfg_get_token(&t, st, 0);
 	if (ret < 0) return -1;
 	if (ret > 0) {
9cd18ef9
 		ERR("%s:%d:%d: Section type missing\n",
e977e0c7
 		    st->file, st->line, st->col);
 		return -1;
 	}
9cd18ef9
 
 	if (t.type != CFG_TOKEN_ALPHA ||
e977e0c7
 	    ((opt = cfg_lookup_token(section_types, &t.val)) == NULL)) {
9cd18ef9
 		ERR("%s:%d:%d: Invalid section type %d:'%.*s'\n",
e977e0c7
 		    st->file, t.start.line, t.start.col, t.type, STR_FMT(&t.val));
 		return -1;
 	}
 	type = opt->val;
29c53ed0
 
e977e0c7
 	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;
9cd18ef9
 
e977e0c7
 		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;
 		}
cbba2c20
 		ldap_tab_options[4].param = &cfg->timelimit;
 		ldap_tab_options[5].param = &cfg->sizelimit;
8a5e170a
 		for(i = 0; deref_values[i].name; i++) {
 			deref_values[i].param = &cfg->chase_references;
 		}
 		ldap_tab_options[7].param = &cfg->chase_referrals;
e977e0c7
 	} 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;
9cd18ef9
 
e977e0c7
 		cfg_set_options(st, ldap_con_options);
 		ldap_con_options[0].param = &con->host;
9cd18ef9
 		ldap_con_options[1].param = &con->port;
 		ldap_con_options[2].param = &con->username;
 		ldap_con_options[3].param = &con->password;
7c7fabe9
 		for(i = 0; auth_values[i].name; i++) {
 			auth_values[i].param = &con->authmech;
 		}
ba8702db
 		ldap_con_options[5].param = &con->tls;
ebbb449f
 		ldap_con_options[6].param = &con->ca_list;
 		ldap_con_options[7].param = &con->req_cert;
e977e0c7
 	} else {
 		BUG("%s:%d:%d: Unsupported section type %c\n",
 			st->file, t.start.line, t.start.col, t.type);
 		return -1;
 	}
29c53ed0
 
e977e0c7
 	ret = cfg_get_token(&t, st, 0);
 	if (ret < 0) return -1;
 	if (ret > 0) {
9cd18ef9
 		ERR("%s:%d:%d: Delimiter ':' expected.\n",
e977e0c7
 		    st->file, st->line, st->col);
29c53ed0
 		return -1;
 	}
 
e977e0c7
 	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);
29c53ed0
 	}
e977e0c7
 
 	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;
29c53ed0
 	}
9cd18ef9
 
e977e0c7
 	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;
 	}
9cd18ef9
 
e977e0c7
 	if (cfg_eat_eol(st, flags)) return -1;
29c53ed0
 	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;
 }
 
 
9cd18ef9
 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;
 }
 
29c53ed0
 
ad806ebf
 static int ld_cfg_validity_check(struct ld_cfg *cfg)
cbba2c20
 {
 	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;
 }
 
 
29c53ed0
 int ld_load_cfg(str* filename)
 {
 	cfg_parser_t* parser;
 	cfg = NULL;
 
14d6f347
 	if ((parser = cfg_parser_init(0, filename)) == NULL) {
29c53ed0
 		ERR("ldap: Error while initializing configuration file parser.\n");
 		return -1;
 	}
 
e977e0c7
 	cfg_section_parser(parser, parse_section, NULL);
29c53ed0
 
50ca02e5
 	if (sr_cfg_parse(parser)) {
29c53ed0
 		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);
cbba2c20
 
ad806ebf
 	if (ld_cfg_validity_check(cfg)) {
 		ld_cfg_free();
cbba2c20
 		return -1;
ad806ebf
 	}
cbba2c20
 
29c53ed0
 	return 0;
 }