/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* plugins/kdb/ldap/libkdb_ldap/ldap_principal2.c */ /* * Copyright (C) 2016 by the Massachusetts Institute of Technology. * 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. * * 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 HOLDER 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. */ /* * 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. */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "ldap_main.h" #include "kdb_ldap.h" #include "ldap_principal.h" #include "princ_xdr.h" #include "ldap_tkt_policy.h" #include "ldap_pwd_policy.h" #include "ldap_err.h" #include #include extern char* principal_attributes[]; extern char* max_pwd_life_attr[]; static char * getstringtime(krb5_timestamp); krb5_error_code berval2tl_data(struct berval *in, krb5_tl_data **out) { *out = (krb5_tl_data *) malloc (sizeof (krb5_tl_data)); if (*out == NULL) return ENOMEM; (*out)->tl_data_length = in->bv_len - 2; (*out)->tl_data_contents = (krb5_octet *) malloc ((*out)->tl_data_length * sizeof (krb5_octet)); if ((*out)->tl_data_contents == NULL) { free (*out); return ENOMEM; } UNSTORE16_INT (in->bv_val, (*out)->tl_data_type); memcpy ((*out)->tl_data_contents, in->bv_val + 2, (*out)->tl_data_length); return 0; } /* * look up a principal in the directory. */ krb5_error_code krb5_ldap_get_principal(krb5_context context, krb5_const_principal searchfor, unsigned int flags, krb5_db_entry **entry_ptr) { char *user=NULL, *filter=NULL, *filtuser=NULL; unsigned int tree=0, ntrees=1, princlen=0; krb5_error_code tempst=0, st=0; char **values=NULL, **subtree=NULL, *cname=NULL; LDAP *ld=NULL; LDAPMessage *result=NULL, *ent=NULL; krb5_ldap_context *ldap_context=NULL; kdb5_dal_handle *dal_handle=NULL; krb5_ldap_server_handle *ldap_server_handle=NULL; krb5_principal cprinc=NULL; krb5_boolean found=FALSE; krb5_db_entry *entry = NULL; *entry_ptr = NULL; /* Clear the global error string */ krb5_clear_error_message(context); if (searchfor == NULL) return EINVAL; dal_handle = context->dal_handle; ldap_context = (krb5_ldap_context *) dal_handle->db_context; CHECK_LDAP_HANDLE(ldap_context); if (!is_principal_in_realm(ldap_context, searchfor)) { st = KRB5_KDB_NOENTRY; k5_setmsg(context, st, _("Principal does not belong to realm")); goto cleanup; } if ((st=krb5_unparse_name(context, searchfor, &user)) != 0) goto cleanup; if ((st=krb5_ldap_unparse_principal_name(user)) != 0) goto cleanup; filtuser = ldap_filter_correct(user); if (filtuser == NULL) { st = ENOMEM; goto cleanup; } princlen = strlen(FILTER) + strlen(filtuser) + 2 + 1; /* 2 for closing brackets */ if ((filter = malloc(princlen)) == NULL) { st = ENOMEM; goto cleanup; } snprintf(filter, princlen, FILTER"%s))", filtuser); if ((st = krb5_get_subtree_info(ldap_context, &subtree, &ntrees)) != 0) goto cleanup; GET_HANDLE(); for (tree=0; tree < ntrees && !found; ++tree) { LDAP_SEARCH(subtree[tree], ldap_context->lrparams->search_scope, filter, principal_attributes); for (ent=ldap_first_entry(ld, result); ent != NULL && !found; ent=ldap_next_entry(ld, ent)) { /* get the associated directory user information */ if ((values=ldap_get_values(ld, ent, "krbprincipalname")) != NULL) { int i; /* a wild-card in a principal name can return a list of kerberos principals. * Make sure that the correct principal is returned. * NOTE: a principalname k* in ldap server will return all the principals starting with a k */ for (i=0; values[i] != NULL; ++i) { if (strcmp(values[i], user) == 0) { found = TRUE; break; } } ldap_value_free(values); if (!found) /* no matching principal found */ continue; } if ((values=ldap_get_values(ld, ent, "krbcanonicalname")) != NULL) { if (values[0] && strcmp(values[0], user) != 0) { /* We matched an alias, not the canonical name. */ st = krb5_ldap_parse_principal_name(values[0], &cname); if (st != 0) goto cleanup; st = krb5_parse_name(context, cname, &cprinc); if (st != 0) goto cleanup; } ldap_value_free(values); if (!found) continue; } entry = k5alloc(sizeof(*entry), &st); if (entry == NULL) goto cleanup; if ((st = populate_krb5_db_entry(context, ldap_context, ld, ent, cprinc ? cprinc : searchfor, entry)) != 0) goto cleanup; } ldap_msgfree(result); result = NULL; } /* for (tree=0 ... */ if (found) { *entry_ptr = entry; entry = NULL; } else st = KRB5_KDB_NOENTRY; cleanup: ldap_msgfree(result); krb5_db_free_principal(context, entry); if (filter) free (filter); if (subtree) { for (; ntrees; --ntrees) if (subtree[ntrees-1]) free (subtree[ntrees-1]); free (subtree); } if (ldap_server_handle) krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); if (user) free(user); if (filtuser) free(filtuser); if (cname) free(cname); if (cprinc) krb5_free_principal(context, cprinc); return st; } typedef enum{ ADD_PRINCIPAL, MODIFY_PRINCIPAL } OPERATION; /* * ptype is creating confusions. Additionally the logic * surronding ptype is redundunt and can be achevied * with the help of dn and containerdn members. * so dropping the ptype member */ typedef struct _xargs_t { char *dn; char *linkdn; krb5_boolean dn_from_kbd; char *containerdn; char *tktpolicydn; }xargs_t; static void free_xargs(xargs_t xargs) { if (xargs.dn) free (xargs.dn); if (xargs.linkdn) free(xargs.linkdn); if (xargs.containerdn) free (xargs.containerdn); if (xargs.tktpolicydn) free (xargs.tktpolicydn); } static krb5_error_code process_db_args(krb5_context context, char **db_args, xargs_t *xargs, OPERATION optype) { int i=0; krb5_error_code st=0; char *arg=NULL, *arg_val=NULL; char **dptr=NULL; unsigned int arg_val_len=0; if (db_args) { for (i=0; db_args[i]; ++i) { arg = strtok_r(db_args[i], "=", &arg_val); arg = (arg != NULL) ? arg : ""; if (strcmp(arg, TKTPOLICY_ARG) == 0) { dptr = &xargs->tktpolicydn; } else { if (strcmp(arg, USERDN_ARG) == 0) { if (optype == MODIFY_PRINCIPAL || xargs->dn != NULL || xargs->containerdn != NULL || xargs->linkdn != NULL) { st = EINVAL; k5_setmsg(context, st, _("%s option not supported"), arg); goto cleanup; } dptr = &xargs->dn; } else if (strcmp(arg, CONTAINERDN_ARG) == 0) { if (optype == MODIFY_PRINCIPAL || xargs->dn != NULL || xargs->containerdn != NULL) { st = EINVAL; k5_setmsg(context, st, _("%s option not supported"), arg); goto cleanup; } dptr = &xargs->containerdn; } else if (strcmp(arg, LINKDN_ARG) == 0) { if (xargs->dn != NULL || xargs->linkdn != NULL) { st = EINVAL; k5_setmsg(context, st, _("%s option not supported"), arg); goto cleanup; } dptr = &xargs->linkdn; } else { st = EINVAL; k5_setmsg(context, st, _("unknown option: %s"), arg); goto cleanup; } xargs->dn_from_kbd = TRUE; if (arg_val == NULL || strlen(arg_val) == 0) { st = EINVAL; k5_setmsg(context, st, _("%s option value missing"), arg); goto cleanup; } } if (arg_val == NULL) { st = EINVAL; k5_setmsg(context, st, _("%s option value missing"), arg); goto cleanup; } arg_val_len = strlen(arg_val) + 1; if (strcmp(arg, TKTPOLICY_ARG) == 0) { if ((st = krb5_ldap_name_to_policydn (context, arg_val, dptr)) != 0) goto cleanup; } else { *dptr = k5memdup(arg_val, arg_val_len, &st); if (*dptr == NULL) goto cleanup; } } } cleanup: return st; } krb5int_access accessor; static krb5_error_code asn1_encode_sequence_of_keys(krb5_key_data *key_data, krb5_int16 n_key_data, krb5_int32 mkvno, krb5_data **code) { krb5_error_code err; ldap_seqof_key_data val; /* * This should be pushed back into other library initialization * code. */ err = kldap_ensure_initialized (); if (err) return err; val.key_data = key_data; val.n_key_data = n_key_data; val.mkvno = mkvno; val.kvno = key_data[0].key_data_kvno; return accessor.asn1_ldap_encode_sequence_of_keys(&val, code); } static krb5_error_code asn1_decode_sequence_of_keys(krb5_data *in, ldap_seqof_key_data *out) { krb5_error_code err; ldap_seqof_key_data *p; int i; memset(out, 0, sizeof(*out)); /* * This should be pushed back into other library initialization * code. */ err = kldap_ensure_initialized (); if (err) return err; err = accessor.asn1_ldap_decode_sequence_of_keys(in, &p); if (err) return err; /* Set kvno and key_data_ver in each key_data element. */ for (i = 0; i < p->n_key_data; i++) { p->key_data[i].key_data_kvno = p->kvno; /* The decoder sets key_data_ver to 1 if no salt is present, but leaves * it at 0 if salt is present. */ if (p->key_data[i].key_data_ver == 0) p->key_data[i].key_data_ver = 2; } *out = *p; free(p); return 0; } /* * Free a NULL-terminated struct berval *array[] and all its contents. * Does not set array to NULL after freeing it. */ void free_berdata(struct berval **array) { int i; if (array != NULL) { for (i = 0; array[i] != NULL; i++) { if (array[i]->bv_val != NULL) free(array[i]->bv_val); free(array[i]); } free(array); } } /* * Encode krb5_key_data into a berval struct for insertion into LDAP. */ static krb5_error_code encode_keys(krb5_key_data *key_data_in, int n_key_data, krb5_kvno mkvno, struct berval **bval_out) { krb5_error_code err = 0; int i; krb5_key_data *key_data = NULL; struct berval *bval = NULL; krb5_data *code; *bval_out = NULL; if (n_key_data <= 0) { err = EINVAL; goto cleanup; } /* Make a shallow copy of the key data so we can alter it. */ key_data = k5calloc(n_key_data, sizeof(*key_data), &err); if (key_data == NULL) goto cleanup; memcpy(key_data, key_data_in, n_key_data * sizeof(*key_data)); /* Unpatched krb5 1.11 and 1.12 cannot decode KrbKey sequences with no salt * field. For compatibility, always encode a salt field. */ for (i = 0; i < n_key_data; i++) { if (key_data[i].key_data_ver == 1) { key_data[i].key_data_ver = 2; key_data[i].key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL; key_data[i].key_data_length[1] = 0; key_data[i].key_data_contents[1] = NULL; } } bval = k5alloc(sizeof(struct berval), &err); if (bval == NULL) goto cleanup; err = asn1_encode_sequence_of_keys(key_data, n_key_data, mkvno, &code); if (err) goto cleanup; /* Steal the data pointer from code for bval and discard code. */ bval->bv_len = code->length; bval->bv_val = code->data; free(code); *bval_out = bval; bval = NULL; cleanup: free(key_data); free(bval); return err; } /* Decoding ASN.1 encoded key */ struct berval ** krb5_encode_krbsecretkey(krb5_key_data *key_data, int n_key_data, krb5_kvno mkvno) { struct berval **ret = NULL; int currkvno; int num_versions = 0; int i, j, last; krb5_error_code err = 0; if (n_key_data < 0) return NULL; /* Find the number of key versions */ if (n_key_data > 0) { for (i = 0, num_versions = 1; i < n_key_data - 1; i++) { if (key_data[i].key_data_kvno != key_data[i + 1].key_data_kvno) num_versions++; } } ret = calloc(num_versions + 1, sizeof(struct berval *)); if (ret == NULL) { err = ENOMEM; goto cleanup; } ret[num_versions] = NULL; /* n_key_data may be 0 if a principal is created without a key. */ if (n_key_data == 0) goto cleanup; currkvno = key_data[0].key_data_kvno; for (i = 0, last = 0, j = 0; i < n_key_data; i++) { if (i == n_key_data - 1 || key_data[i + 1].key_data_kvno != currkvno) { err = encode_keys(key_data + last, (krb5_int16)i - last + 1, mkvno, &ret[j]); if (err) goto cleanup; j++; last = i + 1; if (i < n_key_data - 1) currkvno = key_data[i + 1].key_data_kvno; } } cleanup: if (err != 0) { free_berdata(ret); ret = NULL; } return ret; } /* * Encode a principal's key history for insertion into ldap. */ static struct berval ** krb5_encode_histkey(osa_princ_ent_rec *princ_ent) { unsigned int i; krb5_error_code err = 0; struct berval **ret = NULL; if (princ_ent->old_key_len <= 0) return NULL; ret = k5calloc(princ_ent->old_key_len + 1, sizeof(struct berval *), &err); if (ret == NULL) goto cleanup; for (i = 0; i < princ_ent->old_key_len; i++) { if (princ_ent->old_keys[i].n_key_data <= 0) { err = EINVAL; goto cleanup; } err = encode_keys(princ_ent->old_keys[i].key_data, princ_ent->old_keys[i].n_key_data, princ_ent->admin_history_kvno, &ret[i]); if (err) goto cleanup; } ret[princ_ent->old_key_len] = NULL; cleanup: if (err != 0) { free_berdata(ret); ret = NULL; } return ret; } static krb5_error_code tl_data2berval (krb5_tl_data *in, struct berval **out) { *out = (struct berval *) malloc (sizeof (struct berval)); if (*out == NULL) return ENOMEM; (*out)->bv_len = in->tl_data_length + 2; (*out)->bv_val = (char *) malloc ((*out)->bv_len); if ((*out)->bv_val == NULL) { free (*out); return ENOMEM; } STORE16_INT((*out)->bv_val, in->tl_data_type); memcpy ((*out)->bv_val + 2, in->tl_data_contents, in->tl_data_length); return 0; } /* Parse the "require_auth" string for auth indicators, adding them to the * krbPrincipalAuthInd attribute. */ static krb5_error_code update_ldap_mod_auth_ind(krb5_context context, krb5_db_entry *entry, LDAPMod ***mods) { int i = 0; krb5_error_code ret; char *auth_ind = NULL; char *strval[10] = { 0 }; char *ai, *ai_save = NULL; int mask, sv_num = sizeof(strval) / sizeof(*strval); ret = krb5_dbe_get_string(context, entry, KRB5_KDB_SK_REQUIRE_AUTH, &auth_ind); if (ret) return ret; if (auth_ind == NULL) { /* If we know krbPrincipalAuthInd attributes are present from loading * the entry, delete them. */ ret = krb5_get_attributes_mask(context, entry, &mask); if (!ret && (mask & KDB_AUTH_IND_ATTR)) { return krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd", LDAP_MOD_DELETE, NULL); } return 0; } ai = strtok_r(auth_ind, " ", &ai_save); while (ai != NULL && i < sv_num) { strval[i++] = ai; ai = strtok_r(NULL, " ", &ai_save); } ret = krb5_add_str_mem_ldap_mod(mods, "krbPrincipalAuthInd", LDAP_MOD_REPLACE, strval); krb5_dbe_free_string(context, auth_ind); return ret; } static krb5_error_code check_dn_in_container(krb5_context context, const char *dn, char *const *subtrees, unsigned int ntrees) { unsigned int i; size_t dnlen = strlen(dn), stlen; for (i = 0; i < ntrees; i++) { if (subtrees[i] == NULL || *subtrees[i] == '\0') return 0; stlen = strlen(subtrees[i]); if (dnlen >= stlen && strcasecmp(dn + dnlen - stlen, subtrees[i]) == 0 && (dnlen == stlen || dn[dnlen - stlen - 1] == ',')) return 0; } k5_setmsg(context, EINVAL, _("DN is out of the realm subtree")); return EINVAL; } static krb5_error_code check_dn_exists(krb5_context context, krb5_ldap_server_handle *ldap_server_handle, const char *dn, krb5_boolean nonkrb_only) { krb5_error_code st = 0, tempst; krb5_ldap_context *ldap_context = context->dal_handle->db_context; LDAP *ld = ldap_server_handle->ldap_handle; LDAPMessage *result = NULL, *ent; char *attrs[] = { "krbticketpolicyreference", "krbprincipalname", NULL }; char **values; LDAP_SEARCH_1(dn, LDAP_SCOPE_BASE, 0, attrs, IGNORE_STATUS); if (st != LDAP_SUCCESS) return set_ldap_error(context, st, OP_SEARCH); ent = ldap_first_entry(ld, result); CHECK_NULL(ent); values = ldap_get_values(ld, ent, "krbticketpolicyreference"); if (values != NULL) ldap_value_free(values); values = ldap_get_values(ld, ent, "krbprincipalname"); if (values != NULL) { ldap_value_free(values); if (nonkrb_only) { st = EINVAL; k5_setmsg(context, st, _("ldap object is already kerberized")); goto cleanup; } } cleanup: ldap_msgfree(result); return st; } static krb5_error_code validate_xargs(krb5_context context, krb5_ldap_server_handle *ldap_server_handle, const xargs_t *xargs, const char *standalone_dn, char *const *subtrees, unsigned int ntrees) { krb5_error_code st; if (xargs->dn != NULL) { /* The supplied dn must be within a realm container. */ st = check_dn_in_container(context, xargs->dn, subtrees, ntrees); if (st) return st; /* The supplied dn must exist without Kerberos attributes. */ st = check_dn_exists(context, ldap_server_handle, xargs->dn, TRUE); if (st) return st; } if (xargs->linkdn != NULL) { /* The supplied linkdn must be within a realm container. */ st = check_dn_in_container(context, xargs->linkdn, subtrees, ntrees); if (st) return st; /* The supplied linkdn must exist. */ st = check_dn_exists(context, ldap_server_handle, xargs->linkdn, FALSE); if (st) return st; } if (xargs->containerdn != NULL && standalone_dn != NULL) { /* standalone_dn (likely composed using containerdn) must be within a * container. */ st = check_dn_in_container(context, standalone_dn, subtrees, ntrees); if (st) return st; } return 0; } krb5_error_code krb5_ldap_put_principal(krb5_context context, krb5_db_entry *entry, char **db_args) { int l=0, kerberos_principal_object_type=0; unsigned int ntrees=0, tre=0; krb5_error_code st=0, tempst=0; LDAP *ld=NULL; LDAPMessage *result=NULL, *ent=NULL; char **subtreelist = NULL; char *user=NULL, *subtree=NULL, *principal_dn=NULL; char *strval[10]={NULL}, errbuf[1024]; char *filtuser=NULL; struct berval **bersecretkey=NULL; LDAPMod **mods=NULL; krb5_boolean create_standalone=FALSE; krb5_boolean establish_links=FALSE; char *standalone_principal_dn=NULL; krb5_tl_data *tl_data=NULL; krb5_key_data **keys=NULL; kdb5_dal_handle *dal_handle=NULL; krb5_ldap_context *ldap_context=NULL; krb5_ldap_server_handle *ldap_server_handle=NULL; osa_princ_ent_rec princ_ent = {0}; xargs_t xargs = {0}; char *polname = NULL; OPERATION optype; krb5_boolean found_entry = FALSE; /* Clear the global error string */ krb5_clear_error_message(context); SETUP_CONTEXT(); if (ldap_context->lrparams == NULL || ldap_context->container_dn == NULL) return EINVAL; /* get ldap handle */ GET_HANDLE(); if (!is_principal_in_realm(ldap_context, entry->princ)) { st = EINVAL; k5_setmsg(context, st, _("Principal does not belong to the default realm")); goto cleanup; } /* get the principal information to act on */ if (((st=krb5_unparse_name(context, entry->princ, &user)) != 0) || ((st=krb5_ldap_unparse_principal_name(user)) != 0)) goto cleanup; filtuser = ldap_filter_correct(user); if (filtuser == NULL) { st = ENOMEM; goto cleanup; } /* Identity the type of operation, it can be * add principal or modify principal. * hack if the entry->mask has KRB_PRINCIPAL flag set * then it is a add operation */ if (entry->mask & KADM5_PRINCIPAL) optype = ADD_PRINCIPAL; else optype = MODIFY_PRINCIPAL; if (((st=krb5_get_princ_type(context, entry, &kerberos_principal_object_type)) != 0) || ((st=krb5_get_userdn(context, entry, &principal_dn)) != 0)) goto cleanup; if ((st=process_db_args(context, db_args, &xargs, optype)) != 0) goto cleanup; if (entry->mask & KADM5_LOAD) { unsigned int tree = 0; int numlentries = 0; char *filter = NULL; /* A load operation is special, will do a mix-in (add krbprinc * attrs to a non-krb object entry) if an object exists with a * matching krbprincipalname attribute so try to find existing * object and set principal_dn. This assumes that the * krbprincipalname attribute is unique (only one object entry has * a particular krbprincipalname attribute). */ if (asprintf(&filter, FILTER"%s))", filtuser) < 0) { filter = NULL; st = ENOMEM; goto cleanup; } /* get the current subtree list */ if ((st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees)) != 0) goto cleanup; found_entry = FALSE; /* search for entry with matching krbprincipalname attribute */ for (tree = 0; found_entry == FALSE && tree < ntrees; ++tree) { if (principal_dn == NULL) { LDAP_SEARCH_1(subtreelist[tree], ldap_context->lrparams->search_scope, filter, principal_attributes, IGNORE_STATUS); } else { /* just look for entry with principal_dn */ LDAP_SEARCH_1(principal_dn, LDAP_SCOPE_BASE, filter, principal_attributes, IGNORE_STATUS); } if (st == LDAP_SUCCESS) { numlentries = ldap_count_entries(ld, result); if (numlentries > 1) { free(filter); st = EINVAL; k5_setmsg(context, st, _("operation can not continue, more than one " "entry with principal name \"%s\" found"), user); goto cleanup; } else if (numlentries == 1) { found_entry = TRUE; if (principal_dn == NULL) { ent = ldap_first_entry(ld, result); if (ent != NULL) { /* setting principal_dn will cause that entry to be modified further down */ if ((principal_dn = ldap_get_dn(ld, ent)) == NULL) { ldap_get_option (ld, LDAP_OPT_RESULT_CODE, &st); st = set_ldap_error (context, st, 0); free(filter); goto cleanup; } } } } } else if (st != LDAP_NO_SUCH_OBJECT) { /* could not perform search, return with failure */ st = set_ldap_error (context, st, 0); free(filter); goto cleanup; } ldap_msgfree(result); result = NULL; /* * If it isn't found then assume a standalone princ entry is to * be created. */ } /* end for (tree = 0; principal_dn == ... */ free(filter); if (found_entry == FALSE && principal_dn != NULL) { /* * if principal_dn is null then there is code further down to * deal with setting standalone_principal_dn. Also note that * this will set create_standalone true for * non-mix-in entries which is okay if loading from a dump. */ create_standalone = TRUE; standalone_principal_dn = strdup(principal_dn); CHECK_NULL(standalone_principal_dn); } } /* end if (entry->mask & KADM5_LOAD */ /* time to generate the DN information with the help of * containerdn, principalcontainerreference or * realmcontainerdn information */ if (principal_dn == NULL && xargs.dn == NULL) { /* creation of standalone principal */ /* get the subtree information */ if (entry->princ->length == 2 && entry->princ->data[0].length == strlen("krbtgt") && strncmp(entry->princ->data[0].data, "krbtgt", entry->princ->data[0].length) == 0) { /* if the principal is a inter-realm principal, always created in the realm container */ subtree = strdup(ldap_context->lrparams->realmdn); } else if (xargs.containerdn) { if ((st=checkattributevalue(ld, xargs.containerdn, NULL, NULL, NULL)) != 0) { if (st == KRB5_KDB_NOENTRY || st == KRB5_KDB_CONSTRAINT_VIOLATION) { int ost = st; st = EINVAL; k5_wrapmsg(context, ost, st, _("'%s' not found"), xargs.containerdn); } goto cleanup; } subtree = strdup(xargs.containerdn); } else if (ldap_context->lrparams->containerref && strlen(ldap_context->lrparams->containerref) != 0) { /* * Here the subtree should be changed with * principalcontainerreference attribute value */ subtree = strdup(ldap_context->lrparams->containerref); } else { subtree = strdup(ldap_context->lrparams->realmdn); } CHECK_NULL(subtree); if (asprintf(&standalone_principal_dn, "krbprincipalname=%s,%s", filtuser, subtree) < 0) standalone_principal_dn = NULL; CHECK_NULL(standalone_principal_dn); /* * free subtree when you are done using the subtree * set the boolean create_standalone to TRUE */ create_standalone = TRUE; free(subtree); subtree = NULL; } /* * If the DN information is presented by the user, time to * validate the input to ensure that the DN falls under * any of the subtrees */ if (xargs.dn_from_kbd == TRUE) { /* Get the current subtree list if we haven't already done so. */ if (subtreelist == NULL) { st = krb5_get_subtree_info(ldap_context, &subtreelist, &ntrees); if (st) goto cleanup; } st = validate_xargs(context, ldap_server_handle, &xargs, standalone_principal_dn, subtreelist, ntrees); if (st) goto cleanup; } if (xargs.linkdn != NULL) { /* * link information can be changed using modprinc. * However, link information can be changed only on the * standalone kerberos principal objects. A standalone * kerberos principal object is of type krbprincipal * structural objectclass. * * NOTE: kerberos principals on an ldap object can't be * linked to other ldap objects. */ if (optype == MODIFY_PRINCIPAL && kerberos_principal_object_type != KDB_STANDALONE_PRINCIPAL_OBJECT) { st = EINVAL; snprintf(errbuf, sizeof(errbuf), _("link information can not be set/updated as the " "kerberos principal belongs to an ldap object")); k5_setmsg(context, st, "%s", errbuf); goto cleanup; } /* * Check the link information. If there is already a link * existing then this operation is not allowed. */ { char **linkdns=NULL; int j=0; if ((st=krb5_get_linkdn(context, entry, &linkdns)) != 0) { snprintf(errbuf, sizeof(errbuf), _("Failed getting object references")); k5_setmsg(context, st, "%s", errbuf); goto cleanup; } if (linkdns != NULL) { st = EINVAL; snprintf(errbuf, sizeof(errbuf), _("kerberos principal is already linked to a ldap " "object")); k5_setmsg(context, st, "%s", errbuf); for (j=0; linkdns[j] != NULL; ++j) free (linkdns[j]); free (linkdns); goto cleanup; } } establish_links = TRUE; } if (entry->mask & KADM5_LAST_SUCCESS) { memset(strval, 0, sizeof(strval)); if ((strval[0]=getstringtime(entry->last_success)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastSuccessfulAuth", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free (strval[0]); } if (entry->mask & KADM5_LAST_FAILED) { memset(strval, 0, sizeof(strval)); if ((strval[0]=getstringtime(entry->last_failed)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastFailedAuth", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free(strval[0]); } if (entry->mask & KADM5_FAIL_AUTH_COUNT) { krb5_kvno fail_auth_count; fail_auth_count = entry->fail_auth_count; if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) fail_auth_count++; st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_REPLACE, fail_auth_count); if (st != 0) goto cleanup; } else if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) { int attr_mask = 0; krb5_boolean has_fail_count; /* Check if the krbLoginFailedCount attribute exists. (Through * krb5 1.8.1, it wasn't set in new entries.) */ st = krb5_get_attributes_mask(context, entry, &attr_mask); if (st != 0) goto cleanup; has_fail_count = ((attr_mask & KDB_FAIL_AUTH_COUNT_ATTR) != 0); /* * If the client library and server supports RFC 4525, * then use it to increment by one the value of the * krbLoginFailedCount attribute. Otherwise, assert the * (provided) old value by deleting it before adding. */ #ifdef LDAP_MOD_INCREMENT if (ldap_server_handle->server_info->modify_increment && has_fail_count) { st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_INCREMENT, 1); if (st != 0) goto cleanup; } else { #endif /* LDAP_MOD_INCREMENT */ if (has_fail_count) { st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_DELETE, entry->fail_auth_count); if (st != 0) goto cleanup; } st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_ADD, entry->fail_auth_count + 1); if (st != 0) goto cleanup; #ifdef LDAP_MOD_INCREMENT } #endif } else if (optype == ADD_PRINCIPAL) { /* Initialize krbLoginFailedCount in new entries to help avoid a * race during the first failed login. */ st = krb5_add_int_mem_ldap_mod(&mods, "krbLoginFailedCount", LDAP_MOD_ADD, 0); } if (entry->mask & KADM5_MAX_LIFE) { if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxticketlife", LDAP_MOD_REPLACE, entry->max_life)) != 0) goto cleanup; } if (entry->mask & KADM5_MAX_RLIFE) { if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbmaxrenewableage", LDAP_MOD_REPLACE, entry->max_renewable_life)) != 0) goto cleanup; } if (entry->mask & KADM5_ATTRIBUTES) { if ((st=krb5_add_int_mem_ldap_mod(&mods, "krbticketflags", LDAP_MOD_REPLACE, entry->attributes)) != 0) goto cleanup; } if (entry->mask & KADM5_PRINCIPAL) { memset(strval, 0, sizeof(strval)); strval[0] = user; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalname", LDAP_MOD_REPLACE, strval)) != 0) goto cleanup; } if (entry->mask & KADM5_PRINC_EXPIRE_TIME) { memset(strval, 0, sizeof(strval)); if ((strval[0]=getstringtime(entry->expiration)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbprincipalexpiration", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free (strval[0]); } if (entry->mask & KADM5_PW_EXPIRATION) { memset(strval, 0, sizeof(strval)); if ((strval[0]=getstringtime(entry->pw_expiration)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpasswordexpiration", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free (strval[0]); } if (entry->mask & KADM5_POLICY || entry->mask & KADM5_KEY_HIST) { memset(&princ_ent, 0, sizeof(princ_ent)); for (tl_data=entry->tl_data; tl_data; tl_data=tl_data->tl_data_next) { if (tl_data->tl_data_type == KRB5_TL_KADM_DATA) { if ((st = krb5_lookup_tl_kadm_data(tl_data, &princ_ent)) != 0) { goto cleanup; } break; } } } if (entry->mask & KADM5_POLICY) { if (princ_ent.aux_attributes & KADM5_POLICY) { memset(strval, 0, sizeof(strval)); if ((st = krb5_ldap_name_to_policydn (context, princ_ent.policy, &polname)) != 0) goto cleanup; strval[0] = polname; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, strval)) != 0) goto cleanup; } else { st = EINVAL; k5_setmsg(context, st, "Password policy value null"); goto cleanup; } } else if (entry->mask & KADM5_LOAD && found_entry == TRUE) { /* * a load is special in that existing entries must have attrs that * removed. */ if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_REPLACE, NULL)) != 0) goto cleanup; } if (entry->mask & KADM5_POLICY_CLR) { if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbpwdpolicyreference", LDAP_MOD_DELETE, NULL)) != 0) goto cleanup; } if (entry->mask & KADM5_KEY_HIST) { bersecretkey = krb5_encode_histkey(&princ_ent); if (bersecretkey == NULL) { st = ENOMEM; goto cleanup; } st = krb5_add_ber_mem_ldap_mod(&mods, "krbpwdhistory", LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, bersecretkey); if (st != 0) goto cleanup; free_berdata(bersecretkey); bersecretkey = NULL; } if (entry->mask & KADM5_KEY_DATA || entry->mask & KADM5_KVNO) { krb5_kvno mkvno; if ((st=krb5_dbe_lookup_mkvno(context, entry, &mkvno)) != 0) goto cleanup; bersecretkey = krb5_encode_krbsecretkey (entry->key_data, entry->n_key_data, mkvno); if (bersecretkey == NULL) { st = ENOMEM; goto cleanup; } /* An empty list of bervals is only accepted for modify operations, * not add operations. */ if (bersecretkey[0] != NULL || !create_standalone) { st = krb5_add_ber_mem_ldap_mod(&mods, "krbprincipalkey", LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, bersecretkey); if (st != 0) goto cleanup; } /* Update last password change whenever a new key is set */ { krb5_timestamp last_pw_changed; if ((st=krb5_dbe_lookup_last_pwd_change(context, entry, &last_pw_changed)) != 0) goto cleanup; memset(strval, 0, sizeof(strval)); if ((strval[0] = getstringtime(last_pw_changed)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastPwdChange", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free (strval[0]); } } /* Modify Key data ends here */ /* Set tl_data */ if (entry->tl_data != NULL) { int count = 0; struct berval **ber_tl_data = NULL; krb5_tl_data *ptr; krb5_timestamp unlock_time; /* Normalize required auth indicators, but also store them as string * attributes within krbExtraData. */ st = update_ldap_mod_auth_ind(context, entry, &mods); if (st != 0) goto cleanup; for (ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) { if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE #ifdef SECURID || ptr->tl_data_type == KRB5_TL_DB_ARGS #endif || ptr->tl_data_type == KRB5_TL_KADM_DATA || ptr->tl_data_type == KDB_TL_USER_INFO || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) continue; count++; } if (count != 0) { int j; ber_tl_data = (struct berval **) calloc (count + 1, sizeof (struct berval*)); if (ber_tl_data == NULL) { st = ENOMEM; goto cleanup; } for (j = 0, ptr = entry->tl_data; ptr != NULL; ptr = ptr->tl_data_next) { /* Ignore tl_data that are stored in separate directory * attributes */ if (ptr->tl_data_type == KRB5_TL_LAST_PWD_CHANGE #ifdef SECURID || ptr->tl_data_type == KRB5_TL_DB_ARGS #endif || ptr->tl_data_type == KRB5_TL_KADM_DATA || ptr->tl_data_type == KDB_TL_USER_INFO || ptr->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL || ptr->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) continue; if ((st = tl_data2berval (ptr, &ber_tl_data[j])) != 0) break; j++; } if (st == 0) { ber_tl_data[count] = NULL; st=krb5_add_ber_mem_ldap_mod(&mods, "krbExtraData", LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, ber_tl_data); } free_berdata(ber_tl_data); if (st != 0) goto cleanup; } if ((st=krb5_dbe_lookup_last_admin_unlock(context, entry, &unlock_time)) != 0) goto cleanup; if (unlock_time != 0) { /* Update last admin unlock */ memset(strval, 0, sizeof(strval)); if ((strval[0] = getstringtime(unlock_time)) == NULL) goto cleanup; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbLastAdminUnlock", LDAP_MOD_REPLACE, strval)) != 0) { free (strval[0]); goto cleanup; } free (strval[0]); } } /* Directory specific attribute */ if (xargs.tktpolicydn != NULL) { int tmask=0; if (strlen(xargs.tktpolicydn) != 0) { st = checkattributevalue(ld, xargs.tktpolicydn, "objectclass", policyclass, &tmask); CHECK_CLASS_VALIDITY(st, tmask, _("ticket policy object value: ")); strval[0] = xargs.tktpolicydn; strval[1] = NULL; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_REPLACE, strval)) != 0) goto cleanup; } else { /* if xargs.tktpolicydn is a empty string, then delete * already existing krbticketpolicyreference attr */ if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbticketpolicyreference", LDAP_MOD_DELETE, NULL)) != 0) goto cleanup; } } if (establish_links == TRUE) { memset(strval, 0, sizeof(strval)); strval[0] = xargs.linkdn; if ((st=krb5_add_str_mem_ldap_mod(&mods, "krbObjectReferences", LDAP_MOD_REPLACE, strval)) != 0) goto cleanup; } /* * in case mods is NULL then return * not sure but can happen in a modprinc * so no need to return an error * addprinc will at least have the principal name * and the keys passed in */ if (mods == NULL) goto cleanup; if (create_standalone == TRUE) { memset(strval, 0, sizeof(strval)); strval[0] = "krbprincipal"; strval[1] = "krbprincipalaux"; strval[2] = "krbTicketPolicyAux"; if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0) goto cleanup; st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL); if (st == LDAP_ALREADY_EXISTS && entry->mask & KADM5_LOAD) { /* a load operation must replace an existing entry */ st = ldap_delete_ext_s(ld, standalone_principal_dn, NULL, NULL); if (st != LDAP_SUCCESS) { snprintf(errbuf, sizeof(errbuf), _("Principal delete failed (trying to replace " "entry): %s"), ldap_err2string(st)); st = translate_ldap_error (st, OP_ADD); k5_setmsg(context, st, "%s", errbuf); goto cleanup; } else { st = ldap_add_ext_s(ld, standalone_principal_dn, mods, NULL, NULL); } } if (st != LDAP_SUCCESS) { snprintf(errbuf, sizeof(errbuf), _("Principal add failed: %s"), ldap_err2string(st)); st = translate_ldap_error (st, OP_ADD); k5_setmsg(context, st, "%s", errbuf); goto cleanup; } } else { /* * Here existing ldap object is modified and can be related * to any attribute, so always ensure that the ldap * object is extended with all the kerberos related * objectclasses so that there are no constraint * violations. */ { char *attrvalues[] = {"krbprincipalaux", "krbTicketPolicyAux", NULL}; int p, q, r=0, amask=0; if ((st=checkattributevalue(ld, (xargs.dn) ? xargs.dn : principal_dn, "objectclass", attrvalues, &amask)) != 0) goto cleanup; memset(strval, 0, sizeof(strval)); for (p=1, q=0; p<=2; p<<=1, ++q) { if ((p & amask) == 0) strval[r++] = attrvalues[q]; } if (r != 0) { if ((st=krb5_add_str_mem_ldap_mod(&mods, "objectclass", LDAP_MOD_ADD, strval)) != 0) goto cleanup; } } if (xargs.dn != NULL) st=ldap_modify_ext_s(ld, xargs.dn, mods, NULL, NULL); else st = ldap_modify_ext_s(ld, principal_dn, mods, NULL, NULL); if (st != LDAP_SUCCESS) { snprintf(errbuf, sizeof(errbuf), _("User modification failed: %s"), ldap_err2string(st)); st = translate_ldap_error (st, OP_MOD); k5_setmsg(context, st, "%s", errbuf); goto cleanup; } if (entry->mask & KADM5_FAIL_AUTH_COUNT_INCREMENT) entry->fail_auth_count++; } cleanup: if (user) free(user); if (filtuser) free(filtuser); free_xargs(xargs); if (standalone_principal_dn) free(standalone_principal_dn); if (principal_dn) free (principal_dn); if (polname != NULL) free(polname); for (tre = 0; tre < ntrees; tre++) free(subtreelist[tre]); free(subtreelist); if (subtree) free (subtree); if (bersecretkey) { for (l=0; bersecretkey[l]; ++l) { if (bersecretkey[l]->bv_val) free (bersecretkey[l]->bv_val); free (bersecretkey[l]); } free (bersecretkey); } if (keys) free (keys); ldap_mods_free(mods, 1); ldap_osa_free_princ_ent(&princ_ent); ldap_msgfree(result); krb5_ldap_put_handle_to_pool(ldap_context, ldap_server_handle); return(st); } krb5_error_code krb5_read_tkt_policy(krb5_context context, krb5_ldap_context *ldap_context, krb5_db_entry *entries, char *policy) { krb5_error_code st=0; int mask=0, omask=0; int tkt_mask=(KDB_MAX_LIFE_ATTR | KDB_MAX_RLIFE_ATTR | KDB_TKT_FLAGS_ATTR); krb5_ldap_policy_params *tktpoldnparam=NULL; if ((st=krb5_get_attributes_mask(context, entries, &mask)) != 0) goto cleanup; if ((mask & tkt_mask) == tkt_mask) goto cleanup; if (policy != NULL) { st = krb5_ldap_read_policy(context, policy, &tktpoldnparam, &omask); if (st && st != KRB5_KDB_NOENTRY) { k5_prependmsg(context, st, _("Error reading ticket policy")); goto cleanup; } st = 0; /* reset the return status */ } if ((mask & KDB_MAX_LIFE_ATTR) == 0) { if ((omask & KDB_MAX_LIFE_ATTR) == KDB_MAX_LIFE_ATTR) entries->max_life = tktpoldnparam->maxtktlife; else if (ldap_context->lrparams->max_life) entries->max_life = ldap_context->lrparams->max_life; } if ((mask & KDB_MAX_RLIFE_ATTR) == 0) { if ((omask & KDB_MAX_RLIFE_ATTR) == KDB_MAX_RLIFE_ATTR) entries->max_renewable_life = tktpoldnparam->maxrenewlife; else if (ldap_context->lrparams->max_renewable_life) entries->max_renewable_life = ldap_context->lrparams->max_renewable_life; } if ((mask & KDB_TKT_FLAGS_ATTR) == 0) { if ((omask & KDB_TKT_FLAGS_ATTR) == KDB_TKT_FLAGS_ATTR) entries->attributes = tktpoldnparam->tktflags; else if (ldap_context->lrparams->tktflags) entries->attributes |= ldap_context->lrparams->tktflags; } krb5_ldap_free_policy(context, tktpoldnparam); cleanup: return st; } static void free_ldap_seqof_key_data(ldap_seqof_key_data *keysets, krb5_int16 n_keysets) { int i; if (keysets == NULL) return; for (i = 0; i < n_keysets; i++) k5_free_key_data(keysets[i].n_key_data, keysets[i].key_data); free(keysets); } /* * Decode keys from ldap search results. * * Arguments: * - bvalues * The ldap search results containing the key data. * - mkvno * The master kvno that the keys were encrypted with. * - keysets_out * The decoded keys in a ldap_seqof_key_data struct. Must be freed using * free_ldap_seqof_key_data. * - n_keysets_out * The number of entries in keys_out. * - total_keys_out * An optional argument that if given will be set to the total number of * keys found throughout all the entries: sum(keys_out.n_key_data) * May be NULL. */ static krb5_error_code decode_keys(struct berval **bvalues, ldap_seqof_key_data **keysets_out, krb5_int16 *n_keysets_out, krb5_int16 *total_keys_out) { krb5_error_code err = 0; krb5_int16 n_keys, i, ki, total_keys; ldap_seqof_key_data *keysets = NULL; *keysets_out = NULL; *n_keysets_out = 0; if (total_keys_out) *total_keys_out = 0; /* Precount the number of keys. */ for (n_keys = 0, i = 0; bvalues[i] != NULL; i++) { if (bvalues[i]->bv_len > 0) n_keys++; } keysets = k5calloc(n_keys, sizeof(ldap_seqof_key_data), &err); if (keysets == NULL) goto cleanup; memset(keysets, 0, n_keys * sizeof(ldap_seqof_key_data)); for (i = 0, ki = 0, total_keys = 0; bvalues[i] != NULL; i++) { krb5_data in; if (bvalues[i]->bv_len == 0) continue; in.length = bvalues[i]->bv_len; in.data = bvalues[i]->bv_val; err = asn1_decode_sequence_of_keys(&in, &keysets[ki]); if (err) goto cleanup; if (total_keys_out) total_keys += keysets[ki].n_key_data; ki++; } if (total_keys_out) *total_keys_out = total_keys; *n_keysets_out = n_keys; *keysets_out = keysets; keysets = NULL; n_keys = 0; cleanup: free_ldap_seqof_key_data(keysets, n_keys); return err; } krb5_error_code krb5_decode_krbsecretkey(krb5_context context, krb5_db_entry *entries, struct berval **bvalues, krb5_kvno *mkvno) { krb5_key_data *key_data = NULL, *tmp; krb5_error_code err = 0; ldap_seqof_key_data *keysets = NULL; krb5_int16 i, n_keysets = 0, total_keys = 0; err = decode_keys(bvalues, &keysets, &n_keysets, &total_keys); if (err != 0) { k5_prependmsg(context, err, _("unable to decode stored principal key data")); goto cleanup; } key_data = k5calloc(total_keys, sizeof(krb5_key_data), &err); if (key_data == NULL) goto cleanup; memset(key_data, 0, total_keys * sizeof(krb5_key_data)); if (n_keysets > 0) *mkvno = keysets[0].mkvno; /* Transfer key data values from keysets to a flat list in entries. */ tmp = key_data; for (i = 0; i < n_keysets; i++) { memcpy(tmp, keysets[i].key_data, sizeof(krb5_key_data) * keysets[i].n_key_data); tmp += keysets[i].n_key_data; keysets[i].n_key_data = 0; } entries->n_key_data = total_keys; entries->key_data = key_data; key_data = NULL; cleanup: free_ldap_seqof_key_data(keysets, n_keysets); k5_free_key_data(total_keys, key_data); return err; } static int compare_osa_pw_hist_ent(const void *left_in, const void *right_in) { int kvno_left, kvno_right; osa_pw_hist_ent *left = (osa_pw_hist_ent *)left_in; osa_pw_hist_ent *right = (osa_pw_hist_ent *)right_in; kvno_left = left->n_key_data ? left->key_data[0].key_data_kvno : 0; kvno_right = right->n_key_data ? right->key_data[0].key_data_kvno : 0; return kvno_left - kvno_right; } /* * Decode the key history entries from an LDAP search. * * NOTE: the caller must free princ_ent->old_keys even on error. */ krb5_error_code krb5_decode_histkey(krb5_context context, struct berval **bvalues, osa_princ_ent_rec *princ_ent) { krb5_error_code err = 0; krb5_int16 i, n_keysets = 0; ldap_seqof_key_data *keysets = NULL; err = decode_keys(bvalues, &keysets, &n_keysets, NULL); if (err != 0) { k5_prependmsg(context, err, _("unable to decode stored principal pw history")); goto cleanup; } princ_ent->old_keys = k5calloc(n_keysets, sizeof(osa_pw_hist_ent), &err); if (princ_ent->old_keys == NULL) goto cleanup; princ_ent->old_key_len = n_keysets; if (n_keysets > 0) princ_ent->admin_history_kvno = keysets[0].mkvno; /* Transfer key data pointers from keysets to princ_ent. */ for (i = 0; i < n_keysets; i++) { princ_ent->old_keys[i].n_key_data = keysets[i].n_key_data; princ_ent->old_keys[i].key_data = keysets[i].key_data; keysets[i].n_key_data = 0; keysets[i].key_data = NULL; } /* Sort the principal entries by kvno in ascending order. */ qsort(princ_ent->old_keys, princ_ent->old_key_len, sizeof(osa_pw_hist_ent), &compare_osa_pw_hist_ent); princ_ent->aux_attributes |= KADM5_KEY_HIST; /* Set the next key to the end of the list. The queue will be lengthened * if it isn't full yet; the first entry will be replaced if it is full. */ princ_ent->old_key_next = princ_ent->old_key_len; cleanup: free_ldap_seqof_key_data(keysets, n_keysets); return err; } static char * getstringtime(krb5_timestamp epochtime) { struct tm tme; char *strtime=NULL; time_t posixtime = ts2tt(epochtime); if (gmtime_r(&posixtime, &tme) == NULL) return NULL; strtime = calloc(50, 1); if (strtime == NULL) return NULL; if (strftime(strtime, 50, "%Y%m%d%H%M%SZ", &tme) == 0) { free(strtime); return NULL; } return strtime; }