Commit message: Return only new keys in randkey [CVE-2014-5351]

In kadmind's randkey operation, if a client specifies the keepold
flag, do not include the preserved old keys in the response.

CVE-2014-5351:

An authenticated remote attacker can retrieve the current keys for a
service principal when generating a new set of keys for that
principal.  The attacker needs to be authenticated as a user who has
the elevated privilege for randomizing the keys of other principals.

Normally, when a Kerberos administrator randomizes the keys of a
service principal, kadmind returns only the new keys.  This prevents
an administrator who lacks legitimate privileged access to a service
from forging tickets to authenticate to that service.  If the
"keepold" flag to the kadmin randkey RPC operation is true, kadmind
retains the old keys in the KDC database as intended, but also
unexpectedly returns the old keys to the client, which exposes the
service to ticket forgery attacks from the administrator.

A mitigating factor is that legitimate clients of the affected service
will start failing to authenticate to the service once they begin to
receive service tickets encrypted in the new keys.  The affected
service will be unable to decrypt the newly issued tickets, possibly
alerting the legitimate administrator of the affected service.

CVSSv2: AV:N/AC:H/Au:S/C:P/I:N/A:N/E:POC/RL:OF/RC:C

[tlyu@mit.edu: CVE description and CVSS score]

ticket: 8018 (new)
target_version: 1.13
tags: pullup
CVE: CVE-2014-5351
CWE: CWE-255
Vulnerability description: The kadm5_randkey_principal_3 function in lib/kadm5/srv/svr_principal.c in kadmind in MIT Kerberos 5 (aka krb5) before 1.13 sends old keys in a response to a -randkey -keepold request, which allows remote authenticated users to forge tickets by leveraging administrative access.

Project: krb5
File: svr_principal.c

apply_keysalt_policy(kadm5_server_handle_t handle, const char *policy,
                     int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
                     int *new_n_kstp, krb5_key_salt_tuple **new_kstp)
{
    kadm5_ret_t ret;
    kadm5_policy_ent_rec polent;
    krb5_boolean have_polent;
    int ak_n_ks_tuple = 0;
    int new_n_ks_tuple = 0;
    krb5_key_salt_tuple *ak_ks_tuple = NULL;
    krb5_key_salt_tuple *new_ks_tuple = NULL;
    krb5_key_salt_tuple *subset;
    int i, m;

    if (new_n_kstp != NULL) {
        *new_n_kstp = 0;
        *new_kstp = NULL;
    }

    memset(&polent, 0, sizeof(polent));
    ret = get_policy(handle, policy, &polent, &have_polent);
    if (ret)
        goto cleanup;

    if (polent.allowed_keysalts == NULL) {
        /* Requested keysalts allowed or default to supported_enctypes. */
        if (n_ks_tuple == 0) {
            /* Default to supported_enctypes. */
            n_ks_tuple = handle->params.num_keysalts;
            ks_tuple = handle->params.keysalts;
        }
        /* Dup the requested or defaulted keysalt tuples. */
        new_ks_tuple = malloc(n_ks_tuple * sizeof(*new_ks_tuple));
        if (new_ks_tuple == NULL) {
            ret = ENOMEM;
            goto cleanup;
        }
        memcpy(new_ks_tuple, ks_tuple, n_ks_tuple * sizeof(*new_ks_tuple));
        new_n_ks_tuple = n_ks_tuple;
        ret = 0;
        goto cleanup;
    }

    ret = krb5_string_to_keysalts(polent.allowed_keysalts,
                                  ",",   /* Tuple separators */
                                  NULL,  /* Key/salt separators */
                                  0,     /* No duplicates */
                                  &ak_ks_tuple,
                                  &ak_n_ks_tuple);
    /*
     * Malformed policy?  Shouldn't happen, but it's remotely possible
     * someday, so we don't assert, just bail.
     */
    if (ret)
        goto cleanup;

    /* Check that the requested ks_tuples are within policy, if we have one. */
    for (i = 0; i < n_ks_tuple; i++) {
        if (!ks_tuple_present(ak_n_ks_tuple, ak_ks_tuple, &ks_tuple[i])) {
            ret = KADM5_BAD_KEYSALTS;
            goto cleanup;
        }
    }

    /* Have policy but no ks_tuple input?  Output the policy. */
    if (n_ks_tuple == 0) {
        new_n_ks_tuple = ak_n_ks_tuple;
        new_ks_tuple = ak_ks_tuple;
        ak_ks_tuple = NULL;
        goto cleanup;
    }

    /*
     * Now filter the policy ks tuples by the requested ones so as to
     * preserve in the requested sub-set the relative ordering from the
     * policy.  We could optimize this (if (n_ks_tuple == ak_n_ks_tuple)
     * then skip this), but we don't bother.
     */
    subset = calloc(n_ks_tuple, sizeof(*subset));
    if (subset == NULL) {
        ret = ENOMEM;
        goto cleanup;
    }
    for (m = 0, i = 0; i < ak_n_ks_tuple && m < n_ks_tuple; i++) {
        if (ks_tuple_present(n_ks_tuple, ks_tuple, &ak_ks_tuple[i]))
            subset[m++] = ak_ks_tuple[i];
    }
    new_ks_tuple = subset;
    new_n_ks_tuple = m;
    ret = 0;

cleanup:
    if (have_polent)
        kadm5_free_policy_ent(handle->lhandle, &polent);
    free(ak_ks_tuple);

    if (new_n_kstp != NULL) {
        *new_n_kstp = new_n_ks_tuple;
        *new_kstp = new_ks_tuple;
    } else {
        free(new_ks_tuple);
    }
    return ret;
}

Project: krb5
File: svr_principal.c

check_1_6_dummy(kadm5_principal_ent_t entry, long mask,
                int n_ks_tuple, krb5_key_salt_tuple *ks_tuple, char **passptr)
{
    int i;
    char *password = *passptr;

    /* Old-style randkey operations disallowed tickets to start. */
    if (password == NULL || !(mask & KADM5_ATTRIBUTES) ||
        !(entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX))
        return;

    /* The 1.6 dummy password was the octets 1..255. */
    for (i = 0; (unsigned char) password[i] == i + 1; i++);
    if (password[i] != '\0' || i != 255)
        return;

    /* This will make the caller use a random password instead. */
     *passptr = NULL;
 }

Project: krb5
File: svr_principal.c

int create_history_entry(krb5_context context,
                         krb5_keyblock *hist_key, int n_key_data,
                         krb5_key_data *key_data, osa_pw_hist_ent *hist)
{
    krb5_error_code ret;
    krb5_keyblock key;
    krb5_keysalt salt;
    int i;

    hist->key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
    if (hist->key_data == NULL)
        return ret;

    for (i = 0; i < n_key_data; i++) {
        ret = krb5_dbe_decrypt_key_data(context, NULL, &key_data[i], &key,
                                        &salt);
        if (ret)
            return ret;

        ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
                                        key_data[i].key_data_kvno,
                                        &hist->key_data[i]);
        if (ret)
            return ret;

        krb5_free_keyblock_contents(context, &key);
        /* krb5_free_keysalt(context, &salt); */
    }

    hist->n_key_data = n_key_data;
    return 0;
}

Project: krb5
File: svr_principal.c

get_policy(kadm5_server_handle_t handle, const char *name,
           kadm5_policy_ent_t policy_out, krb5_boolean *have_pol_out)
{
    kadm5_ret_t ret;

    *have_pol_out = FALSE;
    if (name == NULL)
        return 0;
    ret = kadm5_get_policy(handle->lhandle, (char *)name, policy_out);
    if (ret == 0)
        *have_pol_out = TRUE;
    return (ret == KADM5_UNK_POLICY) ? 0 : ret;
}

Project: krb5
File: svr_principal.c

kadm5_chpass_principal_3(void *server_handle,
                         krb5_principal principal, krb5_boolean keepold,
                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
                         char *password)
{
    krb5_int32                  now;
    kadm5_policy_ent_rec        pol;
    osa_princ_ent_rec           adb;
    krb5_db_entry               *kdb;
    int                         ret, ret2, last_pwd, hist_added;
    krb5_boolean                have_pol = FALSE;
    kadm5_server_handle_t       handle = server_handle;
    osa_pw_hist_ent             hist;
    krb5_keyblock               *act_mkey, *hist_keyblocks = NULL;
    krb5_kvno                   act_kvno, hist_kvno;
    int                         new_n_ks_tuple = 0;
    krb5_key_salt_tuple         *new_ks_tuple = NULL;

    CHECK_HANDLE(server_handle);

    krb5_clear_error_message(handle->context);

    hist_added = 0;
    memset(&hist, 0, sizeof(hist));

    if (principal == NULL || password == NULL)
        return EINVAL;
    if ((krb5_principal_compare(handle->context,
                                principal, hist_princ)) == TRUE)
        return KADM5_PROTECT_PRINCIPAL;

    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);

    ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
                               &new_n_ks_tuple, &new_ks_tuple);
    if (ret)
        goto done;

    if ((adb.aux_attributes & KADM5_POLICY)) {
        ret = get_policy(handle, adb.policy, &pol, &have_pol);
        if (ret)
            goto done;
    }
    if (have_pol) {
        /* Create a password history entry before we change kdb's key_data. */
        ret = kdb_get_hist_key(handle, &hist_keyblocks, &hist_kvno);
        if (ret)
            goto done;
        ret = create_history_entry(handle->context, &hist_keyblocks[0],
                                   kdb->n_key_data, kdb->key_data, &hist);
        if (ret)
            goto done;
    }

    if ((ret = passwd_check(handle, password, have_pol ? &pol : NULL,
                            principal)))
        goto done;

    ret = kdb_get_active_mkey(handle, &act_kvno, &act_mkey);
    if (ret)
        goto done;

    ret = krb5_dbe_cpw(handle->context, act_mkey, new_ks_tuple, new_n_ks_tuple,
                       password, 0 /* increment kvno */,
                       keepold, kdb);
    if (ret)
        goto done;

    ret = krb5_dbe_update_mkvno(handle->context, kdb, act_kvno);
    if (ret)
        goto done;

    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;

    ret = krb5_timeofday(handle->context, &now);
    if (ret)
        goto done;

    if ((adb.aux_attributes & KADM5_POLICY)) {
        /* the policy was loaded before */

        ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb, &last_pwd);
        if (ret)
            goto done;

#if 0
        /*
         * The spec says this check is overridden if the caller has
         * modify privilege.  The admin server therefore makes this
         * check itself (in chpass_principal_wrapper, misc.c). A
         * local caller implicitly has all authorization bits.
         */
        if ((now - last_pwd) < pol.pw_min_life &&
            !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
            ret = KADM5_PASS_TOOSOON;
            goto done;
        }
#endif

        ret = check_pw_reuse(handle->context, hist_keyblocks,
                             kdb->n_key_data, kdb->key_data,
                             1, &hist);
        if (ret)
            goto done;

        if (pol.pw_history_num > 1) {
            /* If hist_kvno has changed since the last password change, we
             * can't check the history. */
            if (adb.admin_history_kvno == hist_kvno) {
                ret = check_pw_reuse(handle->context, hist_keyblocks,
                                     kdb->n_key_data, kdb->key_data,
                                     adb.old_key_len, adb.old_keys);
                if (ret)
                    goto done;
            }

            ret = add_to_history(handle->context, hist_kvno, &adb, &pol,
                                 &hist);
            if (ret)
                goto done;
            hist_added = 1;
        }

        if (pol.pw_max_life)
            kdb->pw_expiration = now + pol.pw_max_life;
        else
            kdb->pw_expiration = 0;
    } else {
        kdb->pw_expiration = 0;
    }

#ifdef USE_PASSWORD_SERVER
    if (kadm5_use_password_server () &&
        (krb5_princ_size (handle->context, principal) == 1)) {
        krb5_data *princ = krb5_princ_component (handle->context, principal, 0);
        const char *path = "/usr/sbin/mkpassdb";
        char *argv[] = { "mkpassdb", "-setpassword", NULL, NULL };
        char *pstring = NULL;

        if (!ret) {
            pstring = malloc ((princ->length + 1) * sizeof (char));
            if (pstring == NULL) { ret = ENOMEM; }
        }

        if (!ret) {
            memcpy (pstring, princ->data, princ->length);
            pstring [princ->length] = '\0';
            argv[2] = pstring;

            ret = kadm5_launch_task (handle->context, path, argv, password);
        }

        if (pstring != NULL)
            free (pstring);

        if (ret)
            goto done;
    }
#endif

    ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
    if (ret)
        goto done;

    /* unlock principal on this KDC */
    kdb->fail_auth_count = 0;

    /* key data and attributes changed, let the database provider know */
    kdb->mask = KADM5_KEY_DATA | KADM5_ATTRIBUTES |
        KADM5_FAIL_AUTH_COUNT;
    /* | KADM5_CPW_FUNCTION */

    ret = k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
                               KADM5_HOOK_STAGE_PRECOMMIT, principal, keepold,
                               new_n_ks_tuple, new_ks_tuple, password);
    if (ret)
        goto done;

    if ((ret = kdb_put_entry(handle, kdb, &adb)))
        goto done;

    (void) k5_kadm5_hook_chpass(handle->context, handle->hook_handles,
                                KADM5_HOOK_STAGE_POSTCOMMIT, principal,
                                keepold, new_n_ks_tuple, new_ks_tuple, password);
    ret = KADM5_OK;
done:
    free(new_ks_tuple);
    if (!hist_added && hist.key_data)
        free_history_entry(handle->context, &hist);
    kdb_free_entry(handle, kdb, &adb);
    kdb_free_keyblocks(handle, hist_keyblocks);

    if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
        && !ret)
        ret = ret2;

    return ret;
}

Project: krb5
File: svr_principal.c

kadm5_delete_principal(void *server_handle, krb5_principal principal)
{
    unsigned int                ret;
    krb5_db_entry               *kdb;
    osa_princ_ent_rec           adb;
    kadm5_server_handle_t handle = server_handle;

    CHECK_HANDLE(server_handle);

    krb5_clear_error_message(handle->context);

    if (principal == NULL)
        return EINVAL;

    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);
    ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
                               KADM5_HOOK_STAGE_PRECOMMIT, principal);
    if (ret) {
        kdb_free_entry(handle, kdb, &adb);
        return ret;
    }

    ret = kdb_delete_entry(handle, principal);

    kdb_free_entry(handle, kdb, &adb);

    if (ret == 0)
        (void) k5_kadm5_hook_remove(handle->context,
                                    handle->hook_handles,
                                    KADM5_HOOK_STAGE_POSTCOMMIT, principal);

    return ret;
}

Project: krb5
File: svr_principal.c

kadm5_get_principal(void *server_handle, krb5_principal principal,
                    kadm5_principal_ent_t entry,
                    long in_mask)
{
    krb5_db_entry               *kdb;
    osa_princ_ent_rec           adb;
    krb5_error_code             ret = 0;
    long                        mask;
    int i;
    kadm5_server_handle_t handle = server_handle;

    CHECK_HANDLE(server_handle);

    krb5_clear_error_message(handle->context);

    /*
     * In version 1, all the defined fields are always returned.
     * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
     * filled with allocated memory.
     */
    mask = in_mask;

    memset(entry, 0, sizeof(*entry));

    if (principal == NULL)
        return EINVAL;

    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return ret;

    if ((mask & KADM5_POLICY) &&
        adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
        if ((entry->policy = strdup(adb.policy)) == NULL) {
            ret = ENOMEM;
            goto done;
        }
    }

    if (mask & KADM5_AUX_ATTRIBUTES)
        entry->aux_attributes = adb.aux_attributes;

    if ((mask & KADM5_PRINCIPAL) &&
        (ret = krb5_copy_principal(handle->context, kdb->princ,
                                   &entry->principal))) {
        goto done;
    }

    if (mask & KADM5_PRINC_EXPIRE_TIME)
        entry->princ_expire_time = kdb->expiration;

    if ((mask & KADM5_LAST_PWD_CHANGE) &&
        (ret = krb5_dbe_lookup_last_pwd_change(handle->context, kdb,
                                               &(entry->last_pwd_change)))) {
        goto done;
    }

    if (mask & KADM5_PW_EXPIRATION)
        entry->pw_expiration = kdb->pw_expiration;
    if (mask & KADM5_MAX_LIFE)
        entry->max_life = kdb->max_life;

    /* this is a little non-sensical because the function returns two */
    /* values that must be checked separately against the mask */
    if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
        ret = krb5_dbe_lookup_mod_princ_data(handle->context, kdb,
                                             &(entry->mod_date),
                                             &(entry->mod_name));
        if (ret) {
            goto done;
        }

        if (! (mask & KADM5_MOD_TIME))
            entry->mod_date = 0;
        if (! (mask & KADM5_MOD_NAME)) {
            krb5_free_principal(handle->context, entry->mod_name);
            entry->mod_name = NULL;
        }
    }

    if (mask & KADM5_ATTRIBUTES)
        entry->attributes = kdb->attributes;

    if (mask & KADM5_KVNO)
        for (entry->kvno = 0, i=0; i<kdb->n_key_data; i++)
            if ((krb5_kvno) kdb->key_data[i].key_data_kvno > entry->kvno)
                entry->kvno = kdb->key_data[i].key_data_kvno;

    if (mask & KADM5_MKVNO) {
        ret = krb5_dbe_get_mkvno(handle->context, kdb, &entry->mkvno);
        if (ret)
            goto done;
    }

    if (mask & KADM5_MAX_RLIFE)
        entry->max_renewable_life = kdb->max_renewable_life;
    if (mask & KADM5_LAST_SUCCESS)
        entry->last_success = kdb->last_success;
    if (mask & KADM5_LAST_FAILED)
        entry->last_failed = kdb->last_failed;
    if (mask & KADM5_FAIL_AUTH_COUNT)
        entry->fail_auth_count = kdb->fail_auth_count;
    if (mask & KADM5_TL_DATA) {
        krb5_tl_data *tl, *tl2;

        entry->tl_data = NULL;

        tl = kdb->tl_data;
        while (tl) {
            if (tl->tl_data_type > 255) {
                if ((tl2 = dup_tl_data(tl)) == NULL) {
                    ret = ENOMEM;
                    goto done;
                }
                tl2->tl_data_next = entry->tl_data;
                entry->tl_data = tl2;
                entry->n_tl_data++;
            }

            tl = tl->tl_data_next;
        }
    }
    if (mask & KADM5_KEY_DATA) {
        entry->n_key_data = kdb->n_key_data;
        if(entry->n_key_data) {
            entry->key_data = k5calloc(entry->n_key_data,
                                       sizeof(krb5_key_data), &ret);
            if (entry->key_data == NULL)
                goto done;
        } else
            entry->key_data = NULL;

        for (i = 0; i < entry->n_key_data; i++)
            ret = krb5_copy_key_data_contents(handle->context,
                                              &kdb->key_data[i],
                                              &entry->key_data[i]);
        if (ret)
            goto done;
    }

    ret = KADM5_OK;

done:
    if (ret && entry->principal) {
        krb5_free_principal(handle->context, entry->principal);
        entry->principal = NULL;
    }
    kdb_free_entry(handle, kdb, &adb);

    return ret;
}

Project: krb5
File: svr_principal.c

kadm5_purgekeys(void *server_handle,
                krb5_principal principal,
                int keepkvno)
{
    kadm5_server_handle_t handle = server_handle;
    kadm5_ret_t ret;
    krb5_db_entry *kdb;
    osa_princ_ent_rec adb;
    krb5_key_data *old_keydata;
    int n_old_keydata;
    int i, j, k;

    CHECK_HANDLE(server_handle);

    if (principal == NULL)
        return EINVAL;

    ret = kdb_get_entry(handle, principal, &kdb, &adb);
    if (ret)
        return(ret);

    if (keepkvno <= 0) {
        keepkvno = krb5_db_get_key_data_kvno(handle->context, kdb->n_key_data,
                                             kdb->key_data);
    }

    old_keydata = kdb->key_data;
    n_old_keydata = kdb->n_key_data;
    kdb->n_key_data = 0;
    /* Allocate one extra key_data to avoid allocating 0 bytes. */
    kdb->key_data = krb5_db_alloc(handle->context, NULL,
                                  (n_old_keydata + 1) * sizeof(krb5_key_data));
    if (kdb->key_data == NULL) {
        ret = ENOMEM;
        goto done;
    }
    memset(kdb->key_data, 0, n_old_keydata * sizeof(krb5_key_data));
    for (i = 0, j = 0; i < n_old_keydata; i++) {
        if (old_keydata[i].key_data_kvno < keepkvno)
            continue;

        /* Alias the key_data_contents pointers; we null them out in the
         * source array immediately after. */
        kdb->key_data[j] = old_keydata[i];
        for (k = 0; k < old_keydata[i].key_data_ver; k++) {
            old_keydata[i].key_data_contents[k] = NULL;
        }
        j++;
    }
    kdb->n_key_data = j;
    cleanup_key_data(handle->context, n_old_keydata, old_keydata);

    kdb->mask = KADM5_KEY_DATA;
    ret = kdb_put_entry(handle, kdb, &adb);
    if (ret)
        goto done;

done:
    kdb_free_entry(handle, kdb, &adb);
    return ret;
}

Project: krb5
File: svr_principal.c

kadm5_setkey_principal_3(void *server_handle,
                         krb5_principal principal,
                         krb5_boolean keepold,
                         int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
                         krb5_keyblock *keyblocks,
                         int n_keys)
{
    krb5_db_entry               *kdb;
    osa_princ_ent_rec           adb;
    krb5_int32                  now;
    kadm5_policy_ent_rec        pol;
    krb5_key_data               *old_key_data;
    int                         n_old_keys;
    int                         i, j, k, kvno, ret;
    krb5_boolean                have_pol = FALSE;
#if 0
    int                         last_pwd;
#endif
    kadm5_server_handle_t       handle = server_handle;
    krb5_boolean                similar;
    krb5_keysalt                keysalt;
    krb5_key_data         tmp_key_data;
    krb5_key_data        *tptr;
    krb5_keyblock               *act_mkey;
    krb5_key_salt_tuple         *ks_from_keys = NULL;

    CHECK_HANDLE(server_handle);

    krb5_clear_error_message(handle->context);

    if (principal == NULL || keyblocks == NULL)
        return EINVAL;
    if (hist_princ && /* this will be NULL when initializing the databse */
        ((krb5_principal_compare(handle->context,
                                 principal, hist_princ)) == TRUE))
        return KADM5_PROTECT_PRINCIPAL;

    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);

    if (!n_ks_tuple) {
        /* Apply policy to the key/salt types implied by the given keys */
        ret = make_ks_from_keys(handle->context, n_keys, keyblocks,
                                &ks_from_keys);
        if (ret)
            goto done;
        ret = apply_keysalt_policy(handle, adb.policy, n_keys, ks_from_keys,
                                   NULL, NULL);
        free(ks_from_keys);
    } else {
        /*
         * Apply policy to the given ks_tuples.  Note that further below
         * we enforce keyblocks[i].enctype == ks_tuple[i].ks_enctype for
         * all i from 0 to n_keys, and that n_ks_tuple == n_keys if ks
         * tuples are given.
         */
        ret = apply_keysalt_policy(handle, adb.policy, n_ks_tuple, ks_tuple,
                                   NULL, NULL);
    }
    if (ret)
        goto done;

    for (i = 0; i < n_keys; i++) {
        for (j = i+1; j < n_keys; j++) {
            if ((ret = krb5_c_enctype_compare(handle->context,
                                              keyblocks[i].enctype,
                                              keyblocks[j].enctype,
                                              &similar)))
                return(ret);
            if (similar) {
                if (n_ks_tuple) {
                    if (ks_tuple[i].ks_salttype == ks_tuple[j].ks_salttype)
                        return KADM5_SETKEY_DUP_ENCTYPES;
                } else
                    return KADM5_SETKEY_DUP_ENCTYPES;
            }
        }
    }

    if (n_ks_tuple && n_ks_tuple != n_keys)
        return KADM5_SETKEY3_ETYPE_MISMATCH;

    for (kvno = 0, i=0; i<kdb->n_key_data; i++)
        if (kdb->key_data[i].key_data_kvno > kvno)
            kvno = kdb->key_data[i].key_data_kvno;

    if (keepold) {
        old_key_data = kdb->key_data;
        n_old_keys = kdb->n_key_data;
    } else {
        if (kdb->key_data != NULL)
            cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
        n_old_keys = 0;
        old_key_data = NULL;
    }

    /* Allocate one extra key_data to avoid allocating 0 bytes. */
    kdb->key_data = krb5_db_alloc(handle->context, NULL,
                                  (n_keys + n_old_keys + 1) *
                                  sizeof(krb5_key_data));
    if (kdb->key_data == NULL) {
        ret = ENOMEM;
        goto done;
    }

    memset(kdb->key_data, 0, (n_keys+n_old_keys)*sizeof(krb5_key_data));
    kdb->n_key_data = 0;

    for (i = 0; i < n_keys; i++) {
        if (n_ks_tuple) {
            keysalt.type = ks_tuple[i].ks_salttype;
            keysalt.data.length = 0;
            keysalt.data.data = NULL;
            if (ks_tuple[i].ks_enctype != keyblocks[i].enctype) {
                ret = KADM5_SETKEY3_ETYPE_MISMATCH;
                goto done;
            }
        }
        memset (&tmp_key_data, 0, sizeof(tmp_key_data));

        ret = kdb_get_active_mkey(handle, NULL, &act_mkey);
        if (ret)
            goto done;

        ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey,
                                        &keyblocks[i],
                                        n_ks_tuple ? &keysalt : NULL, kvno + 1,
                                        &tmp_key_data);
        if (ret)
            goto done;

        tptr = &kdb->key_data[i];
        tptr->key_data_ver = tmp_key_data.key_data_ver;
        tptr->key_data_kvno = tmp_key_data.key_data_kvno;
        for (k = 0; k < tmp_key_data.key_data_ver; k++) {
            tptr->key_data_type[k] = tmp_key_data.key_data_type[k];
            tptr->key_data_length[k] = tmp_key_data.key_data_length[k];
            if (tmp_key_data.key_data_contents[k]) {
                tptr->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
                if (tptr->key_data_contents[k] == NULL) {
                    int i1;
                    for (i1 = k; i1 < tmp_key_data.key_data_ver; i1++) {
                        if (tmp_key_data.key_data_contents[i1]) {
                            memset (tmp_key_data.key_data_contents[i1], 0, tmp_key_data.key_data_length[i1]);
                            free (tmp_key_data.key_data_contents[i1]);
                        }
                    }

                    ret =  ENOMEM;
                    goto done;
                }
                memcpy (tptr->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);

                memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
                free (tmp_key_data.key_data_contents[k]);
                tmp_key_data.key_data_contents[k] = NULL;
            }
        }
        kdb->n_key_data++;
    }

    /* copy old key data if necessary */
    for (i = 0; i < n_old_keys; i++) {
        kdb->key_data[i+n_keys] = old_key_data[i];
        memset(&old_key_data[i], 0, sizeof (krb5_key_data));
        kdb->n_key_data++;
    }

    if (old_key_data)
        krb5_db_free(handle->context, old_key_data);

    /* assert(kdb->n_key_data == n_keys + n_old_keys) */
    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;

    if ((ret = krb5_timeofday(handle->context, &now)))
        goto done;

    if ((adb.aux_attributes & KADM5_POLICY)) {
        ret = get_policy(handle, adb.policy, &pol, &have_pol);
        if (ret)
            goto done;
    }
    if (have_pol) {
#if 0
        /*
         * The spec says this check is overridden if the caller has
         * modify privilege.  The admin server therefore makes this
         * check itself (in chpass_principal_wrapper, misc.c).  A
         * local caller implicitly has all authorization bits.
         */
        if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
                                                  kdb, &last_pwd))
            goto done;
        if((now - last_pwd) < pol.pw_min_life &&
           !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
            ret = KADM5_PASS_TOOSOON;
            goto done;
        }
#endif

        if (pol.pw_max_life)
            kdb->pw_expiration = now + pol.pw_max_life;
        else
            kdb->pw_expiration = 0;
    } else {
        kdb->pw_expiration = 0;
    }

    if ((ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now)))
        goto done;

    /* unlock principal on this KDC */
    kdb->fail_auth_count = 0;

    if ((ret = kdb_put_entry(handle, kdb, &adb)))
        goto done;

    ret = KADM5_OK;
done:
    kdb_free_entry(handle, kdb, &adb);
    if (have_pol)
        kadm5_free_policy_ent(handle->lhandle, &pol);

    return ret;
}

Project: krb5
File: svr_principal.c

kadm5_setv4key_principal(void *server_handle,
                         krb5_principal principal,
                         krb5_keyblock *keyblock)
{
    krb5_db_entry               *kdb;
    osa_princ_ent_rec           adb;
    krb5_int32                  now;
    kadm5_policy_ent_rec        pol;
    krb5_keysalt                keysalt;
    int                         i, k, kvno, ret;
    krb5_boolean                have_pol = FALSE;
#if 0
    int                         last_pwd;
#endif
    kadm5_server_handle_t       handle = server_handle;
    krb5_key_data               tmp_key_data;
    krb5_keyblock               *act_mkey;

    memset( &tmp_key_data, 0, sizeof(tmp_key_data));

    CHECK_HANDLE(server_handle);

    krb5_clear_error_message(handle->context);

    if (principal == NULL || keyblock == NULL)
        return EINVAL;
    if (hist_princ && /* this will be NULL when initializing the databse */
        ((krb5_principal_compare(handle->context,
                                 principal, hist_princ)) == TRUE))
        return KADM5_PROTECT_PRINCIPAL;

    if (keyblock->enctype != ENCTYPE_DES_CBC_CRC)
        return KADM5_SETV4KEY_INVAL_ENCTYPE;

    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
        return(ret);

    for (kvno = 0, i=0; i<kdb->n_key_data; i++)
        if (kdb->key_data[i].key_data_kvno > kvno)
            kvno = kdb->key_data[i].key_data_kvno;

    if (kdb->key_data != NULL)
        cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);

    kdb->key_data = (krb5_key_data*)krb5_db_alloc(handle->context, NULL, sizeof(krb5_key_data));
    if (kdb->key_data == NULL)
        return ENOMEM;
    memset(kdb->key_data, 0, sizeof(krb5_key_data));
    kdb->n_key_data = 1;
    keysalt.type = KRB5_KDB_SALTTYPE_V4;
    /* XXX data.magic? */
    keysalt.data.length = 0;
    keysalt.data.data = NULL;

    ret = kdb_get_active_mkey(handle, NULL, &act_mkey);
    if (ret)
        goto done;

    /* use tmp_key_data as temporary location and reallocate later */
    ret = krb5_dbe_encrypt_key_data(handle->context, act_mkey, keyblock,
                                    &keysalt, kvno + 1, &tmp_key_data);
    if (ret) {
        goto done;
    }

    for (k = 0; k < tmp_key_data.key_data_ver; k++) {
        kdb->key_data->key_data_type[k] = tmp_key_data.key_data_type[k];
        kdb->key_data->key_data_length[k] = tmp_key_data.key_data_length[k];
        if (tmp_key_data.key_data_contents[k]) {
            kdb->key_data->key_data_contents[k] = krb5_db_alloc(handle->context, NULL, tmp_key_data.key_data_length[k]);
            if (kdb->key_data->key_data_contents[k] == NULL) {
                cleanup_key_data(handle->context, kdb->n_key_data, kdb->key_data);
                kdb->key_data = NULL;
                kdb->n_key_data = 0;
                ret = ENOMEM;
                goto done;
            }
            memcpy (kdb->key_data->key_data_contents[k], tmp_key_data.key_data_contents[k], tmp_key_data.key_data_length[k]);

            memset (tmp_key_data.key_data_contents[k], 0, tmp_key_data.key_data_length[k]);
            free (tmp_key_data.key_data_contents[k]);
            tmp_key_data.key_data_contents[k] = NULL;
        }
    }



    kdb->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;

    ret = krb5_timeofday(handle->context, &now);
    if (ret)
        goto done;

    if ((adb.aux_attributes & KADM5_POLICY)) {
        ret = get_policy(handle, adb.policy, &pol, &have_pol);
        if (ret)
            goto done;
    }
    if (have_pol) {
#if 0
        /*
         * The spec says this check is overridden if the caller has
         * modify privilege.  The admin server therefore makes this
         * check itself (in chpass_principal_wrapper, misc.c).  A
         * local caller implicitly has all authorization bits.
         */
        if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
                                                  kdb, &last_pwd))
            goto done;
        if((now - last_pwd) < pol.pw_min_life &&
           !(kdb->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
            ret = KADM5_PASS_TOOSOON;
            goto done;
        }
#endif

        if (pol.pw_max_life)
            kdb->pw_expiration = now + pol.pw_max_life;
        else
            kdb->pw_expiration = 0;
    } else {
        kdb->pw_expiration = 0;
    }

    ret = krb5_dbe_update_last_pwd_change(handle->context, kdb, now);
    if (ret)
        goto done;

    /* unlock principal on this KDC */
    kdb->fail_auth_count = 0;

    if ((ret = kdb_put_entry(handle, kdb, &adb)))
        goto done;

    ret = KADM5_OK;
done:
    for (i = 0; i < tmp_key_data.key_data_ver; i++) {
        if (tmp_key_data.key_data_contents[i]) {
            memset (tmp_key_data.key_data_contents[i], 0, tmp_key_data.key_data_length[i]);
            free (tmp_key_data.key_data_contents[i]);
        }
    }

    kdb_free_entry(handle, kdb, &adb);
    if (have_pol)
        kadm5_free_policy_ent(handle->lhandle, &pol);

    return ret;
}

Project: krb5
File: svr_principal.c

ks_tuple_present(int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
                 krb5_key_salt_tuple *looking_for)
{
    int i;

    for (i = 0; i < n_ks_tuple; i++) {
        if (ks_tuple[i].ks_enctype == looking_for->ks_enctype &&
            ks_tuple[i].ks_salttype == looking_for->ks_salttype)
            return TRUE;
    }
    return FALSE;
}

Project: krb5
File: svr_principal.c

make_ks_from_keys(krb5_context context, int n_keys, krb5_keyblock *keyblocks,
                  krb5_key_salt_tuple **ks_tuple)
{
    int i;

    *ks_tuple = calloc(n_keys, sizeof(**ks_tuple));
    if (*ks_tuple == NULL)
        return ENOMEM;

    for (i = 0; i < n_keys; i++) {
        (*ks_tuple)[i].ks_enctype = keyblocks[i].enctype;
        (*ks_tuple)[i].ks_salttype = KRB5_KDB_SALTTYPE_NORMAL;
    }
    return 0;
}