Fix flaws in LDAP DN checking

KDB_TL_USER_INFO tl-data is intended to be internal to the LDAP KDB
module, and not used in disk or wire principal entries.  Prevent
kadmin clients from sending KDB_TL_USER_INFO tl-data by giving it a
type number less than 256 and filtering out type numbers less than 256
in kadm5_create_principal_3().  (We already filter out low type
numbers in kadm5_modify_principal()).

In the LDAP KDB module, if containerdn and linkdn are both specified
in a put_principal operation, check both linkdn and the computed
standalone_principal_dn for container membership.  To that end, factor
out the checks into helper functions and call them on all applicable
client-influenced DNs.

CVE-2018-5729:

In MIT krb5 1.6 or later, an authenticated kadmin user with permission
to add principals to an LDAP Kerberos database can cause a null
dereference in kadmind, or circumvent a DN container check, by
supplying tagged data intended to be internal to the database module.
Thanks to Sharwan Ram and Pooja Anil for discovering the potential
null dereference.

CVE-2018-5730:

In MIT krb5 1.6 or later, an authenticated kadmin user with permission
to add principals to an LDAP Kerberos database can circumvent a DN
containership check by supplying both a "linkdn" and "containerdn"
database argument, or by supplying a DN string which is a left
extension of a container DN string but is not hierarchically within
the container DN.

ticket: 8643 (new)
tags: pullup
target_version: 1.16-next
target_version: 1.15-next
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_out)
{
    int i;
    krb5_error_code ret = 0;
    krb5_keyblock key;
    krb5_keysalt salt;
    krb5_ui_2 kvno;
    osa_pw_hist_ent hist;

    hist_out->key_data = NULL;
    hist_out->n_key_data = 0;

    if (n_key_data < 0)
        return EINVAL;

    memset(&key, 0, sizeof(key));
    memset(&hist, 0, sizeof(hist));

    if (n_key_data == 0)
        goto cleanup;

    hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
    if (hist.key_data == NULL)
        goto cleanup;

    /* We only want to store the most recent kvno, and key_data should already
     * be sorted in descending order by kvno. */
    kvno = key_data[0].key_data_kvno;

    for (i = 0; i < n_key_data; i++) {
        if (key_data[i].key_data_kvno < kvno)
            break;
        ret = krb5_dbe_decrypt_key_data(context, NULL,
                                        &key_data[i], &key,
                                        &salt);
        if (ret)
            goto cleanup;

        ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
                                        key_data[i].key_data_kvno,
                                        &hist.key_data[hist.n_key_data]);
        if (ret)
            goto cleanup;
        hist.n_key_data++;
        krb5_free_keyblock_contents(context, &key);
        /* krb5_free_keysalt(context, &salt); */
    }

    *hist_out = hist;
    hist.n_key_data = 0;
    hist.key_data = NULL;

cleanup:
    krb5_free_keyblock_contents(context, &key);
    free_history_entry(context, &hist);
    return ret;
}

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_out)
{
    int i;
    krb5_error_code ret = 0;
    krb5_keyblock key;
    krb5_keysalt salt;
    krb5_ui_2 kvno;
    osa_pw_hist_ent hist;

    hist_out->key_data = NULL;
    hist_out->n_key_data = 0;

    if (n_key_data < 0)
        return EINVAL;

    memset(&key, 0, sizeof(key));
    memset(&hist, 0, sizeof(hist));

    if (n_key_data == 0)
        goto cleanup;

    hist.key_data = k5calloc(n_key_data, sizeof(krb5_key_data), &ret);
    if (hist.key_data == NULL)
        goto cleanup;

    /* We only want to store the most recent kvno, and key_data should already
     * be sorted in descending order by kvno. */
    kvno = key_data[0].key_data_kvno;

    for (i = 0; i < n_key_data; i++) {
        if (key_data[i].key_data_kvno < kvno)
            break;
        ret = krb5_dbe_decrypt_key_data(context, NULL,
                                        &key_data[i], &key,
                                        &salt);
        if (ret)
            goto cleanup;

        ret = krb5_dbe_encrypt_key_data(context, hist_key, &key, &salt,
                                        key_data[i].key_data_kvno,
                                        &hist.key_data[hist.n_key_data]);
        if (ret)
            goto cleanup;
        hist.n_key_data++;
        krb5_free_keyblock_contents(context, &key);
        /* krb5_free_keysalt(context, &salt); */
    }

    *hist_out = hist;
    hist.n_key_data = 0;
    hist.key_data = NULL;

cleanup:
    krb5_free_keyblock_contents(context, &key);
    free_history_entry(context, &hist);
    return ret;
}
