/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* lib/kdb/kdb_ldap/ldap_tkt_policy.c
*
* 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
* documentation and/or other materials provided with the distribution.
* * 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 "ldap_main.h"
#include "kdb_ldap.h"
#include "ldap_tkt_policy.h"
#include "ldap_err.h"
#include <libintl.h> /* Solaris Kerberos */
/* Ticket policy object management */
/*
* create the Ticket policy object in Directory.
*/
krb5_error_code
krb5_ldap_create_policy(krb5_context context, krb5_ldap_policy_params *policy,
int mask)
{
krb5_error_code st=0;
LDAP *ld=NULL;
char *strval[3]={NULL}, *policy_dn = NULL;
LDAPMod **mods=NULL;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
/* validate the input parameters */
if (policy == NULL || policy->policy == NULL) {
st = EINVAL;
krb5_set_error_message (context, st, gettext("Ticket Policy Name missing"));
goto cleanup;
}
SETUP_CONTEXT();
GET_HANDLE();
if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
goto cleanup;
memset(strval, 0, sizeof(strval));
strval[0] = policy->policy;
if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
goto cleanup;
memset(strval, 0, sizeof(strval));
strval[0] = "krbTicketPolicy";
strval[1] = "krbTicketPolicyaux";
if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
goto cleanup;
if (mask & LDAP_POLICY_MAXTKTLIFE) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_ADD,
policy->maxtktlife)) != 0)
goto cleanup;
}
if (mask & LDAP_POLICY_MAXRENEWLIFE) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_ADD,
policy->maxrenewlife)) != 0)
goto cleanup;
}
if (mask & LDAP_POLICY_TKTFLAGS) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_ADD,
policy->tktflags)) != 0)
goto cleanup;
}
/* ldap add operation */
if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
st = set_ldap_error (context, st, OP_ADD);
goto cleanup;
}
cleanup:
if (policy_dn != NULL)
free(policy_dn);
ldap_mods_free(mods, 1);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
/*
* modify the Ticket policy object in Directory.
*/
krb5_error_code
krb5_ldap_modify_policy(krb5_context context, krb5_ldap_policy_params *policy,
int mask)
{
int objectmask=0;
krb5_error_code st=0;
LDAP *ld=NULL;
char *attrvalues[]={"krbTicketPolicy", "krbTicketPolicyAux", NULL}, *strval[2]={NULL};
char *policy_dn = NULL;
LDAPMod **mods=NULL;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
/* validate the input parameters */
if (policy == NULL || policy->policy==NULL) {
st = EINVAL;
krb5_set_error_message (context, st, gettext("Ticket Policy Name missing"));
goto cleanup;
}
SETUP_CONTEXT();
GET_HANDLE();
if ((st = krb5_ldap_name_to_policydn (context, policy->policy, &policy_dn)) != 0)
goto cleanup;
/* the policydn object should be of the krbTicketPolicy object class */
st = checkattributevalue(ld, policy_dn, "objectClass", attrvalues, &objectmask);
CHECK_CLASS_VALIDITY(st, objectmask, "ticket policy object: ");
if ((objectmask & 0x02) == 0) { /* add krbticketpolicyaux to the object class list */
memset(strval, 0, sizeof(strval));
strval[0] = "krbTicketPolicyAux";
if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
goto cleanup;
}
if (mask & LDAP_POLICY_MAXTKTLIFE) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE,
policy->maxtktlife)) != 0)
goto cleanup;
}
if (mask & LDAP_POLICY_MAXRENEWLIFE) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE,
policy->maxrenewlife)) != 0)
goto cleanup;
}
if (mask & LDAP_POLICY_TKTFLAGS) {
if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE,
policy->tktflags)) != 0)
goto cleanup;
}
if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
st = set_ldap_error (context, st, OP_MOD);
goto cleanup;
}
cleanup:
if (policy_dn != NULL)
free(policy_dn);
ldap_mods_free(mods, 1);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
/*
* Read the policy object from the Directory and populate the krb5_ldap_policy_params
* structure.
*/
krb5_error_code
krb5_ldap_read_policy(krb5_context context, char *policyname,
krb5_ldap_policy_params **policy,
/* Solaris kerberos: unsigned better for mask */
unsigned int *omask)
{
krb5_error_code st=0, tempst=0;
int objectmask=0;
LDAP *ld=NULL;
LDAPMessage *result=NULL,*ent=NULL;
char *attributes[] = { "krbMaxTicketLife", "krbMaxRenewableAge", "krbTicketFlags", NULL};
char *attrvalues[] = { "krbTicketPolicy", NULL}, *policy_dn = NULL;
krb5_ldap_policy_params *lpolicy=NULL;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
/* validate the input parameters */
if (policyname == NULL || policy == NULL) {
st = EINVAL;
krb5_set_error_message(context, st, gettext("Ticket Policy Object information missing"));
goto cleanup;
}
SETUP_CONTEXT();
GET_HANDLE();
if ((st = krb5_ldap_name_to_policydn (context, policyname, &policy_dn)) != 0)
goto cleanup;
/* the policydn object should be of the krbTicketPolicy object class */
st = checkattributevalue(ld, policy_dn, "objectClass", attrvalues, &objectmask);
CHECK_CLASS_VALIDITY(st, objectmask, "ticket policy object: ");
/* Initialize ticket policy structure */
lpolicy =(krb5_ldap_policy_params *) malloc(sizeof(krb5_ldap_policy_params));
CHECK_NULL(lpolicy);
memset(lpolicy, 0, sizeof(krb5_ldap_policy_params));
if ((lpolicy->policy = strdup (policyname)) == NULL) {
st = ENOMEM;
goto cleanup;
}
lpolicy->tl_data = calloc (1, sizeof(*lpolicy->tl_data));
CHECK_NULL(lpolicy->tl_data);
lpolicy->tl_data->tl_data_type = KDB_TL_USER_INFO;
LDAP_SEARCH(policy_dn, LDAP_SCOPE_BASE, "(objectclass=krbTicketPolicy)", attributes);
*omask = 0;
ent=ldap_first_entry(ld, result);
if (ent != NULL) {
if (krb5_ldap_get_value(ld, ent, "krbmaxticketlife", (int *) &(lpolicy->maxtktlife)) == 0)
*omask |= LDAP_POLICY_MAXTKTLIFE;
if (krb5_ldap_get_value(ld, ent, "krbmaxrenewableage", (int *) &(lpolicy->maxrenewlife)) == 0)
*omask |= LDAP_POLICY_MAXRENEWLIFE;
if (krb5_ldap_get_value(ld, ent, "krbticketflags", (int *) &(lpolicy->tktflags)) == 0)
*omask |= LDAP_POLICY_TKTFLAGS;
}
ldap_msgfree(result);
lpolicy->mask = *omask;
store_tl_data(lpolicy->tl_data, KDB_TL_MASK, omask);
*policy = lpolicy;
cleanup:
if (st != 0) {
krb5_ldap_free_policy(context, lpolicy);
*policy = NULL;
}
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
/*
* Function to delete ticket policy object from the directory. Before
* calling this function krb5_ldap_read_policy should be called to
* check the existence of the object. This serves one major purpose,
* i.e., if the object to be is anything other than the ticket policy
* object then the krb5_ldap_read_policy returns an error and thus is
* not accidently deleted in this function.
*
* NOTE: Other kerberos objects (user/realm object) might be having
* references to the policy object to be deleted. This situation is
* not handled here, instead is taken care of at all the places where
* the deleted policy object is read, to ignore a return status of
* LDAP_NO_SUCH_OBJECT and continue.
*/
krb5_error_code
krb5_ldap_delete_policy(krb5_context context, char *policyname)
{
int refcount = 0;
char *policy_dn = NULL;
krb5_error_code st = 0;
LDAP *ld = NULL;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
if (policyname == NULL) {
st = EINVAL;
prepend_err_str (context, gettext("Ticket Policy Object DN missing"),st,st);
goto cleanup;
}
SETUP_CONTEXT();
GET_HANDLE();
if ((st = krb5_ldap_name_to_policydn (context, policyname, &policy_dn)) != 0)
goto cleanup;
/* Checking for policy count for 0 and will not permit delete if
* it is greater than 0. */
if ((st = krb5_ldap_get_reference_count (context, policy_dn,
"krbTicketPolicyReference", &refcount, ld)) != 0)
goto cleanup;
if (refcount == 0) {
if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != 0) {
prepend_err_str (context,ldap_err2string(st),st,st);
goto cleanup;
}
} else {
st = EINVAL;
prepend_err_str (context, gettext("Delete Failed: One or more Principals associated with the Ticket Policy"), st, st);
goto cleanup;
}
cleanup:
if (policy_dn != NULL)
free (policy_dn);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
/*
* list policy objects from Directory
*/
krb5_error_code
krb5_ldap_list_policy(krb5_context context, char *containerdn, char ***policy)
{
int i, j, count;
char **list = NULL;
char *policycontainerdn = containerdn;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_error_code st=0;
SETUP_CONTEXT();
if (policycontainerdn == NULL) {
policycontainerdn = ldap_context->lrparams->realmdn;
}
if ((st = krb5_ldap_list(context, &list, "krbTicketPolicy", policycontainerdn)) != 0)
goto cleanup;
for (i = 0; list[i] != NULL; i++);
count = i;
*policy = (char **) calloc ((unsigned) count + 1, sizeof(char *));
if (*policy == NULL) {
st = ENOMEM;
goto cleanup;
}
for (i = 0, j = 0; list[i] != NULL; i++, j++) {
int ret;
ret = krb5_ldap_policydn_to_name (context, list[i], &(*policy)[i]);
if (ret != 0)
j--;
}
cleanup:
return st;
}
/*
* Function to free the ticket policy object structure.
* Note: this function assumes that memory of the policy structure is dynamically allocated and hence the whole
* structure is freed up. Care should be taken not to call this function on a static structure
*/
krb5_error_code
krb5_ldap_free_policy(krb5_context context, krb5_ldap_policy_params *policy)
{
krb5_error_code st=0;
if (policy == NULL)
return st;
if (policy->policy)
free (policy->policy);
if (policy->tl_data) {
if (policy->tl_data->tl_data_contents)
free (policy->tl_data->tl_data_contents);
free (policy->tl_data);
}
free (policy);
return st;
}
/*
* This function is general object listing routine. It is currently
* used for ticket policy object listing.
*/
krb5_error_code
krb5_ldap_list(krb5_context context, char ***list, char *objectclass,
char *containerdn)
{
char *filter=NULL, *dn=NULL;
krb5_error_code st=0, tempst=0;
int i=0, count=0, filterlen=0;
LDAP *ld=NULL;
LDAPMessage *result=NULL,*ent=NULL;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
SETUP_CONTEXT();
GET_HANDLE();
/* check if the containerdn exists */
if (containerdn) {
if ((st=checkattributevalue(ld, containerdn, NULL, NULL, NULL)) != 0) {
prepend_err_str (context, "Error reading container object: ", st, st);
goto cleanup;
}
}
/* set the filter for the search operation */
filterlen = strlen("(objectclass=") + strlen(objectclass) + 1 + 1;
filter = malloc ((unsigned) filterlen);
if (filter == NULL) {
st = ENOMEM;
goto cleanup;
}
snprintf(filter, (unsigned) filterlen,"(objectclass=%s)",objectclass);
LDAP_SEARCH(containerdn, LDAP_SCOPE_SUBTREE, filter, NULL);
count = ldap_count_entries(ld, result);
if (count == -1) {
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &st);
st = set_ldap_error(context, st, OP_SEARCH);
goto cleanup;
}
*list = (char **) calloc ((unsigned) count+1, sizeof(char *));
if (*list == NULL) {
st = ENOMEM;
goto cleanup;
}
for (ent=ldap_first_entry(ld, result), count=0; ent != NULL; ent=ldap_next_entry(ld, ent), ++count) {
if ((dn=ldap_get_dn(ld, ent)) == NULL)
continue;
if (((*list)[count] = strdup(dn)) == NULL) {
ldap_memfree (dn);
st = ENOMEM;
goto cleanup;
}
ldap_memfree(dn);
}
ldap_msgfree(result);
cleanup:
if (filter)
free (filter);
/* some error, free up all the memory */
if (st != 0) {
if (*list) {
for (i=0; (*list)[i]; ++i)
free ((*list)[i]);
free (*list);
*list = NULL;
}
}
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}