#include "ldap_main.h"
#include "kdb_ldap.h"
#include "ldap_principal.h"
#include "princ_xdr.h"
#include "ldap_err.h"
struct timeval timelimit = {300, 0};
char *principal_attributes[] = { "krbprincipalname",
"krbcanonicalname",
"objectclass",
"krbprincipalkey",
"krbmaxrenewableage",
"krbmaxticketlife",
"krbticketflags",
"krbprincipalexpiration",
"krbticketpolicyreference",
"krbUpEnabled",
"krbpwdpolicyreference",
"krbpasswordexpiration",
"krbLastFailedAuth",
"krbLoginFailedCount",
"krbLastSuccessfulAuth",
"nsAccountLock",
"krbLastPwdChange",
"krbLastAdminUnlock",
"krbPrincipalAuthInd",
"krbExtraData",
"krbObjectReferences",
"krbAllowedToDelegateTo",
"krbPwdHistory",
NULL };
static char *attributes_set[] = { "krbmaxticketlife",
"krbmaxrenewableage",
"krbticketflags",
"krbprincipalexpiration",
"krbticketpolicyreference",
"krbPrincipalAuthInd",
"krbpwdpolicyreference",
"krbpasswordexpiration",
"krbprincipalkey",
"krblastpwdchange",
"krbextradata",
"krbLastSuccessfulAuth",
"krbLastFailedAuth",
"krbLoginFailedCount",
"krbLastAdminUnlock",
"krbPwdHistory",
NULL };
static void
k5_free_key_data_contents(krb5_key_data *key)
{
int16_t i;
for (i = 0; i < key->key_data_ver; i++) {
zapfree(key->key_data_contents[i], key->key_data_length[i]);
key->key_data_contents[i] = NULL;
}
}
void
k5_free_key_data(krb5_int16 n_key_data, krb5_key_data *key_data)
{
int16_t i;
if (key_data == NULL)
return;
for (i = 0; i < n_key_data; i++)
k5_free_key_data_contents(&key_data[i]);
free(key_data);
}
void
krb5_dbe_free_contents(krb5_context context, krb5_db_entry *entry)
{
krb5_tl_data *tl_data_next=NULL;
krb5_tl_data *tl_data=NULL;
if (entry->e_data)
free(entry->e_data);
if (entry->princ)
krb5_free_principal(context, entry->princ);
for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
tl_data_next = tl_data->tl_data_next;
if (tl_data->tl_data_contents)
free(tl_data->tl_data_contents);
free(tl_data);
}
k5_free_key_data(entry->n_key_data, entry->key_data);
memset(entry, 0, sizeof(*entry));
return;
}
static krb5_error_code
iterate_entry(krb5_context context, krb5_ldap_context *ldap_context,
LDAP *ld, LDAPMessage *ent,
krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg)
{
krb5_error_code ret = 0;
krb5_principal cprinc = NULL, nprinc = NULL;
krb5_db_entry entry = { 0 }, *entptr;
char **canon = NULL, **names = NULL, **list;
size_t i;
canon = ldap_get_values(ld, ent, "krbCanonicalName");
names = ldap_get_values(ld, ent, "krbPrincipalName");
if (canon == NULL && names == NULL)
return 0;
list = (canon != NULL) ? canon : names;
for (i = 0; list[i] != NULL; i++) {
krb5_free_principal(context, nprinc);
nprinc = NULL;
ret = krb5_ldap_parse_name(context, list[i], &nprinc);
if (ret)
goto cleanup;
if (is_principal_in_realm(ldap_context, nprinc)) {
ret = populate_krb5_db_entry(context, ldap_context, ld, ent,
nprinc, &entry);
if (ret)
goto cleanup;
ret = (*func)(func_arg, &entry);
krb5_dbe_free_contents(context, &entry);
if (ret)
goto cleanup;
break;
}
}
if (canon != NULL && names != NULL) {
ret = krb5_ldap_parse_name(context, canon[0], &cprinc);
if (ret)
goto cleanup;
for (i = 0; names[i] != NULL; i++) {
if (strcmp(names[i], canon[0]) == 0)
continue;
krb5_free_principal(context, nprinc);
nprinc = NULL;
ret = krb5_ldap_parse_name(context, names[i], &nprinc);
if (ret)
goto cleanup;
ret = krb5_dbe_make_alias_entry(context, nprinc, cprinc, &entptr);
if (ret)
goto cleanup;
ret = (*func)(func_arg, entptr);
krb5_db_free_principal(context, entptr);
if (ret)
goto cleanup;
}
}
cleanup:
krb5_free_principal(context, cprinc);
krb5_free_principal(context, nprinc);
ldap_value_free(canon);
ldap_value_free(names);
return ret;
}
krb5_error_code
krb5_ldap_iterate(krb5_context context, char *match_expr,
krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg, krb5_flags iterflags)
{
krb5_db_entry entry;
char **subtree=NULL, *realm=NULL, *filter=NULL;
size_t tree=0, ntree=1;
krb5_error_code st=0, tempst=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;
char *default_match_expr = "*";
krb5_clear_error_message(context);
memset(&entry, 0, sizeof(krb5_db_entry));
SETUP_CONTEXT();
realm = ldap_context->lrparams->realm_name;
if (realm == NULL) {
realm = context->default_realm;
if (realm == NULL) {
st = EINVAL;
k5_setmsg(context, st, _("Default realm not set"));
goto cleanup;
}
}
if (match_expr == NULL)
match_expr = default_match_expr;
if (asprintf(&filter, FILTER"%s))", match_expr) < 0)
filter = NULL;
CHECK_NULL(filter);
if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntree)) != 0)
goto cleanup;
GET_HANDLE();
for (tree=0; tree < ntree; ++tree) {
LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes);
for (ent=ldap_first_entry(ld, result); ent != NULL; ent=ldap_next_entry(ld, ent)) {
st = iterate_entry(context, ldap_context, ld, ent, func, func_arg);
if (st)
goto cleanup;
}
ldap_msgfree(result);
result = NULL;
}
cleanup:
if (filter)
free (filter);
for (;ntree; --ntree)
if (subtree[ntree-1])
free (subtree[ntree-1]);
free(subtree);
ldap_msgfree(result);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
krb5_error_code
krb5_ldap_delete_principal(krb5_context context,
krb5_const_principal searchfor)
{
char *user=NULL, *DN=NULL, *strval[10] = {NULL};
LDAPMod **mods=NULL;
LDAP *ld=NULL;
size_t j=0;
int ptype=0, pcount=0, attrsetmask=0;
krb5_error_code st=0;
krb5_boolean singleentry=FALSE;
kdb5_dal_handle *dal_handle=NULL;
krb5_ldap_context *ldap_context=NULL;
krb5_ldap_server_handle *ldap_server_handle=NULL;
krb5_db_entry *entry = NULL;
krb5_clear_error_message(context);
SETUP_CONTEXT();
if ((st=krb5_ldap_get_principal(context, searchfor, 0, &entry)))
goto cleanup;
if (((st=krb5_get_princ_type(context, entry, &(ptype))) != 0) ||
((st=krb5_get_attributes_mask(context, entry, &(attrsetmask))) != 0) ||
((st=krb5_get_princ_count(context, entry, &(pcount))) != 0) ||
((st=krb5_get_userdn(context, entry, &(DN))) != 0))
goto cleanup;
if (DN == NULL) {
st = EINVAL;
k5_setmsg(context, st, _("DN information missing"));
goto cleanup;
}
GET_HANDLE();
if (ptype == KDB_STANDALONE_PRINCIPAL_OBJECT &&
(pcount == 1 ||
krb5_principal_compare(context, searchfor, entry->princ))) {
st = ldap_delete_ext_s(ld, DN, NULL, NULL);
if (st != LDAP_SUCCESS) {
st = set_ldap_error (context, st, OP_DEL);
goto cleanup;
}
} else {
st = krb5_ldap_unparse_name(context, searchfor, &user);
if (st)
goto cleanup;
memset(strval, 0, sizeof(strval));
strval[0] = user;
if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_DELETE,
strval)) != 0)
goto cleanup;
singleentry = (pcount == 1) ? TRUE: FALSE;
if (singleentry == TRUE) {
while (attrsetmask) {
if (attrsetmask & 1) {
if ((st=krb5_add_str_mem_ldap_mod(&mods, attributes_set[j], LDAP_MOD_DELETE,
NULL)) != 0)
goto cleanup;
}
attrsetmask >>= 1;
++j;
}
{
char *attrvalues[] = {"krbticketpolicyaux", "krbprincipalaux", NULL};
int p, q, r=0, amask=0;
if ((st=checkattributevalue(ld, DN, "objectclass", attrvalues, &amask)) != 0)
goto cleanup;
memset(strval, 0, sizeof(strval));
for (p=1, q=0; p<=4; p<<=1, ++q)
if (p & amask)
strval[r++] = attrvalues[q];
strval[r] = NULL;
if (r > 0) {
if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_DELETE,
strval)) != 0)
goto cleanup;
}
}
}
st=ldap_modify_ext_s(ld, DN, mods, NULL, NULL);
if (st != LDAP_SUCCESS) {
st = set_ldap_error(context, st, OP_MOD);
goto cleanup;
}
}
cleanup:
if (user)
free (user);
if (DN)
free (DN);
krb5_db_free_principal(context, entry);
ldap_mods_free(mods, 1);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
static inline krb5_error_code
is_standalone_principal(krb5_context kcontext, krb5_db_entry *entry, int *res)
{
krb5_error_code code;
code = krb5_get_princ_type(kcontext, entry, res);
if (!code)
*res = (*res == KDB_STANDALONE_PRINCIPAL_OBJECT) ? 1 : 0;
return code;
}
static krb5_error_code
rename_principal_rdn(krb5_context context, LDAP *ld, const char *dn,
const char *newprinc, char **newdn_out)
{
int ret;
char *newrdn = NULL;
*newdn_out = NULL;
ret = asprintf(&newrdn, "krbprincipalname=%s", newprinc);
if (ret < 0)
return ENOMEM;
ret = ldap_rename_s(ld, dn, newrdn, NULL, 0, NULL, NULL);
if (ret == -1) {
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ret);
ret = set_ldap_error(context, ret, OP_MOD);
goto cleanup;
}
ret = replace_rdn(context, dn, newrdn, newdn_out);
cleanup:
free(newrdn);
return ret;
}
krb5_error_code
krb5_ldap_rename_principal(krb5_context context, krb5_const_principal source,
krb5_const_principal target)
{
int is_standalone;
krb5_error_code st;
char *suser = NULL, *tuser = NULL, *strval[2], *dn = NULL, *newdn = NULL;
krb5_db_entry *entry = NULL;
krb5_kvno mkvno;
struct berval **bersecretkey = NULL;
kdb5_dal_handle *dal_handle = NULL;
krb5_ldap_context *ldap_context = NULL;
krb5_ldap_server_handle *ldap_server_handle = NULL;
LDAP *ld = NULL;
LDAPMod **mods = NULL;
krb5_clear_error_message(context);
SETUP_CONTEXT();
if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL)
return EINVAL;
GET_HANDLE();
st = krb5_ldap_get_principal(context, source, 0, &entry);
if (st)
goto cleanup;
st = is_standalone_principal(context, entry, &is_standalone);
if (st)
goto cleanup;
st = krb5_get_userdn(context, entry, &dn);
if (st)
goto cleanup;
if (dn == NULL) {
st = EINVAL;
k5_setmsg(context, st, _("dn information missing"));
goto cleanup;
}
st = krb5_ldap_unparse_name(context, source, &suser);
if (st)
goto cleanup;
st = krb5_ldap_unparse_name(context, target, &tuser);
if (st)
goto cleanup;
st = krb5_dbe_specialize_salt(context, entry);
if (st)
goto cleanup;
st = krb5_dbe_lookup_mkvno(context, entry, &mkvno);
if (st)
goto cleanup;
bersecretkey = krb5_encode_krbsecretkey(entry->key_data, entry->n_key_data,
mkvno);
if (bersecretkey == NULL) {
st = ENOMEM;
goto cleanup;
}
st = krb5_add_ber_mem_ldap_mod(&mods, "krbPrincipalKey",
LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
bersecretkey);
if (st != 0)
goto cleanup;
st = krb5_ldap_modify_ext(context, ld, dn, mods, OP_MOD);
if (st)
goto cleanup;
ldap_mods_free(mods, 1);
mods = NULL;
if (is_standalone) {
st = rename_principal_rdn(context, ld, dn, tuser, &newdn);
if (st)
goto cleanup;
free(dn);
dn = newdn;
newdn = NULL;
}
strval[0] = suser;
strval[1] = NULL;
st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_DELETE,
strval);
if (st)
goto cleanup;
strval[0] = tuser;
strval[1] = NULL;
if (!is_standalone) {
st = krb5_add_str_mem_ldap_mod(&mods, "krbPrincipalName", LDAP_MOD_ADD,
strval);
if (st)
goto cleanup;
}
st = krb5_add_str_mem_ldap_mod(&mods, "krbCanonicalName", LDAP_MOD_REPLACE,
strval);
if (st)
goto cleanup;
st = krb5_ldap_modify_ext(context, ld, dn, mods, OP_MOD);
if (st)
goto cleanup;
cleanup:
free(dn);
free(suser);
free(tuser);
free_berdata(bersecretkey);
krb5_db_free_principal(context, entry);
ldap_mods_free(mods, 1);
krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle);
return st;
}
krb5_error_code
krb5_ldap_unparse_name(krb5_context context, krb5_const_principal princ,
char **user_out)
{
krb5_error_code ret;
char *p, *q;
ret = krb5_unparse_name(context, princ, user_out);
if (ret)
return ret;
for (q = p = *user_out; *p != '\0'; p++) {
if (*p == '\\' && *(p + 1) == '@')
continue;
*q++ = *p;
}
*q = '\0';
return 0;
}
krb5_error_code
krb5_ldap_parse_name(krb5_context context, const char *username,
krb5_principal *out)
{
krb5_error_code ret;
const char *at_realm, *p;
char *princstr;
struct k5buf buf;
*out = NULL;
at_realm = strrchr(username, '@');
if (at_realm == NULL) {
princstr = strdup(username);
} else {
k5_buf_init_dynamic(&buf);
for (p = username; p < at_realm; p++) {
if (*p == '@')
k5_buf_add(&buf, "\\");
k5_buf_add_len(&buf, p, 1);
}
k5_buf_add(&buf, at_realm);
princstr = k5_buf_cstring(&buf);
}
if (princstr == NULL)
return ENOMEM;
ret = krb5_parse_name(context, princstr, out);
free(princstr);
return ret;
}