/*
 * kadmin/ldap_util/kdb5_ldap_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.
 */

/*
 * Create / Delete / Modify / View / List policy objects.
 */

#include <stdio.h>
#include <time.h>
#include <k5-int.h>
#include <kadm5/admin.h>
#include "kdb5_ldap_util.h"
#include "kdb5_ldap_list.h"
#include "ldap_tkt_policy.h"
extern time_t get_date(char *); /* kadmin/cli/getdate.o */

static void print_policy_params(krb5_ldap_policy_params *policyparams, int mask);
static char *strdur(time_t duration);

extern char *yes;
extern kadm5_config_params global_params;
                                                                                                                             
static krb5_error_code init_ldap_realm (int argc, char *argv[]) {
    /* This operation is being performed in the context of a realm. So,
     * initialize the realm */
    int mask = 0;
    krb5_error_code retval = 0;
    kdb5_dal_handle *dal_handle = NULL;
    krb5_ldap_context *ldap_context=NULL;
                                                                                                                             
    dal_handle = (kdb5_dal_handle *) util_context->db_context;
    ldap_context = (krb5_ldap_context *) dal_handle->db_context;
    if (!ldap_context) {
        retval = EINVAL;
        goto cleanup;
    }
                                                                                                                             
    if (ldap_context->krbcontainer == NULL) {
        retval = krb5_ldap_read_krbcontainer_params (util_context,
                &(ldap_context->krbcontainer));
        if (retval != 0) {
            com_err(argv[0], retval, "while reading kerberos container information");
            goto cleanup;
        }
    }
                                                                                                                             
    if (ldap_context->lrparams == NULL) {
        retval = krb5_ldap_read_realm_params(util_context,
                global_params.realm,
                &(ldap_context->lrparams),
                &mask);
                                                                                                                             
        if (retval != 0) {
            goto cleanup;
        }
    }
cleanup:
    return retval;
}

/*
 * This function will create a ticket policy object with the
 * specified attributes.
 */
void
kdb5_ldap_create_policy(argc, argv)
    int argc;
    char *argv[];
{
    char *me = argv[0];
    krb5_error_code retval = 0;
    krb5_ldap_policy_params *policyparams = NULL;
    krb5_boolean print_usage = FALSE;
    krb5_boolean no_msg = FALSE;
    int mask = 0;
    time_t date = 0;
    time_t now = 0;
    int i = 0;

    /* Check for number of arguments */
    if ((argc < 2) || (argc > 16)) {
	goto err_usage;
    }

    /* Allocate memory for policy parameters structure */
    policyparams = (krb5_ldap_policy_params*) calloc(1, sizeof(krb5_ldap_policy_params));
    if (policyparams == NULL) {
	retval = ENOMEM;
	goto cleanup;
    }

    /* Get current time */
    time (&now);

    /* Parse all arguments */
    for (i = 1; i < argc; i++) {
	if (!strcmp(argv[i], "-maxtktlife")) {
	    if (++i > argc - 1)
		goto err_usage;

	    date = get_date(argv[i]);
	    if (date == (time_t)(-1)) {
		retval = EINVAL;
		com_err (me, retval, "while providing time specification");
		goto err_nomsg;
	    }

	    policyparams->maxtktlife = date - now;

	    mask |= LDAP_POLICY_MAXTKTLIFE;
	} else if (!strcmp(argv[i], "-maxrenewlife")) {
	    if (++i > argc - 1)
		goto err_usage;

	    date = get_date(argv[i]);
	    if (date == (time_t)(-1)) {
		retval = EINVAL;
		com_err (me, retval, "while providing time specification");
		goto err_nomsg;
	    }

	    policyparams->maxrenewlife = date - now;

	    mask |= LDAP_POLICY_MAXRENEWLIFE;
	} else if (!strcmp((argv[i] + 1), "allow_postdated")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_POSTDATED);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_POSTDATED;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_forwardable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_FORWARDABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_FORWARDABLE;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_renewable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_RENEWABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_RENEWABLE;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_proxiable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_PROXIABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_PROXIABLE;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_dup_skey")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_DUP_SKEY);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_DUP_SKEY;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "requires_preauth")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_PRE_AUTH;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_PRE_AUTH);
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "requires_hwauth")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_HW_AUTH;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_HW_AUTH);
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_svr")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_SVR);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_SVR;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_tgs_req")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_TGT_BASED);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_TGT_BASED;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_tix")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_ALL_TIX);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_ALL_TIX;
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "needchange")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_PWCHANGE;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_PWCHANGE);
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "password_changing_service")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_PWCHANGE_SERVICE;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_PWCHANGE_SERVICE);
	    else
		goto err_usage;

	    mask |= LDAP_POLICY_TKTFLAGS;
	} else { /* Any other argument must be policy DN */
	    /* First check if policy DN is already provided --
	       if so, there's a usage error */
            if (policyparams->policy != NULL)
		goto err_usage;

	    /* If not present already, fill up policy DN */
            policyparams->policy = strdup(argv[i]);
            if (policyparams->policy == NULL) {
		retval = ENOMEM;
		com_err(me, retval, "while creating policy object");
		goto err_nomsg;
	    }
	}
    }

    /* policy DN is a mandatory argument. If not provided, print usage */
    if (policyparams->policy == NULL)
	goto err_usage;

    if ((retval = init_ldap_realm (argc, argv))) {
        com_err(me, retval, "while reading realm information");
        goto err_nomsg;
    }

    /* Create object with all attributes provided */
    if ((retval = krb5_ldap_create_policy(util_context, policyparams, mask)) != 0)
	goto cleanup;

    goto cleanup;

err_usage:
    print_usage = TRUE;

err_nomsg:
    no_msg = TRUE;

cleanup:
    /* Clean-up structure */
    krb5_ldap_free_policy (util_context, policyparams);

    if (print_usage)
	db_usage(CREATE_POLICY);

    if (retval) {
	if (!no_msg)
	    com_err(me, retval, "while creating policy object");

	exit_status++;
    }

    return;
}


/*
 * This function will destroy the specified ticket policy
 * object interactively, unless forced through an option.
 */
void
kdb5_ldap_destroy_policy(argc, argv)
    int argc;
    char *argv[];
{
    char *me = argv[0];
    krb5_error_code retval = 0;
    krb5_ldap_policy_params *policyparams = NULL;
    krb5_boolean print_usage = FALSE;
    krb5_boolean no_msg = FALSE;
    char *policy = NULL;
    int mask = 0;
    int force = 0;
    char buf[5] = {0};
    int i = 0;

    if ((argc < 2) || (argc > 3)) {
	goto err_usage;
    }

    for (i = 1; i < argc; i++) {
	if (strcmp(argv[i], "-force") == 0) {
	    force++;
	} else { /* Any other argument must be policy DN */
	    /* First check if policy DN is already provided --
	       if so, there's a usage error */
            if (policy != NULL)
		goto err_usage;

	    /* If not present already, fill up policy DN */
            policy = strdup(argv[i]);
            if (policy == NULL) {
		retval = ENOMEM;
		com_err(me, retval, "while destroying policy object");
		goto err_nomsg;
	    }
	}
    }

    if (policy == NULL)
	goto err_usage;

    if (!force) {
        printf("This will delete the policy object '%s', are you sure?\n", policy);
	printf("(type 'yes' to confirm)? ");

	if (fgets(buf, sizeof(buf), stdin) == NULL) {
	    retval = EINVAL;
	    goto cleanup;
	}

	if (strcmp(buf, yes)) {
	    exit_status++;
	    goto cleanup;
	}
    }

    if ((retval = init_ldap_realm (argc, argv)))
        goto err_nomsg;

    if ((retval = krb5_ldap_read_policy(util_context, policy, &policyparams, &mask)))
	goto cleanup;


    if ((retval = krb5_ldap_delete_policy(util_context, policy)))
	goto cleanup;

    printf("** policy object '%s' deleted.\n", policy);
    goto cleanup;


err_usage:
    print_usage = TRUE;

err_nomsg:
    no_msg = TRUE;

cleanup:
    /* Clean-up structure */
    krb5_ldap_free_policy (util_context, policyparams);

    if (policy) {
	free (policy);
    }

    if (print_usage) {
	db_usage(DESTROY_POLICY);
    }

    if (retval) {
	if (!no_msg)
	    com_err(me, retval, "while destroying policy object");

	exit_status++;
    }

    return;
}


/*
 * This function will modify the attributes of a given ticket
 * policy object.
 */
void
kdb5_ldap_modify_policy(argc, argv)
    int argc;
    char *argv[];
{
    char *me = argv[0];
    krb5_error_code retval = 0;
    krb5_ldap_policy_params *policyparams = NULL;
    krb5_boolean print_usage = FALSE;
    krb5_boolean no_msg = FALSE;
    char *policy = NULL;
    int in_mask = 0, out_mask = 0;
    time_t date = 0;
    time_t now = 0;
    int i = 0;

    /* Check for number of arguments -- minimum is 3
       since atleast one parameter should be given in
       addition to 'modify_policy' and policy DN */
    if ((argc < 3) || (argc > 16)) {
	goto err_usage;
    }

    /* Parse all arguments, only to pick up policy DN (Pass 1) */
    for (i = 1; i < argc; i++) {
	/* Skip arguments next to 'maxtktlife'
	   and 'maxrenewlife' arguments */
	if (!strcmp(argv[i], "-maxtktlife")) {
	    ++i;
	} else if (!strcmp(argv[i], "-maxrenewlife")) {
	    ++i;
	}
	/* Do nothing for ticket flag arguments */
	else if (!strcmp((argv[i] + 1), "allow_postdated") ||
		 !strcmp((argv[i] + 1), "allow_forwardable") ||
		 !strcmp((argv[i] + 1), "allow_renewable") ||
		 !strcmp((argv[i] + 1), "allow_proxiable") ||
		 !strcmp((argv[i] + 1), "allow_dup_skey") ||
		 !strcmp((argv[i] + 1), "requires_preauth") ||
		 !strcmp((argv[i] + 1), "requires_hwauth") ||
		 !strcmp((argv[i] + 1), "allow_svr") ||
		 !strcmp((argv[i] + 1), "allow_tgs_req") ||
		 !strcmp((argv[i] + 1), "allow_tix") ||
		 !strcmp((argv[i] + 1), "needchange") ||
		 !strcmp((argv[i] + 1), "password_changing_service")) {
	} else { /* Any other argument must be policy DN */
	    /* First check if policy DN is already provided --
	       if so, there's a usage error */
            if (policy != NULL)
		goto err_usage;

	    /* If not present already, fill up policy DN */
            policy = strdup(argv[i]);
            if (policy == NULL) {
		retval = ENOMEM;
		com_err(me, retval, "while modifying policy object");
		goto err_nomsg;
	    }
	}
    }

    if (policy == NULL)
	goto err_usage;

    if ((retval = init_ldap_realm (argc, argv)))
	goto cleanup;

    retval = krb5_ldap_read_policy(util_context, policy, &policyparams, &in_mask);
    if (retval) {
        com_err(me, retval, "while reading information of policy '%s'", policy);
	goto err_nomsg;
    }

    /* Get current time */
    time (&now);

    /* Parse all arguments, but skip policy DN (Pass 2) */
    for (i = 1; i < argc; i++) {
	if (!strcmp(argv[i], "-maxtktlife")) {
	    if (++i > argc - 1)
		goto err_usage;

	    date = get_date(argv[i]);
	    if (date == (time_t)(-1)) {
		retval = EINVAL;
		com_err (me, retval, "while providing time specification");
		goto err_nomsg;
	    }

	    policyparams->maxtktlife = date - now;

	    out_mask |= LDAP_POLICY_MAXTKTLIFE;
	} else if (!strcmp(argv[i], "-maxrenewlife")) {
	    if (++i > argc - 1)
		goto err_usage;

	    date = get_date(argv[i]);
	    if (date == (time_t)(-1)) {
		retval = EINVAL;
		com_err (me, retval, "while providing time specification");
		goto err_nomsg;
	    }

	    policyparams->maxrenewlife = date - now;

	    out_mask |= LDAP_POLICY_MAXRENEWLIFE;
	} else if (!strcmp((argv[i] + 1), "allow_postdated")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_POSTDATED);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_POSTDATED;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_forwardable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_FORWARDABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_FORWARDABLE;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_renewable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_RENEWABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_RENEWABLE;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_proxiable")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_PROXIABLE);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_PROXIABLE;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_dup_skey")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_DUP_SKEY);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_DUP_SKEY;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "requires_preauth")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_PRE_AUTH;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_PRE_AUTH);
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "requires_hwauth")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_HW_AUTH;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_HW_AUTH);
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_svr")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_SVR);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_SVR;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_tgs_req")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_TGT_BASED);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_TGT_BASED;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "allow_tix")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags &= (int)(~KRB5_KDB_DISALLOW_ALL_TIX);
	    else if (*(argv[i]) == '-')
		policyparams->tktflags |= KRB5_KDB_DISALLOW_ALL_TIX;
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "needchange")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_REQUIRES_PWCHANGE;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_REQUIRES_PWCHANGE);
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else if (!strcmp((argv[i] + 1), "password_changing_service")) {
	    if (*(argv[i]) == '+')
		policyparams->tktflags |= KRB5_KDB_PWCHANGE_SERVICE;
	    else if (*(argv[i]) == '-')
		policyparams->tktflags &= (int)(~KRB5_KDB_PWCHANGE_SERVICE);
	    else
		goto err_usage;

	    out_mask |= LDAP_POLICY_TKTFLAGS;
	} else {
	    /* Any other argument must be policy DN
	       -- skip it */
	}
    }

    /* Modify attributes of object */
    if ((retval = krb5_ldap_modify_policy(util_context, policyparams, out_mask)))
	goto cleanup;

    goto cleanup;

err_usage:
    print_usage = TRUE;

err_nomsg:
    no_msg = TRUE;

cleanup:
    /* Clean-up structure */
    krb5_ldap_free_policy (util_context, policyparams);

    if (policy)
        free (policy);

    if (print_usage)
	db_usage(MODIFY_POLICY);

    if (retval) {
	if (!no_msg)
	    com_err(me, retval, "while modifying policy object");

	exit_status++;
    }

    return;
}


/*
 * This function will display information about the given policy object,
 * fetching the information from the LDAP Server.
 */
void
kdb5_ldap_view_policy(argc, argv)
    int argc;
    char *argv[];
{
    char *me = argv[0];
    krb5_ldap_policy_params *policyparams = NULL;
    krb5_error_code retval = 0;
    krb5_boolean print_usage = FALSE;
    char *policy = NULL;
    int mask = 0;

    if (argc != 2) {
	goto err_usage;
    }

    policy = strdup(argv[1]);
    if (policy == NULL) {
	com_err(me, ENOMEM, "while viewing policy");
	exit_status++;
	goto cleanup;
    }

    if ((retval = init_ldap_realm (argc, argv)))
        goto cleanup;

    if ((retval = krb5_ldap_read_policy(util_context, policy, &policyparams, &mask))) {
	com_err(me, retval, "while viewing policy '%s'", policy);
	exit_status++;
	goto cleanup;
    }

    print_policy_params (policyparams, mask);

    goto cleanup;

err_usage:
    print_usage = TRUE;

cleanup:
    krb5_ldap_free_policy (util_context, policyparams);

    if (policy)
	free (policy);

    if (print_usage) {
	db_usage(VIEW_POLICY);
    }

    return;
}


/*
 * This function will print the policy object information to the
 * standard output.
 */
static void
print_policy_params(policyparams, mask)
    krb5_ldap_policy_params *policyparams;
    int mask;
{
    /* Print the policy DN */
    printf("%25s: %s\n", "Ticket policy", policyparams->policy);

    /* Print max. ticket life and max. renewable life, if present */
    if (mask & LDAP_POLICY_MAXTKTLIFE)
	printf("%25s: %s\n", "Maximum ticket life", strdur(policyparams->maxtktlife));
    if (mask & LDAP_POLICY_MAXRENEWLIFE)
	printf("%25s: %s\n", "Maximum renewable life", strdur(policyparams->maxrenewlife));

    /* Service flags are printed */
    printf("%25s: ", "Ticket flags");
    if (mask & LDAP_POLICY_TKTFLAGS) {
	int ticketflags = policyparams->tktflags;

	if (ticketflags & KRB5_KDB_DISALLOW_POSTDATED)
	    printf("%s ","DISALLOW_POSTDATED");

	if (ticketflags & KRB5_KDB_DISALLOW_FORWARDABLE)
	    printf("%s ","DISALLOW_FORWARDABLE");

	if (ticketflags & KRB5_KDB_DISALLOW_RENEWABLE)
	    printf("%s ","DISALLOW_RENEWABLE");

	if (ticketflags & KRB5_KDB_DISALLOW_PROXIABLE)
	    printf("%s ","DISALLOW_PROXIABLE");

	if (ticketflags & KRB5_KDB_DISALLOW_DUP_SKEY)
	    printf("%s ","DISALLOW_DUP_SKEY");

	if (ticketflags & KRB5_KDB_REQUIRES_PRE_AUTH)
	    printf("%s ","REQUIRES_PRE_AUTH");

	if (ticketflags & KRB5_KDB_REQUIRES_HW_AUTH)
	    printf("%s ","REQUIRES_HW_AUTH");

	if (ticketflags & KRB5_KDB_DISALLOW_SVR)
	    printf("%s ","DISALLOW_SVR");

	if (ticketflags & KRB5_KDB_DISALLOW_TGT_BASED)
	    printf("%s ","DISALLOW_TGT_BASED");

	if (ticketflags & KRB5_KDB_DISALLOW_ALL_TIX)
	    printf("%s ","DISALLOW_ALL_TIX");

	if (ticketflags & KRB5_KDB_REQUIRES_PWCHANGE)
	    printf("%s ","REQUIRES_PWCHANGE");

	if (ticketflags & KRB5_KDB_PWCHANGE_SERVICE)
	    printf("%s ","PWCHANGE_SERVICE");
    }
    printf("\n");

    return;
}


/*
 * This function will list the DNs of policy objects under a specific
 * sub-tree (entire tree by default)
 */
void kdb5_ldap_list_policies(argc, argv)
    int argc;
    char *argv[];
{
    char *me = argv[0];
    krb5_error_code retval = 0;
    krb5_boolean print_usage = FALSE;
    char *basedn = NULL;
    char **list = NULL;
    char **plist = NULL;

    /* Check for number of arguments */
    if ((argc != 1) && (argc != 3)) {
	goto err_usage;
    }

    if ((retval = init_ldap_realm (argc, argv)))
	goto cleanup;

    retval = krb5_ldap_list_policy(util_context, basedn, &list);
    if ((retval != 0) || (list == NULL))
	goto cleanup;

    for (plist = list; *plist != NULL; plist++) {
	printf("%s\n", *plist);
    }

    goto cleanup;

err_usage:
    print_usage = TRUE;

cleanup:
    if (list != NULL) {
	krb5_free_list_entries (list);
	free (list);
    }

    if (basedn)
	free (basedn);

    if (print_usage) {
	db_usage(LIST_POLICY);
    }

    if (retval) {
	com_err(me, retval, "while listing policy objects");
	exit_status++;
    }

    return;
}


/* Reproduced from kadmin.c, instead of linking
   the entire kadmin.o */
static char *strdur(duration)
    time_t duration;
{
    static char out[50];
    int neg, days, hours, minutes, seconds;

    if (duration < 0) {
	duration *= -1;
	neg = 1;
    } else
	neg = 0;
    days = duration / (24 * 3600);
    duration %= 24 * 3600;
    hours = duration / 3600;
    duration %= 3600;
    minutes = duration / 60;
    duration %= 60;
    seconds = duration;
    sprintf(out, "%s%d %s %02d:%02d:%02d", neg ? "-" : "",
	    days, days == 1 ? "day" : "days",
	    hours, minutes, seconds);
    return out;
}
