Beispiel #1
0
krb5_error_code krb5int_hmacmd5_checksum(const struct krb5_cksumtypes *ctp,
                                         krb5_key key, krb5_keyusage usage,
                                         const krb5_crypto_iov *data,
                                         size_t num_data,
                                         krb5_data *output)
{
    krb5_keyusage ms_usage;
    krb5_error_code ret;
    krb5_keyblock ks, *keyblock;
    krb5_crypto_iov *hash_iov = NULL, iov;
    krb5_data ds = empty_data(), hashval = empty_data();
    char t[4];

    if (key == NULL || key->keyblock.length > ctp->hash->blocksize)
        return KRB5_BAD_ENCTYPE;
    if (ctp->ctype == CKSUMTYPE_HMAC_MD5_ARCFOUR) {
        /* Compute HMAC(key, "signaturekey\0") to get the signing key ks. */
        ret = alloc_data(&ds, ctp->hash->hashsize);
        if (ret != 0)
            goto cleanup;

        iov.flags = KRB5_CRYPTO_TYPE_DATA;
        iov.data = make_data("signaturekey", 13);
        ret = krb5int_hmac(ctp->hash, key, &iov, 1, &ds);
        if (ret)
            goto cleanup;
        ks.length = key->keyblock.length;
        ks.contents = (krb5_octet *) ds.data;
        keyblock = &ks;
    } else  /* For md5-hmac, just use the key. */
        keyblock = &key->keyblock;

    /* Compute the MD5 value of the input. */
    ms_usage = krb5int_arcfour_translate_usage(usage);
    store_32_le(ms_usage, t);
    hash_iov = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
    if (hash_iov == NULL)
        goto cleanup;
    hash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    hash_iov[0].data = make_data(t, 4);
    memcpy(hash_iov + 1, data, num_data * sizeof(krb5_crypto_iov));
    ret = alloc_data(&hashval, ctp->hash->hashsize);
    if (ret != 0)
        goto cleanup;
    ret = ctp->hash->hash(hash_iov, num_data + 1, &hashval);
    if (ret != 0)
        goto cleanup;

    /* Compute HMAC(ks, md5value). */
    iov.flags = KRB5_CRYPTO_TYPE_DATA;
    iov.data = hashval;
    ret = krb5int_hmac_keyblock(ctp->hash, keyblock, &iov, 1, output);

cleanup:
    zapfree(ds.data, ds.length);
    zapfree(hashval.data, hashval.length);
    free(hash_iov);
    return ret;
}
Beispiel #2
0
krb5_error_code krb5int_confounder_verify(const struct krb5_cksumtypes *ctp,
                                          krb5_key key, krb5_keyusage usage,
                                          const krb5_crypto_iov *data,
                                          size_t num_data,
                                          const krb5_data *input,
                                          krb5_boolean *valid)
{
    krb5_error_code ret;
    unsigned char *plaintext = NULL;
    krb5_key xorkey = NULL;
    krb5_data computed = empty_data();
    krb5_crypto_iov *hash_iov = NULL, iov;
    size_t blocksize = ctp->enc->block_size, hashsize = ctp->hash->hashsize;

    plaintext = k5memdup(input->data, input->length, &ret);
    if (plaintext == NULL)
        return ret;

    ret = mk_xorkey(key, &xorkey);
    if (ret != 0)
        goto cleanup;

    /* Decrypt the input checksum. */
    iov.flags = KRB5_CRYPTO_TYPE_DATA;
    iov.data = make_data(plaintext, input->length);
    ret = ctp->enc->decrypt(xorkey, NULL, &iov, 1);
    if (ret != 0)
        goto cleanup;

    /* Hash the confounder, then the input data. */
    hash_iov = k5alloc((num_data + 1) * sizeof(krb5_crypto_iov), &ret);
    if (hash_iov == NULL)
        goto cleanup;
    hash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    hash_iov[0].data = make_data(plaintext, blocksize);
    memcpy(hash_iov + 1, data, num_data * sizeof(krb5_crypto_iov));
    ret = alloc_data(&computed, hashsize);
    if (ret != 0)
        goto cleanup;
    ret = ctp->hash->hash(hash_iov, num_data + 1, &computed);
    if (ret != 0)
        goto cleanup;

    /* Compare the decrypted hash to the computed one. */
    *valid = (memcmp(plaintext + blocksize, computed.data, hashsize) == 0);

cleanup:
    zapfree(plaintext, input->length);
    zapfree(computed.data, hashsize);
    free(hash_iov);
    krb5_k_free_key(NULL, xorkey);
    return ret;
}
Beispiel #3
0
void
krb5_dbe_free(krb5_context context, krb5_db_entry *entry)
{
    krb5_tl_data        * tl_data_next;
    krb5_tl_data        * tl_data;
    int i, j;

    if (entry == NULL)
        return;
    free(entry->e_data);
    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;
        free(tl_data->tl_data_contents);
        free(tl_data);
    }
    if (entry->key_data) {
        for (i = 0; i < entry->n_key_data; i++) {
            for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
                if (entry->key_data[i].key_data_length[j]) {
                    zapfree(entry->key_data[i].key_data_contents[j],
                            entry->key_data[i].key_data_length[j]);
                }
                entry->key_data[i].key_data_contents[j] = NULL;
                entry->key_data[i].key_data_length[j] = 0;
                entry->key_data[i].key_data_type[j] = 0;
            }
        }
        free(entry->key_data);
    }
    free(entry);
}
Beispiel #4
0
/*
 * Marshal a KRB-PRIV message into der_out, encrypted with key.  Store the
 * ciphertext in enc_out.  Use the timestamp and sequence number from rdata and
 * the addresses from local_addr and remote_addr (the second of which may be
 * NULL).  der_out and enc_out should be freed by the caller when finished.
 */
static krb5_error_code
create_krbpriv(krb5_context context, const krb5_data *userdata,
               krb5_key key, const krb5_replay_data *rdata,
               krb5_address *local_addr, krb5_address *remote_addr,
               krb5_data *cstate, krb5_data *der_out, krb5_enc_data *enc_out)
{
    krb5_enctype enctype = krb5_k_key_enctype(context, key);
    krb5_error_code ret;
    krb5_priv privmsg;
    krb5_priv_enc_part encpart;
    krb5_data *der_encpart = NULL, *der_krbpriv;
    size_t enclen;

    memset(&privmsg, 0, sizeof(privmsg));
    privmsg.enc_part.kvno = 0;
    privmsg.enc_part.enctype = enctype;
    encpart.user_data = *userdata;
    encpart.s_address = local_addr;
    encpart.r_address = remote_addr;
    encpart.timestamp = rdata->timestamp;
    encpart.usec = rdata->usec;
    encpart.seq_number = rdata->seq;

    /* Start by encoding the to-be-encrypted part of the message. */
    ret = encode_krb5_enc_priv_part(&encpart, &der_encpart);
    if (ret)
        return ret;

    /* put together an eblock for this encryption */
    ret = krb5_c_encrypt_length(context, enctype, der_encpart->length,
                                &enclen);
    if (ret)
        goto cleanup;

    ret = alloc_data(&privmsg.enc_part.ciphertext, enclen);
    if (ret)
        goto cleanup;

    ret = krb5_k_encrypt(context, key, KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
                         (cstate->length > 0) ? cstate : NULL, der_encpart,
                         &privmsg.enc_part);
    if (ret)
        goto cleanup;

    ret = encode_krb5_priv(&privmsg, &der_krbpriv);
    if (ret)
        goto cleanup;

    *der_out = *der_krbpriv;
    free(der_krbpriv);

    *enc_out = privmsg.enc_part;
    memset(&privmsg.enc_part, 0, sizeof(privmsg.enc_part));

cleanup:
    zapfree(privmsg.enc_part.ciphertext.data,
            privmsg.enc_part.ciphertext.length);
    zapfreedata(der_encpart);
    return ret;
}
Beispiel #5
0
/*
 * Compute a derived key into the keyblock outkey.  This variation on
 * krb5int_derive_key does not cache the result, as it is only used
 * directly in situations which are not expected to be repeated with
 * the same inkey and constant.
 */
krb5_error_code
krb5int_derive_keyblock(const struct krb5_enc_provider *enc,
                        krb5_key inkey, krb5_keyblock *outkey,
                        const krb5_data *in_constant)
{
    krb5_error_code ret;
    krb5_data rawkey = empty_data();

    /* Allocate a buffer for the raw key bytes. */
    ret = alloc_data(&rawkey, enc->keybytes);
    if (ret)
        goto cleanup;

    /* Derive pseudo-random data for the key bytes. */
    ret = krb5int_derive_random(enc, inkey, &rawkey, in_constant);
    if (ret)
        goto cleanup;

    /* Postprocess the key. */
    ret = enc->make_key(&rawkey, outkey);

cleanup:
    zapfree(rawkey.data, enc->keybytes);
    return ret;
}
Beispiel #6
0
/*
 * Decrypt and decode the enc_part of a krb5_cred using the receiving subkey or
 * the session key of authcon.  If neither key is present, ctext->ciphertext is
 * assumed to be unencrypted plain text (RFC 6448).
 */
static krb5_error_code
decrypt_encpart(krb5_context context, krb5_enc_data *ctext,
                krb5_auth_context authcon, krb5_cred_enc_part **encpart_out)
{
    krb5_error_code ret;
    krb5_data plain = empty_data();
    krb5_boolean decrypted = FALSE;

    *encpart_out = NULL;

    if (authcon->recv_subkey == NULL && authcon->key == NULL)
        return decode_krb5_enc_cred_part(&ctext->ciphertext, encpart_out);

    ret = alloc_data(&plain, ctext->ciphertext.length);
    if (ret)
        return ret;
    if (authcon->recv_subkey != NULL) {
        ret = krb5_k_decrypt(context, authcon->recv_subkey,
                             KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain);
        decrypted = (ret == 0);
    }
    if (!decrypted && authcon->key != NULL) {
        ret = krb5_k_decrypt(context, authcon->key,
                             KRB5_KEYUSAGE_KRB_CRED_ENCPART, 0, ctext, &plain);
        decrypted = (ret == 0);
    }
    if (decrypted)
        ret = decode_krb5_enc_cred_part(&plain, encpart_out);
    zapfree(plain.data, plain.length);
    return ret;
}
Beispiel #7
0
krb5_error_code KRB5_CALLCONV
krb5_k_decrypt(krb5_context context, krb5_key key,
               krb5_keyusage usage, const krb5_data *ivec,
               const krb5_enc_data *input, krb5_data *output)
{
    const struct krb5_keytypes *ktp;
    krb5_crypto_iov iov[4];
    krb5_error_code ret;
    unsigned int header_len, trailer_len, plain_len;
    char *scratch = NULL;

    ktp = find_enctype(key->keyblock.enctype);
    if (ktp == NULL)
        return KRB5_BAD_ENCTYPE;

    if (input->enctype != ENCTYPE_UNKNOWN && ktp->etype != input->enctype)
        return KRB5_BAD_ENCTYPE;

    /* Verify the input and output lengths. */
    header_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_HEADER);
    trailer_len = ktp->crypto_length(ktp, KRB5_CRYPTO_TYPE_TRAILER);
    if (input->ciphertext.length < header_len + trailer_len)
        return KRB5_BAD_MSIZE;
    plain_len = input->ciphertext.length - header_len - trailer_len;
    if (output->length < plain_len)
        return KRB5_BAD_MSIZE;

    scratch = k5alloc(header_len + trailer_len, &ret);
    if (scratch == NULL)
        return ret;

    iov[0].flags = KRB5_CRYPTO_TYPE_HEADER;
    iov[0].data = make_data(scratch, header_len);
    memcpy(iov[0].data.data, input->ciphertext.data, header_len);

    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[1].data = make_data(output->data, plain_len);
    memcpy(iov[1].data.data, input->ciphertext.data + header_len, plain_len);

    /* Use empty padding since tokens don't indicate the padding length. */
    iov[2].flags = KRB5_CRYPTO_TYPE_PADDING;
    iov[2].data = empty_data();

    iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER;
    iov[3].data = make_data(scratch + header_len, trailer_len);
    memcpy(iov[3].data.data, input->ciphertext.data + header_len + plain_len,
           trailer_len);

    ret = ktp->decrypt(ktp, key, usage, ivec, iov, 4);
    if (ret != 0)
        zap(output->data, plain_len);
    else
        output->length = plain_len;
    zapfree(scratch, header_len + trailer_len);
    return ret;
}
Beispiel #8
0
krb5_error_code
krb5int_derive_random(const struct krb5_enc_provider *enc,
                      krb5_key inkey, krb5_data *outrnd,
                      const krb5_data *in_constant)
{
    size_t blocksize, keybytes, n;
    krb5_crypto_iov iov;
    krb5_error_code ret;

    blocksize = enc->block_size;
    keybytes = enc->keybytes;

    if (blocksize == 1)
        return KRB5_BAD_ENCTYPE;
    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
        return KRB5_CRYPTO_INTERNAL;

    /* Allocate encryption data buffer. */
    iov.flags = KRB5_CRYPTO_TYPE_DATA;
    ret = alloc_data(&iov.data, blocksize);
    if (ret)
        return ret;

    /* Initialize the input block. */
    if (in_constant->length == blocksize) {
        memcpy(iov.data.data, in_constant->data, blocksize);
    } else {
        krb5int_nfold(in_constant->length * 8,
                      (unsigned char *) in_constant->data,
                      blocksize * 8, (unsigned char *) iov.data.data);
    }

    /* Loop encrypting the blocks until enough key bytes are generated. */
    n = 0;
    while (n < keybytes) {
        ret = enc->encrypt(inkey, 0, &iov, 1);
        if (ret)
            goto cleanup;

        if ((keybytes - n) <= blocksize) {
            memcpy(outrnd->data + n, iov.data.data, (keybytes - n));
            break;
        }

        memcpy(outrnd->data + n, iov.data.data, blocksize);
        n += blocksize;
    }

cleanup:
    zapfree(iov.data.data, blocksize);
    return ret;
}
Beispiel #9
0
krb5_error_code KRB5_CALLCONV
krb5_mk_priv(krb5_context context, krb5_auth_context authcon,
             const krb5_data *userdata, krb5_data *der_out,
             krb5_replay_data *rdata_out)
{
    krb5_error_code ret;
    krb5_key key;
    krb5_replay_data rdata;
    krb5_data der_krbpriv = empty_data();
    krb5_enc_data enc;
    krb5_address *local_addr, *remote_addr, lstorage, rstorage;

    *der_out = empty_data();
    memset(&enc, 0, sizeof(enc));
    memset(&lstorage, 0, sizeof(lstorage));
    memset(&rstorage, 0, sizeof(rstorage));
    if (!authcon->local_addr)
        return KRB5_LOCAL_ADDR_REQUIRED;

    ret = k5_privsafe_gen_rdata(context, authcon, &rdata, rdata_out);
    if (ret)
        goto cleanup;

    ret = k5_privsafe_gen_addrs(context, authcon, &lstorage, &rstorage,
                                &local_addr, &remote_addr);
    if (ret)
        goto cleanup;

    key = (authcon->send_subkey != NULL) ? authcon->send_subkey : authcon->key;
    ret = create_krbpriv(context, userdata, key, &rdata, local_addr,
                         remote_addr, &authcon->cstate, &der_krbpriv, &enc);
    if (ret)
        goto cleanup;

    ret = k5_privsafe_check_replay(context, authcon, NULL, &enc, NULL);
    if (ret)
        goto cleanup;

    *der_out = der_krbpriv;
    der_krbpriv = empty_data();
    if ((authcon->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
        (authcon->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
        authcon->local_seq_number++;

cleanup:
    krb5_free_data_contents(context, &der_krbpriv);
    zapfree(enc.ciphertext.data, enc.ciphertext.length);
    free(lstorage.contents);
    free(rstorage.contents);
    return ret;
}
Beispiel #10
0
krb5_error_code
krb5int_dk_cmac_encrypt(const struct krb5_keytypes *ktp, krb5_key key,
			krb5_keyusage usage, const krb5_data *ivec,
			krb5_crypto_iov *data, size_t num_data)
{
    const struct krb5_enc_provider *enc = ktp->enc;
    krb5_error_code ret;
    krb5_crypto_iov *header, *trailer, *padding;
    krb5_data cksum = empty_data();
    krb5_key ke = NULL, ki = NULL;

    /* E(Confounder | Plaintext | Pad) | Checksum */

    /* Validate header and trailer lengths, and zero out padding length. */
    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
    if (header == NULL || header->data.length < enc->block_size)
        return KRB5_BAD_MSIZE;
    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
    if (trailer == NULL || trailer->data.length < enc->block_size)
        return KRB5_BAD_MSIZE;
    padding = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
    if (padding != NULL)
	padding->data.length = 0;

    /* Derive the encryption and integrity keys. */
    ret = derive_keys(enc, key, usage, &ke, &ki);
    if (ret != 0)
        goto cleanup;

    /* Generate confounder. */
    header->data.length = enc->block_size;
    ret = krb5_c_random_make_octets(NULL, &header->data);
    if (ret != 0)
        goto cleanup;

    /* Checksum the plaintext. */
    ret = krb5int_cmac_checksum(enc, ki, data, num_data, &trailer->data);
    if (ret != 0)
        goto cleanup;

    /* Encrypt the plaintext (header | data | padding) */
    ret = enc->encrypt(ke, ivec, data, num_data);
    if (ret != 0)
        goto cleanup;

cleanup:
    krb5_k_free_key(NULL, ke);
    krb5_k_free_key(NULL, ki);
    zapfree(cksum.data, cksum.length);
    return ret;
}
Beispiel #11
0
static krb5_error_code
k5_arcfour_docrypt(krb5_key key, const krb5_data *state, krb5_crypto_iov *data,
                   size_t num_data)
{
    ArcfourContext *arcfour_ctx = NULL;
    ArcFourCipherState *cipher_state = NULL;
    krb5_error_code ret;
    size_t i;

    if (key->keyblock.length != 16)
        return KRB5_BAD_KEYSIZE;
    if (state != NULL && (state->length != sizeof(ArcFourCipherState)))
        return KRB5_BAD_MSIZE;

    if (state != NULL) {
        cipher_state = (ArcFourCipherState *)state->data;
        arcfour_ctx = &cipher_state->ctx;
        if (cipher_state->initialized == 0) {
            ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents,
                                  key->keyblock.length);
            if (ret != 0)
                return ret;

            cipher_state->initialized = 1;
        }
    } else {
        arcfour_ctx = (ArcfourContext *)malloc(sizeof(ArcfourContext));
        if (arcfour_ctx == NULL)
            return ENOMEM;

        ret = k5_arcfour_init(arcfour_ctx, key->keyblock.contents,
                              key->keyblock.length);
        if (ret != 0) {
            free(arcfour_ctx);
            return ret;
        }
    }

    for (i = 0; i < num_data; i++) {
        krb5_crypto_iov *iov = &data[i];

        if (ENCRYPT_IOV(iov))
            k5_arcfour_crypt(arcfour_ctx, (unsigned char *)iov->data.data,
                             (const unsigned char *)iov->data.data, iov->data.length);
    }

    if (state == NULL)
        zapfree(arcfour_ctx, sizeof(ArcfourContext));

    return 0;
}
Beispiel #12
0
krb5_error_code KRB5_CALLCONV
krb5_k_make_checksum(krb5_context context, krb5_cksumtype cksumtype,
                     krb5_key key, krb5_keyusage usage,
                     const krb5_data *input, krb5_checksum *cksum)
{
    const struct krb5_cksumtypes *ctp;
    krb5_crypto_iov iov;
    krb5_data cksum_data;
    krb5_octet *trunc;
    krb5_error_code ret;

    if (cksumtype == 0) {
        ret = krb5int_c_mandatory_cksumtype(context, key->keyblock.enctype,
                                            &cksumtype);
        if (ret != 0)
            return ret;
    }
    ctp = find_cksumtype(cksumtype);
    if (ctp == NULL)
        return KRB5_BAD_ENCTYPE;

    ret = verify_key(ctp, key);
    if (ret != 0)
        return ret;

    ret = alloc_data(&cksum_data, ctp->compute_size);
    if (ret != 0)
        return ret;

    iov.flags = KRB5_CRYPTO_TYPE_DATA;
    iov.data = *input;
    ret = ctp->checksum(ctp, key, usage, &iov, 1, &cksum_data);
    if (ret != 0)
        goto cleanup;

    cksum->magic = KV5M_CHECKSUM;
    cksum->checksum_type = cksumtype;
    cksum->length = ctp->output_size;
    cksum->contents = (krb5_octet *) cksum_data.data;
    cksum_data.data = NULL;
    if (ctp->output_size < ctp->compute_size) {
        trunc = realloc(cksum->contents, ctp->output_size);
        if (trunc != NULL)
            cksum->contents = trunc;
    }

cleanup:
    zapfree(cksum_data.data, ctp->compute_size);
    return ret;
}
Beispiel #13
0
krb5_error_code KRB5_CALLCONV
krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype,
                                 const krb5_data *string,
                                 const krb5_data *salt,
                                 const krb5_data *params, krb5_keyblock *key)
{
    krb5_error_code ret;
    const struct krb5_keytypes *ktp;
    size_t keylength;

    ktp = find_enctype(enctype);
    if (ktp == NULL)
        return KRB5_BAD_ENCTYPE;
    keylength = ktp->enc->keylength;

    /*
     * xxx AFS string2key function is indicated by a special length  in
     * the salt in much of the code.  However only the DES enctypes can
     * deal with this.  Using s2kparams would be a much better solution.
     */
    if (salt && salt->length == SALT_TYPE_AFS_LENGTH) {
        switch (enctype) {
        case ENCTYPE_DES_CBC_CRC:
        case ENCTYPE_DES_CBC_MD4:
        case ENCTYPE_DES_CBC_MD5:
            break;
        default:
            return KRB5_CRYPTO_INTERNAL;
        }
    }

    key->contents = malloc(keylength);
    if (key->contents == NULL)
        return ENOMEM;

    key->magic = KV5M_KEYBLOCK;
    key->enctype = enctype;
    key->length = keylength;

    ret = (*ktp->str2key)(ktp, string, salt, params, key);
    if (ret) {
        zapfree(key->contents, keylength);
        key->length = 0;
        key->contents = NULL;
    }

    return ret;
}
Beispiel #14
0
krb5_error_code
krb5int_dk_cmac_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
                        krb5_keyusage usage, const krb5_data *ivec,
                        krb5_crypto_iov *data, size_t num_data)
{
    const struct krb5_enc_provider *enc = ktp->enc;
    krb5_error_code ret;
    krb5_crypto_iov *header, *trailer;
    krb5_data cksum = empty_data();
    krb5_key ke = NULL, ki = NULL;

    /* E(Confounder | Plaintext | Pad) | Checksum */

    /* Validate header and trailer lengths. */
    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
    if (header == NULL || header->data.length != enc->block_size)
        return KRB5_BAD_MSIZE;
    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
    if (trailer == NULL || trailer->data.length != enc->block_size)
        return KRB5_BAD_MSIZE;

    /* Derive the encryption and integrity keys. */
    ret = derive_keys(enc, key, usage, &ke, &ki);
    if (ret != 0)
        goto cleanup;

    /* Decrypt the plaintext (header | data | padding). */
    ret = enc->decrypt(ke, ivec, data, num_data);
    if (ret != 0)
        goto cleanup;

    /* Verify the hash. */
    ret = alloc_data(&cksum, enc->block_size);
    if (ret != 0)
        goto cleanup;
    ret = krb5int_cmac_checksum(enc, ki, data, num_data, &cksum);
    if (ret != 0)
        goto cleanup;
    if (k5_bcmp(cksum.data, trailer->data.data, enc->block_size) != 0)
        ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;

cleanup:
    krb5_k_free_key(NULL, ke);
    krb5_k_free_key(NULL, ki);
    zapfree(cksum.data, cksum.length);
    return ret;
}
Beispiel #15
0
krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
                   krb5_key inkey, krb5_key *outkey,
                   const krb5_data *in_constant)
{
    krb5_keyblock keyblock;
    krb5_error_code ret;
    krb5_key dkey;

    *outkey = NULL;

    /* Check for a cached result. */
    dkey = find_cached_dkey(inkey->derived, in_constant);
    if (dkey != NULL) {
        *outkey = dkey;
        return 0;
    }

    /* Derive into a temporary keyblock. */
    keyblock.length = enc->keylength;
    keyblock.contents = malloc(keyblock.length);
    /* Set the enctype as the krb5_k_free_key will iterate over list
       or derived keys and invoke krb5_k_free_key which will lookup
       the enctype for key_cleanup handler */
    keyblock.enctype = inkey->keyblock.enctype;
    if (keyblock.contents == NULL)
        return ENOMEM;
    ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant);
    if (ret)
        goto cleanup;

    /* Cache the derived key. */
    ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey);
    if (ret != 0)
        goto cleanup;

    *outkey = dkey;

cleanup:
    zapfree(keyblock.contents, keyblock.length);
    return ret;
}
Beispiel #16
0
krb5_error_code KRB5_CALLCONV
krb5_c_string_to_key_with_params(krb5_context context, krb5_enctype enctype,
                                 const krb5_data *string,
                                 const krb5_data *salt,
                                 const krb5_data *params, krb5_keyblock *key)
{
    krb5_error_code ret;
    krb5_data empty = empty_data();
    const struct krb5_keytypes *ktp;
    size_t keylength;

    ktp = find_enctype(enctype);
    if (ktp == NULL)
        return KRB5_BAD_ENCTYPE;
    keylength = ktp->enc->keylength;

    /* For compatibility with past behavior, treat a null salt as empty. */
    if (salt == NULL)
        salt = &empty;

    /* Fail gracefully if someone is using the old AFS string-to-key hack. */
    if (salt->length == SALT_TYPE_AFS_LENGTH)
        return EINVAL;

    key->contents = malloc(keylength);
    if (key->contents == NULL)
        return ENOMEM;

    key->magic = KV5M_KEYBLOCK;
    key->enctype = enctype;
    key->length = keylength;

    ret = (*ktp->str2key)(ktp, string, salt, params, key);
    if (ret) {
        zapfree(key->contents, keylength);
        key->length = 0;
        key->contents = NULL;
    }

    return ret;
}
Beispiel #17
0
krb5_error_code
krb5int_derive_key(const struct krb5_enc_provider *enc,
                   krb5_key inkey, krb5_key *outkey,
                   const krb5_data *in_constant, enum deriv_alg alg)
{
    krb5_keyblock keyblock;
    krb5_error_code ret;
    krb5_key dkey;

    *outkey = NULL;

    /* Check for a cached result. */
    dkey = find_cached_dkey(inkey->derived, in_constant);
    if (dkey != NULL) {
        *outkey = dkey;
        return 0;
    }

    /* Derive into a temporary keyblock. */
    keyblock.length = enc->keylength;
    keyblock.contents = malloc(keyblock.length);
    keyblock.enctype = inkey->keyblock.enctype;
    if (keyblock.contents == NULL)
        return ENOMEM;
    ret = krb5int_derive_keyblock(enc, inkey, &keyblock, in_constant, alg);
    if (ret)
        goto cleanup;

    /* Cache the derived key. */
    ret = add_cached_dkey(inkey, in_constant, &keyblock, &dkey);
    if (ret != 0)
        goto cleanup;

    *outkey = dkey;

cleanup:
    zapfree(keyblock.contents, keyblock.length);
    return ret;
}
Beispiel #18
0
/* Derive a key by XOR with 0xF0 bytes. */
static krb5_error_code
mk_xorkey(krb5_key origkey, krb5_key *xorkey)
{
    krb5_error_code retval = 0;
    unsigned char *xorbytes;
    krb5_keyblock xorkeyblock;
    size_t i = 0;

    xorbytes = k5memdup(origkey->keyblock.contents, origkey->keyblock.length,
                        &retval);
    if (xorbytes == NULL)
        return retval;
    for (i = 0; i < origkey->keyblock.length; i++)
        xorbytes[i] ^= 0xf0;

    /* Do a shallow copy here. */
    xorkeyblock = origkey->keyblock;
    xorkeyblock.contents = xorbytes;

    retval = krb5_k_create_key(0, &xorkeyblock, xorkey);
    zapfree(xorbytes, sizeof(xorbytes));
    return retval;
}
Beispiel #19
0
/* Construct an AP-REQ message for a TGS request. */
static krb5_error_code
tgs_construct_ap_req(krb5_context context, krb5_data *checksum_data,
                     krb5_creds *tgt, krb5_keyblock *subkey,
                     krb5_data **ap_req_asn1_out)
{
    krb5_cksumtype cksumtype;
    krb5_error_code ret;
    krb5_checksum checksum;
    krb5_authenticator authent;
    krb5_ap_req ap_req;
    krb5_data *authent_asn1 = NULL;
    krb5_ticket *ticket = NULL;
    krb5_enc_data authent_enc;

    *ap_req_asn1_out = NULL;
    memset(&checksum, 0, sizeof(checksum));
    memset(&ap_req, 0, sizeof(ap_req));
    memset(&authent_enc, 0, sizeof(authent_enc));

    /* Determine the authenticator checksum type. */
    switch (tgt->keyblock.enctype) {
    case ENCTYPE_DES_CBC_CRC:
    case ENCTYPE_DES_CBC_MD4:
    case ENCTYPE_DES_CBC_MD5:
    case ENCTYPE_ARCFOUR_HMAC:
    case ENCTYPE_ARCFOUR_HMAC_EXP:
        cksumtype = context->kdc_req_sumtype;
        break;
    default:
        ret = krb5int_c_mandatory_cksumtype(context, tgt->keyblock.enctype,
                                            &cksumtype);
        if (ret)
            goto cleanup;
    }

    /* Generate checksum. */
    ret = krb5_c_make_checksum(context, cksumtype, &tgt->keyblock,
                               KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM, checksum_data,
                               &checksum);
    if (ret)
        goto cleanup;

    /* Construct, encode, and encrypt an authenticator. */
    authent.subkey = subkey;
    authent.seq_number = 0;
    authent.checksum = &checksum;
    authent.client = tgt->client;
    authent.authorization_data = tgt->authdata;
    ret = krb5_us_timeofday(context, &authent.ctime, &authent.cusec);
    if (ret)
        goto cleanup;
    ret = encode_krb5_authenticator(&authent, &authent_asn1);
    if (ret)
        goto cleanup;
    ret = krb5_encrypt_helper(context, &tgt->keyblock,
                              KRB5_KEYUSAGE_TGS_REQ_AUTH, authent_asn1,
                              &authent_enc);
    if (ret)
        goto cleanup;

    ret = decode_krb5_ticket(&tgt->ticket, &ticket);
    if (ret)
        goto cleanup;

    /* Encode the AP-REQ. */
    ap_req.authenticator = authent_enc;
    ap_req.ticket = ticket;
    ret = encode_krb5_ap_req(&ap_req, ap_req_asn1_out);

cleanup:
    free(checksum.contents);
    krb5_free_ticket(context, ticket);
    krb5_free_data_contents(context, &authent_enc.ciphertext);
    if (authent_asn1 != NULL)
        zapfree(authent_asn1->data, authent_asn1->length);
    free(authent_asn1);
    return ret;
}
Beispiel #20
0
/* Construct a cookie pa-data item using the cookie values from state, or a
 * trivial "MIT" cookie if no values are set. */
krb5_error_code
kdc_fast_make_cookie(krb5_context context, struct kdc_request_state *state,
                     krb5_db_entry *local_tgt,
                     krb5_const_principal client_princ,
                     krb5_pa_data **cookie_out)
{
    krb5_error_code ret;
    krb5_secure_cookie cookie;
    krb5_pa_data **contents = state->out_cookie_padata, *pa;
    krb5_keyblock *key = NULL;
    krb5_timestamp now;
    krb5_enc_data enc;
    krb5_data *der_cookie = NULL;
    krb5_kvno kvno;
    size_t ctlen;

    *cookie_out = NULL;
    memset(&enc, 0, sizeof(enc));

    /* Make a trivial cookie if there are no contents to marshal or we don't
     * have a TGT entry to encrypt them. */
    if (contents == NULL || *contents == NULL || local_tgt == NULL)
        return make_padata(KRB5_PADATA_FX_COOKIE, "MIT", 3, cookie_out);

    ret = get_cookie_key(context, local_tgt, 0, client_princ, &key, &kvno);
    if (ret)
        goto cleanup;

    /* Encode the cookie. */
    ret = krb5_timeofday(context, &now);
    if (ret)
        goto cleanup;
    cookie.time = now;
    cookie.data = contents;
    ret = encode_krb5_secure_cookie(&cookie, &der_cookie);
    if (ret)
        goto cleanup;

    /* Encrypt the cookie in key. */
    ret = krb5_c_encrypt_length(context, key->enctype, der_cookie->length,
                                &ctlen);
    if (ret)
        goto cleanup;
    ret = alloc_data(&enc.ciphertext, ctlen);
    if (ret)
        goto cleanup;
    ret = krb5_c_encrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL,
                         der_cookie, &enc);
    if (ret)
        goto cleanup;

    /* Construct the cookie pa-data entry. */
    ret = alloc_padata(KRB5_PADATA_FX_COOKIE, 8 + enc.ciphertext.length, &pa);
    memcpy(pa->contents, "MIT1", 4);
    store_32_be(kvno, pa->contents + 4);
    memcpy(pa->contents + 8, enc.ciphertext.data, enc.ciphertext.length);
    *cookie_out = pa;

cleanup:
    krb5_free_keyblock(context, key);
    if (der_cookie != NULL) {
        zapfree(der_cookie->data, der_cookie->length);
        free(der_cookie);
    }
    krb5_free_data_contents(context, &enc.ciphertext);
    return ret;
}
Beispiel #21
0
/*
 * Locate and decode the FAST cookie in req, storing its contents in state for
 * later access by preauth modules.  If the cookie is expired, return
 * KRB5KDC_ERR_PREAUTH_EXPIRED if its contents are relevant to req, and ignore
 * it if they aren't.
 */
krb5_error_code
kdc_fast_read_cookie(krb5_context context, struct kdc_request_state *state,
                     krb5_kdc_req *req, krb5_db_entry *local_tgt)
{
    krb5_error_code ret;
    krb5_secure_cookie *cookie = NULL;
    krb5_timestamp now;
    krb5_keyblock *key = NULL;
    krb5_enc_data enc;
    krb5_pa_data *pa;
    krb5_kvno kvno;
    krb5_data plain = empty_data();

    pa = krb5int_find_pa_data(context, req->padata, KRB5_PADATA_FX_COOKIE);
    if (pa == NULL)
        return 0;

    /* If it's not an MIT version 1 cookie, ignore it.  It may be an empty
     * "MIT" cookie or a cookie generated by a different KDC implementation. */
    if (pa->length <= 8 || memcmp(pa->contents, "MIT1", 4) != 0)
        return 0;

    /* Extract the kvno and generate the corresponding cookie key. */
    kvno = load_32_be(pa->contents + 4);
    ret = get_cookie_key(context, local_tgt, kvno, req->client, &key, NULL);
    if (ret)
        goto cleanup;

    /* Decrypt and decode the cookie. */
    memset(&enc, 0, sizeof(enc));
    enc.enctype = key->enctype;
    enc.ciphertext = make_data(pa->contents + 8, pa->length - 8);
    ret = alloc_data(&plain, pa->length - 8);
    if (ret)
        goto cleanup;
    ret = krb5_c_decrypt(context, key, KRB5_KEYUSAGE_PA_FX_COOKIE, NULL, &enc,
                         &plain);
    if (ret)
        goto cleanup;
    ret = decode_krb5_secure_cookie(&plain, &cookie);
    if (ret)
        goto cleanup;

    /* Check if the cookie is expired. */
    ret = krb5_timeofday(context, &now);
    if (ret)
        goto cleanup;
    if (now - COOKIE_LIFETIME > cookie->time) {
        /* Don't accept the cookie contents.  Only return an error if the
         * cookie is relevant to the request. */
        if (is_relevant(cookie->data, req->padata))
            ret = KRB5KDC_ERR_PREAUTH_EXPIRED;
        goto cleanup;
    }

    /* Steal the pa-data list pointer from the cookie and store it in state. */
    state->in_cookie_padata = cookie->data;
    cookie->data = NULL;

cleanup:
    zapfree(plain.data, plain.length);
    krb5_free_keyblock(context, key);
    k5_free_secure_cookie(context, cookie);
    return 0;
}
Beispiel #22
0
krb5_error_code
krb5int_c_combine_keys(krb5_context context, krb5_keyblock *key1,
                       krb5_keyblock *key2, krb5_keyblock *outkey)
{
    unsigned char *r1 = NULL, *r2 = NULL, *combined = NULL, *rnd = NULL;
    unsigned char *output = NULL;
    size_t keybytes, keylength;
    const struct krb5_enc_provider *enc;
    krb5_data input, randbits;
    krb5_keyblock tkeyblock;
    krb5_key tkey = NULL;
    krb5_error_code ret;
    const struct krb5_keytypes *ktp;
    krb5_boolean myalloc = FALSE;

    if (!enctype_ok(key1->enctype) || !enctype_ok(key2->enctype))
        return KRB5_CRYPTO_INTERNAL;

    if (key1->length != key2->length || key1->enctype != key2->enctype)
        return KRB5_CRYPTO_INTERNAL;

    /* Find our encryption algorithm. */
    ktp = find_enctype(key1->enctype);
    if (ktp == NULL)
        return KRB5_BAD_ENCTYPE;
    enc = ktp->enc;

    keybytes = enc->keybytes;
    keylength = enc->keylength;

    /* Allocate and set up buffers. */
    r1 = k5alloc(keybytes, &ret);
    if (ret)
        goto cleanup;
    r2 = k5alloc(keybytes, &ret);
    if (ret)
        goto cleanup;
    rnd = k5alloc(keybytes, &ret);
    if (ret)
        goto cleanup;
    combined = k5alloc(keybytes * 2, &ret);
    if (ret)
        goto cleanup;
    output = k5alloc(keylength, &ret);
    if (ret)
        goto cleanup;

    /*
     * Get R1 and R2 (by running the input keys through the DR algorithm.
     * Note this is most of derive-key, but not all.
     */

    input.length = key2->length;
    input.data = (char *) key2->contents;
    ret = dr(enc, key1, r1, &input);
    if (ret)
        goto cleanup;

    input.length = key1->length;
    input.data = (char *) key1->contents;
    ret = dr(enc, key2, r2, &input);
    if (ret)
        goto cleanup;

    /*
     * Concatenate the two keys together, and then run them through
     * n-fold to reduce them to a length appropriate for the random-to-key
     * operation.  Note here that krb5int_nfold() takes sizes in bits, hence
     * the multiply by 8.
     */

    memcpy(combined, r1, keybytes);
    memcpy(combined + keybytes, r2, keybytes);

    krb5int_nfold((keybytes * 2) * 8, combined, keybytes * 8, rnd);

    /*
     * Run the "random" bits through random-to-key to produce a encryption
     * key.
     */

    randbits.length = keybytes;
    randbits.data = (char *) rnd;
    tkeyblock.length = keylength;
    tkeyblock.contents = output;

    ret = (*ktp->rand2key)(&randbits, &tkeyblock);
    if (ret)
        goto cleanup;

    ret = krb5_k_create_key(NULL, &tkeyblock, &tkey);
    if (ret)
        goto cleanup;

    /*
     * Run through derive-key one more time to produce the final key.
     * Note that the input to derive-key is the ASCII string "combine".
     */

    input.length = 7;
    input.data = "combine";

    /*
     * Just FYI: _if_ we have space here in the key, then simply use it
     * without modification.  But if the key is blank (no allocated storage)
     * then allocate some memory for it.  This allows programs to use one of
     * the existing keys as the output key, _or_ pass in a blank keyblock
     * for us to allocate.  It's easier for us to allocate it since we already
     * know the crypto library internals
     */

    if (outkey->length == 0 || outkey->contents == NULL) {
        outkey->contents = k5alloc(keylength, &ret);
        if (ret)
            goto cleanup;
        outkey->length = keylength;
        outkey->enctype = key1->enctype;
        myalloc = TRUE;
    }

    ret = krb5int_derive_keyblock(enc, tkey, outkey, &input, DERIVE_RFC3961);
    if (ret) {
        if (myalloc) {
            free(outkey->contents);
            outkey->contents = NULL;
        }
        goto cleanup;
    }

cleanup:
    zapfree(r1, keybytes);
    zapfree(r2, keybytes);
    zapfree(rnd, keybytes);
    zapfree(combined, keybytes * 2);
    zapfree(output, keylength);
    krb5_k_free_key(NULL, tkey);
    return ret;
}
Beispiel #23
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle, char *realm,
                     krb5_keytab keytab, const krb5_fulladdr *local_faddr,
                     const krb5_fulladdr *remote_faddr, krb5_data *req,
                     krb5_data *rep)
{
    krb5_error_code ret;
    char *ptr;
    unsigned int plen, vno;
    krb5_data ap_req, ap_rep = empty_data();
    krb5_data cipher = empty_data(), clear = empty_data();
    krb5_auth_context auth_context = NULL;
    krb5_principal changepw = NULL;
    krb5_principal client, target = NULL;
    krb5_ticket *ticket = NULL;
    krb5_replay_data replay;
    krb5_error krberror;
    int numresult;
    char strresult[1024];
    char *clientstr = NULL, *targetstr = NULL;
    const char *errmsg = NULL;
    size_t clen;
    char *cdots;
    struct sockaddr_storage ss;
    socklen_t salen;
    char addrbuf[100];
    krb5_address *addr = remote_faddr->address;

    *rep = empty_data();

    if (req->length < 4) {
        /* either this, or the server is printing bad messages,
           or the caller passed in garbage */
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated", sizeof(strresult));
        goto bailout;
    }

    ptr = req->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request length was inconsistent",
                sizeof(strresult));
        goto bailout;
    }

    /* verify version number */

    vno = (*ptr++ & 0xff) ;
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1 && vno != RFC3244_VERSION) {
        ret = KRB5KDC_ERR_BAD_PVNO;
        numresult = KRB5_KPASSWD_BAD_VERSION;
        snprintf(strresult, sizeof(strresult),
                 "Request contained unknown protocol version number %d", vno);
        goto bailout;
    }

    /* read, check ap-req length */

    ap_req.length = (*ptr++ & 0xff);
    ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_req.length >= req->data + req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated in AP-REQ",
                sizeof(strresult));
        goto bailout;
    }

    /* verify ap_req */

    ap_req.data = ptr;
    ptr += ap_req.length;

    ret = krb5_auth_con_init(context, &auth_context);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_auth_con_setflags(context, auth_context,
                                 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
                               "kadmin", "changepw", NULL);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed building kadmin/changepw principal",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
                      NULL, &ticket);

    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed reading application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* construct the ap-rep */

    ret = krb5_mk_rep(context, auth_context, &ap_rep);
    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed replying to application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* decrypt the ChangePasswdData */

    cipher.length = (req->data + req->length) - ptr;
    cipher.data = ptr;

    /*
     * Don't set a remote address in auth_context before calling krb5_rd_priv,
     * so that we can work against clients behind a NAT.  Reflection attacks
     * aren't a concern since we use sequence numbers and since our requests
     * don't look anything like our responses.  Also don't set a local address,
     * since we don't know what interface the request was received on.
     */

    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
        goto chpwfail;
    }

    client = ticket->enc_part2->client;

    /* decode ChangePasswdData for setpw requests */
    if (vno == RFC3244_VERSION) {
        krb5_data *clear_data;

        ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
        if (ret != 0) {
            numresult = KRB5_KPASSWD_MALFORMED;
            strlcpy(strresult, "Failed decoding ChangePasswdData",
                    sizeof(strresult));
            goto chpwfail;
        }

        zapfree(clear.data, clear.length);

        clear = *clear_data;
        free(clear_data);

        if (target != NULL) {
            ret = krb5_unparse_name(context, target, &targetstr);
            if (ret != 0) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed unparsing target name for log",
                        sizeof(strresult));
                goto chpwfail;
            }
        }
    }

    ret = krb5_unparse_name(context, client, &clientstr);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed unparsing client name for log",
                sizeof(strresult));
        goto chpwfail;
    }

    /* for cpw, verify that this is an AS_REQ ticket */
    if (vno == 1 &&
        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
        strlcpy(strresult, "Ticket must be derived from a password",
                sizeof(strresult));
        goto chpwfail;
    }

    /* change the password */

    ptr = k5memdup0(clear.data, clear.length, &ret);
    ret = schpw_util_wrapper(server_handle, client, target,
                             (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
                             ptr, NULL, strresult, sizeof(strresult));
    if (ret)
        errmsg = krb5_get_error_message(context, ret);

    /* zap the password */
    zapfree(clear.data, clear.length);
    zapfree(ptr, clear.length);
    clear = empty_data();

    clen = strlen(clientstr);
    trunc_name(&clen, &cdots);

    switch (addr->addrtype) {
    case ADDRTYPE_INET: {
        struct sockaddr_in *sin = ss2sin(&ss);

        sin->sin_family = AF_INET;
        memcpy(&sin->sin_addr, addr->contents, addr->length);
        sin->sin_port = htons(remote_faddr->port);
        salen = sizeof(*sin);
        break;
    }
    case ADDRTYPE_INET6: {
        struct sockaddr_in6 *sin6 = ss2sin6(&ss);

        sin6->sin6_family = AF_INET6;
        memcpy(&sin6->sin6_addr, addr->contents, addr->length);
        sin6->sin6_port = htons(remote_faddr->port);
        salen = sizeof(*sin6);
        break;
    }
    default: {
        struct sockaddr *sa = ss2sa(&ss);

        sa->sa_family = AF_UNSPEC;
        salen = sizeof(*sa);
        break;
    }
    }

    if (getnameinfo(ss2sa(&ss), salen,
                    addrbuf, sizeof(addrbuf), NULL, 0,
                    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
        strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));

    if (vno == RFC3244_VERSION) {
        size_t tlen;
        char *tdots;
        const char *targetp;

        if (target == NULL) {
            tlen = clen;
            tdots = cdots;
            targetp = targetstr;
        } else {
            tlen = strlen(targetstr);
            trunc_name(&tlen, &tdots);
            targetp = clientstr;
        }

        krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
                                       "%.*s%s: %s"), addrbuf, (int) clen,
                         clientstr, cdots, (int) tlen, targetp, tdots,
                         errmsg ? errmsg : "success");
    } else {
        krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
                         addrbuf, (int) clen, clientstr, cdots,
                         errmsg ? errmsg : "success");
    }
    switch (ret) {
    case KADM5_AUTH_CHANGEPW:
        numresult = KRB5_KPASSWD_ACCESSDENIED;
        break;
    case KADM5_PASS_Q_TOOSHORT:
    case KADM5_PASS_REUSE:
    case KADM5_PASS_Q_CLASS:
    case KADM5_PASS_Q_DICT:
    case KADM5_PASS_Q_GENERIC:
    case KADM5_PASS_TOOSOON:
        numresult = KRB5_KPASSWD_SOFTERROR;
        break;
    case 0:
        numresult = KRB5_KPASSWD_SUCCESS;
        strlcpy(strresult, "", sizeof(strresult));
        break;
    default:
        numresult = KRB5_KPASSWD_HARDERROR;
        break;
    }

chpwfail:

    ret = alloc_data(&clear, 2 + strlen(strresult));
    if (ret)
        goto bailout;

    ptr = clear.data;

    *ptr++ = (numresult>>8) & 0xff;
    *ptr++ = numresult & 0xff;

    memcpy(ptr, strresult, strlen(strresult));

    cipher = empty_data();

    if (ap_rep.length) {
        ret = krb5_auth_con_setaddrs(context, auth_context,
                                     local_faddr->address, NULL);
        if (ret) {
            numresult = KRB5_KPASSWD_HARDERROR;
            strlcpy(strresult,
                    "Failed storing client and server internet addresses",
                    sizeof(strresult));
        } else {
            ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
                               &replay);
            if (ret) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed encrypting reply",
                        sizeof(strresult));
            }
        }
    }

    /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
       if this fails, just bail.  there's nothing else we can do. */

    if (cipher.length == 0) {
        /* clear out ap_rep now, so that it won't be inserted in the
           reply */

        if (ap_rep.length) {
            free(ap_rep.data);
            ap_rep = empty_data();
        }

        krberror.ctime = 0;
        krberror.cusec = 0;
        krberror.susec = 0;
        ret = krb5_timeofday(context, &krberror.stime);
        if (ret)
            goto bailout;

        /* this is really icky.  but it's what all the other callers
           to mk_error do. */
        krberror.error = ret;
        krberror.error -= ERROR_TABLE_BASE_krb5;
        if (krberror.error < 0 || krberror.error > KRB_ERR_MAX)
            krberror.error = KRB_ERR_GENERIC;

        krberror.client = NULL;

        ret = krb5_build_principal(context, &krberror.server,
                                   strlen(realm), realm,
                                   "kadmin", "changepw", NULL);
        if (ret)
            goto bailout;
        krberror.text.length = 0;
        krberror.e_data = clear;

        ret = krb5_mk_error(context, &krberror, &cipher);

        krb5_free_principal(context, krberror.server);

        if (ret)
            goto bailout;
    }

    /* construct the reply */

    ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
    if (ret)
        goto bailout;
    ptr = rep->data;

    /* length */

    *ptr++ = (rep->length>>8) & 0xff;
    *ptr++ = rep->length & 0xff;

    /* version == 0x0001 big-endian */

    *ptr++ = 0;
    *ptr++ = 1;

    /* ap_rep length, big-endian */

    *ptr++ = (ap_rep.length>>8) & 0xff;
    *ptr++ = ap_rep.length & 0xff;

    /* ap-rep data */

    if (ap_rep.length) {
        memcpy(ptr, ap_rep.data, ap_rep.length);
        ptr += ap_rep.length;
    }

    /* krb-priv or krb-error */

    memcpy(ptr, cipher.data, cipher.length);

bailout:
    krb5_auth_con_free(context, auth_context);
    krb5_free_principal(context, changepw);
    krb5_free_ticket(context, ticket);
    free(ap_rep.data);
    free(clear.data);
    free(cipher.data);
    krb5_free_principal(context, target);
    krb5_free_unparsed_name(context, targetstr);
    krb5_free_unparsed_name(context, clientstr);
    krb5_free_error_message(context, errmsg);
    return ret;
}
Beispiel #24
0
/*
 * NIST SP800-108 KDF in feedback mode (section 5.2).
 * Parameters:
 *   - CMAC (with enc as the enc provider) is the PRF.
 *   - A block counter of four bytes is used.
 *   - Label is the key derivation constant.
 *   - Context is empty.
 *   - Four bytes are used to encode the output length in the PRF input.
 */
static krb5_error_code
derive_random_sp800_108_cmac(const struct krb5_enc_provider *enc,
                             krb5_key inkey, krb5_data *outrnd,
                             const krb5_data *in_constant)
{
    size_t blocksize, keybytes, n;
    krb5_crypto_iov iov[6];
    krb5_error_code ret;
    krb5_data prf;
    unsigned int i;
    unsigned char ibuf[4], Lbuf[4];

    blocksize = enc->block_size;
    keybytes = enc->keybytes;

    if (inkey->keyblock.length != enc->keylength || outrnd->length != keybytes)
        return KRB5_CRYPTO_INTERNAL;

    /* Allocate encryption data buffer. */
    ret = alloc_data(&prf, blocksize);
    if (ret)
        return ret;

    /* K(i-1): the previous block of PRF output, initially all-zeros. */
    iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[0].data = prf;
    /* [i]2: four-byte big-endian binary string giving the block counter */
    iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[1].data = make_data(ibuf, sizeof(ibuf));
    /* Label: the fixed derived-key input */
    iov[2].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[2].data = *in_constant;
    /* 0x00: separator byte */
    iov[3].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[3].data = make_data("", 1);
    /* Context: (unused) */
    iov[4].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[4].data = empty_data();
    /* [L]2: four-byte big-endian binary string giving the output length */
    iov[5].flags = KRB5_CRYPTO_TYPE_DATA;
    iov[5].data = make_data(Lbuf, sizeof(Lbuf));
    store_32_be(outrnd->length * 8, Lbuf);

    for (i = 1, n = 0; n < keybytes; i++) {
        /* Update the block counter. */
        store_32_be(i, ibuf);

        /* Compute a CMAC checksum, storing the result into K(i-1). */
        ret = krb5int_cmac_checksum(enc, inkey, iov, 6, &prf);
        if (ret)
            goto cleanup;

        /* Copy the result into the appropriate part of the output buffer. */
        if (keybytes - n <= blocksize) {
            memcpy(outrnd->data + n, prf.data, keybytes - n);
            break;
        }
        memcpy(outrnd->data + n, prf.data, blocksize);
        n += blocksize;
    }

cleanup:
    zapfree(prf.data, blocksize);
    return ret;
}
Beispiel #25
0
krb5_error_code
krb5int_arcfour_decrypt(const struct krb5_keytypes *ktp, krb5_key key,
                        krb5_keyusage usage, const krb5_data *ivec,
                        krb5_crypto_iov *data, size_t num_data)
{
    const struct krb5_enc_provider *enc = ktp->enc;
    const struct krb5_hash_provider *hash = ktp->hash;
    krb5_error_code ret;
    krb5_crypto_iov *header, *trailer;
    krb5_keyblock *usage_keyblock = NULL, *enc_keyblock = NULL;
    krb5_data checksum, header_data, comp_checksum = empty_data();

    header = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
    if (header == NULL ||
        header->data.length != hash->hashsize + CONFOUNDERLENGTH)
        return KRB5_BAD_MSIZE;

    header_data = header->data;

    trailer = krb5int_c_locate_iov(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
    if (trailer != NULL && trailer->data.length != 0)
        return KRB5_BAD_MSIZE;

    /* Allocate buffers. */
    ret = alloc_data(&comp_checksum, hash->hashsize);
    if (ret != 0)
        goto cleanup;
    ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
                                  &usage_keyblock);
    if (ret != 0)
        goto cleanup;
    ret = krb5int_c_init_keyblock(NULL, key->keyblock.enctype, enc->keybytes,
                                  &enc_keyblock);
    if (ret != 0)
        goto cleanup;

    checksum = make_data(header->data.data, hash->hashsize);

    /* Adjust pointers so confounder is at start of header. */
    header->data.length -= hash->hashsize;
    header->data.data += hash->hashsize;

    /* We may have to try two usage values; see below. */
    do {
        /* Derive a usage key from the session key and usage. */
        ret = usage_key(enc, hash, &key->keyblock, usage, usage_keyblock);
        if (ret != 0)
            goto cleanup;

        /* Derive the encryption key from the usage key and checksum. */
        ret = enc_key(enc, hash, usage_keyblock, &checksum, enc_keyblock);
        if (ret)
            goto cleanup;

        /* Decrypt the ciphertext. */
        ret = keyblock_crypt(enc, enc_keyblock, ivec, data, num_data);
        if (ret != 0)
            goto cleanup;

        /* Compute HMAC(usage key, plaintext) to get the checksum. */
        ret = krb5int_hmac_keyblock(hash, usage_keyblock, data, num_data,
                                    &comp_checksum);
        if (ret != 0)
            goto cleanup;

        if (k5_bcmp(checksum.data, comp_checksum.data, hash->hashsize) != 0) {
            if (usage == 9) {
                /*
                 * RFC 4757 specifies usage 8 for TGS-REP encrypted parts
                 * encrypted in a subkey, but the value used by MS is actually
                 * 9.  We now use 9 to start with, but fall back to 8 on
                 * failure in case we are communicating with a KDC using the
                 * value from the RFC.  ivec is always NULL in this case.
                 * We need to re-encrypt the data in the wrong key first.
                 */
                ret = keyblock_crypt(enc, enc_keyblock, NULL, data, num_data);
                if (ret != 0)
                    goto cleanup;
                usage = 8;
                continue;
            }
            ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
            goto cleanup;
        }

        break;
    } while (1);

cleanup:
    header->data = header_data; /* Restore header pointers. */
    krb5int_c_free_keyblock(NULL, usage_keyblock);
    krb5int_c_free_keyblock(NULL, enc_keyblock);
    zapfree(comp_checksum.data, comp_checksum.length);
    return ret;
}
Beispiel #26
0
static void
aes_key_cleanup(krb5_key key)
{
    zapfree(key->cache, sizeof(struct aes_key_info_cache));
}
Beispiel #27
0
krb5_error_code
krb5int_hmac_keyblock(const struct krb5_hash_provider *hash,
                      const krb5_keyblock *keyblock,
                      const krb5_crypto_iov *data, size_t num_data,
                      krb5_data *output)
{
    unsigned char *xorkey = NULL, *ihash = NULL;
    unsigned int i;
    krb5_crypto_iov *ihash_iov = NULL, ohash_iov[2];
    krb5_data hashout;
    krb5_error_code ret;

    if (keyblock->length > hash->blocksize)
        return KRB5_CRYPTO_INTERNAL;
    if (output->length < hash->hashsize)
        return KRB5_BAD_MSIZE;

    /* Allocate space for the xor key, hash input vector, and inner hash */
    xorkey = k5alloc(hash->blocksize, &ret);
    if (xorkey == NULL)
        goto cleanup;
    ihash = k5alloc(hash->hashsize, &ret);
    if (ihash == NULL)
        goto cleanup;
    ihash_iov = k5calloc(num_data + 1, sizeof(krb5_crypto_iov), &ret);
    if (ihash_iov == NULL)
        goto cleanup;

    /* Create the inner padded key. */
    memset(xorkey, 0x36, hash->blocksize);
    for (i = 0; i < keyblock->length; i++)
        xorkey[i] ^= keyblock->contents[i];

    /* Compute the inner hash over the inner key and input data. */
    ihash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    ihash_iov[0].data = make_data(xorkey, hash->blocksize);
    memcpy(ihash_iov + 1, data, num_data * sizeof(krb5_crypto_iov));
    hashout = make_data(ihash, hash->hashsize);
    ret = hash->hash(ihash_iov, num_data + 1, &hashout);
    if (ret != 0)
        goto cleanup;

    /* Create the outer padded key. */
    memset(xorkey, 0x5c, hash->blocksize);
    for (i = 0; i < keyblock->length; i++)
        xorkey[i] ^= keyblock->contents[i];

    /* Compute the outer hash over the outer key and inner hash value. */
    ohash_iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
    ohash_iov[0].data = make_data(xorkey, hash->blocksize);
    ohash_iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
    ohash_iov[1].data = make_data(ihash, hash->hashsize);
    output->length = hash->hashsize;
    ret = hash->hash(ohash_iov, 2, output);
    if (ret != 0)
        memset(output->data, 0, output->length);

cleanup:
    zapfree(xorkey, hash->blocksize);
    zapfree(ihash, hash->hashsize);
    free(ihash_iov);
    return ret;
}
Beispiel #28
0
krb5_error_code
krb5int_dk_string_to_key(const struct krb5_keytypes *ktp,
                         const krb5_data *string, const krb5_data *salt,
                         const krb5_data *parms, krb5_keyblock *keyblock)
{
    krb5_error_code ret;
    size_t keybytes, keylength, concatlen;
    unsigned char *concat = NULL, *foldstring = NULL, *foldkeydata = NULL;
    krb5_data indata;
    krb5_keyblock foldkeyblock;
    krb5_key foldkey = NULL;

    /* keyblock->length is checked by krb5int_derive_key. */

    keybytes = ktp->enc->keybytes;
    keylength = ktp->enc->keylength;

    concatlen = string->length + salt->length;

    concat = k5alloc(concatlen, &ret);
    if (ret != 0)
        goto cleanup;
    foldstring = k5alloc(keybytes, &ret);
    if (ret != 0)
        goto cleanup;
    foldkeydata = k5alloc(keylength, &ret);
    if (ret != 0)
        goto cleanup;

    /* construct input string ( = string + salt), fold it, make_key it */

    if (string->length > 0)
        memcpy(concat, string->data, string->length);
    if (salt->length > 0)
        memcpy(concat + string->length, salt->data, salt->length);

    krb5int_nfold(concatlen*8, concat, keybytes*8, foldstring);

    indata.length = keybytes;
    indata.data = (char *) foldstring;
    foldkeyblock.length = keylength;
    foldkeyblock.contents = foldkeydata;
    foldkeyblock.enctype = ktp->etype;

    ret = ktp->rand2key(&indata, &foldkeyblock);
    if (ret != 0)
        goto cleanup;

    ret = krb5_k_create_key(NULL, &foldkeyblock, &foldkey);
    if (ret != 0)
        goto cleanup;

    /* now derive the key from this one */

    indata.length = kerberos_len;
    indata.data = (char *) kerberos;

    ret = krb5int_derive_keyblock(ktp->enc, NULL, foldkey, keyblock, &indata,
                                  DERIVE_RFC3961);
    if (ret != 0)
        memset(keyblock->contents, 0, keyblock->length);

cleanup:
    zapfree(concat, concatlen);
    zapfree(foldstring, keybytes);
    zapfree(foldkeydata, keylength);
    krb5_k_free_key(NULL, foldkey);
    return ret;
}