2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A/*
2N/A * lib/kdb/kdb_ldap/kdb_ldap.c
2N/A *
2N/A * Copyright (c) 2004-2005, Novell, Inc.
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions are met:
2N/A *
2N/A * * Redistributions of source code must retain the above copyright notice,
2N/A * this list of conditions and the following disclaimer.
2N/A * * Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * * The copyright holder's name is not used to endorse or promote products
2N/A * derived from this software without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2N/A * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2N/A * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2N/A * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2N/A * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2N/A * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2N/A * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2N/A * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2N/A * POSSIBILITY OF SUCH DAMAGE.
2N/A */
2N/A
2N/A#include "autoconf.h"
2N/A#if HAVE_UNISTD_H
2N/A#include <unistd.h>
2N/A#endif
2N/A
2N/A#include <ctype.h>
2N/A#include "kdb_ldap.h"
2N/A#include "ldap_misc.h"
2N/A#include <kdb5.h>
2N/A#include <kadm5/admin.h>
2N/A
2N/A/* Solaris Kerberos */
2N/A#include <libintl.h>
2N/A#include "ldap_main.h"
2N/A
2N/A#if defined(NEED_ISBLANK_PROTO) && !defined(isblank)
2N/Aextern int isblank();
2N/A#endif
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_get_db_opt(char *input, char **opt, char **val)
2N/A{
2N/A char *pos = strchr(input, '=');
2N/A
2N/A *val = NULL;
2N/A if (pos == NULL) {
2N/A *opt = strdup(input);
2N/A if (*opt == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A } else {
2N/A int len = pos - input;
2N/A *opt = malloc((unsigned) len + 1);
2N/A if (!*opt) {
2N/A return ENOMEM;
2N/A }
2N/A memcpy(*opt, input, (unsigned) len);
2N/A /* ignore trailing blanks */
2N/A while (isblank((*opt)[len-1]))
2N/A --len;
2N/A (*opt)[len] = '\0';
2N/A
2N/A pos += 1; /* move past '=' */
2N/A while (isblank(*pos)) /* ignore leading blanks */
2N/A pos += 1;
2N/A if (*pos != '\0') {
2N/A *val = strdup (pos);
2N/A if (!*val) {
2N/A free (*opt);
2N/A return ENOMEM;
2N/A }
2N/A }
2N/A }
2N/A return (0);
2N/A
2N/A}
2N/A
2N/A
2N/A/*
2N/A * ldap get age
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_db_get_age(context, db_name, age)
2N/A krb5_context context;
2N/A char *db_name;
2N/A time_t *age;
2N/A{
2N/A time (age);
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * read startup information - kerberos and realm container
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_read_startup_information(krb5_context context)
2N/A{
2N/A krb5_error_code retval = 0;
2N/A kdb5_dal_handle *dal_handle=NULL;
2N/A krb5_ldap_context *ldap_context=NULL;
2N/A int mask = 0;
2N/A
2N/A SETUP_CONTEXT();
2N/A if ((retval=krb5_ldap_read_krbcontainer_params(context, &(ldap_context->krbcontainer)))) {
2N/A prepend_err_str (context, gettext("Unable to read Kerberos container"), retval, retval);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((retval=krb5_ldap_read_realm_params(context, context->default_realm, &(ldap_context->lrparams), &mask))) {
2N/A prepend_err_str (context, gettext("Unable to read Realm"), retval, retval);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (((mask & LDAP_REALM_MAXTICKETLIFE) == 0) || ((mask & LDAP_REALM_MAXRENEWLIFE) == 0)
2N/A || ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0)) {
2N/A kadm5_config_params params_in, params_out;
2N/A
2N/A memset(&params_in, 0, sizeof(params_in));
2N/A memset(&params_out, 0, sizeof(params_out));
2N/A
2N/A retval = kadm5_get_config_params(context, 1, &params_in, &params_out);
2N/A if (retval) {
2N/A if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
2N/A ldap_context->lrparams->max_life = 24 * 60 * 60; /* 1 day */
2N/A }
2N/A if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
2N/A ldap_context->lrparams->max_renewable_life = 0;
2N/A }
2N/A if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
2N/A ldap_context->lrparams->tktflags = KRB5_KDB_DEF_FLAGS;
2N/A }
2N/A retval = 0;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((mask & LDAP_REALM_MAXTICKETLIFE) == 0) {
2N/A if (params_out.mask & KADM5_CONFIG_MAX_LIFE)
2N/A ldap_context->lrparams->max_life = params_out.max_life;
2N/A }
2N/A
2N/A if ((mask & LDAP_REALM_MAXRENEWLIFE) == 0) {
2N/A if (params_out.mask & KADM5_CONFIG_MAX_RLIFE)
2N/A ldap_context->lrparams->max_renewable_life = params_out.max_rlife;
2N/A }
2N/A
2N/A if ((mask & LDAP_REALM_KRBTICKETFLAGS) == 0) {
2N/A if (params_out.mask & KADM5_CONFIG_FLAGS)
2N/A ldap_context->lrparams->tktflags = params_out.flags;
2N/A }
2N/A
2N/A kadm5_free_config_params(context, &params_out);
2N/A }
2N/A
2N/Acleanup:
2N/A return retval;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Interrogate the root DSE (zero length DN) for an attribute
2N/A * value assertion.
2N/A */
2N/Astatic int
2N/Ahas_rootdse_ava(krb5_context context, char *ldap_server, char *attribute,
2N/A char *value)
2N/A{
2N/A int i=0, flag=0, ret=0, retval=0;
2N/A char *attrs[2], **values=NULL;
2N/A LDAP *ld=NULL;
2N/A LDAPMessage *msg=NULL, *res=NULL;
2N/A struct berval cred;
2N/A
2N/A attrs[0] = attribute;
2N/A attrs[1] = NULL;
2N/A
2N/A /*
2N/A * Solaris Kerberos: don't use SSL since we are checking to see if SASL
2N/A * Externnal mech is supported.
2N/A */
2N/A retval = ldap_initialize(&ld, ldap_server, SSL_OFF, NULL);
2N/A if (retval != LDAP_SUCCESS) {
2N/A ret = 2; /* Don't know */
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* Solaris Kerberos: anon bind not needed */
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A cred.bv_val = "";
2N/A cred.bv_len = 0;
2N/A
2N/A /* Anonymous bind */
2N/A retval = ldap_sasl_bind_s(ld, "", NULL, &cred, NULL, NULL, NULL);
2N/A if (retval != LDAP_SUCCESS) {
2N/A ret = 2; /* Don't know */
2N/A goto cleanup;
2N/A }
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A retval = ldap_search_ext_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, NULL, NULL, NULL, 0, &res);
2N/A if (retval != LDAP_SUCCESS) {
2N/A ret = 2; /* Don't know */
2N/A goto cleanup;
2N/A }
2N/A
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A msg = ldap_first_message(ld, res);
2N/A#else
2N/A /* Solaris Kerberos: more accurate */
2N/A msg = ldap_first_entry(ld, res);
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A if (msg == NULL) {
2N/A ret = 2; /* Don't know */
2N/A goto cleanup;
2N/A }
2N/A
2N/A values = ldap_get_values(ld, msg, attribute);
2N/A if (values == NULL) {
2N/A ret = 1; /* Not supported */
2N/A goto cleanup;
2N/A }
2N/A
2N/A for (i = 0; values[i] != NULL; i++) {
2N/A if (strcmp(values[i], value) == 0) {
2N/A flag = 1;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A if (flag != 1) {
2N/A ret = 1; /* Not supported */
2N/A goto cleanup;
2N/A }
2N/A
2N/Acleanup:
2N/A
2N/A if (values != NULL)
2N/A ldap_value_free(values);
2N/A
2N/A if (res != NULL)
2N/A ldap_msgfree(res);
2N/A
2N/A if (ld != NULL)
2N/A ldap_unbind_ext_s(ld, NULL, NULL);
2N/A
2N/A return ret;
2N/A}
2N/A
2N/A#define ERR_MSG1 "Unable to check if SASL EXTERNAL mechanism is supported by LDAP server. Proceeding anyway ..."
2N/A#define ERR_MSG2 "SASL EXTERNAL mechanism not supported by LDAP server. Can't perform certificate-based bind."
2N/A
2N/A/* Function to check if a LDAP server supports the SASL external mechanism
2N/A *Return values:
2N/A * 0 => supports
2N/A * 1 => does not support
2N/A * 2 => don't know
2N/A */
2N/Aint
2N/Ahas_sasl_external_mech(krb5_context context, char *ldap_server)
2N/A{
2N/A int ret;
2N/A
2N/A ret = has_rootdse_ava(context, ldap_server,
2N/A "supportedSASLMechanisms", "EXTERNAL");
2N/A switch (ret) {
2N/A case 1: /* not supported */
2N/A krb5_set_error_message(context, 1, "%s", gettext(ERR_MSG2));
2N/A break;
2N/A case 2: /* don't know */
2N/A krb5_set_error_message(context, 1, "%s", gettext(ERR_MSG1));
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A return ret;
2N/A}
2N/A
2N/Aint
2N/Ahas_modify_increment(context, ldap_server)
2N/A krb5_context context;
2N/A char *ldap_server;
2N/A{
2N/A return has_rootdse_ava(context, ldap_server,
2N/A "supportedFeatures", "1.3.6.1.1.14");
2N/A}
2N/A
2N/Avoid *
2N/Akrb5_ldap_alloc(krb5_context context, void *ptr, size_t size)
2N/A{
2N/A return realloc(ptr, size);
2N/A}
2N/A
2N/Avoid
2N/Akrb5_ldap_free(krb5_context context, void *ptr)
2N/A{
2N/A free(ptr);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_open(krb5_context context, char *conf_section, char **db_args,
2N/A int mode)
2N/A{
2N/A krb5_error_code status = 0;
2N/A char **t_ptr = db_args;
2N/A krb5_ldap_context *ldap_context=NULL;
2N/A int srv_cnt = 0;
2N/A kdb5_dal_handle *dal_handle=NULL;
2N/A
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A ldap_context = calloc(1, sizeof(krb5_ldap_context));
2N/A if (ldap_context == NULL) {
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A ldap_context->kcontext = context;
2N/A
2N/A while (t_ptr && *t_ptr) {
2N/A char *opt = NULL, *val = NULL;
2N/A
2N/A if ((status = krb5_ldap_get_db_opt(*t_ptr, &opt, &val)) != 0) {
2N/A goto clean_n_exit;
2N/A }
2N/A if (opt && !strcmp(opt, "binddn")) {
2N/A if (ldap_context->bind_dn) {
2N/A free (opt);
2N/A free (val);
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'binddn' missing"));
2N/A goto clean_n_exit;
2N/A }
2N/A if (val == NULL) {
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'binddn' value missing"));
2N/A free(opt);
2N/A goto clean_n_exit;
2N/A }
2N/A ldap_context->bind_dn = strdup(val);
2N/A if (ldap_context->bind_dn == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A } else if (opt && !strcmp(opt, "nconns")) {
2N/A if (ldap_context->max_server_conns) {
2N/A free (opt);
2N/A free (val);
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'nconns' missing"));
2N/A goto clean_n_exit;
2N/A }
2N/A if (val == NULL) {
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'nconns' value missing"));
2N/A free(opt);
2N/A goto clean_n_exit;
2N/A }
2N/A ldap_context->max_server_conns = atoi(val) ? atoi(val) : DEFAULT_CONNS_PER_SERVER;
2N/A } else if (opt && !strcmp(opt, "bindpwd")) {
2N/A if (ldap_context->bind_pwd) {
2N/A free (opt);
2N/A free (val);
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'bindpwd' missing"));
2N/A goto clean_n_exit;
2N/A }
2N/A if (val == NULL) {
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'bindpwd' value missing"));
2N/A free(opt);
2N/A goto clean_n_exit;
2N/A }
2N/A ldap_context->bind_pwd = strdup(val);
2N/A if (ldap_context->bind_pwd == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A } else if (opt && !strcmp(opt, "host")) {
2N/A if (val == NULL) {
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, gettext("'host' value missing"));
2N/A free(opt);
2N/A goto clean_n_exit;
2N/A }
2N/A if (ldap_context->server_info_list == NULL)
2N/A ldap_context->server_info_list = (krb5_ldap_server_info **) calloc (SERV_COUNT+1, sizeof (krb5_ldap_server_info *)) ;
2N/A
2N/A if (ldap_context->server_info_list == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A ldap_context->server_info_list[srv_cnt] = (krb5_ldap_server_info *) calloc (1, sizeof (krb5_ldap_server_info));
2N/A if (ldap_context->server_info_list[srv_cnt] == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A ldap_context->server_info_list[srv_cnt]->server_status = NOTSET;
2N/A
2N/A ldap_context->server_info_list[srv_cnt]->server_name = strdup(val);
2N/A if (ldap_context->server_info_list[srv_cnt]->server_name == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A srv_cnt++;
2N/A#ifdef HAVE_EDIRECTORY
2N/A } else if (opt && !strcmp(opt, "cert")) {
2N/A if (val == NULL) {
2N/A status = EINVAL;
2N/A krb5_set_error_message (context, status, "'cert' value missing");
2N/A free(opt);
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A if (ldap_context->root_certificate_file == NULL) {
2N/A ldap_context->root_certificate_file = strdup(val);
2N/A if (ldap_context->root_certificate_file == NULL) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A } else {
2N/A char *newstr;
2N/A
2N/A if (asprintf(&newstr, "%s %s",
2N/A ldap_context->root_certificate_file, val) < 0) {
2N/A free (opt);
2N/A free (val);
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A free(ldap_context->root_certificate_file);
2N/A ldap_context->root_certificate_file = newstr;
2N/A }
2N/A#endif
2N/A } else {
2N/A /* ignore hash argument. Might have been passed from create */
2N/A status = EINVAL;
2N/A if (opt && !strcmp(opt, "temporary")) {
2N/A /*
2N/A * temporary is passed in when kdb5_util load without -update is done.
2N/A * This is unsupported by the LDAP plugin.
2N/A */
2N/A krb5_set_error_message (context, status,
2N/A gettext("open of LDAP directory aborted, plugin requires -update argument"));
2N/A } else {
2N/A krb5_set_error_message (context, status, gettext("unknown option \'%s\'"),
2N/A opt?opt:val);
2N/A }
2N/A free(opt);
2N/A free(val);
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A free(opt);
2N/A free(val);
2N/A t_ptr++;
2N/A }
2N/A
2N/A dal_handle = context->dal_handle;
2N/A dal_handle->db_context = ldap_context;
2N/A
2N/A /* Solaris Kerberos */
2N/A ldap_context = NULL;
2N/A
2N/A status = krb5_ldap_read_server_params(context, conf_section, mode & 0x0300);
2N/A if (status) {
2N/A prepend_err_str (context, gettext("Error reading LDAP server params: "), status, status);
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A /* Solaris Kerberos */
2N/A if ((status=krb5_ldap_db_init(context, dal_handle->db_context)) != 0) {
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A if ((status=krb5_ldap_read_startup_information(context)) != 0) {
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/Aclean_n_exit:
2N/A /* may be clearing up is not required db_fini might do it for us, check out */
2N/A if (status) {
2N/A /*
2N/A * Solaris Kerberos
2N/A * This code path can be called before
2N/A * dal_handle->db_context == ldap_context in which case ldap_context
2N/A * should be freed. After dal_handle->db_context == ldap_context
2N/A * ldap_context will be set to NULL and the memory cleaned up by
2N/A * krb5_ldap_free_ldap_context() (via the context argument).
2N/A */
2N/A if (ldap_context)
2N/A krb5_ldap_free_ldap_context(ldap_context);
2N/A krb5_ldap_close(context);
2N/A }
2N/A return status;
2N/A}
2N/A
2N/A#include "ldap_err.h"
2N/Aint
2N/Aset_ldap_error(krb5_context ctx, int st, int op)
2N/A{
2N/A int translated_st = translate_ldap_error(st, op);
2N/A krb5_set_error_message(ctx, translated_st, "%s", ldap_err2string(st));
2N/A return translated_st;
2N/A}
2N/A
2N/Avoid
2N/Aprepend_err_str(krb5_context ctx, const char *str, krb5_error_code err,
2N/A krb5_error_code oerr)
2N/A{
2N/A const char *omsg;
2N/A if (oerr == 0) oerr = err;
2N/A omsg = krb5_get_error_message (ctx, err);
2N/A krb5_set_error_message (ctx, err, "%s %s", str, omsg);
2N/A /* Solaris Kerberos: Memleak */
2N/A krb5_free_error_message(ctx, omsg);
2N/A}
2N/A
2N/Aextern krb5int_access accessor;
2N/AMAKE_INIT_FUNCTION(kldap_init_fn);
2N/A
2N/Aint
2N/Akldap_init_fn(void)
2N/A{
2N/A /* Global (per-module) initialization. */
2N/A return krb5int_accessor (&accessor, KRB5INT_ACCESS_VERSION);
2N/A}
2N/A
2N/Aint
2N/Akldap_ensure_initialized(void)
2N/A{
2N/A return CALL_INIT_FUNCTION (kldap_init_fn);
2N/A}