2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/kdb/kdb_ldap/ldap_misc.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/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <string.h>
2N/A#include <time.h>
2N/A#include "kdb_ldap.h"
2N/A#include "ldap_misc.h"
2N/A#include "ldap_handle.h"
2N/A#include "ldap_err.h"
2N/A#include "ldap_principal.h"
2N/A#include "princ_xdr.h"
2N/A#include "ldap_pwd_policy.h"
2N/A#include <libintl.h> /* Solaris Kerberos */
2N/A
2N/A#ifdef NEED_STRPTIME_PROTO
2N/Aextern char *strptime (const char *, const char *, struct tm *);
2N/A#endif
2N/A
2N/Astatic krb5_error_code
2N/Aremove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
2N/A int sscope);
2N/A
2N/A/* Linux (GNU Libc) provides a length-limited variant of strdup.
2N/A But all the world's not Linux. */
2N/A#undef strndup
2N/A#define strndup my_strndup
2N/A#ifdef HAVE_LDAP_STR2DN
2N/Astatic char *
2N/Amy_strndup(const char *input, size_t limit)
2N/A{
2N/A size_t len = strlen(input);
2N/A char *result;
2N/A if (len > limit) {
2N/A result = malloc(1 + limit);
2N/A if (result != NULL) {
2N/A memcpy(result, input, limit);
2N/A result[limit] = 0;
2N/A }
2N/A return result;
2N/A } else
2N/A return strdup(input);
2N/A}
2N/A#endif
2N/A
2N/A/* Get integer or string values from the config section, falling back
2N/A to the default section, then to hard-coded values. */
2N/Astatic errcode_t
2N/Aprof_get_integer_def(krb5_context ctx, const char *conf_section,
2N/A const char *name, int dfl, krb5_ui_4 *out)
2N/A{
2N/A errcode_t err;
2N/A int out_temp = 0;
2N/A
2N/A err = profile_get_integer (ctx->profile,
2N/A KDB_MODULE_SECTION, conf_section, name,
2N/A 0, &out_temp);
2N/A if (err) {
2N/A krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
2N/A name, error_message(err));
2N/A return err;
2N/A }
2N/A if (out_temp != 0) {
2N/A *out = out_temp;
2N/A return 0;
2N/A }
2N/A err = profile_get_integer (ctx->profile,
2N/A KDB_MODULE_DEF_SECTION, name, 0,
2N/A dfl, &out_temp);
2N/A if (err) {
2N/A krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
2N/A name, error_message(err));
2N/A return err;
2N/A }
2N/A *out = out_temp;
2N/A return 0;
2N/A}
2N/A
2N/A/* We don't have non-null defaults in any of our calls, so don't
2N/A bother with the extra argument. */
2N/Astatic errcode_t
2N/Aprof_get_string_def(krb5_context ctx, const char *conf_section,
2N/A const char *name, char **out)
2N/A{
2N/A errcode_t err;
2N/A
2N/A err = profile_get_string (ctx->profile,
2N/A KDB_MODULE_SECTION, conf_section, name,
2N/A 0, out);
2N/A if (err) {
2N/A krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
2N/A name, error_message(err));
2N/A return err;
2N/A }
2N/A if (*out != 0)
2N/A return 0;
2N/A err = profile_get_string (ctx->profile,
2N/A KDB_MODULE_DEF_SECTION, name, 0,
2N/A 0, out);
2N/A if (err) {
2N/A krb5_set_error_message (ctx, err, gettext("Error reading '%s' attribute: %s"),
2N/A name, error_message(err));
2N/A return err;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * This function reads the parameters from the krb5.conf file. The
2N/A * parameters read here are DAL-LDAP specific attributes. Some of
2N/A * these are ldap_server ....
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_read_server_params(krb5_context context, char *conf_section,
2N/A int srv_type)
2N/A{
2N/A char *tempval=NULL, *save_ptr=NULL;
2N/A const char *delims="\t\n\f\v\r ,";
2N/A krb5_error_code st=0;
2N/A kdb5_dal_handle *dal_handle=NULL;
2N/A krb5_ldap_context *ldap_context=NULL;
2N/A krb5_ldap_server_info ***server_info=NULL;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A ldap_context = (krb5_ldap_context *) dal_handle->db_context;
2N/A
2N/A /* copy the conf_section into ldap_context for later use */
2N/A if (conf_section) {
2N/A ldap_context->conf_section = strdup (conf_section);
2N/A if (ldap_context->conf_section == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /* initialize the mutexs and condition variable */
2N/A /* this portion logically doesn't fit here should be moved appropriately */
2N/A
2N/A /* this mutex is used in ldap reconnection pool */
2N/A if (k5_mutex_init(&(ldap_context->hndl_lock)) != 0) {
2N/A st = KRB5_KDB_SERVER_INTERNAL_ERR;
2N/A#if 0
2N/A st = -1;
2N/A krb5_ldap_dal_err_funcp(context, krb5_err_have_str, st,
2N/A "k5_mutex_init failed");
2N/A#endif
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * If max_server_conns is not set read it from database module
2N/A * section of conf file this parameter defines maximum ldap
2N/A * connections per ldap server.
2N/A */
2N/A if (ldap_context->max_server_conns == 0) {
2N/A st = prof_get_integer_def (context, conf_section,
2N/A KRB5_CONF_LDAP_CONNS_PER_SERVER,
2N/A DEFAULT_CONNS_PER_SERVER,
2N/A &ldap_context->max_server_conns);
2N/A if (st)
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (ldap_context->max_server_conns < 2) {
2N/A st = EINVAL;
2N/A krb5_set_error_message (context, st,
2N/A gettext("Minimum connections required per server is 2"));
2N/A goto cleanup;
2N/A }
2N/A
2N/A /*
2N/A * If the bind dn is not set read it from the database module
2N/A * section of conf file this paramter is populated by one of the
2N/A * KDC, ADMIN or PASSWD dn to be used to connect to LDAP
2N/A * server. The srv_type decides which dn to read.
2N/A */
2N/A if (ldap_context->bind_dn == NULL) {
2N/A char *name = 0;
2N/A if (srv_type == KRB5_KDB_SRV_TYPE_KDC)
2N/A name = KRB5_CONF_LDAP_KDC_DN;
2N/A else if (srv_type == KRB5_KDB_SRV_TYPE_ADMIN)
2N/A name = KRB5_CONF_LDAP_KADMIN_DN;
2N/A else if (srv_type == KRB5_KDB_SRV_TYPE_PASSWD)
2N/A name = "ldap_kpasswdd_dn";
2N/A
2N/A if (name) {
2N/A st = prof_get_string_def (context, conf_section, name,
2N/A &ldap_context->bind_dn);
2N/A if (st)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Read service_password_file parameter from database module
2N/A * section of conf file this file contains stashed passwords of
2N/A * the KDC, ADMIN and PASSWD dns.
2N/A */
2N/A if (ldap_context->service_password_file == NULL) {
2N/A st = prof_get_string_def (context, conf_section,
2N/A KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE,
2N/A &ldap_context->service_password_file);
2N/A if (st)
2N/A goto cleanup;
2N/A
2N/A /* Solaris Kerberos: providing a default. */
2N/A if (ldap_context->service_password_file == NULL) {
2N/A st = profile_get_string (context->profile, KDB_MODULE_DEF_SECTION,
2N/A KRB5_CONF_LDAP_SERVICE_PASSWORD_FILE,
2N/A NULL,
2N/A DEF_SERVICE_PASSWD_FILE,
2N/A &ldap_context->service_password_file);
2N/A
2N/A if (st)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A
2N/A/*
2N/A * Solaris Kerberos: we must use root_certificate_file
2N/A *
2N/A * Note, I've changed the ldap_root_certificate_file config parameter to
2N/A * ldap_cert_path which is more appropriate for that parameter.
2N/A */
2N/A/* #ifdef HAVE_EDIRECTORY */
2N/A /*
2N/A * If root certificate file is not set read it from database
2N/A * module section of conf file this is the trusted root
2N/A * certificate of the Directory.
2N/A */
2N/A if (ldap_context->root_certificate_file == NULL) {
2N/A st = prof_get_string_def (context, conf_section,
2N/A KRB5_CONF_LDAP_ROOT_CERTIFICATE_FILE,
2N/A &ldap_context->root_certificate_file);
2N/A if (st)
2N/A goto cleanup;
2N/A }
2N/A/* #endif */
2N/A
2N/A /*
2N/A * If the ldap server parameter is not set read the list of ldap
2N/A * servers from the database module section of the conf file.
2N/A */
2N/A
2N/A if (ldap_context->server_info_list == NULL) {
2N/A unsigned int ele=0;
2N/A
2N/A server_info = &(ldap_context->server_info_list);
2N/A *server_info = (krb5_ldap_server_info **) calloc (SERV_COUNT+1,
2N/A sizeof (krb5_ldap_server_info *));
2N/A
2N/A if (*server_info == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((st=profile_get_string(context->profile, KDB_MODULE_SECTION, conf_section,
2N/A KRB5_CONF_LDAP_SERVERS, NULL, &tempval)) != 0) {
2N/A krb5_set_error_message (context, st, gettext("Error reading 'ldap_servers' attribute"));
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (tempval == NULL) {
2N/A
2N/A (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
2N/A sizeof(krb5_ldap_server_info));
2N/A
2N/A (*server_info)[ele]->server_name = strdup("ldapi://");
2N/A if ((*server_info)[ele]->server_name == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A (*server_info)[ele]->server_status = NOTSET;
2N/A } else {
2N/A char *item=NULL;
2N/A
2N/A item = strtok_r(tempval,delims,&save_ptr);
2N/A while (item != NULL && ele<SERV_COUNT) {
2N/A (*server_info)[ele] = (krb5_ldap_server_info *)calloc(1,
2N/A sizeof(krb5_ldap_server_info));
2N/A if ((*server_info)[ele] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A (*server_info)[ele]->server_name = strdup(item);
2N/A if ((*server_info)[ele]->server_name == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A (*server_info)[ele]->server_status = NOTSET;
2N/A item = strtok_r(NULL,delims,&save_ptr);
2N/A ++ele;
2N/A }
2N/A profile_release_string(tempval);
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A return(st);
2N/A}
2N/A
2N/A/*
2N/A * This function frees the krb5_ldap_context structure members.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_free_server_context_params(krb5_ldap_context *ldap_context)
2N/A{
2N/A int i=0;
2N/A krb5_ldap_server_handle *ldap_server_handle=NULL, *next_ldap_server_handle=NULL;
2N/A
2N/A if (ldap_context == NULL)
2N/A return 0;
2N/A
2N/A /* Free all ldap servers list and the ldap handles associated with
2N/A the ldap server. */
2N/A if (ldap_context->server_info_list) {
2N/A while (ldap_context->server_info_list[i]) {
2N/A if (ldap_context->server_info_list[i]->server_name) {
2N/A free (ldap_context->server_info_list[i]->server_name);
2N/A }
2N/A#ifdef HAVE_EDIRECTORY
2N/A if (ldap_context->server_info_list[i]->root_certificate_file) {
2N/A free (ldap_context->server_info_list[i]->root_certificate_file);
2N/A }
2N/A#endif
2N/A if (ldap_context->server_info_list[i]->ldap_server_handles) {
2N/A ldap_server_handle = ldap_context->server_info_list[i]->ldap_server_handles;
2N/A while (ldap_server_handle) {
2N/A ldap_unbind_ext_s(ldap_server_handle->ldap_handle, NULL, NULL);
2N/A ldap_server_handle->ldap_handle = NULL;
2N/A next_ldap_server_handle = ldap_server_handle->next;
2N/A krb5_xfree(ldap_server_handle);
2N/A ldap_server_handle = next_ldap_server_handle;
2N/A }
2N/A }
2N/A krb5_xfree(ldap_context->server_info_list[i]);
2N/A i++;
2N/A }
2N/A krb5_xfree(ldap_context->server_info_list);
2N/A }
2N/A
2N/A if (ldap_context->conf_section != NULL) {
2N/A krb5_xfree(ldap_context->conf_section);
2N/A ldap_context->conf_section = NULL;
2N/A }
2N/A
2N/A if (ldap_context->bind_dn != NULL) {
2N/A krb5_xfree(ldap_context->bind_dn);
2N/A ldap_context->bind_dn = NULL;
2N/A }
2N/A
2N/A if (ldap_context->bind_pwd != NULL) {
2N/A memset(ldap_context->bind_pwd, 0, strlen(ldap_context->bind_pwd));
2N/A krb5_xfree(ldap_context->bind_pwd);
2N/A ldap_context->bind_pwd = NULL;
2N/A }
2N/A
2N/A if (ldap_context->service_password_file != NULL) {
2N/A krb5_xfree(ldap_context->service_password_file);
2N/A ldap_context->service_password_file = NULL;
2N/A }
2N/A
2N/A/* Solaris Kerberos */
2N/A/* #ifdef HAVE_EDIRECTORY */
2N/A if (ldap_context->root_certificate_file != NULL) {
2N/A krb5_xfree(ldap_context->root_certificate_file);
2N/A ldap_context->root_certificate_file = NULL;
2N/A }
2N/A/* #endif */
2N/A
2N/A if (ldap_context->service_cert_path != NULL) {
2N/A krb5_xfree(ldap_context->service_cert_path);
2N/A ldap_context->service_cert_path = NULL;
2N/A }
2N/A
2N/A if (ldap_context->service_cert_pass != NULL) {
2N/A krb5_xfree(ldap_context->service_cert_pass);
2N/A ldap_context->service_cert_pass = NULL;
2N/A }
2N/A
2N/A if (ldap_context->certificates) {
2N/A i=0;
2N/A while (ldap_context->certificates[i] != NULL) {
2N/A krb5_xfree(ldap_context->certificates[i]->certificate);
2N/A krb5_xfree(ldap_context->certificates[i]);
2N/A ++i;
2N/A }
2N/A krb5_xfree(ldap_context->certificates);
2N/A }
2N/A
2N/A return(0);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_free_server_params(krb5_ldap_context *ldap_context)
2N/A{
2N/A if (ldap_context == NULL)
2N/A return 0;
2N/A
2N/A krb5_ldap_free_server_context_params(ldap_context);
2N/A
2N/A k5_mutex_destroy(&ldap_context->hndl_lock);
2N/A krb5_xfree(ldap_context);
2N/A return(0);
2N/A}
2N/A
2N/A/*
2N/A * check to see if the principal belongs to the default realm.
2N/A * The default realm is present in the krb5_ldap_context structure.
2N/A * The principal has a realm portion. This realm portion is compared with the default realm
2N/A * to check whether the principal belong to the default realm.
2N/A * Return 0 if principal belongs to default realm else 1.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Ais_principal_in_realm(krb5_ldap_context *ldap_context,
2N/A krb5_const_principal searchfor)
2N/A{
2N/A size_t defrealmlen=0;
2N/A char *defrealm=NULL;
2N/A
2N/A#define FIND_MAX(a,b) ((a) > (b) ? (a) : (b))
2N/A
2N/A defrealmlen = strlen(ldap_context->lrparams->realm_name);
2N/A defrealm = ldap_context->lrparams->realm_name;
2N/A
2N/A /*
2N/A * Care should be taken for inter-realm principals as the default
2N/A * realm can exist in the realm part of the principal name or can
2N/A * also exist in the second portion of the name part. However, if
2N/A * the default realm exist in the second part of the principal
2N/A * portion, then the first portion of the principal name SHOULD be
2N/A * "krbtgt". All this check is done in the immediate block.
2N/A */
2N/A if (searchfor->length == 2) {
2N/A if (data_eq_string(searchfor->data[0], "krbtgt") &&
2N/A data_eq_string(searchfor->data[1], defrealm))
2N/A return 0;
2N/A }
2N/A
2N/A /* first check the length, if they are not equal, then they are not same */
2N/A if (strlen(defrealm) != searchfor->realm.length)
2N/A return 1;
2N/A
2N/A /* if the length is equal, check for the contents */
2N/A if (strncmp(defrealm, searchfor->realm.data,
2N/A searchfor->realm.length) != 0)
2N/A return 1;
2N/A /* if we are here, then the realm portions match, return 0 */
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Deduce the subtree information from the context. A realm can have
2N/A * multiple subtrees.
2N/A * 1. the Realm container
2N/A * 2. the actual subtrees associated with the Realm
2N/A *
2N/A * However, there are some conditions to be considered to deduce the
2N/A * actual subtree/s associated with the realm. The conditions are as
2N/A * follows:
2N/A * 1. If the subtree information of the Realm is [Root] or NULL (that
2N/A * is internal a [Root]) then the realm has only one subtree
2N/A * i.e [Root], i.e. whole of the tree.
2N/A * 2. If the subtree information of the Realm is missing/absent, then the
2N/A * realm has only one, i.e., the Realm container. NOTE: In all cases
2N/A * Realm container SHOULD be the one among the subtrees or the only
2N/A * one subtree.
2N/A * 3. The subtree information of the realm is overlapping the realm
2N/A * container of the realm, then the realm has only one subtree and
2N/A * it is the subtree information associated with the realm.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_get_subtree_info(krb5_ldap_context *ldap_context, char ***subtreearr,
2N/A unsigned int *ntree)
2N/A{
2N/A int st=0, i=0, subtreecount=0;
2N/A int ncount=0, search_scope=0;
2N/A char **subtree=NULL, *realm_cont_dn=NULL;
2N/A char **subtarr=NULL;
2N/A char *containerref=NULL;
2N/A char **newsubtree=NULL;
2N/A
2N/A containerref = ldap_context->lrparams->containerref;
2N/A subtree = ldap_context->lrparams->subtree;
2N/A realm_cont_dn = ldap_context->lrparams->realmdn;
2N/A subtreecount = ldap_context->lrparams->subtreecount;
2N/A search_scope = ldap_context->lrparams->search_scope;
2N/A
2N/A subtarr = (char **) malloc(sizeof(char *) * (subtreecount + 1 /*realm dn*/ + 1 /*containerref*/ + 1));
2N/A if (subtarr == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A memset(subtarr, 0, (sizeof(char *) * (subtreecount+1+1+1)));
2N/A
2N/A /* get the complete subtree list */
2N/A for (i=0; i<subtreecount && subtree[i]!=NULL; i++) {
2N/A subtarr[i] = strdup(subtree[i]);
2N/A if (subtarr[i] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A subtarr[i] = strdup(realm_cont_dn);
2N/A if (subtarr[i++] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (containerref != NULL) {
2N/A subtarr[i] = strdup(containerref);
2N/A if (subtarr[i++] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A ncount = i;
2N/A newsubtree = (char **) malloc(sizeof(char *) * (ncount + 1));
2N/A if (newsubtree == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A memset(newsubtree, 0, (sizeof(char *) * (ncount+1)));
2N/A if ((st = remove_overlapping_subtrees(subtarr, newsubtree, &ncount,
2N/A search_scope)) != 0) {
2N/A goto cleanup;
2N/A }
2N/A
2N/A *ntree = ncount;
2N/A *subtreearr = newsubtree;
2N/A
2N/Acleanup:
2N/A if (subtarr != NULL) {
2N/A for (i=0; subtarr[i] != NULL; i++)
2N/A free(subtarr[i]);
2N/A free(subtarr);
2N/A }
2N/A
2N/A if (st != 0) {
2N/A if (newsubtree != NULL) {
2N/A for (i=0; newsubtree[i] != NULL; i++)
2N/A free(newsubtree[i]);
2N/A free(newsubtree);
2N/A }
2N/A }
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * This function appends the content with a type into the tl_data
2N/A * structure. Based on the type the length of the content is either
2N/A * pre-defined or computed from the content. Returns 0 in case of
2N/A * success and 1 if the type associated with the content is undefined.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Astore_tl_data(krb5_tl_data *tl_data, int tl_type, void *value)
2N/A{
2N/A unsigned int currlen=0, tldatalen=0;
2N/A unsigned char *curr=NULL;
2N/A void *reallocptr=NULL;
2N/A
2N/A tl_data->tl_data_type = KDB_TL_USER_INFO;
2N/A switch (tl_type) {
2N/A case KDB_TL_PRINCCOUNT:
2N/A case KDB_TL_PRINCTYPE:
2N/A case KDB_TL_MASK:
2N/A {
2N/A int *iptr = (int *)value;
2N/A int ivalue = *iptr;
2N/A
2N/A currlen = tl_data->tl_data_length;
2N/A tl_data->tl_data_length += 1 + 2 + 2;
2N/A /* allocate required memory */
2N/A reallocptr = tl_data->tl_data_contents;
2N/A tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
2N/A tl_data->tl_data_length);
2N/A if (tl_data->tl_data_contents == NULL) {
2N/A if (reallocptr)
2N/A free (reallocptr);
2N/A return ENOMEM;
2N/A }
2N/A curr = (tl_data->tl_data_contents + currlen);
2N/A
2N/A /* store the tl_type value */
2N/A memset(curr, tl_type, 1);
2N/A curr += 1;
2N/A /* store the content length */
2N/A tldatalen = 2;
2N/A STORE16_INT(curr, tldatalen);
2N/A curr += 2;
2N/A /* store the content */
2N/A STORE16_INT(curr, ivalue);
2N/A curr += 2;
2N/A break;
2N/A }
2N/A
2N/A case KDB_TL_USERDN:
2N/A case KDB_TL_LINKDN:
2N/A {
2N/A char *cptr = (char *)value;
2N/A
2N/A currlen = tl_data->tl_data_length;
2N/A tl_data->tl_data_length += 1 + 2 + strlen(cptr);
2N/A /* allocate required memory */
2N/A reallocptr = tl_data->tl_data_contents;
2N/A tl_data->tl_data_contents = realloc(tl_data->tl_data_contents,
2N/A tl_data->tl_data_length);
2N/A if (tl_data->tl_data_contents == NULL) {
2N/A if (reallocptr)
2N/A free (reallocptr);
2N/A return ENOMEM;
2N/A }
2N/A curr = (tl_data->tl_data_contents + currlen);
2N/A
2N/A /* store the tl_type value */
2N/A memset(curr, tl_type, 1);
2N/A curr += 1;
2N/A /* store the content length */
2N/A tldatalen = strlen(cptr);
2N/A STORE16_INT(curr, tldatalen);
2N/A curr += 2;
2N/A /* store the content */
2N/A memcpy(curr, cptr, tldatalen);
2N/A curr += tldatalen;
2N/A break;
2N/A }
2N/A
2N/A default:
2N/A return 1;
2N/A
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * This function scans the tl_data structure to get the value of a
2N/A * type defined by the tl_type (second parameter). The tl_data
2N/A * structure has all the data in the tl_data_contents member. The
2N/A * format of the tl_data_contents is as follows. The first byte
2N/A * defines the type of the content that follows. The next 2 bytes
2N/A * define the size n (in terms of bytes) of the content that
2N/A * follows. The next n bytes define the content itself.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Adecode_tl_data(krb5_tl_data *tl_data, int tl_type, void **data)
2N/A{
2N/A int subtype=0, i=0, limit=10;
2N/A unsigned int sublen=0;
2N/A unsigned char *curr=NULL;
2N/A int *intptr=NULL;
2N/A long *longptr=NULL;
2N/A char *DN=NULL, **DNarr=NULL;
2N/A krb5_error_code st=-1;
2N/A
2N/A *data = NULL;
2N/A
2N/A curr = tl_data->tl_data_contents;
2N/A while (curr < (tl_data->tl_data_contents + tl_data->tl_data_length)) {
2N/A
2N/A /* get the type of the content */
2N/A subtype = (int) curr[0];
2N/A /* forward by 1 byte*/
2N/A curr += 1;
2N/A
2N/A if (subtype == tl_type) {
2N/A switch (subtype) {
2N/A
2N/A case KDB_TL_PRINCCOUNT:
2N/A case KDB_TL_PRINCTYPE:
2N/A case KDB_TL_MASK:
2N/A /* get the length of the content */
2N/A UNSTORE16_INT(curr, sublen);
2N/A /* forward by 2 bytes */
2N/A curr += 2;
2N/A /* get the actual content */
2N/A if (sublen == 2) {
2N/A /* intptr = malloc(sublen); */
2N/A intptr = malloc(sizeof(krb5_int32));
2N/A if (intptr == NULL)
2N/A return ENOMEM;
2N/A memset(intptr, 0, sublen);
2N/A UNSTORE16_INT(curr, (*intptr));
2N/A *data = intptr;
2N/A } else {
2N/A longptr = malloc(sublen);
2N/A if (longptr == NULL)
2N/A return ENOMEM;
2N/A memset(longptr, 0, sublen);
2N/A UNSTORE32_INT(curr, (*longptr));
2N/A *data = longptr;
2N/A }
2N/A curr += sublen;
2N/A st = 0;
2N/A return st;
2N/A break;
2N/A
2N/A case KDB_TL_CONTAINERDN:
2N/A case KDB_TL_USERDN:
2N/A /* get the length of the content */
2N/A UNSTORE16_INT(curr, sublen);
2N/A /* forward by 2 bytes */
2N/A curr += 2;
2N/A DN = malloc (sublen + 1);
2N/A if (DN == NULL)
2N/A return ENOMEM;
2N/A memcpy(DN, curr, sublen);
2N/A DN[sublen] = 0;
2N/A *data = DN;
2N/A curr += sublen;
2N/A st = 0;
2N/A return st;
2N/A break;
2N/A
2N/A case KDB_TL_LINKDN:
2N/A if (DNarr == NULL) {
2N/A DNarr = calloc(limit, sizeof(char *));
2N/A if (DNarr == NULL)
2N/A return ENOMEM;
2N/A }
2N/A if (i == limit-1) {
2N/A /* Solaris Kerberos */
2N/A char **tmp;
2N/A limit *= 2;
2N/A tmp = realloc(DNarr, sizeof(char *) * (limit));
2N/A if (tmp == NULL) {
2N/A free(DNarr);
2N/A return ENOMEM;
2N/A }
2N/A DNarr = tmp;
2N/A }
2N/A
2N/A /* get the length of the content */
2N/A UNSTORE16_INT(curr, sublen);
2N/A /* forward by 2 bytes */
2N/A curr += 2;
2N/A DNarr[i] = malloc (sublen + 1);
2N/A if (DNarr[i] == NULL) {
2N/A /* Solaris Kerberos - What about DNarr[i-1 .. 0] ? */
2N/A free(DNarr);
2N/A return ENOMEM;
2N/A }
2N/A memcpy(DNarr[i], curr, sublen);
2N/A DNarr[i][sublen] = 0;
2N/A ++i;
2N/A curr += sublen;
2N/A *data = DNarr;
2N/A st=0;
2N/A break;
2N/A }
2N/A } else {
2N/A /* move to the current content block */
2N/A UNSTORE16_INT(curr, sublen);
2N/A curr += 2 + sublen;
2N/A }
2N/A }
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * wrapper routines for decode_tl_data
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_get_int_from_tl_data(krb5_context context, krb5_db_entry *entries,
2N/A int type, int *intval)
2N/A{
2N/A krb5_error_code st=0;
2N/A krb5_tl_data tl_data;
2N/A void *voidptr=NULL;
2N/A int *intptr=NULL;
2N/A
2N/A tl_data.tl_data_type = KDB_TL_USER_INFO;
2N/A if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
2N/A goto cleanup;
2N/A
2N/A if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
2N/A intptr = (int *) voidptr;
2N/A *intval = *intptr;
2N/A free(intptr);
2N/A }
2N/A
2N/Acleanup:
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * Get the mask representing the attributes set on the directory
2N/A * object (user, policy ...).
2N/A */
2N/Akrb5_error_code
2N/Akrb5_get_attributes_mask(krb5_context context, krb5_db_entry *entries,
2N/A int *mask)
2N/A{
2N/A return krb5_get_int_from_tl_data(context, entries, KDB_TL_MASK, mask);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_get_princ_type(krb5_context context, krb5_db_entry *entries, int *ptype)
2N/A{
2N/A return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCTYPE, ptype);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_get_princ_count(krb5_context context, krb5_db_entry *entries, int *pcount)
2N/A{
2N/A return krb5_get_int_from_tl_data(context, entries, KDB_TL_PRINCCOUNT, pcount);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_get_linkdn(krb5_context context, krb5_db_entry *entries, char ***link_dn)
2N/A{
2N/A krb5_error_code st=0;
2N/A krb5_tl_data tl_data;
2N/A void *voidptr=NULL;
2N/A
2N/A *link_dn = NULL;
2N/A tl_data.tl_data_type = KDB_TL_USER_INFO;
2N/A if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
2N/A goto cleanup;
2N/A
2N/A if (decode_tl_data(&tl_data, KDB_TL_LINKDN, &voidptr) == 0) {
2N/A *link_dn = (char **) voidptr;
2N/A }
2N/A
2N/Acleanup:
2N/A return st;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_get_str_from_tl_data(krb5_context context, krb5_db_entry *entries,
2N/A int type, char **strval)
2N/A{
2N/A krb5_error_code st=0;
2N/A krb5_tl_data tl_data;
2N/A void *voidptr=NULL;
2N/A
2N/A if (type != KDB_TL_USERDN && type != KDB_TL_CONTAINERDN) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A tl_data.tl_data_type = KDB_TL_USER_INFO;
2N/A if (((st=krb5_dbe_lookup_tl_data(context, entries, &tl_data)) != 0) || tl_data.tl_data_length == 0)
2N/A goto cleanup;
2N/A
2N/A if (decode_tl_data(&tl_data, type, &voidptr) == 0) {
2N/A *strval = (char *) voidptr;
2N/A }
2N/A
2N/Acleanup:
2N/A return st;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_get_userdn(krb5_context context, krb5_db_entry *entries, char **userdn)
2N/A{
2N/A *userdn = NULL;
2N/A return krb5_get_str_from_tl_data(context, entries, KDB_TL_USERDN, userdn);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_get_containerdn(krb5_context context, krb5_db_entry *entries,
2N/A char **containerdn)
2N/A{
2N/A *containerdn = NULL;
2N/A return krb5_get_str_from_tl_data(context, entries, KDB_TL_CONTAINERDN, containerdn);
2N/A}
2N/A
2N/A/*
2N/A * This function reads the attribute values (if the attribute is
2N/A * non-null) from the dn. The read attribute values is compared
2N/A * aganist the attrvalues passed to the function and a bit mask is set
2N/A * for all the matching attributes (attributes existing in both list).
2N/A * The bit to be set is selected such that the index of the attribute
2N/A * in the attrvalues parameter is the position of the bit. For ex:
2N/A * the first element in the attrvalues is present in both list shall
2N/A * set the LSB of the bit mask.
2N/A *
2N/A * In case if either the attribute or the attrvalues parameter to the
2N/A * function is NULL, then the existence of the object is considered
2N/A * and appropriate status is returned back.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Acheckattributevalue(LDAP *ld, char *dn, char *attribute, char **attrvalues,
2N/A int *mask)
2N/A{
2N/A int st=0, one=1;
2N/A char **values=NULL, *attributes[2] = {NULL};
2N/A LDAPMessage *result=NULL, *entry=NULL;
2N/A
2N/A if (strlen(dn) == 0) {
2N/A st = set_ldap_error(0, LDAP_NO_SUCH_OBJECT, OP_SEARCH);
2N/A return st;
2N/A }
2N/A
2N/A attributes[0] = attribute;
2N/A
2N/A /* read the attribute values from the dn */
2N/A if ((st = ldap_search_ext_s(ld,
2N/A dn,
2N/A LDAP_SCOPE_BASE,
2N/A 0,
2N/A attributes,
2N/A 0,
2N/A NULL,
2N/A NULL,
2N/A &timelimit,
2N/A LDAP_NO_LIMIT,
2N/A &result)) != LDAP_SUCCESS) {
2N/A st = set_ldap_error(0, st, OP_SEARCH);
2N/A return st;
2N/A }
2N/A
2N/A /*
2N/A * If the attribute/attrvalues is NULL, then check for the
2N/A * existence of the object alone.
2N/A */
2N/A if (attribute == NULL || attrvalues == NULL)
2N/A goto cleanup;
2N/A
2N/A /* reset the bit mask */
2N/A *mask = 0;
2N/A
2N/A if ((entry=ldap_first_entry(ld, result)) != NULL) {
2N/A /* read the attribute values */
2N/A if ((values=ldap_get_values(ld, entry, attribute)) != NULL) {
2N/A int i,j;
2N/A
2N/A /*
2N/A * Compare the read attribute values with the attrvalues
2N/A * array and set the appropriate bit mask.
2N/A */
2N/A for (j=0; attrvalues[j]; ++j) {
2N/A for (i=0; values[i]; ++i) {
2N/A if (strcasecmp(values[i], attrvalues[j]) == 0) {
2N/A *mask |= (one<<j);
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A ldap_msgfree(result);
2N/A return st;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This function updates a single attribute with a single value of a
2N/A * specified dn. This function is mainly used to update
2N/A * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
2N/A * ADMIN, PASSWD servers are associated with some realms or vice
2N/A * versa.
2N/A */
2N/A
2N/Akrb5_error_code
2N/AupdateAttribute(LDAP *ld, char *dn, char *attribute, char *value)
2N/A{
2N/A int st=0;
2N/A LDAPMod modAttr, *mods[2]={NULL};
2N/A char *values[2]={NULL};
2N/A
2N/A values[0] = value;
2N/A
2N/A /* data to update the {attr,attrval} combination */
2N/A memset(&modAttr, 0, sizeof(modAttr));
2N/A modAttr.mod_type = attribute;
2N/A modAttr.mod_op = LDAP_MOD_ADD;
2N/A modAttr.mod_values = values;
2N/A mods[0] = &modAttr;
2N/A
2N/A /* ldap modify operation */
2N/A st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
2N/A
2N/A /* if the {attr,attrval} combination is already present return a success
2N/A * LDAP_ALREADY_EXISTS is for single-valued attribute
2N/A * LDAP_TYPE_OR_VALUE_EXISTS is for multi-valued attribute
2N/A */
2N/A if (st == LDAP_ALREADY_EXISTS || st == LDAP_TYPE_OR_VALUE_EXISTS)
2N/A st = 0;
2N/A
2N/A if (st != 0) {
2N/A st = set_ldap_error (0, st, OP_MOD);
2N/A }
2N/A
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * This function deletes a single attribute with a single value of a
2N/A * specified dn. This function is mainly used to delete
2N/A * krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
2N/A * ADMIN, PASSWD servers are disassociated with some realms or vice
2N/A * versa.
2N/A */
2N/A
2N/Akrb5_error_code
2N/AdeleteAttribute(LDAP *ld, char *dn, char *attribute, char *value)
2N/A{
2N/A krb5_error_code st=0;
2N/A LDAPMod modAttr, *mods[2]={NULL};
2N/A char *values[2]={NULL};
2N/A
2N/A values[0] = value;
2N/A
2N/A /* data to delete the {attr,attrval} combination */
2N/A memset(&modAttr, 0, sizeof(modAttr));
2N/A modAttr.mod_type = attribute;
2N/A modAttr.mod_op = LDAP_MOD_DELETE;
2N/A modAttr.mod_values = values;
2N/A mods[0] = &modAttr;
2N/A
2N/A /* ldap modify operation */
2N/A st = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
2N/A
2N/A /* if either the attribute or the attribute value is missing return a success */
2N/A if (st == LDAP_NO_SUCH_ATTRIBUTE || st == LDAP_UNDEFINED_TYPE)
2N/A st = 0;
2N/A
2N/A if (st != 0) {
2N/A st = set_ldap_error (0, st, OP_MOD);
2N/A }
2N/A
2N/A return st;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * This function takes in 2 string arrays, compares them to remove the
2N/A * matching entries. The first array is the original list and the
2N/A * second array is the modified list. Removing the matching entries
2N/A * will result in a reduced array, where the left over first array
2N/A * elements are the deleted entries and the left over second array
2N/A * elements are the added entries. These additions and deletions has
2N/A * resulted in the modified second array.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Adisjoint_members(char **src, char **dest)
2N/A{
2N/A int i=0, j=0, slen=0, dlen=0;
2N/A
2N/A /* validate the input parameters */
2N/A if (src == NULL || dest == NULL)
2N/A return 0;
2N/A
2N/A /* compute the first array length */
2N/A for (i=0;src[i]; ++i)
2N/A ;
2N/A
2N/A /* return if the length is 0 */
2N/A if (i==0)
2N/A return 0;
2N/A
2N/A /* index of the last element and also the length of the array */
2N/A slen = i-1;
2N/A
2N/A /* compute the second array length */
2N/A for (i=0;dest[i]; ++i)
2N/A ;
2N/A
2N/A /* return if the length is 0 */
2N/A if (i==0)
2N/A return 0;
2N/A
2N/A /* index of the last element and also the length of the array */
2N/A dlen = i-1;
2N/A
2N/A /* check for the similar elements and delete them from both the arrays */
2N/A for (i=0; src[i]; ++i) {
2N/A
2N/A for (j=0; dest[j]; ++j) {
2N/A
2N/A /* if the element are same */
2N/A if (strcasecmp(src[i], dest[j]) == 0) {
2N/A /*
2N/A * If the matched element is in the middle, then copy
2N/A * the last element to the matched index.
2N/A */
2N/A if (i != slen) {
2N/A free (src[i]);
2N/A src[i] = src[slen];
2N/A src[slen] = NULL;
2N/A } else {
2N/A /*
2N/A * If the matched element is the last, free it and
2N/A * set it to NULL.
2N/A */
2N/A free (src[i]);
2N/A src[i] = NULL;
2N/A }
2N/A /* reduce the array length by 1 */
2N/A slen -= 1;
2N/A
2N/A /* repeat the same processing for the second array too */
2N/A if (j != dlen) {
2N/A free(dest[j]);
2N/A dest[j] = dest[dlen];
2N/A dest[dlen] = NULL;
2N/A } else {
2N/A free(dest[j]);
2N/A dest[j] = NULL;
2N/A }
2N/A dlen -=1;
2N/A
2N/A /*
2N/A * The source array is reduced by 1, so reduce the
2N/A * index variable used for source array by 1. No need
2N/A * to adjust the second array index variable as it is
2N/A * reset while entering the inner loop.
2N/A */
2N/A i -= 1;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * This function replicates the contents of the src array for later
2N/A * use. Mostly the contents of the src array is obtained from a
2N/A * ldap_search operation and the contents are required for later use.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Acopy_arrays(char **src, char ***dest, int count)
2N/A{
2N/A krb5_error_code st=0;
2N/A int i=0;
2N/A
2N/A /* validate the input parameters */
2N/A if (src == NULL || dest == NULL)
2N/A return 0;
2N/A
2N/A /* allocate memory for the dest array */
2N/A *dest = (char **) calloc((unsigned) count+1, sizeof(char *));
2N/A if (*dest == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* copy the members from src to dest array. */
2N/A for (i=0; i < count && src[i] != NULL; ++i) {
2N/A (*dest)[i] = strdup(src[i]);
2N/A if ((*dest)[i] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A /* in case of error free up everything and return */
2N/A if (st != 0) {
2N/A if (*dest != NULL) {
2N/A for (i=0; (*dest)[i]; ++i) {
2N/A free ((*dest)[i]);
2N/A (*dest)[i] = NULL;
2N/A }
2N/A free (*dest);
2N/A *dest = NULL;
2N/A }
2N/A }
2N/A return st;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Agetepochtime(char *strtime, krb5_timestamp *epochtime)
2N/A{
2N/A struct tm tme;
2N/A
2N/A memset(&tme, 0, sizeof(tme));
2N/A if (strptime(strtime,"%Y%m%d%H%M%SZ", &tme) == NULL) {
2N/A *epochtime = 0;
2N/A return EINVAL;
2N/A }
2N/A *epochtime = krb5int_gmt_mktime(&tme);
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * krb5_ldap_get_value() - get the integer value of the attribute
2N/A * Returns, 0 if the attribute is present, 1 if the attribute is missing.
2N/A * The retval is 0 if the attribute is missing.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_get_value(LDAP *ld, LDAPMessage *ent, char *attribute, int *retval)
2N/A{
2N/A char **values=NULL;
2N/A
2N/A *retval = 0;
2N/A values=ldap_get_values(ld, ent, attribute);
2N/A if (values != NULL) {
2N/A if (values[0] != NULL)
2N/A *retval = atoi(values[0]);
2N/A ldap_value_free(values);
2N/A return 0;
2N/A }
2N/A return 1;
2N/A}
2N/A
2N/A/*
2N/A * krb5_ldap_get_string() - Returns the first string of the
2N/A * attribute. Intended to
2N/A *
2N/A *
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_get_string(LDAP *ld, LDAPMessage *ent, char *attribute,
2N/A char **retstr, krb5_boolean *attr_present)
2N/A{
2N/A char **values=NULL;
2N/A krb5_error_code st=0;
2N/A
2N/A *retstr = NULL;
2N/A if (attr_present != NULL)
2N/A *attr_present = FALSE;
2N/A
2N/A values=ldap_get_values(ld, ent, attribute);
2N/A if (values != NULL) {
2N/A if (values[0] != NULL) {
2N/A if (attr_present!= NULL)
2N/A *attr_present = TRUE;
2N/A *retstr = strdup(values[0]);
2N/A if (*retstr == NULL)
2N/A st = ENOMEM;
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * krb5_ldap_get_strings() - Returns all the values
2N/A * of the attribute.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_get_strings(LDAP *ld, LDAPMessage *ent, char *attribute,
2N/A char ***retarr, krb5_boolean *attr_present)
2N/A{
2N/A char **values=NULL;
2N/A krb5_error_code st=0;
2N/A unsigned int i=0, count=0;
2N/A
2N/A *retarr = NULL;
2N/A if (attr_present != NULL)
2N/A *attr_present = FALSE;
2N/A
2N/A values=ldap_get_values(ld, ent, attribute);
2N/A if (values != NULL) {
2N/A if (attr_present != NULL)
2N/A *attr_present = TRUE;
2N/A
2N/A count = ldap_count_values(values);
2N/A *retarr = (char **) calloc(count+1, sizeof(char *));
2N/A if (*retarr == NULL) {
2N/A st = ENOMEM;
2N/A return st;
2N/A }
2N/A for (i=0; i< count; ++i) {
2N/A (*retarr)[i] = strdup(values[i]);
2N/A if ((*retarr)[i] == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A
2N/Acleanup:
2N/A if (st != 0) {
2N/A if (*retarr != NULL) {
2N/A for (i=0; i< count; ++i)
2N/A if ((*retarr)[i] != NULL)
2N/A free ((*retarr)[i]);
2N/A free (*retarr);
2N/A }
2N/A }
2N/A return st;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_get_time(LDAP *ld, LDAPMessage *ent, char *attribute,
2N/A krb5_timestamp *rettime, krb5_boolean *attr_present)
2N/A{
2N/A char **values=NULL;
2N/A krb5_error_code st=0;
2N/A
2N/A *rettime = 0;
2N/A *attr_present = FALSE;
2N/A
2N/A values=ldap_get_values(ld, ent, attribute);
2N/A if (values != NULL) {
2N/A if (values[0] != NULL) {
2N/A *attr_present = TRUE;
2N/A st = getepochtime(values[0], rettime);
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * Function to allocate, set the values of LDAPMod structure. The
2N/A * LDAPMod structure is then added to the array at the ind
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_add_member(LDAPMod ***mods, int *count)
2N/A{
2N/A int i=0;
2N/A LDAPMod **lmods=NULL;
2N/A
2N/A if ((*mods) != NULL) {
2N/A for (;(*mods)[i] != NULL; ++i)
2N/A ;
2N/A }
2N/A lmods = (LDAPMod **) realloc((*mods), (2+i) * sizeof(LDAPMod *));
2N/A if (lmods == NULL)
2N/A return ENOMEM;
2N/A
2N/A *mods = lmods;
2N/A (*mods)[i+1] = NULL;
2N/A (*mods)[i] = (LDAPMod *) calloc(1, sizeof (LDAPMod));
2N/A if ((*mods)[i] == NULL)
2N/A return ENOMEM;
2N/A *count = i;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_add_str_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
2N/A char **values)
2N/A{
2N/A int i=0, j=0;
2N/A krb5_error_code st=0;
2N/A
2N/A if ((st=krb5_add_member(mods, &i)) != 0)
2N/A return st;
2N/A
2N/A (*mods)[i]->mod_type = strdup(attribute);
2N/A if ((*mods)[i]->mod_type == NULL)
2N/A return ENOMEM;
2N/A (*mods)[i]->mod_op = op;
2N/A
2N/A (*mods)[i]->mod_values = NULL;
2N/A
2N/A if (values != NULL) {
2N/A for (j=0; values[j] != NULL; ++j)
2N/A ;
2N/A (*mods)[i]->mod_values = malloc (sizeof(char *) * (j+1));
2N/A if ((*mods)[i]->mod_values == NULL)
2N/A return ENOMEM;
2N/A
2N/A for (j=0; values[j] != NULL; ++j) {
2N/A (*mods)[i]->mod_values[j] = strdup(values[j]);
2N/A if ((*mods)[i]->mod_values[j] == NULL)
2N/A return ENOMEM;
2N/A }
2N/A (*mods)[i]->mod_values[j] = NULL;
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_add_ber_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
2N/A struct berval **ber_values)
2N/A{
2N/A int i=0, j=0;
2N/A krb5_error_code st=0;
2N/A
2N/A if ((st=krb5_add_member(mods, &i)) != 0)
2N/A return st;
2N/A
2N/A (*mods)[i]->mod_type = strdup(attribute);
2N/A if ((*mods)[i]->mod_type == NULL)
2N/A return ENOMEM;
2N/A (*mods)[i]->mod_op = op;
2N/A
2N/A for (j=0; ber_values[j] != NULL; ++j)
2N/A ;
2N/A (*mods)[i]->mod_bvalues = malloc (sizeof(struct berval *) * (j+1));
2N/A if ((*mods)[i]->mod_bvalues == NULL)
2N/A return ENOMEM;
2N/A
2N/A for (j=0; ber_values[j] != NULL; ++j) {
2N/A (*mods)[i]->mod_bvalues[j] = calloc(1, sizeof(struct berval));
2N/A if ((*mods)[i]->mod_bvalues[j] == NULL)
2N/A return ENOMEM;
2N/A
2N/A (*mods)[i]->mod_bvalues[j]->bv_len = ber_values[j]->bv_len;
2N/A (*mods)[i]->mod_bvalues[j]->bv_val = malloc((*mods)[i]->mod_bvalues[j]->bv_len);
2N/A if ((*mods)[i]->mod_bvalues[j]->bv_val == NULL)
2N/A return ENOMEM;
2N/A
2N/A memcpy((*mods)[i]->mod_bvalues[j]->bv_val, ber_values[j]->bv_val,
2N/A ber_values[j]->bv_len);
2N/A }
2N/A (*mods)[i]->mod_bvalues[j] = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Astatic inline char *
2N/Aformat_d (int val)
2N/A{
2N/A char tmpbuf[2+3*sizeof(val)];
2N/A snprintf(tmpbuf, sizeof(tmpbuf), "%d", val);
2N/A return strdup(tmpbuf);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_add_int_arr_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op,
2N/A int *value)
2N/A{
2N/A int i=0, j=0;
2N/A krb5_error_code st=0;
2N/A
2N/A if ((st=krb5_add_member(mods, &i)) != 0)
2N/A return st;
2N/A
2N/A (*mods)[i]->mod_type = strdup(attribute);
2N/A if ((*mods)[i]->mod_type == NULL)
2N/A return ENOMEM;
2N/A (*mods)[i]->mod_op = op;
2N/A
2N/A for (j=0; value[j] != -1; ++j)
2N/A ;
2N/A
2N/A (*mods)[i]->mod_values = malloc(sizeof(char *) * (j+1));
2N/A
2N/A for (j=0; value[j] != -1; ++j) {
2N/A if (((*mods)[i]->mod_values[j] = format_d(value[j])) == NULL)
2N/A return ENOMEM;
2N/A }
2N/A (*mods)[i]->mod_values[j] = NULL;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_add_int_mem_ldap_mod(LDAPMod ***mods, char *attribute, int op, int value)
2N/A{
2N/A int i=0;
2N/A krb5_error_code st=0;
2N/A
2N/A if ((st=krb5_add_member(mods, &i)) != 0)
2N/A return st;
2N/A
2N/A (*mods)[i]->mod_type = strdup(attribute);
2N/A if ((*mods)[i]->mod_type == NULL)
2N/A return ENOMEM;
2N/A
2N/A (*mods)[i]->mod_op = op;
2N/A (*mods)[i]->mod_values = calloc (2, sizeof(char *));
2N/A if (((*mods)[i]->mod_values[0] = format_d(value)) == NULL)
2N/A return ENOMEM;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_set_option(krb5_context kcontext, int option, void *value)
2N/A{
2N/A krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
2N/A krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_lock(krb5_context kcontext, int mode)
2N/A{
2N/A krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
2N/A krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_unlock(krb5_context kcontext)
2N/A{
2N/A krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
2N/A krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_supported_realms(krb5_context kcontext, char **realms)
2N/A{
2N/A krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
2N/A krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_free_supported_realms(krb5_context kcontext, char **realms)
2N/A{
2N/A krb5_error_code status = KRB5_PLUGIN_OP_NOTSUPP;
2N/A krb5_set_error_message(kcontext, status, "LDAP %s", error_message(status));
2N/A return status;
2N/A}
2N/A
2N/Aconst char *
2N/Akrb5_ldap_errcode_2_string(krb5_context kcontext, long err_code)
2N/A{
2N/A return krb5_get_error_message(kcontext, err_code);
2N/A}
2N/A
2N/Avoid
2N/Akrb5_ldap_release_errcode_string(krb5_context kcontext, const char *msg)
2N/A{
2N/A krb5_free_error_message(kcontext, msg);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Get the number of times an object has been referred to in a realm. this is
2N/A * needed to find out if deleting the attribute will cause dangling links.
2N/A *
2N/A * An LDAP handle may be optionally specified to prevent race condition - there
2N/A * are a limited number of LDAP handles.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_get_reference_count(krb5_context context, char *dn, char *refattr,
2N/A int *count, LDAP *ld)
2N/A{
2N/A int st = 0, tempst = 0, gothandle = 0;
2N/A unsigned int i, ntrees;
2N/A char *refcntattr[2];
2N/A char *filter = NULL;
2N/A char **subtree = NULL, *ptr = NULL;
2N/A kdb5_dal_handle *dal_handle = NULL;
2N/A krb5_ldap_context *ldap_context = NULL;
2N/A krb5_ldap_server_handle *ldap_server_handle = NULL;
2N/A LDAPMessage *result = NULL;
2N/A
2N/A
2N/A if (dn == NULL || refattr == NULL) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A SETUP_CONTEXT();
2N/A if (ld == NULL) {
2N/A GET_HANDLE();
2N/A gothandle = 1;
2N/A }
2N/A
2N/A refcntattr [0] = refattr;
2N/A refcntattr [1] = NULL;
2N/A
2N/A ptr = ldap_filter_correct (dn);
2N/A if (ptr == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (asprintf (&filter, "%s=%s", refattr, ptr) < 0) {
2N/A filter = NULL;
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0)
2N/A goto cleanup;
2N/A
2N/A for (i = 0, *count = 0; i < ntrees; i++) {
2N/A int n;
2N/A
2N/A LDAP_SEARCH(subtree[i],
2N/A LDAP_SCOPE_SUBTREE,
2N/A filter,
2N/A refcntattr);
2N/A n = ldap_count_entries (ld, result);
2N/A if (n == -1) {
2N/A int ret, errcode = 0;
2N/A ret = ldap_parse_result (ld, result, &errcode, NULL, NULL, NULL, NULL, 0);
2N/A if (ret != LDAP_SUCCESS)
2N/A errcode = ret;
2N/A st = translate_ldap_error (errcode, OP_SEARCH);
2N/A goto cleanup;
2N/A }
2N/A
2N/A ldap_msgfree(result);
2N/A result = NULL;
2N/A
2N/A *count += n;
2N/A }
2N/A
2N/Acleanup:
2N/A if (filter != NULL)
2N/A free (filter);
2N/A
2N/A if (result != NULL)
2N/A ldap_msgfree (result);
2N/A
2N/A if (subtree != NULL) {
2N/A for (i = 0; i < ntrees; i++)
2N/A free (subtree[i]);
2N/A free (subtree);
2N/A }
2N/A
2N/A if (ptr != NULL)
2N/A free (ptr);
2N/A
2N/A if (gothandle == 1)
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * For now, policy objects are expected to be directly under the realm
2N/A * container.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_policydn_to_name(krb5_context context, char *policy_dn, char **name)
2N/A{
2N/A int len1, len2;
2N/A krb5_error_code st = 0;
2N/A kdb5_dal_handle *dal_handle=NULL;
2N/A krb5_ldap_context *ldap_context=NULL;
2N/A
2N/A SETUP_CONTEXT();
2N/A
2N/A if (ldap_context->lrparams->realmdn == NULL) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A len1 = strlen (ldap_context->lrparams->realmdn);
2N/A len2 = strlen (policy_dn);
2N/A if (len1 == 0 || len2 == 0 || len1 > len2) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if (strcmp (ldap_context->lrparams->realmdn, policy_dn + (len2 - len1)) != 0) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A#if defined HAVE_LDAP_STR2DN
2N/A {
2N/A char *rdn;
2N/A LDAPDN dn;
2N/A rdn = strndup(policy_dn, len2 - len1 - 1); /* 1 character for ',' */
2N/A
2N/A if (ldap_str2dn (rdn, &dn, LDAP_DN_FORMAT_LDAPV3 | LDAP_DN_PEDANTIC) != 0) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A if (dn[0] == NULL || dn[1] != NULL)
2N/A st = EINVAL;
2N/A else if (strcasecmp (dn[0][0]->la_attr.bv_val, "cn") != 0)
2N/A st = EINVAL;
2N/A else {
2N/A *name = strndup(dn[0][0]->la_value.bv_val, dn[0][0]->la_value.bv_len);
2N/A if (*name == NULL)
2N/A st = EINVAL;
2N/A }
2N/A
2N/A ldap_memfree (dn);
2N/A }
2N/A#elif defined HAVE_LDAP_EXPLODE_DN
2N/A {
2N/A char **parsed_dn;
2N/A
2N/A /* 1 = return DN components without type prefix */
2N/A parsed_dn = ldap_explode_dn(policy_dn, 1);
2N/A if (parsed_dn == NULL) {
2N/A st = EINVAL;
2N/A } else {
2N/A *name = strdup(parsed_dn[0]);
2N/A if (*name == NULL)
2N/A st = EINVAL;
2N/A
2N/A ldap_value_free(parsed_dn);
2N/A }
2N/A }
2N/A#else
2N/A st = EINVAL;
2N/A#endif
2N/A
2N/Acleanup:
2N/A return st;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_name_to_policydn(krb5_context context, char *name, char **policy_dn)
2N/A{
2N/A int len;
2N/A char *ptr = NULL;
2N/A krb5_error_code st = 0;
2N/A kdb5_dal_handle *dal_handle=NULL;
2N/A krb5_ldap_context *ldap_context=NULL;
2N/A
2N/A *policy_dn = NULL;
2N/A
2N/A /* validate the input parameters */
2N/A if (name == NULL) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* Used for removing policy reference from an object */
2N/A if (name[0] == '\0') {
2N/A if ((*policy_dn = strdup ("")) == NULL)
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A SETUP_CONTEXT();
2N/A
2N/A if (ldap_context->lrparams->realmdn == NULL) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A len = strlen (ldap_context->lrparams->realmdn);
2N/A
2N/A ptr = ldap_filter_correct (name);
2N/A if (ptr == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A len += strlen (ptr);
2N/A
2N/A len += sizeof ("cn=") + 3;
2N/A
2N/A *policy_dn = (char *) malloc (len);
2N/A if (*policy_dn == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A
2N/A sprintf (*policy_dn, "cn=%s,%s", ptr, ldap_context->lrparams->realmdn);
2N/A
2N/Acleanup:
2N/A if (ptr != NULL)
2N/A free (ptr);
2N/A return st;
2N/A}
2N/A
2N/A/* remove overlapping and repeated subtree entries from the list of subtrees */
2N/Astatic krb5_error_code
2N/Aremove_overlapping_subtrees(char **listin, char **listop, int *subtcount,
2N/A int sscope)
2N/A{
2N/A int slen=0, k=0, j=0, lendiff=0;
2N/A int count = *subtcount;
2N/A char **subtree = listop;
2N/A
2N/A slen = count-1;
2N/A for (k=0; k<=slen && listin[k]!=NULL ; k++) {
2N/A for (j=k+1; j<=slen && listin[j]!=NULL ;j++) {
2N/A lendiff = strlen(listin[k]) - strlen(listin[j]);
2N/A if (sscope == 2) {
2N/A if ((lendiff > 0) && (strcasecmp((listin[k])+lendiff, listin[j])==0)) {
2N/A if (k != slen) {
2N/A free(listin[k]);
2N/A listin[k] = listin[slen];
2N/A listin[slen] = NULL;
2N/A } else {
2N/A free(listin[k]);
2N/A listin[k] = NULL;
2N/A }
2N/A slen-=1;
2N/A k-=1;
2N/A break;
2N/A } else if ((lendiff < 0) && (strcasecmp((listin[j])+abs(lendiff), listin[k])==0)) {
2N/A if (j != slen) {
2N/A free(listin[j]);
2N/A listin[j] = listin[slen];
2N/A listin[slen]=NULL;
2N/A } else {
2N/A free(listin[j]);
2N/A listin[j] = NULL;
2N/A }
2N/A slen-=1;
2N/A j-=1;
2N/A }
2N/A }
2N/A if ((lendiff == 0) && (strcasecmp(listin[j], listin[k])==0)) {
2N/A if (j != slen) {
2N/A free(listin[j]);
2N/A listin[j] = listin[slen];
2N/A listin[slen]=NULL;
2N/A } else {
2N/A free(listin[j]);
2N/A listin[j] = NULL;
2N/A }
2N/A slen -=1;
2N/A j-=1;
2N/A }
2N/A }
2N/A }
2N/A *subtcount=slen+1;
2N/A for (k=0; k<*subtcount && listin[k]!=NULL; k++) {
2N/A subtree[k] = strdup(listin[k]);
2N/A if (subtree[k] == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Fill out a krb5_db_entry princ entry struct given a LDAP message containing
2N/A * the results of a principal search of the directory.
2N/A */
2N/Akrb5_error_code
2N/Apopulate_krb5_db_entry(krb5_context context, krb5_ldap_context *ldap_context,
2N/A LDAP *ld, LDAPMessage *ent, krb5_const_principal princ,
2N/A krb5_db_entry *entry)
2N/A{
2N/A krb5_error_code st = 0;
2N/A unsigned int mask = 0;
2N/A krb5_boolean attr_present = FALSE;
2N/A char **values = NULL, *policydn = NULL, *pwdpolicydn = NULL;
2N/A char *polname = NULL, *tktpolname = NULL;
2N/A struct berval **bvalues = NULL;
2N/A krb5_tl_data userinfo_tl_data = {0};
2N/A char **link_references = NULL;
2N/A char *DN = NULL;
2N/A
2N/A if (princ == NULL) {
2N/A /* XXX WAF probably should just extract princ from ldap result */
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A } else {
2N/A if ((st=krb5_copy_principal(context, princ, &(entry->princ))) != 0)
2N/A goto cleanup;
2N/A }
2N/A /* get the associated directory user information */
2N/A if ((values = ldap_get_values(ld, ent, "krbprincipalname")) != NULL) {
2N/A int i, pcount=0, kerberos_principal_object_type=0;
2N/A char *user;
2N/A
2N/A if ((st=krb5_unparse_name(context, princ, &user)) != 0)
2N/A goto cleanup;
2N/A
2N/A for (i=0; values[i] != NULL; ++i) {
2N/A if (strcasecmp(values[i], user) == 0) {
2N/A pcount = ldap_count_values(values);
2N/A break;
2N/A }
2N/A }
2N/A ldap_value_free(values);
2N/A free(user);
2N/A
2N/A if ((DN = ldap_get_dn(ld, ent)) == NULL) {
2N/A ldap_get_option(ld, LDAP_OPT_RESULT_CODE, &st);
2N/A st = set_ldap_error(context, st, 0);
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((values=ldap_get_values(ld, ent, "objectclass")) != NULL) {
2N/A for (i=0; values[i] != NULL; ++i)
2N/A if (strcasecmp(values[i], "krbprincipal") == 0) {
2N/A kerberos_principal_object_type = KDB_STANDALONE_PRINCIPAL_OBJECT;
2N/A if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCTYPE,
2N/A &kerberos_principal_object_type)) != 0)
2N/A goto cleanup;
2N/A break;
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A
2N/A /* add principalcount, DN and principaltype user information to tl_data */
2N/A if (((st=store_tl_data(&userinfo_tl_data, KDB_TL_PRINCCOUNT, &pcount)) != 0) ||
2N/A ((st=store_tl_data(&userinfo_tl_data, KDB_TL_USERDN, DN)) != 0))
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* read all the kerberos attributes */
2N/A
2N/A /* KRBLASTSUCCESSFULAUTH */
2N/A if ((st=krb5_ldap_get_time(ld, ent, "krbLastSuccessfulAuth",
2N/A &(entry->last_success), &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE)
2N/A mask |= KDB_LAST_SUCCESS_ATTR;
2N/A
2N/A /* KRBLASTFAILEDAUTH */
2N/A if ((st=krb5_ldap_get_time(ld, ent, "krbLastFailedAuth",
2N/A &(entry->last_failed), &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE)
2N/A mask |= KDB_LAST_FAILED_ATTR;
2N/A
2N/A /* KRBLOGINFAILEDCOUNT */
2N/A if (krb5_ldap_get_value(ld, ent, "krbLoginFailedCount",
2N/A (int *)&(entry->fail_auth_count)) == 0)
2N/A mask |= KDB_FAIL_AUTH_COUNT_ATTR;
2N/A
2N/A /* KRBMAXTICKETLIFE */
2N/A if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", &(entry->max_life)) == 0)
2N/A mask |= KDB_MAX_LIFE_ATTR;
2N/A
2N/A /* KRBMAXRENEWABLEAGE */
2N/A if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage",
2N/A &(entry->max_renewable_life)) == 0)
2N/A mask |= KDB_MAX_RLIFE_ATTR;
2N/A
2N/A /* KRBTICKETFLAGS */
2N/A if (krb5_ldap_get_value(ld, ent, "krbticketflags", &(entry->attributes)) == 0)
2N/A mask |= KDB_TKT_FLAGS_ATTR;
2N/A
2N/A /* PRINCIPAL EXPIRATION TIME */
2N/A if ((st=krb5_ldap_get_time(ld, ent, "krbprincipalexpiration", &(entry->expiration),
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE)
2N/A mask |= KDB_PRINC_EXPIRE_TIME_ATTR;
2N/A
2N/A /* PASSWORD EXPIRATION TIME */
2N/A if ((st=krb5_ldap_get_time(ld, ent, "krbpasswordexpiration", &(entry->pw_expiration),
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE)
2N/A mask |= KDB_PWD_EXPIRE_TIME_ATTR;
2N/A
2N/A /* KRBPOLICYREFERENCE */
2N/A
2N/A if ((st=krb5_ldap_get_string(ld, ent, "krbticketpolicyreference", &policydn,
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE) {
2N/A mask |= KDB_POL_REF_ATTR;
2N/A /* Ensure that the policy is inside the realm container */
2N/A if ((st = krb5_ldap_policydn_to_name (context, policydn, &tktpolname)) != 0)
2N/A goto cleanup;
2N/A }
2N/A
2N/A /* KRBPWDPOLICYREFERENCE */
2N/A if ((st=krb5_ldap_get_string(ld, ent, "krbpwdpolicyreference", &pwdpolicydn,
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE) {
2N/A krb5_tl_data kadm_tl_data;
2N/A
2N/A mask |= KDB_PWD_POL_REF_ATTR;
2N/A
2N/A /* Ensure that the policy is inside the realm container */
2N/A if ((st = krb5_ldap_policydn_to_name (context, pwdpolicydn, &polname)) != 0)
2N/A goto cleanup;
2N/A
2N/A if ((st = krb5_update_tl_kadm_data(polname, &kadm_tl_data)) != 0) {
2N/A goto cleanup;
2N/A }
2N/A krb5_dbe_update_tl_data(context, entry, &kadm_tl_data);
2N/A }
2N/A
2N/A /* KRBSECRETKEY */
2N/A if ((bvalues=ldap_get_values_len(ld, ent, "krbprincipalkey")) != NULL) {
2N/A krb5_kvno mkvno = 0;
2N/A
2N/A mask |= KDB_SECRET_KEY_ATTR;
2N/A if ((st=krb5_decode_krbsecretkey(context, entry, bvalues, &userinfo_tl_data, &mkvno)) != 0)
2N/A goto cleanup;
2N/A if (mkvno != 0) {
2N/A /* don't add the tl data if mkvno == 0 */
2N/A if ((st=krb5_dbe_update_mkvno(context, entry, mkvno)) != 0)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /* LAST PASSWORD CHANGE */
2N/A {
2N/A krb5_timestamp lstpwdchng=0;
2N/A if ((st=krb5_ldap_get_time(ld, ent, "krbLastPwdChange",
2N/A &lstpwdchng, &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE) {
2N/A if ((st=krb5_dbe_update_last_pwd_change(context, entry,
2N/A lstpwdchng)))
2N/A goto cleanup;
2N/A mask |= KDB_LAST_PWD_CHANGE_ATTR;
2N/A }
2N/A }
2N/A
2N/A /* ALLOWED TO DELEGATE TO */
2N/A {
2N/A char **a2d2 = NULL;
2N/A int i;
2N/A krb5_tl_data **tlp;
2N/A
2N/A st = krb5_ldap_get_strings(ld, ent, "krbAllowedToDelegateTo",
2N/A &a2d2, &attr_present);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A
2N/A if (attr_present == TRUE) {
2N/A for (tlp = &entry->tl_data; *tlp; tlp = &(*tlp)->tl_data_next)
2N/A ;
2N/A for (i = 0; a2d2[i] != NULL; i++) {
2N/A krb5_tl_data *tl = k5alloc(sizeof(*tl), &st);
2N/A if (st != 0) {
2N/A ldap_value_free(a2d2);
2N/A goto cleanup;
2N/A }
2N/A tl->tl_data_type = KRB5_TL_CONSTRAINED_DELEGATION_ACL;
2N/A tl->tl_data_length = strlen(a2d2[i]);
2N/A tl->tl_data_contents = (krb5_octet *)strdup(a2d2[i]);
2N/A if (tl->tl_data_contents == NULL) {
2N/A st = ENOMEM;
2N/A ldap_value_free(a2d2);
2N/A free(tl);
2N/A goto cleanup;
2N/A }
2N/A tl->tl_data_next = NULL;
2N/A *tlp = tl;
2N/A tlp = &tl->tl_data_next;
2N/A }
2N/A ldap_value_free(a2d2);
2N/A }
2N/A }
2N/A
2N/A /* KRBOBJECTREFERENCES */
2N/A {
2N/A int i=0;
2N/A
2N/A if ((st = krb5_ldap_get_strings(ld, ent, "krbobjectreferences",
2N/A &link_references, &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (link_references != NULL) {
2N/A for (i=0; link_references[i] != NULL; ++i) {
2N/A if ((st = store_tl_data(&userinfo_tl_data, KDB_TL_LINKDN,
2N/A link_references[i])) != 0)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Set tl_data */
2N/A {
2N/A int i;
2N/A struct berval **ber_tl_data = NULL;
2N/A krb5_tl_data *ptr = NULL;
2N/A
2N/A if ((ber_tl_data = ldap_get_values_len (ld, ent, "krbExtraData")) != NULL) {
2N/A for (i = 0; ber_tl_data[i] != NULL; i++) {
2N/A if ((st = berval2tl_data (ber_tl_data[i] , &ptr)) != 0)
2N/A break;
2N/A if ((st = krb5_dbe_update_tl_data(context, entry, ptr)) != 0)
2N/A break;
2N/A }
2N/A ldap_value_free_len (ber_tl_data);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A mask |= KDB_EXTRA_DATA_ATTR;
2N/A }
2N/A }
2N/A
2N/A /* update the mask of attributes present on the directory object to the tl_data */
2N/A if ((st=store_tl_data(&userinfo_tl_data, KDB_TL_MASK, &mask)) != 0)
2N/A goto cleanup;
2N/A if ((st=krb5_dbe_update_tl_data(context, entry, &userinfo_tl_data)) != 0)
2N/A goto cleanup;
2N/A
2N/A#ifdef HAVE_EDIRECTORY
2N/A {
2N/A krb5_timestamp expiretime=0;
2N/A char *is_login_disabled=NULL;
2N/A
2N/A /* LOGIN EXPIRATION TIME */
2N/A if ((st=krb5_ldap_get_time(ld, ent, "loginexpirationtime", &expiretime,
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A
2N/A if (attr_present == TRUE) {
2N/A if ((mask & KDB_PRINC_EXPIRE_TIME_ATTR) == 1) {
2N/A if (expiretime < entry->expiration)
2N/A entry->expiration = expiretime;
2N/A } else {
2N/A entry->expiration = expiretime;
2N/A }
2N/A }
2N/A
2N/A /* LOGIN DISABLED */
2N/A if ((st=krb5_ldap_get_string(ld, ent, "logindisabled", &is_login_disabled,
2N/A &attr_present)) != 0)
2N/A goto cleanup;
2N/A if (attr_present == TRUE) {
2N/A if (strcasecmp(is_login_disabled, "TRUE")== 0)
2N/A entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
2N/A free (is_login_disabled);
2N/A }
2N/A }
2N/A#endif
2N/A
2N/A if ((st=krb5_read_tkt_policy (context, ldap_context, entry, tktpolname)) !=0)
2N/A goto cleanup;
2N/A
2N/A /* We already know that the policy is inside the realm container. */
2N/A if (polname) {
2N/A osa_policy_ent_t pwdpol;
2N/A int cnt=0;
2N/A krb5_timestamp last_pw_changed;
2N/A krb5_ui_4 pw_max_life;
2N/A
2N/A memset(&pwdpol, 0, sizeof(pwdpol));
2N/A
2N/A if ((st=krb5_ldap_get_password_policy(context, polname, &pwdpol, &cnt)) != 0)
2N/A goto cleanup;
2N/A pw_max_life = pwdpol->pw_max_life;
2N/A free (pwdpol);
2N/A
2N/A if (pw_max_life > 0) {
2N/A if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0)
2N/A goto cleanup;
2N/A
2N/A if ((mask & KDB_PWD_EXPIRE_TIME_ATTR) == 1) {
2N/A if ((last_pw_changed + pw_max_life) < entry->pw_expiration)
2N/A entry->pw_expiration = last_pw_changed + pw_max_life;
2N/A } else
2N/A entry->pw_expiration = last_pw_changed + pw_max_life;
2N/A }
2N/A }
2N/A /* XXX so krb5_encode_princ_contents() will be happy */
2N/A entry->len = KRB5_KDB_V1_BASE_LENGTH;
2N/A
2N/Acleanup:
2N/A
2N/A if (DN != NULL)
2N/A ldap_memfree(DN);
2N/A
2N/A if (userinfo_tl_data.tl_data_contents != NULL)
2N/A free(userinfo_tl_data.tl_data_contents);
2N/A
2N/A if (pwdpolicydn != NULL)
2N/A free(pwdpolicydn);
2N/A
2N/A if (polname != NULL)
2N/A free(polname);
2N/A
2N/A if (tktpolname != NULL)
2N/A free (tktpolname);
2N/A
2N/A if (policydn != NULL)
2N/A free(policydn);
2N/A
2N/A if (link_references) {
2N/A int i;
2N/A for (i=0; link_references[i] != NULL; ++i)
2N/A free (link_references[i]);
2N/A free (link_references);
2N/A }
2N/A
2N/A return (st);
2N/A}
2N/A
2N/A#if 0 /* gtbtmp183 */
2N/A/*
2N/A * Solaris libldap does not provide the following functions which are in
2N/A * OpenLDAP.
2N/A */
2N/A#ifndef HAVE_LDAP_INITIALIZE
2N/Aint
2N/Aldap_initialize(LDAP **ldp, char *url)
2N/A{
2N/A int rc = 0;
2N/A LDAP *ld = NULL;
2N/A LDAPURLDesc *ludp = NULL;
2N/A
2N/A /* For now, we don't use any DN that may be provided. And on
2N/A Solaris (based on Mozilla's LDAP client code), we need the
2N/A _nodn form to parse "ldap://host" without a trailing slash.
2N/A
2N/A Also, this version won't handle an input string which contains
2N/A multiple URLs, unlike the OpenLDAP ldap_initialize. See
2N/A https://bugzilla.mozilla.org/show_bug.cgi?id=353336#c1 . */
2N/A#ifdef HAVE_LDAP_URL_PARSE_NODN
2N/A rc = ldap_url_parse_nodn(url, &ludp);
2N/A#else
2N/A rc = ldap_url_parse(url, &ludp);
2N/A#endif
2N/A if (rc == 0) {
2N/A
2N/A ld = ldap_init(ludp->lud_host, ludp->lud_port);
2N/A if (ld != NULL) {
2N/A *ldp = ld;
2N/A#if 0
2N/A printf("lud_host %s lud_port %d\n", ludp->lud_host,
2N/A ludp->lud_port);
2N/A#endif
2N/A }
2N/A else
2N/A rc = KRB5_KDB_ACCESS_ERROR;
2N/A
2N/A ldap_free_urldesc(ludp);
2N/A }
2N/A return rc;
2N/A}
2N/A#endif /* HAVE_LDAP_INITIALIZE */
2N/A#endif /* gtbtmp183 */
2N/A
2N/A#ifndef HAVE_LDAP_UNBIND_EXT_S
2N/Aint
2N/Aldap_unbind_ext_s(LDAP *ld, LDAPControl **sctrls, LDAPControl **cctrls)
2N/A{
2N/A return ldap_unbind_ext(ld, sctrls, cctrls);
2N/A}
2N/A#endif /* HAVE_LDAP_UNBIND_EXT_S */