2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/kdb/kdb_ldap/ldap_pwd_policy.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 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "ldap_main.h"
2N/A#include "kdb_ldap.h"
2N/A#include "ldap_pwd_policy.h"
2N/A#include "ldap_err.h"
2N/A#include <libintl.h> /* Solaris Kerberos */
2N/A
2N/Astatic char *password_policy_attributes[] = { "cn", "krbmaxpwdlife", "krbminpwdlife",
2N/A "krbpwdmindiffchars", "krbpwdminlength",
2N/A "krbpwdhistorylength", "krbpwdmaxfailure",
2N/A "krbpwdfailurecountinterval",
2N/A "krbpwdlockoutduration", NULL };
2N/A
2N/A/*
2N/A * Function to create password policy object.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_create_password_policy(krb5_context context, osa_policy_ent_t policy)
2N/A{
2N/A krb5_error_code st=0;
2N/A LDAP *ld=NULL;
2N/A LDAPMod **mods={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 char **rdns=NULL, *strval[2]={NULL}, *policy_dn;
2N/A
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A /* validate the input parameters */
2N/A if (policy == NULL || policy->name == NULL)
2N/A return EINVAL;
2N/A
2N/A SETUP_CONTEXT();
2N/A GET_HANDLE();
2N/A
2N/A st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A
2N/A /* get the first component of the dn to set the cn attribute */
2N/A rdns = ldap_explode_dn(policy_dn, 1);
2N/A if (rdns == NULL) {
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("Invalid password policy DN syntax"));
2N/A goto cleanup;
2N/A }
2N/A
2N/A strval[0] = rdns[0];
2N/A if ((st=krb5_add_str_mem_ldap_mod(&mods, "cn", LDAP_MOD_ADD, strval)) != 0)
2N/A goto cleanup;
2N/A
2N/A strval[0] = "krbPwdPolicy";
2N/A if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0)
2N/A goto cleanup;
2N/A
2N/A if (((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxpwdlife", LDAP_MOD_ADD,
2N/A (signed) policy->pw_max_life)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbminpwdlife", LDAP_MOD_ADD,
2N/A (signed) policy->pw_min_life)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmindiffchars", LDAP_MOD_ADD,
2N/A (signed) policy->pw_min_classes)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_ADD,
2N/A (signed) policy->pw_min_length)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_ADD,
2N/A (signed) policy->pw_history_num)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmaxfailure", LDAP_MOD_ADD,
2N/A (signed) policy->pw_max_fail)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdfailurecountinterval", LDAP_MOD_ADD,
2N/A (signed) policy->pw_failcnt_interval)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdlockoutduration", LDAP_MOD_ADD,
2N/A (signed) policy->pw_lockout_duration)) != 0))
2N/A goto cleanup;
2N/A
2N/A /* password policy object creation */
2N/A if ((st=ldap_add_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
2N/A st = set_ldap_error (context, st, OP_ADD);
2N/A goto cleanup;
2N/A }
2N/A
2N/Acleanup:
2N/A if (rdns)
2N/A ldap_value_free(rdns);
2N/A
2N/A if (policy_dn != NULL)
2N/A free (policy_dn);
2N/A ldap_mods_free(mods, 1);
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A return(st);
2N/A}
2N/A
2N/A/*
2N/A * Function to modify password policy object.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_put_password_policy(krb5_context context, osa_policy_ent_t policy)
2N/A{
2N/A char *policy_dn;
2N/A krb5_error_code st=0;
2N/A LDAP *ld=NULL;
2N/A LDAPMod **mods=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
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A /* validate the input parameters */
2N/A if (policy == NULL || policy->name == NULL)
2N/A return EINVAL;
2N/A
2N/A SETUP_CONTEXT();
2N/A GET_HANDLE();
2N/A
2N/A st = krb5_ldap_name_to_policydn (context, policy->name, &policy_dn);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A
2N/A if (((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxpwdlife", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_max_life)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbminpwdlife", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_min_life)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmindiffchars", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_min_classes)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdminlength", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_min_length)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdhistorylength", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_history_num)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdmaxfailure", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_max_fail)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdfailurecountinterval", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_failcnt_interval)) != 0)
2N/A || ((st=krb5_add_int_mem_ldap_mod(&mods, "krbpwdlockoutduration", LDAP_MOD_REPLACE,
2N/A (signed) policy->pw_lockout_duration)) != 0))
2N/A goto cleanup;
2N/A
2N/A /* modify the password policy object. */
2N/A /*
2N/A * This will fail if the 'policy_dn' is anywhere other than under the realm
2N/A * container. This is correct behaviour. 'kdb5_ldap_util' will support
2N/A * management of only such policy objects.
2N/A */
2N/A if ((st=ldap_modify_ext_s(ld, policy_dn, mods, NULL, NULL)) != LDAP_SUCCESS) {
2N/A st = set_ldap_error (context, st, OP_MOD);
2N/A goto cleanup;
2N/A }
2N/A
2N/Acleanup:
2N/A if (policy_dn != NULL)
2N/A free (policy_dn);
2N/A ldap_mods_free(mods, 1);
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A return(st);
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Apopulate_policy(krb5_context context,
2N/A LDAP *ld,
2N/A LDAPMessage *ent,
2N/A char *pol_name,
2N/A osa_policy_ent_t pol_entry)
2N/A{
2N/A int st = 0;
2N/A char *pol_dn;
2N/A
2N/A pol_entry->name = strdup(pol_name);
2N/A CHECK_NULL(pol_entry->name);
2N/A pol_entry->version = 1;
2N/A
2N/A krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", &(pol_entry->pw_max_life));
2N/A krb5_ldap_get_value(ld, ent, "krbminpwdlife", &(pol_entry->pw_min_life));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", &(pol_entry->pw_min_classes));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdminlength", &(pol_entry->pw_min_length));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &(pol_entry->pw_history_num));
2N/A
2N/A krb5_ldap_get_value(ld, ent, "krbpwdmaxfailure", &(pol_entry->pw_max_fail));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdfailurecountinterval", &(pol_entry->pw_failcnt_interval));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdlockoutduration", &(pol_entry->pw_lockout_duration));
2N/A
2N/A /* Get the reference count */
2N/A pol_dn = ldap_get_dn(ld, ent);
2N/A st = krb5_ldap_get_reference_count (context, pol_dn, "krbPwdPolicyReference",
2N/A &(pol_entry->policy_refcnt), ld);
2N/A ldap_memfree(pol_dn);
2N/A
2N/Acleanup:
2N/A /* Solaris Kerberos: trying to avoid memory leaks */
2N/A if (st != 0) {
2N/A free(pol_entry->name);
2N/A pol_entry->name = NULL;
2N/A }
2N/A
2N/A return st;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_ldap_get_password_policy_from_dn(krb5_context context, char *pol_name,
2N/A char *pol_dn, osa_policy_ent_t *policy,
2N/A int *cnt)
2N/A{
2N/A krb5_error_code st=0, tempst=0;
2N/A LDAP *ld=NULL;
2N/A LDAPMessage *result=NULL,*ent=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
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A /* validate the input parameters */
2N/A if (pol_dn == NULL)
2N/A return EINVAL;
2N/A
2N/A *policy = NULL;
2N/A SETUP_CONTEXT();
2N/A GET_HANDLE();
2N/A
2N/A *cnt = 0;
2N/A *(policy) = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec));
2N/A if (*policy == NULL) {
2N/A st = ENOMEM;
2N/A goto cleanup;
2N/A }
2N/A memset(*policy, 0, sizeof(osa_policy_ent_rec));
2N/A
2N/A LDAP_SEARCH(pol_dn, LDAP_SCOPE_BASE, "(objectclass=krbPwdPolicy)", password_policy_attributes);
2N/A *cnt = 1;
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A (*policy)->name = strdup(name);
2N/A CHECK_NULL((*policy)->name);
2N/A (*policy)->version = 1;
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A ent=ldap_first_entry(ld, result);
2N/A if (ent != NULL) {
2N/A if ((st = populate_policy(context, ld, ent, pol_name, *policy)) != 0)
2N/A goto cleanup;
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", &((*policy)->pw_max_life));
2N/A krb5_ldap_get_value(ld, ent, "krbminpwdlife", &((*policy)->pw_min_life));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", &((*policy)->pw_min_classes));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdminlength", &((*policy)->pw_min_length));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &((*policy)->pw_history_num));
2N/A
2N/A /* Get the reference count */
2N/A st = krb5_ldap_get_reference_count (context,
2N/A name,
2N/A "krbPwdPolicyReference",
2N/A &(*policy)->policy_refcnt,
2N/A ld);
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A }
2N/A
2N/Acleanup:
2N/A ldap_msgfree(result);
2N/A if (st != 0) {
2N/A if (*policy != NULL) {
2N/A krb5_ldap_free_password_policy(context, *policy);
2N/A *policy = NULL;
2N/A }
2N/A }
2N/A
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A return st;
2N/A}
2N/A
2N/A/*
2N/A * Convert 'name' into a directory DN and call
2N/A * 'krb5_ldap_get_password_policy_from_dn'
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_get_password_policy(krb5_context context, char *name,
2N/A osa_policy_ent_t *policy, int *cnt)
2N/A{
2N/A krb5_error_code st = 0;
2N/A char *policy_dn = NULL;
2N/A
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
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 st = krb5_ldap_name_to_policydn(context, name, &policy_dn);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A
2N/A st = krb5_ldap_get_password_policy_from_dn(context, name, policy_dn, policy, cnt);
2N/A
2N/Acleanup:
2N/A if (policy_dn != NULL)
2N/A free (policy_dn);
2N/A return st;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_delete_password_policy(krb5_context context, char *policy)
2N/A{
2N/A int mask = 0;
2N/A char *policy_dn = NULL, *class[] = {"krbpwdpolicy", NULL};
2N/A krb5_error_code st=0;
2N/A LDAP *ld=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
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A /* validate the input parameters */
2N/A if (policy == NULL)
2N/A return EINVAL;
2N/A
2N/A SETUP_CONTEXT();
2N/A GET_HANDLE();
2N/A
2N/A st = krb5_ldap_name_to_policydn (context, policy, &policy_dn);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A
2N/A /* Ensure that the object is a password policy */
2N/A if ((st=checkattributevalue(ld, policy_dn, "objectclass", class, &mask)) != 0)
2N/A goto cleanup;
2N/A
2N/A if (mask == 0) {
2N/A st = KRB5_KDB_NOENTRY;
2N/A goto cleanup;
2N/A }
2N/A
2N/A if ((st=ldap_delete_ext_s(ld, policy_dn, NULL, NULL)) != LDAP_SUCCESS) {
2N/A st = set_ldap_error (context, st, OP_DEL);
2N/A goto cleanup;
2N/A }
2N/A
2N/Acleanup:
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A if (policy_dn != NULL)
2N/A free (policy_dn);
2N/A
2N/A return st;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_iterate_password_policy(krb5_context context, char *match_expr,
2N/A void (*func)(krb5_pointer, osa_policy_ent_t),
2N/A krb5_pointer func_arg)
2N/A{
2N/A osa_policy_ent_rec *entry=NULL;
2N/A char *policy=NULL;
2N/A krb5_error_code st=0, tempst=0;
2N/A LDAP *ld=NULL;
2N/A LDAPMessage *result=NULL, *ent=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
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A SETUP_CONTEXT();
2N/A GET_HANDLE();
2N/A
2N/A if (ldap_context->lrparams->realmdn == NULL) {
2N/A st = EINVAL;
2N/A goto cleanup;
2N/A }
2N/A
2N/A LDAP_SEARCH(ldap_context->lrparams->realmdn, LDAP_SCOPE_ONELEVEL, "(objectclass=krbpwdpolicy)", password_policy_attributes);
2N/A for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
2N/A krb5_boolean attr_present;
2N/A
2N/A st = krb5_ldap_get_string(ld, ent, "cn", &policy, &attr_present);
2N/A if (st != 0)
2N/A goto cleanup;
2N/A if (attr_present == FALSE)
2N/A continue;
2N/A
2N/A entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec));
2N/A CHECK_NULL(entry);
2N/A memset(entry, 0, sizeof(osa_policy_ent_rec));
2N/A if ((st = populate_policy(context, ld, ent, policy, entry)) != 0)
2N/A goto cleanup;
2N/A#if 0 /************** Begin IFDEF'ed OUT *******************************/
2N/A entry->name = policy;
2N/A entry->version = 1;
2N/A
2N/A krb5_ldap_get_value(ld, ent, "krbmaxpwdlife", &(entry->pw_max_life));
2N/A krb5_ldap_get_value(ld, ent, "krbminpwdlife", &(entry->pw_min_life));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdmindiffchars", &(entry->pw_min_classes));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdminlength", &(entry->pw_min_length));
2N/A krb5_ldap_get_value(ld, ent, "krbpwdhistorylength", &(entry->pw_history_num));
2N/A
2N/A /* Get the reference count */
2N/A st = krb5_ldap_get_reference_count (context,
2N/A policy,
2N/A "krbPwdPolicyReference",
2N/A &(entry->policy_refcnt),
2N/A ld);
2N/A#endif /**************** END IFDEF'ed OUT *******************************/
2N/A
2N/A (*func)(func_arg, entry);
2N/A /* XXX this will free policy so don't free it */
2N/A krb5_ldap_free_password_policy(context, entry);
2N/A entry = NULL;
2N/A }
2N/A ldap_msgfree(result);
2N/A
2N/Acleanup:
2N/A if (entry)
2N/A free (entry);
2N/A
2N/A krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
2N/A return st;
2N/A}
2N/A
2N/Avoid
2N/Akrb5_ldap_free_password_policy (context, entry)
2N/A krb5_context context;
2N/A osa_policy_ent_t entry;
2N/A{
2N/A if (entry) {
2N/A if (entry->name)
2N/A free(entry->name);
2N/A free(entry);
2N/A }
2N/A return;
2N/A}