/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
*
* Copyright (c) 2004-2005, Novell, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* * The copyright holder's name is not used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
*/
#include <string.h>
#include <time.h>
#include "kdb_ldap.h"
#include "ldap_misc.h"
#include "ldap_handle.h"
#include "ldap_err.h"
#include "ldap_principal.h"
#include "princ_xdr.h"
#include "ldap_pwd_policy.h"
#include <libintl.h> /* Solaris Kerberos */
#ifdef NEED_STRPTIME_PROTO
#endif
static krb5_error_code
int sscope);
/* Linux (GNU Libc) provides a length-limited variant of strdup.
But all the world's not Linux. */
#ifdef HAVE_LDAP_STR2DN
static char *
{
char *result;
}
return result;
} else
}
#endif
/* Get integer or string values from the config section, falling back
to the default section, then to hard-coded values. */
static errcode_t
{
int out_temp = 0;
0, &out_temp);
if (err) {
return err;
}
if (out_temp != 0) {
return 0;
}
if (err) {
return err;
}
return 0;
}
/* We don't have non-null defaults in any of our calls, so don't
bother with the extra argument. */
static errcode_t
{
0, out);
if (err) {
return err;
}
if (*out != 0)
return 0;
0, out);
if (err) {
return err;
}
return 0;
}
/*
* This function reads the parameters from the krb5.conf file. The
* parameters read here are DAL-LDAP specific attributes. Some of
* these are ldap_server ....
*/
int srv_type)
{
/* copy the conf_section into ldap_context for later use */
if (conf_section) {
goto cleanup;
}
}
/* initialize the mutexs and condition variable */
/* this portion logically doesn't fit here should be moved appropriately */
/* this mutex is used in ldap reconnection pool */
#if 0
st = -1;
"k5_mutex_init failed");
#endif
goto cleanup;
}
/*
* If max_server_conns is not set read it from database module
* section of conf file this parameter defines maximum ldap
* connections per ldap server.
*/
if (ldap_context->max_server_conns == 0) {
if (st)
goto cleanup;
}
gettext("Minimum connections required per server is 2"));
goto cleanup;
}
/*
* If the bind dn is not set read it from the database module
* section of conf file this paramter is populated by one of the
* KDC, ADMIN or PASSWD dn to be used to connect to LDAP
* server. The srv_type decides which dn to read.
*/
char *name = 0;
if (srv_type == KRB5_KDB_SRV_TYPE_KDC)
else if (srv_type == KRB5_KDB_SRV_TYPE_ADMIN)
else if (srv_type == KRB5_KDB_SRV_TYPE_PASSWD)
name = "ldap_kpasswdd_dn";
if (name) {
&ldap_context->bind_dn);
if (st)
goto cleanup;
}
}
/*
* Read service_password_file parameter from database module
* section of conf file this file contains stashed passwords of
* the KDC, ADMIN and PASSWD dns.
*/
if (st)
goto cleanup;
/* Solaris Kerberos: providing a default. */
NULL,
if (st)
goto cleanup;
}
}
/*
* Solaris Kerberos: we must use root_certificate_file
*
* Note, I've changed the ldap_root_certificate_file config parameter to
* ldap_cert_path which is more appropriate for that parameter.
*/
/* #ifdef HAVE_EDIRECTORY */
/*
* If root certificate file is not set read it from database
* module section of conf file this is the trusted root
* certificate of the Directory.
*/
if (st)
goto cleanup;
}
/* #endif */
/*
* If the ldap server parameter is not set read the list of ldap
* servers from the database module section of the conf file.
*/
unsigned int ele=0;
sizeof (krb5_ldap_server_info *));
if (*server_info == NULL) {
goto cleanup;
}
goto cleanup;
}
sizeof(krb5_ldap_server_info));
goto cleanup;
}
} else {
sizeof(krb5_ldap_server_info));
goto cleanup;
}
goto cleanup;
}
++ele;
}
}
}
return(st);
}
/*
* This function frees the krb5_ldap_context structure members.
*/
{
int i=0;
if (ldap_context == NULL)
return 0;
/* Free all ldap servers list and the ldap handles associated with
the ldap server. */
if (ldap_context->server_info_list) {
while (ldap_context->server_info_list[i]) {
}
#ifdef HAVE_EDIRECTORY
}
#endif
while (ldap_server_handle) {
}
}
i++;
}
}
}
}
}
}
/* Solaris Kerberos */
/* #ifdef HAVE_EDIRECTORY */
}
/* #endif */
}
}
if (ldap_context->certificates) {
i=0;
++i;
}
}
return(0);
}
{
if (ldap_context == NULL)
return 0;
return(0);
}
/*
* check to see if the principal belongs to the default realm.
* The default realm is present in the krb5_ldap_context structure.
* The principal has a realm portion. This realm portion is compared with the default realm
* to check whether the principal belong to the default realm.
* Return 0 if principal belongs to default realm else 1.
*/
{
#define FIND_MAX(a,b) ((a) > (b) ? (a) : (b))
/*
* Care should be taken for inter-realm principals as the default
* realm can exist in the realm part of the principal name or can
* also exist in the second portion of the name part. However, if
* the default realm exist in the second part of the principal
* portion, then the first portion of the principal name SHOULD be
* "krbtgt". All this check is done in the immediate block.
*/
return 0;
}
/* first check the length, if they are not equal, then they are not same */
return 1;
/* if the length is equal, check for the contents */
return 1;
/* if we are here, then the realm portions match, return 0 */
return 0;
}
/*
* Deduce the subtree information from the context. A realm can have
* multiple subtrees.
* 1. the Realm container
* 2. the actual subtrees associated with the Realm
*
* However, there are some conditions to be considered to deduce the
* actual subtree/s associated with the realm. The conditions are as
* follows:
* 1. If the subtree information of the Realm is [Root] or NULL (that
* is internal a [Root]) then the realm has only one subtree
* i.e [Root], i.e. whole of the tree.
* realm has only one, i.e., the Realm container. NOTE: In all cases
* Realm container SHOULD be the one among the subtrees or the only
* one subtree.
* 3. The subtree information of the realm is overlapping the realm
* container of the realm, then the realm has only one subtree and
* it is the subtree information associated with the realm.
*/
unsigned int *ntree)
{
subtarr = (char **) malloc(sizeof(char *) * (subtreecount + 1 /*realm dn*/ + 1 /*containerref*/ + 1));
goto cleanup;
}
/* get the complete subtree list */
goto cleanup;
}
}
goto cleanup;
}
if (containerref != NULL) {
goto cleanup;
}
}
ncount = i;
if (newsubtree == NULL) {
goto cleanup;
}
search_scope)) != 0) {
goto cleanup;
}
*subtreearr = newsubtree;
}
if (st != 0) {
if (newsubtree != NULL) {
for (i=0; newsubtree[i] != NULL; i++)
free(newsubtree[i]);
}
}
return st;
}
/*
* This function appends the content with a type into the tl_data
* structure. Based on the type the length of the content is either
* pre-defined or computed from the content. Returns 0 in case of
* success and 1 if the type associated with the content is undefined.
*/
{
switch (tl_type) {
case KDB_TL_PRINCCOUNT:
case KDB_TL_PRINCTYPE:
case KDB_TL_MASK:
{
/* allocate required memory */
if (reallocptr)
free (reallocptr);
return ENOMEM;
}
/* store the tl_type value */
curr += 1;
/* store the content length */
tldatalen = 2;
curr += 2;
/* store the content */
curr += 2;
break;
}
case KDB_TL_USERDN:
case KDB_TL_LINKDN:
{
/* allocate required memory */
if (reallocptr)
free (reallocptr);
return ENOMEM;
}
/* store the tl_type value */
curr += 1;
/* store the content length */
curr += 2;
/* store the content */
break;
}
default:
return 1;
}
return 0;
}
/*
* This function scans the tl_data structure to get the value of a
* type defined by the tl_type (second parameter). The tl_data
* structure has all the data in the tl_data_contents member. The
* format of the tl_data_contents is as follows. The first byte
* defines the type of the content that follows. The next 2 bytes
* define the size n (in terms of bytes) of the content that
* follows. The next n bytes define the content itself.
*/
{
unsigned int sublen=0;
/* get the type of the content */
/* forward by 1 byte*/
curr += 1;
switch (subtype) {
case KDB_TL_PRINCCOUNT:
case KDB_TL_PRINCTYPE:
case KDB_TL_MASK:
/* get the length of the content */
/* forward by 2 bytes */
curr += 2;
/* get the actual content */
if (sublen == 2) {
/* intptr = malloc(sublen); */
return ENOMEM;
} else {
return ENOMEM;
}
st = 0;
return st;
break;
case KDB_TL_CONTAINERDN:
case KDB_TL_USERDN:
/* get the length of the content */
/* forward by 2 bytes */
curr += 2;
return ENOMEM;
st = 0;
return st;
break;
case KDB_TL_LINKDN:
return ENOMEM;
}
if (i == limit-1) {
/* Solaris Kerberos */
char **tmp;
limit *= 2;
return ENOMEM;
}
}
/* get the length of the content */
/* forward by 2 bytes */
curr += 2;
/* Solaris Kerberos - What about DNarr[i-1 .. 0] ? */
return ENOMEM;
}
++i;
st=0;
break;
}
} else {
/* move to the current content block */
}
}
return st;
}
/*
* wrapper routines for decode_tl_data
*/
static krb5_error_code
{
goto cleanup;
}
return st;
}
/*
* Get the mask representing the attributes set on the directory
* object (user, policy ...).
*/
int *mask)
{
}
{
}
{
}
{
goto cleanup;
}
return st;
}
static krb5_error_code
{
goto cleanup;
}
goto cleanup;
}
return st;
}
{
}
char **containerdn)
{
*containerdn = NULL;
}
/*
* This function reads the attribute values (if the attribute is
* non-null) from the dn. The read attribute values is compared
* aganist the attrvalues passed to the function and a bit mask is set
* for all the matching attributes (attributes existing in both list).
* The bit to be set is selected such that the index of the attribute
* in the attrvalues parameter is the position of the bit. For ex:
* the first element in the attrvalues is present in both list shall
* set the LSB of the bit mask.
*
* In case if either the attribute or the attrvalues parameter to the
* function is NULL, then the existence of the object is considered
* and appropriate status is returned back.
*/
int *mask)
{
return st;
}
attributes[0] = attribute;
/* read the attribute values from the dn */
dn,
0,
0,
NULL,
NULL,
&result)) != LDAP_SUCCESS) {
return st;
}
/*
* If the attribute/attrvalues is NULL, then check for the
* existence of the object alone.
*/
goto cleanup;
/* reset the bit mask */
*mask = 0;
/* read the attribute values */
int i,j;
/*
* Compare the read attribute values with the attrvalues
* array and set the appropriate bit mask.
*/
for (j=0; attrvalues[j]; ++j) {
for (i=0; values[i]; ++i) {
break;
}
}
}
}
}
return st;
}
/*
* This function updates a single attribute with a single value of a
* specified dn. This function is mainly used to update
* krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
* ADMIN, PASSWD servers are associated with some realms or vice
* versa.
*/
{
int st=0;
/* data to update the {attr,attrval} combination */
/* ldap modify operation */
/* if the {attr,attrval} combination is already present return a success
* LDAP_ALREADY_EXISTS is for single-valued attribute
* LDAP_TYPE_OR_VALUE_EXISTS is for multi-valued attribute
*/
st = 0;
if (st != 0) {
}
return st;
}
/*
* This function deletes a single attribute with a single value of a
* specified dn. This function is mainly used to delete
* krbRealmReferences, krbKdcServers, krbAdminServers... when KDC,
* ADMIN, PASSWD servers are disassociated with some realms or vice
* versa.
*/
{
/* data to delete the {attr,attrval} combination */
/* ldap modify operation */
/* if either the attribute or the attribute value is missing return a success */
st = 0;
if (st != 0) {
}
return st;
}
/*
* This function takes in 2 string arrays, compares them to remove the
* matching entries. The first array is the original list and the
* second array is the modified list. Removing the matching entries
* will result in a reduced array, where the left over first array
* elements are the deleted entries and the left over second array
* elements are the added entries. These additions and deletions has
* resulted in the modified second array.
*/
{
/* validate the input parameters */
return 0;
/* compute the first array length */
for (i=0;src[i]; ++i)
;
/* return if the length is 0 */
if (i==0)
return 0;
/* index of the last element and also the length of the array */
slen = i-1;
/* compute the second array length */
for (i=0;dest[i]; ++i)
;
/* return if the length is 0 */
if (i==0)
return 0;
/* index of the last element and also the length of the array */
dlen = i-1;
/* check for the similar elements and delete them from both the arrays */
for (i=0; src[i]; ++i) {
for (j=0; dest[j]; ++j) {
/* if the element are same */
/*
* If the matched element is in the middle, then copy
* the last element to the matched index.
*/
if (i != slen) {
} else {
/*
* If the matched element is the last, free it and
* set it to NULL.
*/
}
/* reduce the array length by 1 */
slen -= 1;
/* repeat the same processing for the second array too */
if (j != dlen) {
} else {
}
dlen -=1;
/*
* The source array is reduced by 1, so reduce the
* index variable used for source array by 1. No need
* to adjust the second array index variable as it is
* reset while entering the inner loop.
*/
i -= 1;
break;
}
}
}
return 0;
}
/*
* This function replicates the contents of the src array for later
* use. Mostly the contents of the src array is obtained from a
* ldap_search operation and the contents are required for later use.
*/
{
int i=0;
/* validate the input parameters */
return 0;
/* allocate memory for the dest array */
goto cleanup;
}
/* copy the members from src to dest array. */
goto cleanup;
}
}
/* in case of error free up everything and return */
if (st != 0) {
for (i=0; (*dest)[i]; ++i) {
}
}
}
return st;
}
static krb5_error_code
{
*epochtime = 0;
return EINVAL;
}
return 0;
}
/*
* krb5_ldap_get_value() - get the integer value of the attribute
* Returns, 0 if the attribute is present, 1 if the attribute is missing.
* The retval is 0 if the attribute is missing.
*/
{
*retval = 0;
return 0;
}
return 1;
}
/*
* krb5_ldap_get_string() - Returns the first string of the
* attribute. Intended to
*
*
*/
{
if (attr_present != NULL)
*attr_present = FALSE;
if (attr_present!= NULL)
*attr_present = TRUE;
}
}
return st;
}
/*
* krb5_ldap_get_strings() - Returns all the values
* of the attribute.
*/
{
unsigned int i=0, count=0;
if (attr_present != NULL)
*attr_present = FALSE;
if (attr_present != NULL)
*attr_present = TRUE;
return st;
}
for (i=0; i< count; ++i) {
goto cleanup;
}
}
}
if (st != 0) {
for (i=0; i< count; ++i)
}
}
return st;
}
{
*rettime = 0;
*attr_present = FALSE;
*attr_present = TRUE;
}
}
return st;
}
/*
* Function to allocate, set the values of LDAPMod structure. The
* LDAPMod structure is then added to the array at the ind
*/
{
int i=0;
;
}
return ENOMEM;
return ENOMEM;
*count = i;
return 0;
}
char **values)
{
int i=0, j=0;
return st;
return ENOMEM;
;
return ENOMEM;
return ENOMEM;
}
}
return 0;
}
struct berval **ber_values)
{
int i=0, j=0;
return st;
return ENOMEM;
for (j=0; ber_values[j] != NULL; ++j)
;
return ENOMEM;
for (j=0; ber_values[j] != NULL; ++j) {
return ENOMEM;
return ENOMEM;
ber_values[j]->bv_len);
}
return 0;
}
static inline char *
{
}
int *value)
{
int i=0, j=0;
return st;
return ENOMEM;
for (j=0; value[j] != -1; ++j)
;
for (j=0; value[j] != -1; ++j) {
return ENOMEM;
}
return 0;
}
{
int i=0;
return st;
return ENOMEM;
return ENOMEM;
return 0;
}
{
return status;
}
{
return status;
}
{
return status;
}
{
return status;
}
{
return status;
}
const char *
{
}
void
{
}
/*
* Get the number of times an object has been referred to in a realm. this is
* needed to find out if deleting the attribute will cause dangling links.
*
* An LDAP handle may be optionally specified to prevent race condition - there
* are a limited number of LDAP handles.
*/
{
unsigned int i, ntrees;
goto cleanup;
}
GET_HANDLE();
gothandle = 1;
}
refcntattr [0] = refattr;
goto cleanup;
}
goto cleanup;
}
goto cleanup;
int n;
LDAP_SEARCH(subtree[i],
if (n == -1) {
if (ret != LDAP_SUCCESS)
goto cleanup;
}
*count += n;
}
for (i = 0; i < ntrees; i++)
}
if (gothandle == 1)
return st;
}
/*
* For now, policy objects are expected to be directly under the realm
* container.
*/
{
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
#if defined HAVE_LDAP_STR2DN
{
char *rdn;
goto cleanup;
}
else {
}
ldap_memfree (dn);
}
#elif defined HAVE_LDAP_EXPLODE_DN
{
char **parsed_dn;
/* 1 = return DN components without type prefix */
} else {
}
}
#else
#endif
return st;
}
{
int len;
/* validate the input parameters */
goto cleanup;
}
/* Used for removing policy reference from an object */
if (name[0] == '\0') {
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
return st;
}
/* remove overlapping and repeated subtree entries from the list of subtrees */
static krb5_error_code
int sscope)
{
if (sscope == 2) {
if (k != slen) {
} else {
}
slen-=1;
k-=1;
break;
if (j != slen) {
} else {
}
slen-=1;
j-=1;
}
}
if (j != slen) {
} else {
}
slen -=1;
j-=1;
}
}
}
return ENOMEM;
}
}
return 0;
}
/*
* Fill out a krb5_db_entry princ entry struct given a LDAP message containing
* the results of a principal search of the directory.
*/
{
unsigned int mask = 0;
/* XXX WAF probably should just extract princ from ldap result */
goto cleanup;
} else {
goto cleanup;
}
/* get the associated directory user information */
char *user;
goto cleanup;
break;
}
}
goto cleanup;
}
&kerberos_principal_object_type)) != 0)
goto cleanup;
break;
}
}
/* add principalcount, DN and principaltype user information to tl_data */
goto cleanup;
}
/* read all the kerberos attributes */
/* KRBLASTSUCCESSFULAUTH */
goto cleanup;
if (attr_present == TRUE)
/* KRBLASTFAILEDAUTH */
goto cleanup;
if (attr_present == TRUE)
/* KRBLOGINFAILEDCOUNT */
(int *)&(entry->fail_auth_count)) == 0)
/* KRBMAXTICKETLIFE */
/* KRBMAXRENEWABLEAGE */
&(entry->max_renewable_life)) == 0)
/* KRBTICKETFLAGS */
/* PRINCIPAL EXPIRATION TIME */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE)
/* PASSWORD EXPIRATION TIME */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE)
/* KRBPOLICYREFERENCE */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE) {
mask |= KDB_POL_REF_ATTR;
/* Ensure that the policy is inside the realm container */
goto cleanup;
}
/* KRBPWDPOLICYREFERENCE */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE) {
/* Ensure that the policy is inside the realm container */
goto cleanup;
goto cleanup;
}
}
/* KRBSECRETKEY */
goto cleanup;
if (mkvno != 0) {
/* don't add the tl data if mkvno == 0 */
goto cleanup;
}
}
/* LAST PASSWORD CHANGE */
{
&lstpwdchng, &attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE) {
lstpwdchng)))
goto cleanup;
}
}
/* ALLOWED TO DELEGATE TO */
{
int i;
&a2d2, &attr_present);
if (st != 0)
goto cleanup;
if (attr_present == TRUE) {
;
if (st != 0) {
goto cleanup;
}
goto cleanup;
}
}
}
}
/* KRBOBJECTREFERENCES */
{
int i=0;
&link_references, &attr_present)) != 0)
goto cleanup;
if (link_references != NULL) {
for (i=0; link_references[i] != NULL; ++i) {
link_references[i])) != 0)
goto cleanup;
}
}
}
/* Set tl_data */
{
int i;
for (i = 0; ber_tl_data[i] != NULL; i++) {
break;
break;
}
if (st != 0)
goto cleanup;
}
}
/* update the mask of attributes present on the directory object to the tl_data */
goto cleanup;
goto cleanup;
#ifdef HAVE_EDIRECTORY
{
/* LOGIN EXPIRATION TIME */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE) {
} else {
}
}
/* LOGIN DISABLED */
&attr_present)) != 0)
goto cleanup;
if (attr_present == TRUE) {
}
}
#endif
goto cleanup;
/* We already know that the policy is inside the realm container. */
if (polname) {
int cnt=0;
goto cleanup;
if (pw_max_life > 0) {
goto cleanup;
} else
}
}
/* XXX so krb5_encode_princ_contents() will be happy */
if (pwdpolicydn != NULL)
if (tktpolname != NULL)
free (tktpolname);
if (link_references) {
int i;
for (i=0; link_references[i] != NULL; ++i)
free (link_references[i]);
}
return (st);
}
#if 0 /* gtbtmp183 */
/*
* Solaris libldap does not provide the following functions which are in
* OpenLDAP.
*/
#ifndef HAVE_LDAP_INITIALIZE
int
{
int rc = 0;
/* For now, we don't use any DN that may be provided. And on
Solaris (based on Mozilla's LDAP client code), we need the
_nodn form to parse "ldap://host" without a trailing slash.
Also, this version won't handle an input string which contains
multiple URLs, unlike the OpenLDAP ldap_initialize. See
#ifdef HAVE_LDAP_URL_PARSE_NODN
#else
#endif
if (rc == 0) {
#if 0
#endif
}
else
}
return rc;
}
#endif /* HAVE_LDAP_INITIALIZE */
#endif /* gtbtmp183 */
#ifndef HAVE_LDAP_UNBIND_EXT_S
int
{
}
#endif /* HAVE_LDAP_UNBIND_EXT_S */