2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/kdb/kdb_ldap/ldap_principal.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_principal.h"
2N/A#include "princ_xdr.h"
2N/A#include "ldap_err.h"
2N/A#include <libintl.h> /* Solaris Kerberos */
2N/A
2N/Astruct timeval timelimit = {300, 0}; /* 5 minutes */
2N/Achar *principal_attributes[] = { "krbprincipalname",
2N/A "krbcanonicalname",
2N/A "objectclass",
2N/A "krbprincipalkey",
2N/A "krbmaxrenewableage",
2N/A "krbmaxticketlife",
2N/A "krbticketflags",
2N/A "krbprincipalexpiration",
2N/A "krbticketpolicyreference",
2N/A "krbUpEnabled",
2N/A "krbpwdpolicyreference",
2N/A "krbpasswordexpiration",
2N/A "krbLastFailedAuth",
2N/A "krbLoginFailedCount",
2N/A "krbLastSuccessfulAuth",
2N/A#ifdef HAVE_EDIRECTORY
2N/A "loginexpirationtime",
2N/A "logindisabled",
2N/A#endif
2N/A "krbLastPwdChange",
2N/A "krbExtraData",
2N/A "krbObjectReferences",
2N/A "krbAllowedToDelegateTo",
2N/A NULL };
2N/A
2N/A/* Must match KDB_*_ATTR macros in ldap_principal.h. */
2N/Astatic char *attributes_set[] = { "krbmaxticketlife",
2N/A "krbmaxrenewableage",
2N/A "krbticketflags",
2N/A "krbprincipalexpiration",
2N/A "krbticketpolicyreference",
2N/A "krbUpEnabled",
2N/A "krbpwdpolicyreference",
2N/A "krbpasswordexpiration",
2N/A "krbprincipalkey",
2N/A "krblastpwdchange",
2N/A "krbextradata",
2N/A "krbLastSuccessfulAuth",
2N/A "krbLastFailedAuth",
2N/A "krbLoginFailedCount",
2N/A NULL };
2N/A
2N/Avoid
2N/Akrb5_dbe_free_contents(krb5_context context, krb5_db_entry *entry)
2N/A{
2N/A krb5_tl_data *tl_data_next=NULL;
2N/A krb5_tl_data *tl_data=NULL;
2N/A int i, j;
2N/A
2N/A if (entry->e_data)
2N/A free(entry->e_data);
2N/A if (entry->princ)
2N/A krb5_free_principal(context, entry->princ);
2N/A for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
2N/A tl_data_next = tl_data->tl_data_next;
2N/A if (tl_data->tl_data_contents)
2N/A free(tl_data->tl_data_contents);
2N/A free(tl_data);
2N/A }
2N/A if (entry->key_data) {
2N/A for (i = 0; i < entry->n_key_data; i++) {
2N/A for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
2N/A if (entry->key_data[i].key_data_length[j]) {
2N/A if (entry->key_data[i].key_data_contents[j]) {
2N/A memset(entry->key_data[i].key_data_contents[j],
2N/A 0,
2N/A (unsigned) entry->key_data[i].key_data_length[j]);
2N/A free (entry->key_data[i].key_data_contents[j]);
2N/A }
2N/A }
2N/A entry->key_data[i].key_data_contents[j] = NULL;
2N/A entry->key_data[i].key_data_length[j] = 0;
2N/A entry->key_data[i].key_data_type[j] = 0;
2N/A }
2N/A }
2N/A free(entry->key_data);
2N/A }
2N/A memset(entry, 0, sizeof(*entry));
2N/A return;
2N/A}
2N/A
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_free_principal(krb5_context kcontext, krb5_db_entry *entries,
2N/A int nentries)
2N/A{
2N/A register int i;
2N/A for (i = 0; i < nentries; i++)
2N/A krb5_dbe_free_contents(kcontext, &entries[i]);
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_iterate(krb5_context context, char *match_expr,
2N/A krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
2N/A krb5_pointer func_arg,
2N/A /* Solaris Kerberos: adding support for -rev/recurse flags */
2N/A char **db_args)
2N/A{
2N/A krb5_db_entry entry;
2N/A krb5_principal principal;
2N/A char **subtree=NULL, *princ_name=NULL, *realm=NULL, **values=NULL, *filter=NULL;
2N/A unsigned int tree=0, ntree=1, i=0;
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 char *default_match_expr = "*";
2N/A
2N/A /* Clear the global error string */
2N/A krb5_clear_error_message(context);
2N/A
2N/A /* Solaris Kerberos: adding support for -rev/recurse flags */
2N/A if (db_args) {
2N/A /* LDAP does not support db_args DB arguments for krb5_ldap_iterate */
2N/A krb5_set_error_message(context, EINVAL,
2N/A gettext("Unsupported argument \"%s\" for ldap"),
2N/A db_args[0]);
2N/A return EINVAL;
2N/A }
2N/A
2N/A memset(&entry, 0, sizeof(krb5_db_entry));
2N/A SETUP_CONTEXT();
2N/A
2N/A realm = ldap_context->lrparams->realm_name;
2N/A if (realm == NULL) {
2N/A realm = context->default_realm;
2N/A if (realm == NULL) {
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("Default realm not set"));
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * If no match_expr then iterate through all krb princs like the db2 plugin
2N/A */
2N/A if (match_expr == NULL)
2N/A match_expr = default_match_expr;
2N/A
2N/A if (asprintf(&filter, FILTER"%s))", match_expr) < 0)
2N/A filter = NULL;
2N/A CHECK_NULL(filter);
2N/A
2N/A if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0)
2N/A goto cleanup;
2N/A
2N/A GET_HANDLE();
2N/A
2N/A for (tree=0; tree < ntree; ++tree) {
2N/A
2N/A LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
2N/A for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
2N/A values=ldap_get_values(ld, ent, "krbcanonicalname");
2N/A if (values == NULL)
2N/A values=ldap_get_values(ld, ent, "krbprincipalname");
2N/A if (values != NULL) {
2N/A for (i=0; values[i] != NULL; ++i) {
2N/A if (krb5_ldap_parse_principal_name(values[i], &princ_name) != 0)
2N/A continue;
2N/A if (krb5_parse_name(context, princ_name, &principal) != 0)
2N/A continue;
2N/A if (is_principal_in_realm(ldap_context, principal) == 0) {
2N/A if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, principal,
2N/A &entry)) != 0)
2N/A goto cleanup;
2N/A (*func)(func_arg, &entry);
2N/A krb5_dbe_free_contents(context, &entry);
2N/A (void) krb5_free_principal(context, principal);
2N/A free(princ_name);
2N/A break;
2N/A }
2N/A (void) krb5_free_principal(context, principal);
2N/A free(princ_name);
2N/A }
2N/A ldap_value_free(values);
2N/A }
2N/A } /* end of for (ent= ... */
2N/A ldap_msgfree(result);
2N/A } /* end of for (tree= ... */
2N/A
2N/Acleanup:
2N/A if (filter)
2N/A free (filter);
2N/A
2N/A for (;ntree; --ntree)
2N/A if (subtree[ntree-1])
2N/A free (subtree[ntree-1]);
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/*
2N/A * delete a principal from the directory.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_ldap_delete_principal(krb5_context context,
2N/A krb5_const_principal searchfor, int *nentries)
2N/A{
2N/A char *user=NULL, *DN=NULL, *strval[10] = {NULL};
2N/A LDAPMod **mods=NULL;
2N/A LDAP *ld=NULL;
2N/A int j=0, ptype=0, pcount=0, attrsetmask=0;
2N/A krb5_error_code st=0;
2N/A krb5_boolean singleentry=FALSE;
2N/A KEY *secretkey=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 krb5_db_entry entries;
2N/A krb5_boolean more=0;
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 the principal info */
2N/A if ((st=krb5_ldap_get_principal(context, searchfor, 0, &entries, nentries, &more)) != 0 || *nentries == 0)
2N/A goto cleanup;
2N/A
2N/A if (((st=krb5_get_princ_type(context, &entries, &(ptype))) != 0) ||
2N/A ((st=krb5_get_attributes_mask(context, &entries, &(attrsetmask))) != 0) ||
2N/A ((st=krb5_get_princ_count(context, &entries, &(pcount))) != 0) ||
2N/A ((st=krb5_get_userdn(context, &entries, &(DN))) != 0))
2N/A goto cleanup;
2N/A
2N/A if (DN == NULL) {
2N/A st = EINVAL;
2N/A krb5_set_error_message(context, st, gettext("DN information missing"));
2N/A goto cleanup;
2N/A }
2N/A
2N/A GET_HANDLE();
2N/A
2N/A if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT) {
2N/A st = ldap_delete_ext_s(ld, DN, NULL, NULL);
2N/A if (st != LDAP_SUCCESS) {
2N/A st = set_ldap_error (context, st, OP_DEL);
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A if (((st=krb5_unparse_name(context, searchfor, &user)) != 0)
2N/A || ((st=krb5_ldap_unparse_principal_name(user)) != 0))
2N/A goto cleanup;
2N/A
2N/A memset(strval, 0, sizeof(strval));
2N/A strval[0] = user;
2N/A if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE,
2N/A strval)) != 0)
2N/A goto cleanup;
2N/A
2N/A singleentry = (pcount == 1) ? TRUE: FALSE;
2N/A if (singleentry == FALSE) {
2N/A if (secretkey != NULL) {
2N/A if ((st=krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_DELETE | LDAP_MOD_BVALUES,
2N/A secretkey->keys)) != 0)
2N/A goto cleanup;
2N/A }
2N/A } else {
2N/A /*
2N/A * If the Kerberos user principal to be deleted happens to be the last one associated
2N/A * with the directory user object, then it is time to delete the other kerberos
2N/A * specific attributes like krbmaxticketlife, i.e, unkerberize the directory user.
2N/A * From the attrsetmask value, identify the attributes set on the directory user
2N/A * object and delete them.
2N/A * NOTE: krbsecretkey attribute has per principal entries. There can be chances that the
2N/A * other principals' keys are exisiting/left-over. So delete all the values.
2N/A */
2N/A while (attrsetmask) {
2N/A if (attrsetmask & 1) {
2N/A if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE,
2N/A NULL)) != 0)
2N/A goto cleanup;
2N/A }
2N/A attrsetmask >>= 1;
2N/A ++j;
2N/A }
2N/A
2N/A /* the same should be done with the objectclass attributes */
2N/A {
2N/A char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL};
2N/A/* char *attrvalues[] = {"krbpwdpolicyrefaux", "krbticketpolicyaux", "krbprincipalaux", NULL}; */
2N/A int p, q, r=0, amask=0;
2N/A
2N/A if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0)
2N/A goto cleanup;
2N/A memset(strval, 0, sizeof(strval));
2N/A for (p=1, q=0; p<=4; p<<=1, ++q)
2N/A if (p & amask)
2N/A strval[r++] = attrvalues[q];
2N/A strval[r] = NULL;
2N/A if (r > 0) {
2N/A if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE,
2N/A strval)) != 0)
2N/A goto cleanup;
2N/A }
2N/A }
2N/A }
2N/A st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL);
2N/A if (st != LDAP_SUCCESS) {
2N/A st = set_ldap_error(context, st, OP_MOD);
2N/A goto cleanup;
2N/A }
2N/A }
2N/A
2N/Acleanup:
2N/A if (user)
2N/A free (user);
2N/A
2N/A if (DN)
2N/A free (DN);
2N/A
2N/A if (secretkey != NULL) {
2N/A int i=0;
2N/A while (i < secretkey->nkey) {
2N/A free (secretkey->keys[i]->bv_val);
2N/A free (secretkey->keys[i]);
2N/A ++i;
2N/A }
2N/A free (secretkey->keys);
2N/A free (secretkey);
2N/A }
2N/A
2N/A if (st == 0)
2N/A krb5_ldap_free_principal(context, &entries, *nentries);
2N/A
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/*
2N/A * Function: krb5_ldap_unparse_principal_name
2N/A *
2N/A * Purpose: Removes '\\' that comes before every occurence of '@'
2N/A * in the principal name component.
2N/A *
2N/A * Arguments:
2N/A * user_name (input/output) Principal name
2N/A *
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_unparse_principal_name(char *user_name)
2N/A{
2N/A char *in, *out;
2N/A
2N/A out = user_name;
2N/A for (in = user_name; *in; in++) {
2N/A if (*in == '\\' && *(in + 1) == '@')
2N/A continue;
2N/A *out++ = *in;
2N/A }
2N/A *out = '\0';
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Function: krb5_ldap_parse_principal_name
2N/A *
2N/A * Purpose: Inserts '\\' before every occurence of '@'
2N/A * in the principal name component.
2N/A *
2N/A * Arguments:
2N/A * i_princ_name (input) Principal name without '\\'
2N/A * o_princ_name (output) Principal name with '\\'
2N/A *
2N/A * Note: The caller has to free the memory allocated for o_princ_name.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_ldap_parse_principal_name(char *i_princ_name, char **o_princ_name)
2N/A{
2N/A const char *at_rlm_name, *p;
2N/A struct k5buf buf;
2N/A
2N/A at_rlm_name = strrchr(i_princ_name, '@');
2N/A if (!at_rlm_name) {
2N/A *o_princ_name = strdup(i_princ_name);
2N/A if (!o_princ_name)
2N/A return ENOMEM;
2N/A } else {
2N/A krb5int_buf_init_dynamic(&buf);
2N/A for (p = i_princ_name; p < at_rlm_name; p++) {
2N/A if (*p == '@')
2N/A krb5int_buf_add(&buf, "\\");
2N/A krb5int_buf_add_len(&buf, p, 1);
2N/A }
2N/A krb5int_buf_add(&buf, at_rlm_name);
2N/A *o_princ_name = krb5int_buf_data(&buf);
2N/A if (!*o_princ_name)
2N/A return ENOMEM;
2N/A }
2N/A return 0;
2N/A}