Ejemplo n.º 1
0
/*
 * Fill in the caller out, realm, and flags output variables.  out is filled in
 * with ctx->previous_request, which the caller should set, and realm is filled
 * in with the realm of ctx->cur_tgt.
 */
static krb5_error_code
set_caller_request(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    const krb5_data *req = &ctx->previous_request;
    const krb5_data *realm = &ctx->cur_tgt->server->data[1];
    krb5_data out_copy = empty_data(), realm_copy = empty_data();

    code = krb5int_copy_data_contents(context, req, &out_copy);
    if (code != 0)
        goto cleanup;
    code = krb5int_copy_data_contents(context, realm, &realm_copy);
    if (code != 0)
        goto cleanup;

    *ctx->caller_out = out_copy;
    *ctx->caller_realm = realm_copy;
    *ctx->caller_flags = KRB5_TKT_CREDS_STEP_FLAG_CONTINUE;
    return 0;

cleanup:
    krb5_free_data_contents(context, &out_copy);
    krb5_free_data_contents(context, &realm_copy);
    return code;
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
Archivo: mk_priv.c Proyecto: krb5/krb5
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;
}
Ejemplo n.º 4
0
Archivo: ktest.c Proyecto: WeiY/krb5
void
ktest_make_sample_sam_challenge_2_body(krb5_sam_challenge_2_body *p)
{
    p->sam_type = 42;
    p->sam_flags = KRB5_SAM_USE_SAD_AS_KEY;
    krb5_data_parse(&p->sam_type_name, "type name");
    p->sam_track_id = empty_data();
    krb5_data_parse(&p->sam_challenge_label, "challenge label");
    krb5_data_parse(&p->sam_challenge, "challenge ipse");
    krb5_data_parse(&p->sam_response_prompt, "response_prompt ipse");
    p->sam_pk_for_sad = empty_data();
    p->sam_nonce = 0x543210;
    p->sam_etype = ENCTYPE_DES_CBC_CRC;
}
Ejemplo n.º 5
0
/*
 * Set up the request given by ctx->tgs_in_creds, using ctx->cur_tgt.  KDC
 * options for the requests are determined by ctx->cur_tgt->ticket_flags and
 * extra_options.
 */
static krb5_error_code
make_request(krb5_context context, krb5_tkt_creds_context ctx,
             int extra_options)
{
    krb5_error_code code;
    krb5_data request = empty_data();

    ctx->kdcopt = extra_options | FLAGS2OPTS(ctx->cur_tgt->ticket_flags);

    /* XXX This check belongs in gc_via_tgt.c or nowhere. */
    if (!krb5_c_valid_enctype(ctx->cur_tgt->keyblock.enctype))
        return KRB5_PROG_ETYPE_NOSUPP;

    code = krb5int_make_tgs_request(context, ctx->cur_tgt, ctx->kdcopt,
                                    ctx->cur_tgt->addresses, NULL,
                                    ctx->tgs_in_creds, NULL, NULL, &request,
                                    &ctx->timestamp, &ctx->nonce,
                                    &ctx->subkey);
    if (code != 0)
        return code;

    krb5_free_data_contents(context, &ctx->previous_request);
    ctx->previous_request = request;
    return set_caller_request(context, ctx);
}
Ejemplo n.º 6
0
/* Determines if a pin is required. If it is, it will be prompted for. */
static inline krb5_error_code
collect_pin(krb5_context context, krb5_prompter_fct prompter,
            void *prompter_data, const krb5_otp_tokeninfo *ti,
            krb5_data *out_pin)
{
    krb5_error_code retval;
    char otppin[1024];
    krb5_flags collect;
    krb5_data pin;

    /* If no PIN will be collected, don't prompt. */
    collect = ti->flags & (KRB5_OTP_FLAG_COLLECT_PIN |
                           KRB5_OTP_FLAG_SEPARATE_PIN);
    if (collect == 0) {
        *out_pin = empty_data();
        return 0;
    }

    /* Collect the PIN. */
    retval = doprompt(context, prompter, prompter_data, NULL,
                      _("OTP Token PIN"), otppin, sizeof(otppin));
    if (retval != 0)
        return retval;

    /* Set the PIN. */
    pin = make_data(strdup(otppin), strlen(otppin));
    if (pin.data == NULL)
        return ENOMEM;

    *out_pin = pin;
    return 0;
}
Ejemplo n.º 7
0
Archivo: ktest.c Proyecto: WeiY/krb5
void
ktest_make_sha256_alg(krb5_algorithm_identifier *p)
{
    /* { 2 16 840 1 101 3 4 2 1 } */
    krb5_data_parse(&p->algorithm, "\x60\x86\x48\x01\x65\x03\x04\x02\x01");
    p->parameters = empty_data();
}
Ejemplo n.º 8
0
/*
 * Convert a krb5_principal into the default salt for that principal.
 */
static krb5_error_code
principal2salt_internal(krb5_context context, krb5_const_principal pr,
                        krb5_data *ret, int use_realm)
{
    unsigned int size = 0, offset=0;
    krb5_int32 i;

    *ret = empty_data();
    if (pr == NULL)
        return 0;

    if (use_realm)
        size += pr->realm.length;

    for (i = 0; i < pr->length; i++)
        size += pr->data[i].length;

    if (alloc_data(ret, size))
        return ENOMEM;

    if (use_realm) {
        offset = pr->realm.length;
        if (offset > 0)
            memcpy(ret->data, pr->realm.data, offset);
    }

    for (i = 0; i < pr->length; i++) {
        if (pr->data[i].length > 0)
            memcpy(&ret->data[offset], pr->data[i].data, pr->data[i].length);
        offset += pr->data[i].length;
    }
    return 0;
}
Ejemplo n.º 9
0
Archivo: ktest.c Proyecto: WeiY/krb5
void
ktest_make_sha1_alg(krb5_algorithm_identifier *p)
{
    /* { 1 3 14 3 2 26 } */
    krb5_data_parse(&p->algorithm, "\x2b\x0e\x03\x02\x1a");
    p->parameters = empty_data();
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
Archivo: rd_cred.c Proyecto: krb5/krb5
/*
 * 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;
}
Ejemplo n.º 12
0
static krb5_error_code
prepare_error_as (struct kdc_request_state *rstate, krb5_kdc_req *request,
                  int error, krb5_pa_data **e_data, krb5_boolean typed_e_data,
                  krb5_principal canon_client, krb5_data **response,
                  const char *status)
{
    krb5_error errpkt;
    krb5_error_code retval;
    krb5_data *scratch = NULL, *e_data_asn1 = NULL, *fast_edata = NULL;
    kdc_realm_t *kdc_active_realm = rstate->realm_data;

    errpkt.ctime = request->nonce;
    errpkt.cusec = 0;

    retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
    if (retval)
        return retval;
    errpkt.error = error;
    errpkt.server = request->server;
    errpkt.client = (error == KRB5KDC_ERR_WRONG_REALM) ? canon_client :
        request->client;
    errpkt.text = string2data((char *)status);

    if (e_data != NULL) {
        if (typed_e_data)
            retval = encode_krb5_typed_data(e_data, &e_data_asn1);
        else
            retval = encode_krb5_padata_sequence(e_data, &e_data_asn1);
        if (retval)
            goto cleanup;
        errpkt.e_data = *e_data_asn1;
    } else
        errpkt.e_data = empty_data();

    retval = kdc_fast_handle_error(kdc_context, rstate, request, e_data,
                                   &errpkt, &fast_edata);
    if (retval)
        goto cleanup;
    if (fast_edata != NULL)
        errpkt.e_data = *fast_edata;

    scratch = k5alloc(sizeof(*scratch), &retval);
    if (scratch == NULL)
        goto cleanup;
    if (kdc_fast_hide_client(rstate) && errpkt.client != NULL)
        errpkt.client = (krb5_principal)krb5_anonymous_principal();
    retval = krb5_mk_error(kdc_context, &errpkt, scratch);
    if (retval)
        goto cleanup;

    *response = scratch;
    scratch = NULL;

cleanup:
    krb5_free_data(kdc_context, fast_edata);
    krb5_free_data(kdc_context, e_data_asn1);
    free(scratch);
    return retval;
}
Ejemplo n.º 13
0
krb5_error_code KRB5_CALLCONV
krb5_parse_name_flags(krb5_context context, const char *name,
                      int flags, krb5_principal *principal_out)
{
    krb5_error_code ret;
    krb5_principal princ = NULL;
    char *default_realm;
    krb5_boolean has_realm;
    krb5_boolean enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
    krb5_boolean require_realm = (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM);
    krb5_boolean no_realm = (flags & KRB5_PRINCIPAL_PARSE_NO_REALM);
    krb5_boolean ignore_realm = (flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM);

    *principal_out = NULL;

    ret = allocate_princ(context, name, enterprise, &princ, &has_realm);
    if (ret)
        goto cleanup;
    parse_name_into_princ(name, enterprise, princ);

    /*
     * If a realm was not found, then use the default realm, unless
     * KRB5_PRINCIPAL_PARSE_NO_REALM was specified in which case the
     * realm will be empty.
     */
    if (!has_realm) {
        if (require_realm) {
            ret = KRB5_PARSE_MALFORMED;
            krb5_set_error_message(context, ret,
                                   _("Principal %s is missing required realm"),
                                   name);
            goto cleanup;
        }
        if (!no_realm && !ignore_realm) {
            ret = krb5_get_default_realm(context, &default_realm);
            if (ret)
                goto cleanup;
            princ->realm = string2data(default_realm);
        }
    } else if (no_realm) {
        ret = KRB5_PARSE_MALFORMED;
        krb5_set_error_message(context, ret,
                               _("Principal %s has realm present"), name);
        goto cleanup;
    } else if (ignore_realm) {
        krb5_free_data_contents(context, &princ->realm);
        princ->realm = empty_data();
    }

    princ->type = (enterprise) ? KRB5_NT_ENTERPRISE_PRINCIPAL :
        KRB5_NT_PRINCIPAL;
    princ->magic = KV5M_PRINCIPAL;
    *principal_out = princ;
    princ = NULL;

cleanup:
    krb5_free_principal(context, princ);
    return ret;
}
Ejemplo n.º 14
0
/* Add realm to ctx->realms_seen so that we can avoid revisiting it later. */
static krb5_error_code
remember_realm(krb5_context context, krb5_tkt_creds_context ctx,
               const krb5_data *realm)
{
    size_t len = 0;
    krb5_data *new_list;

    if (ctx->realms_seen != NULL) {
        for (len = 0; ctx->realms_seen[len].data != NULL; len++);
    }
    new_list = realloc(ctx->realms_seen, (len + 2) * sizeof(krb5_data));
    if (new_list == NULL)
        return ENOMEM;
    ctx->realms_seen = new_list;
    new_list[len] = empty_data();
    new_list[len + 1] = empty_data();
    return krb5int_copy_data_contents(context, realm, &new_list[len]);
}
Ejemplo n.º 15
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;
}
Ejemplo n.º 16
0
krb5_error_code KRB5_CALLCONV
krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context ctx,
                    krb5_data *in, krb5_data *out, krb5_data *realm,
                    unsigned int *flags)
{
    krb5_error_code code;
    krb5_boolean no_input = (in == NULL || in->length == 0);

    *out = empty_data();
    *realm = empty_data();
    *flags = 0;

    /* We should receive an empty input on the first step only, and should not
     * get called after completion. */
    if (no_input != (ctx->state == STATE_BEGIN) ||
        ctx->state == STATE_COMPLETE)
        return EINVAL;

    ctx->caller_out = out;
    ctx->caller_realm = realm;
    ctx->caller_flags = flags;

    if (!no_input) {
        /* Convert the input token into a credential and store it in ctx. */
        code = get_creds_from_tgs_reply(context, ctx, in);
        if (code != 0)
            return code;
    }

    if (ctx->state == STATE_BEGIN)
        return begin(context, ctx);
    else if (ctx->state == STATE_GET_TGT)
        return step_get_tgt(context, ctx);
    else if (ctx->state == STATE_GET_TGT_OFFPATH)
        return step_get_tgt_offpath(context, ctx);
    else if (ctx->state == STATE_REFERRALS)
        return step_referrals(context, ctx);
    else if (ctx->state == STATE_NON_REFERRAL)
        return step_non_referral(context, ctx);
    else
        return EINVAL;
}
Ejemplo n.º 17
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;
}
Ejemplo n.º 18
0
static krb5_error_code
derive_random_rfc3961(const struct krb5_enc_provider *enc,
                      krb5_key inkey, krb5_data *outrnd,
                      const krb5_data *in_constant)
{
    size_t blocksize, keybytes, n;
    krb5_error_code ret;
    krb5_data block = empty_data();

    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. */
    ret = alloc_data(&block, blocksize);
    if (ret)
        return ret;

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

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

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

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

cleanup:
    zapfree(block.data, blocksize);
    return ret;
}
Ejemplo n.º 19
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;
}
Ejemplo n.º 20
0
Archivo: chpw.c Proyecto: jmoldow/krb5
krb5_error_code
krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context,
                    krb5_data *packet, int *result_code_out,
                    krb5_data *result_data_out)
{
    krb5_error_code ret;
    krb5_data result_data, *clear = NULL;
    krb5_boolean is_error;
    char *ptr;
    int result_code;

    *result_code_out = 0;
    *result_data_out = empty_data();

    ret = get_clear_result(context, auth_context, packet, &clear, &is_error);
    if (ret)
        return ret;

    if (clear->length < 2) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    /* Decode and check the result code. */
    ptr = clear->data;
    result_code = (*ptr++ & 0xff);
    result_code = (result_code << 8) | (*ptr++ & 0xff);
    if (result_code < KRB5_KPASSWD_SUCCESS ||
        result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    /* Successful replies must not come from errors. */
    if (is_error && result_code == KRB5_KPASSWD_SUCCESS) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        goto cleanup;
    }

    result_data = make_data(ptr, clear->data + clear->length - ptr);
    ret = krb5int_copy_data_contents(context, &result_data, result_data_out);
    if (ret)
        goto cleanup;
    *result_code_out = result_code;

cleanup:
    krb5_free_data(context, clear);
    return ret;
}
Ejemplo n.º 21
0
krb5_error_code KRB5_CALLCONV
krb5_tkt_creds_get(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    krb5_data request = empty_data(), reply = empty_data();
    krb5_data realm = empty_data();
    unsigned int flags = 0;
    int tcp_only = 0, use_master;

    for (;;) {
        /* Get the next request and realm.  Turn on TCP if necessary. */
        code = krb5_tkt_creds_step(context, ctx, &reply, &request, &realm,
                                   &flags);
        if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !tcp_only) {
            TRACE_TKT_CREDS_RETRY_TCP(context);
            tcp_only = 1;
        } else if (code != 0 || !(flags & KRB5_TKT_CREDS_STEP_FLAG_CONTINUE))
            break;
        krb5_free_data_contents(context, &reply);

        /* Send it to a KDC for the appropriate realm. */
        use_master = 0;
        code = krb5_sendto_kdc(context, &request, &realm,
                               &reply, &use_master, tcp_only);
        if (code != 0)
            break;

        krb5_free_data_contents(context, &request);
        krb5_free_data_contents(context, &realm);
    }

    krb5_free_data_contents(context, &request);
    krb5_free_data_contents(context, &reply);
    krb5_free_data_contents(context, &realm);
    return code;
}
Ejemplo n.º 22
0
/* If state contains a cookie value for pa_type, set *out to the corresponding
 * data and return true.  Otherwise set *out to empty and return false. */
krb5_boolean
kdc_fast_search_cookie(struct kdc_request_state *state,
                       krb5_preauthtype pa_type, krb5_data *out)
{
    krb5_pa_data *pa;

    pa = krb5int_find_pa_data(NULL, state->in_cookie_padata, pa_type);
    if (pa == NULL) {
        *out = empty_data();
        return FALSE;
    } else {
        *out = make_data(pa->contents, pa->length);
        return TRUE;
    }
}
Ejemplo n.º 23
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;
}
Ejemplo n.º 24
0
krb5_error_code
k5_client_realm_path(krb5_context context, const krb5_data *client,
                     const krb5_data *server, krb5_data **rpath_out)
{
    krb5_error_code retval;
    char **capvals;
    size_t i;
    krb5_data *rpath = NULL, d;

    retval = rtree_capath_vals(context, client, server, &capvals);
    if (retval)
        return retval;

    /* Count capaths (if any) and allocate space.  Leave room for the client
     * realm, server realm, and terminator. */
    for (i = 0; capvals != NULL && capvals[i] != NULL; i++);
    rpath = calloc(i + 3, sizeof(*rpath));
    if (rpath == NULL)
        return ENOMEM;

    /* Populate rpath with the client realm, capaths, and server realm. */
    retval = krb5int_copy_data_contents(context, client, &rpath[0]);
    if (retval)
        goto cleanup;
    for (i = 0; capvals != NULL && capvals[i] != NULL; i++) {
        d = make_data(capvals[i], strcspn(capvals[i], "\t "));
        retval = krb5int_copy_data_contents(context, &d, &rpath[i + 1]);
        if (retval)
            goto cleanup;
    }
    retval = krb5int_copy_data_contents(context, server, &rpath[i + 1]);
    if (retval)
        goto cleanup;

    /* Terminate rpath and return it. */
    rpath[i + 2] = empty_data();
    *rpath_out = rpath;
    rpath = NULL;

cleanup:
    krb5int_free_data_list(context, rpath);
    return retval;
}
Ejemplo n.º 25
0
/* Initialize the realm path fields for getting a TGT for
 * ctx->server->realm. */
static krb5_error_code
init_realm_path(krb5_context context, krb5_tkt_creds_context ctx)
{
    krb5_error_code code;
    krb5_principal *tgt_princ_list = NULL;
    krb5_data *realm_path;
    size_t nrealms, i;

    /* Construct a list of TGT principals from client to server.  We will throw
     * this away after grabbing the remote realms from each principal. */
    code = krb5_walk_realm_tree(context, &ctx->client->realm,
                                &ctx->server->realm,
                                &tgt_princ_list, KRB5_REALM_BRANCH_CHAR);
    if (code != 0)
        return code;

    /* Count the number of principals and allocate the realm path. */
    for (nrealms = 0; tgt_princ_list[nrealms]; nrealms++);
    assert(nrealms > 1);
    realm_path = k5alloc((nrealms + 1) * sizeof(*realm_path), &code);
    if (realm_path == NULL)
        goto cleanup;

    /* Steal the remote realm field from each TGT principal. */
    for (i = 0; i < nrealms; i++) {
        assert(tgt_princ_list[i]->length == 2);
        realm_path[i] = tgt_princ_list[i]->data[1];
        tgt_princ_list[i]->data[1].data = NULL;
    }
    realm_path[nrealms] = empty_data();

    /* Initialize the realm path fields in ctx. */
    krb5int_free_data_list(context, ctx->realm_path);
    ctx->realm_path = realm_path;
    ctx->last_realm = realm_path + nrealms - 1;
    ctx->cur_realm = realm_path;
    ctx->next_realm = ctx->last_realm;
    realm_path = NULL;

cleanup:
    krb5_free_realm_tree(context, tgt_princ_list);
    return 0;
}
Ejemplo n.º 26
0
Archivo: hooks.c Proyecto: INNOAUS/krb5
/* Create a fake error reply. */
static krb5_error_code
test_send_error(krb5_context context, void *data, const krb5_data *realm,
                const krb5_data *message, krb5_data **new_message_out,
                krb5_data **reply_out)
{
    krb5_error_code ret;
    krb5_error err;
    krb5_principal client, server;
    char *realm_str, *princ_str;
    int r;

    realm_str = k5memdup0(realm->data, realm->length, &ret);
    check(ret);

    r = asprintf(&princ_str, "invalid@%s", realm_str);
    assert(r > 0);
    check(krb5_parse_name(ctx, princ_str, &client));
    free(princ_str);

    r = asprintf(&princ_str, "krbtgt@%s", realm_str);
    assert(r > 0);
    check(krb5_parse_name(ctx, princ_str, &server));
    free(princ_str);
    free(realm_str);

    err.magic = KV5M_ERROR;
    err.ctime = 1971196337;
    err.cusec = 0;
    err.susec = 97008;
    err.stime = 1458219390;
    err.error = 6;
    err.client = client;
    err.server = server;
    err.text = string2data("CLIENT_NOT_FOUND");
    err.e_data = empty_data();
    check(encode_krb5_error(&err, reply_out));

    krb5_free_principal(ctx, client);
    krb5_free_principal(ctx, server);
    return 0;
}
Ejemplo n.º 27
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;
}
Ejemplo n.º 28
0
int main( int argc, char *argv[] ) 
/********************************/
{
    FILE                *in;
    FILE                *out;
    char                buf[MAX_LINE_LEN];
    int                 elt;
    char                *end;
    char                *line;
    char                type[50];

    if( argc != 4 ) {
        printf( "FORMAT: parsectl [input file] [output file] [Ctl data name]\n" );
        return( -1 );
    }

    in = fopen( argv[1], "r" );

    if( in == NULL ) {
        printf( "Could not open input file: %s\n", argv[1] );
        return( -1 );
    }

    out = fopen( argv[2], "w" );
    if( out == NULL ) {
        printf( "Could not open output file: %s\n", argv[2] );
        return( -1 );
    }

    fputs( "/**** DO NOT MODIFY THIS FILE BY HAND. CREATED BY PARSECTL ****/\n\n\n",
           out );

    /* Create Data struct definition */
    fputs( "struct {\n", out );
    fputs( "    int            num_ctls;\n", out );

    for( elt = 0;; ++elt ) {
        line = get_line( buf, in );
        if( line == NULL ) {
            break;
        }
        end = strpbrk( line, White_space );
        if( end == NULL ) {
            printf( "No control on line %d\n", Line );
            goto error;
        }
        *end = '\0';
        strcpy( type, line );

        line = get_line( buf, in );     // skip over data_offset
        if( line == NULL ) {
            printf( "No data offset at line %d\n", Line );
            goto error;
        }

        fputs( "    struct {\n", out );
        fputs( "        ctl_type      type;\n", out );
        fputs( "        int           control;\n", out );
        fputs( "        bool          modified;\n", out );
        fputs( "        unsigned int  data_offset;\n", out );
        fputs( "        union {\n", out );
        line = get_line( buf, in );     // skip over data
        if( line == NULL ) {
            printf( "No data at line %d\n", Line );
            goto error;
        }
        if( !empty_data( line ) ) {
            fprintf( out,  "            %s            d1;\n", my_strlwr( type ) );
        }
        fputs( "            ctl_info      d2;\n", out );
        fputs( "        } d3;\n", out );
        fprintf( out,  "    } d%-d;\n", elt );
    }

    fclose( in );

    fprintf( out, "} %s = {\n", argv[3] );
    fprintf( out, "%d,\n", elt );

    in = fopen( argv[1], "r" );

    for( ;; ) {
        line = get_line( buf, in );
        if( line == NULL ) {
            break;
        }

        end = strpbrk( line, White_space );
        *end = '\0';
        ++end;

        fprintf( out, "{ %s, %s, false,", my_strupr( line ), end );

        line = get_line( buf, in );
        fprintf( out, " %s", line );

        line = get_line( buf, in );
        if( !empty_data( line ) ) {
            fprintf( out, ", %s },\n", line );
        } else {
            fputs( "},\n", out );
        }
    }
    fputs( "};\n\n", out );

    fclose( in );
    fclose( out );

    return( 0 );

error:
    fclose( in );
    fclose( out );

    return( 1 );
}
Ejemplo n.º 29
0
krb5_error_code KRB5_CALLCONV
krb5_mk_priv(krb5_context context, krb5_auth_context auth_context,
             const krb5_data *userdata, krb5_data *outbuf,
             krb5_replay_data *outdata)
{
    krb5_error_code       retval;
    krb5_key              key;
    krb5_replay_data      replaydata;
    krb5_data             buf = empty_data();

    *outbuf = empty_data();

    /* Clear replaydata block */
    memset(&replaydata, 0, sizeof(krb5_replay_data));

    /* Get keyblock */
    if ((key = auth_context->send_subkey) == NULL)
        key = auth_context->key;

    /* Get replay info */
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) &&
        (auth_context->rcache == NULL))
        return KRB5_RC_REQUIRED;

    if (((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) ||
         (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
        (outdata == NULL))
        /* Need a better error */
        return KRB5_RC_REQUIRED;

    if (!auth_context->local_addr)
        return KRB5_LOCAL_ADDR_REQUIRED;

    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME)) {
        if ((retval = krb5_us_timeofday(context, &replaydata.timestamp,
                                        &replaydata.usec)))
            return retval;
        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_TIME) {
            outdata->timestamp = replaydata.timestamp;
            outdata->usec = replaydata.usec;
        }
    }
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)) {
        replaydata.seq = auth_context->local_seq_number++;
        if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
            outdata->seq = replaydata.seq;
    }

    {
        krb5_address * premote_fulladdr = NULL;
        krb5_address * plocal_fulladdr;
        krb5_address remote_fulladdr;
        krb5_address local_fulladdr;
        CLEANUP_INIT(2);

        if (auth_context->local_port) {
            if (!(retval = krb5_make_fulladdr(context, auth_context->local_addr,
                                              auth_context->local_port,
                                              &local_fulladdr))) {
                CLEANUP_PUSH(local_fulladdr.contents, free);
                plocal_fulladdr = &local_fulladdr;
            } else {
                goto error;
            }
        } else {
            plocal_fulladdr = auth_context->local_addr;
        }

        if (auth_context->remote_addr) {
            if (auth_context->remote_port) {
                if (!(retval = krb5_make_fulladdr(context,auth_context->remote_addr,
                                                  auth_context->remote_port,
                                                  &remote_fulladdr))){
                    CLEANUP_PUSH(remote_fulladdr.contents, free);
                    premote_fulladdr = &remote_fulladdr;
                } else {
                    CLEANUP_DONE();
                    goto error;
                }
            } else {
                premote_fulladdr = auth_context->remote_addr;
            }
        }

        if ((retval = mk_priv_basic(context, userdata, key, &replaydata,
                                    plocal_fulladdr, premote_fulladdr,
                                    &auth_context->cstate, &buf))) {
            CLEANUP_DONE();
            goto error;
        }

        CLEANUP_DONE();
    }

    if (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_TIME) {
        krb5_donot_replay replay;

        if ((retval = krb5_gen_replay_name(context, auth_context->local_addr,
                                           "_priv", &replay.client)))
            goto error;

        replay.server = "";             /* XXX */
        replay.msghash = NULL;
        replay.cusec = replaydata.usec;
        replay.ctime = replaydata.timestamp;
        if ((retval = krb5_rc_store(context, auth_context->rcache, &replay))) {
            /* should we really error out here? XXX */
            free(replay.client);
            goto error;
        }
        free(replay.client);
    }

    *outbuf = buf;
    return 0;

error:
    krb5_free_data_contents(context, &buf);
    if ((auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) ||
        (auth_context->auth_context_flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE))
        auth_context->local_seq_number--;

    return retval;
}
Ejemplo n.º 30
0
krb5_error_code
k5_sendto(krb5_context context, const krb5_data *message,
          const krb5_data *realm, const struct serverlist *servers,
          k5_transport_strategy strategy,
          struct sendto_callback_info* callback_info, krb5_data *reply,
          struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
          int *server_used,
          /* return 0 -> keep going, 1 -> quit */
          int (*msg_handler)(krb5_context, const krb5_data *, void *),
          void *msg_handler_data)
{
    int pass;
    time_ms delay;
    krb5_error_code retval;
    struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
    size_t s;
    struct select_state *sel_state = NULL, *seltemp;
    char *udpbuf = NULL;
    krb5_boolean done = FALSE;

    *reply = empty_data();

    /* One for use here, listing all our fds in use, and one for
     * temporary use in service_fds, for the fds of interest.  */
    sel_state = malloc(2 * sizeof(*sel_state));
    if (sel_state == NULL) {
        retval = ENOMEM;
        goto cleanup;
    }
    seltemp = &sel_state[1];
    cm_init_selstate(sel_state);

    /* First pass: resolve server hosts, communicate with resulting addresses
     * of the preferred transport, and wait 1s for an answer from each. */
    for (s = 0; s < servers->nservers && !done; s++) {
        /* Find the current tail pointer. */
        for (tailptr = &conns; *tailptr != NULL; tailptr = &(*tailptr)->next);
        retval = resolve_server(context, realm, servers, s, strategy, message,
                                &udpbuf, &conns);
        if (retval)
            goto cleanup;
        for (state = *tailptr; state != NULL && !done; state = state->next) {
            /* Contact each new connection, deferring those which use the
             * non-preferred RFC 4120 transport. */
            if (state->defer)
                continue;
            if (maybe_send(context, state, message, sel_state, realm,
                           callback_info))
                continue;
            done = service_fds(context, sel_state, 1000, conns, seltemp,
                               realm, msg_handler, msg_handler_data, &winner);
        }
    }

    /* Complete the first pass by contacting servers of the non-preferred RFC
     * 4120 transport (if given), waiting 1s for an answer from each. */
    for (state = conns; state != NULL && !done; state = state->next) {
        if (!state->defer)
            continue;
        if (maybe_send(context, state, message, sel_state, realm,
                       callback_info))
            continue;
        done = service_fds(context, sel_state, 1000, conns, seltemp,
                           realm, msg_handler, msg_handler_data, &winner);
    }

    /* Wait for two seconds at the end of the first pass. */
    if (!done) {
        done = service_fds(context, sel_state, 2000, conns, seltemp,
                           realm, msg_handler, msg_handler_data, &winner);
    }

    /* Make remaining passes over all of the connections. */
    delay = 4000;
    for (pass = 1; pass < MAX_PASS && !done; pass++) {
        for (state = conns; state != NULL && !done; state = state->next) {
            if (maybe_send(context, state, message, sel_state, realm,
                           callback_info))
                continue;
            done = service_fds(context, sel_state, 1000, conns, seltemp,
                               realm, msg_handler, msg_handler_data, &winner);
            if (sel_state->nfds == 0)
                break;
        }
        /* Wait for the delay backoff at the end of this pass. */
        if (!done) {
            done = service_fds(context, sel_state, delay, conns, seltemp,
                               realm, msg_handler, msg_handler_data, &winner);
        }
        if (sel_state->nfds == 0)
            break;
        delay *= 2;
    }

    if (sel_state->nfds == 0 || !done || winner == NULL) {
        retval = KRB5_KDC_UNREACH;
        goto cleanup;
    }
    /* Success!  */
    *reply = make_data(winner->in.buf, winner->in.pos);
    retval = 0;
    winner->in.buf = NULL;
    if (server_used != NULL)
        *server_used = winner->server_index;
    if (remoteaddr != NULL && remoteaddrlen != 0 && *remoteaddrlen > 0)
        (void)getpeername(winner->fd, remoteaddr, remoteaddrlen);
    TRACE_SENDTO_KDC_RESPONSE(context, reply->length, &winner->addr);

cleanup:
    for (state = conns; state != NULL; state = next) {
        next = state->next;
        if (state->fd != INVALID_SOCKET) {
            if (socktype_for_transport(state->addr.transport) == SOCK_STREAM)
                TRACE_SENDTO_KDC_TCP_DISCONNECT(context, &state->addr);
            closesocket(state->fd);
            free_http_tls_data(context, state);
        }
        if (state->state == READING && state->in.buf != udpbuf)
            free(state->in.buf);
        if (callback_info) {
            callback_info->pfn_cleanup(callback_info->data,
                                       &state->callback_buffer);
        }
        free(state);
    }

    if (reply->data != udpbuf)
        free(udpbuf);
    free(sel_state);
    return retval;
}