#include "autoconf.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#include "kdb_ldap.h"
#include "ldap_misc.h"
#include <kdb5.h>
#include <kadm5/admin.h>
krb5_error_code
krb5_ldap_get_db_opt(char *input, char **opt, char **val)
{
char *pos = strchr(input, '=');
*val = NULL;
if (pos == NULL) {
*opt = strdup(input);
if (*opt == NULL) {
return ENOMEM;
}
} else {
int len = pos - input;
*opt = malloc((unsigned) len + 1);
if (!*opt) {
return ENOMEM;
}
memcpy(*opt, input, (unsigned) len);
while (isblank((*opt)[len-1]))
--len;
(*opt)[len] = '\0';
pos += 1;
while (isblank(*pos))
pos += 1;
if (*pos != '\0') {
*val = strdup (pos);
if (!*val) {
free (*opt);
return ENOMEM;
}
}
}
return (0);
}
krb5_error_code
krb5_ldap_db_get_age(context, db_name, age)
krb5_context context;
char *db_name;
time_t *age;
{
time (age);
return 0;
}
krb5_error_code
krb5_ldap_read_startup_information(krb5_context context)
{
krb5_error_code retval = 0;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
int mask = 0;
SETUP_CONTEXT();
if ((retval=krb5_ldap_read_krbcontainer_params(context, &(ldap_context->krbcontainer)))) {
prepend_err_str (context, "Unable to read Kerberos container", retval, retval);
goto cleanup;
}
if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
prepend_err_str (context, "Unable to read Realm", retval, retval);
goto cleanup;
}
if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
|| ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
kadm5_config_params params_in, params_out;
memset((char *) ¶ms_in, 0, sizeof(params_in));
memset((char *) ¶ms_out, 0, sizeof(params_out));
retval = kadm5_get_config_params(context, 1, ¶ms_in, ¶ms_out);
if (retval) {
if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
ldap_context->lrparams->max_life = 24 * 60 * 60;
}
if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
ldap_context->lrparams->max_renewable_life = 0;
}
if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
}
retval = 0;
goto cleanup;
}
if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
ldap_context->lrparams->max_life = params_out.max_life;
}
if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
}
if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
if (params_out.mask & KADM5_CONFIG_FLAGS)
ldap_context->lrparams->tktflags = params_out.flags;
}
kadm5_free_config_params(context, ¶ms_out);
}
cleanup:
return retval;
}
#define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..."
#define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind."
int
has_sasl_external_mech(context, ldap_server)
krb5_context context;
char *ldap_server;
{
int i=0, flag=0, ret=0, retval=0;
char *attrs[]={"supportedSASLMechanisms", NULL}, **values=NULL;
LDAP *ld=NULL;
LDAPMessage *msg=NULL, *res=NULL;
retval = ldap_initialize(&ld, ldap_server);
if (retval != LDAP_SUCCESS) {
krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2;
goto cleanup;
}
retval = ldap_sasl_bind_s(ld, NULL, NULL, NULL, NULL, NULL, NULL);
if (retval != LDAP_SUCCESS) {
krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2;
goto cleanup;
}
retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res);
if (retval != LDAP_SUCCESS) {
krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2;
goto cleanup;
}
msg = ldap_first_message(ld, res);
if (msg == NULL) {
krb5_set_error_message(context, 2, "%s", ERR_MSG1);
ret = 2;
goto cleanup;
}
values = ldap_get_values(ld, msg, "supportedSASLMechanisms");
if (values == NULL) {
krb5_set_error_message(context, 1, "%s", ERR_MSG2);
ret = 1;
goto cleanup;
}
for (i = 0; values[i] != NULL; i++) {
if (strcmp(values[i], "EXTERNAL"))
continue;
flag = 1;
}
if (flag != 1) {
krb5_set_error_message(context, 1, "%s", ERR_MSG2);
ret = 1;
goto cleanup;
}
cleanup:
if (values != NULL)
ldap_value_free(values);
if (res != NULL)
ldap_msgfree(res);
if (ld != NULL)
ldap_unbind_ext_s(ld, NULL, NULL);
return ret;
}
void * krb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void krb5_ldap_free(krb5_context context, void *ptr)
{
free(ptr);
}
krb5_error_code krb5_ldap_open(krb5_context context,
char *conf_section,
char **db_args,
int mode)
{
krb5_error_code status = 0;
char **t_ptr = db_args;
krb5_ldap_context *ldap_context=NULL;
int srv_cnt = 0;
kdb5_dal_handle *dal_handle=NULL;
krb5_clear_error_message(context);
ldap_context = calloc(1, sizeof(krb5_ldap_context));
if (ldap_context == NULL) {
status = ENOMEM;
goto clean_n_exit;
}
ldap_context->kcontext = context;
while (t_ptr && *t_ptr) {
char *opt = NULL, *val = NULL;
if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
goto clean_n_exit;
}
if (opt && !strcmp(opt, "binddn")) {
if (ldap_context->bind_dn) {
free (opt);
free (val);
status = EINVAL;
krb5_set_error_message (context, status, "'binddn' missing");
goto clean_n_exit;
}
if (val == NULL) {
status = EINVAL;
krb5_set_error_message (context, status, "'binddn' value missing");
free(opt);
goto clean_n_exit;
}
ldap_context->bind_dn = strdup(val);
if (ldap_context->bind_dn == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
} else if (opt && !strcmp(opt, "nconns")) {
if (ldap_context->max_server_conns) {
free (opt);
free (val);
status = EINVAL;
krb5_set_error_message (context, status, "'nconns' missing");
goto clean_n_exit;
}
if (val == NULL) {
status = EINVAL;
krb5_set_error_message (context, status, "'nconns' value missing");
free(opt);
goto clean_n_exit;
}
ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
} else if (opt && !strcmp(opt, "bindpwd")) {
if (ldap_context->bind_pwd) {
free (opt);
free (val);
status = EINVAL;
krb5_set_error_message (context, status, "'bindpwd' missing");
goto clean_n_exit;
}
if (val == NULL) {
status = EINVAL;
krb5_set_error_message (context, status, "'bindpwd' value missing");
free(opt);
goto clean_n_exit;
}
ldap_context->bind_pwd = strdup(val);
if (ldap_context->bind_pwd == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
} else if (opt && !strcmp(opt, "host")) {
if (val == NULL) {
status = EINVAL;
krb5_set_error_message (context, status, "'host' value missing");
free(opt);
goto clean_n_exit;
}
if (ldap_context->server_info_list == NULL)
ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (SERV_COUNT+1, sizeof (krb5_ldap_server_info *)) ;
if (ldap_context->server_info_list == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
ldap_context->server_info_list[srv_cnt] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info));
if (ldap_context->server_info_list[srv_cnt] == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
srv_cnt++;
#ifdef HAVE_EDIRECTORY
} else if (opt && !strcmp(opt, "cert")) {
if (val == NULL) {
status = EINVAL;
krb5_set_error_message (context, status, "'cert' value missing");
free(opt);
goto clean_n_exit;
}
if (ldap_context->root_certificate_file == NULL) {
ldap_context->root_certificate_file = strdup(val);
if (ldap_context->root_certificate_file == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
} else {
void *tmp=NULL;
char *oldstr = NULL;
unsigned int len=0;
oldstr = strdup(ldap_context->root_certificate_file);
if (oldstr == NULL) {
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
tmp = ldap_context->root_certificate_file;
len = strlen(ldap_context->root_certificate_file) + 2 + strlen(val);
ldap_context->root_certificate_file = realloc(ldap_context->root_certificate_file,
len);
if (ldap_context->root_certificate_file == NULL) {
free (tmp);
free (opt);
free (val);
status = ENOMEM;
goto clean_n_exit;
}
memset(ldap_context->root_certificate_file, 0, len);
sprintf(ldap_context->root_certificate_file,"%s %s", oldstr, val);
free (oldstr);
}
#endif
} else {
status = EINVAL;
if (opt && !strcmp(opt, "temporary")) {
krb5_set_error_message (context, status,
"open of LDAP directory aborted, plugin requires -update argument");
} else {
krb5_set_error_message (context, status, "unknown option \'%s\'",
opt?opt:val);
}
free(opt);
free(val);
goto clean_n_exit;
}
free(opt);
free(val);
t_ptr++;
}
dal_handle = (kdb5_dal_handle *) context->db_context;
dal_handle->db_context = ldap_context;
status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
if (status) {
if (ldap_context)
krb5_ldap_free_ldap_context(ldap_context);
ldap_context = NULL;
dal_handle->db_context = NULL;
prepend_err_str (context, "Error reading LDAP server params: ", status, status);
goto clean_n_exit;
}
if ((status=krb5_ldap_db_init(context, ldap_context)) != 0) {
goto clean_n_exit;
}
if ((status=krb5_ldap_read_startup_information(context)) != 0) {
goto clean_n_exit;
}
clean_n_exit:
if (status) {
krb5_ldap_close(context);
}
return status;
}
#include "ldap_err.h"
int
set_ldap_error (krb5_context ctx, int st, int op)
{
int translated_st = translate_ldap_error(st, op);
krb5_set_error_message(ctx, translated_st, "%s", ldap_err2string(st));
return translated_st;
}
void
prepend_err_str (krb5_context ctx, const char *str, krb5_error_code err,
krb5_error_code oerr)
{
const char *omsg;
if (oerr == 0) oerr = err;
omsg = krb5_get_error_message (ctx, err);
krb5_set_error_message (ctx, err, "%s %s", str, omsg);
}
extern krb5int_access accessor;
MAKE_INIT_FUNCTION(kldap_init_fn);
int kldap_init_fn(void)
{
return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
}
int kldap_ensure_initialized(void)
{
return CALL_INIT_FUNCTION (kldap_init_fn);
}