Beispiel #1
0
krb5_error_code KRB5_CALLCONV
krb5_decrypt_tkt_part(krb5_context context, const krb5_keyblock *srv_key, register krb5_ticket *ticket)
{
    krb5_enc_tkt_part *dec_tkt_part;
    krb5_data scratch;
    krb5_error_code retval;

    if (!krb5_c_valid_enctype(ticket->enc_part.enctype))
	return KRB5_PROG_ETYPE_NOSUPP;

    scratch.length = ticket->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(ticket->enc_part.ciphertext.length)))
	return(ENOMEM);

    /* call the encryption routine */
    if ((retval = krb5_c_decrypt(context, srv_key,
				KRB5_KEYUSAGE_KDC_REP_TICKET, 0,
				&ticket->enc_part, &scratch))) {
	free(scratch.data);
	return retval;
    }

#define clean_scratch() {memset(scratch.data, 0, scratch.length); \
free(scratch.data);}

    /*  now decode the decrypted stuff */
    retval = decode_krb5_enc_tkt_part(&scratch, &dec_tkt_part);
    if (!retval) {
	ticket->enc_part2 = dec_tkt_part;
    }
    clean_scratch();
    return retval;
}
krb5_error_code KRB5_CALLCONV
krb5_decrypt(krb5_context context, krb5_const_pointer inptr,
	     krb5_pointer outptr, size_t size, krb5_encrypt_block *eblock,
	     krb5_pointer ivec)
{
    krb5_enc_data inputd;
    krb5_data outputd, ivecd;
    size_t blocksize;
    krb5_error_code ret;

    if (ivec) {
	if ((ret = krb5_c_block_size(context, eblock->key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    /* size is the length of the input ciphertext data */
    inputd.enctype = eblock->key->enctype;
    inputd.ciphertext.length = size;
    inputd.ciphertext.data = inptr;

    /* we don't really know how big this is, but the code tends to assume
       that the output buffer size should be the same as the input
       buffer size */
    outputd.length = size;
    outputd.data = outptr;

    return(krb5_c_decrypt(context, eblock->key, 0, ivec?&ivecd:0,
			  &inputd, &outputd));
}
Beispiel #3
0
static int gp_decrypt_buffer(krb5_context context, krb5_keyblock *key,
                             octet_string *in, size_t *len, void *buf)
{
    int ret;
    krb5_data data_out;
    krb5_enc_data enc_handle;

    memset(&enc_handle, '\0', sizeof(krb5_enc_data));

    enc_handle.enctype = GP_CREDS_HANDLE_KEY_ENCTYPE;
    enc_handle.ciphertext.data = in->octet_string_val;
    enc_handle.ciphertext.length = in->octet_string_len;

    data_out.length = *len;
    data_out.data = buf;

    ret = krb5_c_decrypt(context,
                         key,
                         KRB5_KEYUSAGE_APP_DATA_ENCRYPT,
                         NULL,
                         &enc_handle,
                         &data_out);
    if (ret) {
        return EINVAL;
    }

    *len = data_out.length;

    return 0;
}
krb5_error_code krb5_decrypt_data(krb5_context context, krb5_keyblock *key,
				  krb5_pointer ivec, krb5_enc_data *enc_data,
				  krb5_data *data)
{
    krb5_error_code ret;
    krb5_data ivecd;
    size_t blocksize;

    if (ivec) {
	if ((ret = krb5_c_block_size(context, key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    data->length = enc_data->ciphertext.length;
    if ((data->data = (char *) malloc(data->length)) == NULL)
	return(ENOMEM);

    if ((ret = krb5_c_decrypt(context, key, 0, ivec?&ivecd:0, enc_data, data)))
	free(data->data);

    return(0);
}
Beispiel #5
0
static krb5_error_code
decrypt_encdata(krb5_context context, krb5_keyblock *armor_key,
                krb5_pa_otp_req *req, krb5_data *out)
{
    krb5_error_code retval;
    krb5_data plaintext;

    if (req == NULL)
        return EINVAL;

    retval = alloc_data(&plaintext, req->enc_data.ciphertext.length);
    if (retval)
        return retval;

    retval = krb5_c_decrypt(context, armor_key, KRB5_KEYUSAGE_PA_OTP_REQUEST,
                            NULL, &req->enc_data, &plaintext);
    if (retval != 0) {
        com_err("otp", retval, "Unable to decrypt encData in PA-OTP-REQUEST");
        free(plaintext.data);
        return retval;
    }

    *out = plaintext;
    return 0;
}
Beispiel #6
0
static krb5_error_code
decrypt_authenticator(krb5_context context, const krb5_ap_req *request,
		      krb5_authenticator **authpp, int is_ap_req)
{
    krb5_authenticator *local_auth;
    krb5_error_code retval;
    krb5_data scratch;
    krb5_keyblock *sesskey;

    sesskey = request->ticket->enc_part2->session;

    scratch.length = request->authenticator.ciphertext.length;
    if (!(scratch.data = malloc(scratch.length)))
	return(ENOMEM);

    if ((retval = krb5_c_decrypt(context, sesskey,
				 is_ap_req?KRB5_KEYUSAGE_AP_REQ_AUTH:
				 KRB5_KEYUSAGE_TGS_REQ_AUTH, 0,
				 &request->authenticator, &scratch))) {
	free(scratch.data);
	return(retval);
    }

#define clean_scratch() {memset(scratch.data, 0, scratch.length); \
free(scratch.data);}

    /*  now decode the decrypted stuff */
    if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) {
	*authpp = local_auth;
    }
    clean_scratch();
    return retval;
}
Beispiel #7
0
static krb5_error_code
decrypt_fast_reply(krb5_context context,
                   struct krb5int_fast_request_state *state,
                   krb5_pa_data **in_padata,
                   krb5_fast_response **response)
{
    krb5_error_code retval = 0;
    krb5_data scratch;
    krb5_enc_data *encrypted_response = NULL;
    krb5_pa_data *fx_reply = NULL;
    krb5_fast_response *local_resp = NULL;

    assert(state != NULL);
    assert(state->armor_key);
    fx_reply = krb5int_find_pa_data(context, in_padata, KRB5_PADATA_FX_FAST);
    if (fx_reply == NULL)
        retval = KRB5_ERR_FAST_REQUIRED;
    TRACE_FAST_DECODE(context);
    if (retval == 0) {
        scratch.data = (char *) fx_reply->contents;
        scratch.length = fx_reply->length;
        retval = decode_krb5_pa_fx_fast_reply(&scratch, &encrypted_response);
    }
    scratch.data = NULL;
    if (retval == 0) {
        scratch.data = malloc(encrypted_response->ciphertext.length);
        if (scratch.data == NULL)
            retval = ENOMEM;
        scratch.length = encrypted_response->ciphertext.length;
    }
    if (retval == 0)
        retval = krb5_c_decrypt(context, state->armor_key,
                                KRB5_KEYUSAGE_FAST_REP, NULL,
                                encrypted_response, &scratch);
    if (retval != 0)
        k5_prependmsg(context, retval, _("Failed to decrypt FAST reply"));
    if (retval == 0)
        retval = decode_krb5_fast_response(&scratch, &local_resp);
    if (retval == 0) {
        if (local_resp->nonce != state->nonce) {
            retval = KRB5_KDCREP_MODIFIED;
            k5_setmsg(context, retval, _("nonce modified in FAST response: "
                                         "KDC response modified"));
        }
    }
    if (retval == 0) {
        *response = local_resp;
        local_resp = NULL;
    }
    if (scratch.data)
        free(scratch.data);
    if (encrypted_response)
        krb5_free_enc_data(context, encrypted_response);
    if (local_resp)
        krb5_free_fast_response(context, local_resp);
    return retval;
}
void
DecryptionManager::PacketHandler::decryptApplicationMessage(bool fromClient) {
  // Split and coalesce the cipher data into a continuous buffer
  unique_ptr<IOBuf> msg = cipher_->split(cipher_->chainLength());
  msg->coalesce();

  // Start decryption. This is mostly copied from k5sealv3.c.
  krb5_error_code code;
  unsigned char *ptr;
  unsigned int bodysize;

  ptr = (unsigned char *)msg->data() + 4;
  code = gssint_g_verify_token_header(gss_mech_krb5, // always use krb5
                                      &bodysize,
                                      &ptr,
                                      -1,
                                      msg->length() - 4,
                                      0);

  if (code) {
    onError("Failed to verify application message header");
    return;
  }

  size_t ec = load_16_be(ptr+4);
  size_t rrc = load_16_be(ptr+6);
  if (!gss_krb5int_rotate_left(ptr+16, bodysize-16, rrc)) {
    throw std::runtime_error("Cannot allocate memory");
  }

  krb5_enc_data cipher;
  krb5_data plain;
  int keyUsage;
  cipher.enctype = subkey_->enctype;
  cipher.ciphertext.length = bodysize - 16;
  cipher.ciphertext.data = (char*)ptr + 16;
  plain.length = bodysize - 16;
  plain.data = (char*)malloc(plain.length);
  if (!plain.data) {
    throw std::runtime_error("Cannot allocate memory");
  }

  SCOPE_EXIT {
    free(plain.data);
  };

  keyUsage = (fromClient ? KG_USAGE_INITIATOR_SEAL : KG_USAGE_ACCEPTOR_SEAL);
  code = krb5_c_decrypt(ctx_->get(), subkey_, keyUsage, 0, &cipher, &plain);
  if (code) {
    onError("Cannot decrypt application message");
    return;
  }

  decryptedMessages_.push_back(IOBuf::copyBuffer(plain.data,
                                                 plain.length - ec - 16));
}
Beispiel #9
0
krb5_error_code KRB5_CALLCONV
krb5_rd_rep_dce(krb5_context context, krb5_auth_context auth_context,
		const krb5_data *inbuf, krb5_ui_4 *nonce)
{
    krb5_error_code 	  retval;
    krb5_ap_rep 	* reply;
    krb5_data 	 	  scratch;
    krb5_ap_rep_enc_part *repl = NULL;

    if (!krb5_is_ap_rep(inbuf))
	return KRB5KRB_AP_ERR_MSG_TYPE;

    /* decode it */

    if ((retval = decode_krb5_ap_rep(inbuf, &reply)))
	return retval;

    /* put together an eblock for this encryption */

    scratch.length = reply->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(scratch.length))) {
	krb5_free_ap_rep(context, reply);
	return(ENOMEM);
    }

    if ((retval = krb5_c_decrypt(context, auth_context->keyblock,
				 KRB5_KEYUSAGE_AP_REP_ENCPART, 0,
				 &reply->enc_part, &scratch)))
	goto clean_scratch;

    /* now decode the decrypted stuff */
    retval = decode_krb5_ap_rep_enc_part(&scratch, &repl);
    if (retval)
	goto clean_scratch;

    *nonce = repl->seq_number;
    if (*nonce != auth_context->local_seq_number) {
	retval = KRB5_MUTUAL_FAILED;
	goto clean_scratch;
    }

    /* Must be NULL to prevent echoing for client AP-REP */
    if (repl->subkey != NULL) {
	retval = KRB5_MUTUAL_FAILED;
	goto clean_scratch;
    }

clean_scratch:
    memset(scratch.data, 0, scratch.length); 

    if (repl != NULL)
	krb5_free_ap_rep_enc_part(context, repl);
    krb5_free_ap_rep(context, reply);
    free(scratch.data);
    return retval;
}
Beispiel #10
0
static krb5_error_code
test_process(krb5_context context, krb5_clpreauth_moddata moddata,
             krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
             krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
             krb5_kdc_req *request, krb5_data *encoded_request_body,
             krb5_data *encoded_previous_request, krb5_pa_data *pa_data,
             krb5_prompter_fct prompter, void *prompter_data,
             krb5_pa_data ***out_pa_data)
{
    struct client_state *st = (struct client_state *)moddata;
    struct client_request_state *reqst = (struct client_request_state *)modreq;
    krb5_error_code ret;
    krb5_keyblock *k;
    krb5_enc_data enc;
    krb5_data plain;
    const char *indstr;

    if (pa_data->length == 0) {
        /* This is an optimistic preauth test.  Send a recognizable padata
         * value so the KDC knows not to expect a cookie. */
        if (st->fail_optimistic) {
            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced optimistic fail");
            return KRB5_PREAUTH_FAILED;
        }
        *out_pa_data = make_pa_list("optimistic", 10);
        return 0;
    } else if (reqst->second_round_trip) {
        printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
        if (st->fail_2rt) {
            k5_setmsg(context, KRB5_PREAUTH_FAILED, "induced 2rt fail");
            return KRB5_PREAUTH_FAILED;
        }
    } else if (pa_data->length == 6 &&
               memcmp(pa_data->contents, "no key", 6) == 0) {
        printf("no key\n");
    } else {
        /* This fails during s4u_identify_user(), so don't assert. */
        ret = cb->get_as_key(context, rock, &k);
        if (ret)
            return ret;
        ret = alloc_data(&plain, pa_data->length);
        assert(!ret);
        enc.enctype = k->enctype;
        enc.ciphertext = make_data(pa_data->contents, pa_data->length);
        ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain);
        assert(!ret);
        printf("%.*s\n", plain.length, plain.data);
        free(plain.data);
    }
    reqst->second_round_trip = TRUE;

    indstr = (st->indicators != NULL) ? st->indicators : "";
    *out_pa_data = make_pa_list(indstr, strlen(indstr));
    return 0;
}
Beispiel #11
0
static void
test_enctype(krb5_enctype enctype)
{
    krb5_error_code ret;
    krb5_keyblock keyblock;
    krb5_enc_data input;
    krb5_data output;
    krb5_crypto_iov iov[2];
    unsigned int dummy;
    size_t min_len, len;

    printf("Testing enctype %d\n", (int) enctype);
    x(krb5_c_encrypt_length(NULL, enctype, 0, &min_len));
    x(krb5_c_make_random_key(NULL, enctype, &keyblock));
    input.enctype = enctype;

    /* Try each length up to the minimum length. */
    for (len = 0; len <= min_len; len++) {
        input.ciphertext.data = calloc(len, 1);
        input.ciphertext.length = len;
        output.data = calloc(len, 1);
        output.length = len;

        /* Attempt a normal decryption. */
        ret = krb5_c_decrypt(NULL, &keyblock, 0, NULL, &input, &output);
        check_decrypt_result(ret, len, min_len);

        if (krb5_c_crypto_length(NULL, enctype, KRB5_CRYPTO_TYPE_HEADER,
                                 &dummy) == 0) {
            /* Attempt an IOV stream decryption. */
            iov[0].flags = KRB5_CRYPTO_TYPE_STREAM;
            iov[0].data = input.ciphertext;
            iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
            iov[1].data.data = NULL;
            iov[1].data.length = 0;
            ret = krb5_c_decrypt_iov(NULL, &keyblock, 0, NULL, iov, 2);
            check_decrypt_result(ret, len, min_len);
        }

        free(input.ciphertext.data);
        free(output.data);
    }
    krb5int_c_free_keyblock_contents (NULL, &keyblock);

}
Beispiel #12
0
/*ARGSUSED*/
krb5_error_code
krb5_kdc_rep_decrypt_proc(krb5_context context, const krb5_keyblock *key, krb5_const_pointer decryptarg, krb5_kdc_rep *dec_rep)
{
    krb5_error_code retval;
    krb5_data scratch;
    krb5_enc_kdc_rep_part *local_encpart;
    krb5_keyusage usage;

    if (decryptarg) {
        usage = *(const krb5_keyusage *) decryptarg;
    } else {
        usage = KRB5_KEYUSAGE_AS_REP_ENCPART;
    }

    /* set up scratch decrypt/decode area */

    scratch.length = dec_rep->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(dec_rep->enc_part.ciphertext.length))) {
        return(ENOMEM);
    }

    /*dec_rep->enc_part.enctype;*/

    if ((retval = krb5_c_decrypt(context, key, usage, 0, &dec_rep->enc_part,
                                 &scratch))) {
        free(scratch.data);
        return(retval);
    }

#define clean_scratch() {memset(scratch.data, 0, scratch.length);       \
        free(scratch.data);}

    /* and do the decode */
    retval = decode_krb5_enc_kdc_rep_part(&scratch, &local_encpart);
    clean_scratch();
    if (retval)
        return retval;

    dec_rep->enc_part2 = local_encpart;

    return 0;
}
Beispiel #13
0
static int
krb_enc_mit(krb5_context context,
	    krb5_enctype enctype,
	    krb5_keyblock *key,
	    unsigned usage,
	    krb5_data *cipher,
	    krb5_data *clear)
{
#ifndef HEIMDAL_SMALLER
    krb5_error_code ret;
    krb5_enc_data e;
    krb5_data decrypt;
    size_t len;

    e.kvno = 0;
    e.enctype = enctype;
    e.ciphertext = *cipher;

    ret = krb5_c_decrypt(context, *key, usage, NULL, &e, &decrypt);
    if (ret)
	return ret;

    if (decrypt.length != clear->length ||
	memcmp(decrypt.data, clear->data, decrypt.length) != 0) {
	krb5_warnx(context, "clear text not same");
	return EINVAL;
    }

    krb5_data_free(&decrypt);

    ret = krb5_c_encrypt_length(context, enctype, clear->length, &len);
    if (ret)
	return ret;

    if (len != cipher->length) {
	krb5_warnx(context, "c_encrypt_length wrong %lu != %lu",
		   (unsigned long)len, (unsigned long)cipher->length);
	return EINVAL;
    }
#endif /* HEIMDAL_SMALLER */
    return 0;
}
Beispiel #14
0
static krb5_error_code
securid_decrypt_track_data_2(krb5_context context, krb5_db_entry *client,
                             krb5_data *enc_track_data, krb5_data *output)
{
    krb5_error_code retval;
    krb5_keyblock sam_key;
    krb5_enc_data tmp_enc_data;
    sam_key.contents = NULL;

    retval = get_securid_key(context, client, &sam_key);
    if (retval != 0)
        return retval;

    tmp_enc_data.ciphertext = *enc_track_data;
    tmp_enc_data.enctype = ENCTYPE_UNKNOWN;
    tmp_enc_data.kvno = 0;

    output->length = tmp_enc_data.ciphertext.length;
    free(output->data);
    output->data = k5alloc(output->length, &retval);
    if (output->data == NULL)
        goto cleanup;
    retval = krb5_c_decrypt(context, &sam_key,
                            KRB5_KEYUSAGE_PA_SAM_CHALLENGE_TRACKID, 0,
                            &tmp_enc_data, output);
cleanup:
    krb5_free_keyblock_contents(context, &sam_key);

    if (retval) {
        output->length = 0;
        free(output->data);
        output->data = NULL;
        return retval;
    }

    return 0;
}
Beispiel #15
0
static krb5_error_code
test_process(krb5_context context, krb5_clpreauth_moddata moddata,
             krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
             krb5_clpreauth_callbacks cb, krb5_clpreauth_rock rock,
             krb5_kdc_req *request, krb5_data *encoded_request_body,
             krb5_data *encoded_previous_request, krb5_pa_data *pa_data,
             krb5_prompter_fct prompter, void *prompter_data,
             krb5_pa_data ***out_pa_data)
{
    struct client_state *st = (struct client_state *)moddata;
    struct client_request_state *reqst = (struct client_request_state *)modreq;
    krb5_error_code ret;
    krb5_pa_data **list, *pa;
    krb5_keyblock *k;
    krb5_enc_data enc;
    krb5_data plain;
    const char *indstr;

    if (pa_data->length == 0) {
        /* This is an optimistic preauth test.  Send a recognizable padata
         * value so the KDC knows not to expect a cookie. */
        list = k5calloc(2, sizeof(*list), &ret);
        assert(!ret);
        pa = k5alloc(sizeof(*pa), &ret);
        assert(!ret);
        pa->pa_type = TEST_PA_TYPE;
        pa->contents = (uint8_t *)strdup("optimistic");
        assert(pa->contents != NULL);
        pa->length = 10;
        list[0] = pa;
        list[1] = NULL;
        *out_pa_data = list;
        return 0;
    } else if (reqst->second_round_trip) {
        printf("2rt: %.*s\n", pa_data->length, pa_data->contents);
    } else if (pa_data->length == 6 &&
               memcmp(pa_data->contents, "no key", 6) == 0) {
        printf("no key\n");
    } else {
        /* This fails during s4u_identify_user(), so don't assert. */
        ret = cb->get_as_key(context, rock, &k);
        if (ret)
            return ret;
        ret = alloc_data(&plain, pa_data->length);
        assert(!ret);
        enc.enctype = k->enctype;
        enc.ciphertext = make_data(pa_data->contents, pa_data->length);
        ret = krb5_c_decrypt(context, k, 1024, NULL, &enc, &plain);
        assert(!ret);
        printf("%.*s\n", plain.length, plain.data);
        free(plain.data);
    }
    reqst->second_round_trip = TRUE;

    indstr = (st->indicators != NULL) ? st->indicators : "";
    list = k5calloc(2, sizeof(*list), &ret);
    assert(!ret);
    pa = k5alloc(sizeof(*pa), &ret);
    assert(!ret);
    pa->pa_type = TEST_PA_TYPE;
    pa->contents = (uint8_t *)strdup(indstr);
    assert(pa->contents != NULL);
    pa->length = strlen(indstr);
    list[0] = pa;
    list[1] = NULL;
    *out_pa_data = list;
    return 0;
}
Beispiel #16
0
static krb5_error_code
krb5_rd_priv_basic(krb5_context context, const krb5_data *inbuf,
		   const krb5_keyblock *keyblock,
		   const krb5_address *local_addr,
		   const krb5_address *remote_addr, krb5_pointer i_vector,
		   krb5_replay_data *replaydata, krb5_data *outbuf)
{
    krb5_error_code 	  retval;
    krb5_priv 		* privmsg;
    krb5_data 		  scratch;
    krb5_priv_enc_part  * privmsg_enc_part;
    size_t		  blocksize;
    krb5_data		  ivdata;

    if (!krb5_is_krb_priv(inbuf))
	return KRB5KRB_AP_ERR_MSG_TYPE;

    /* decode private message */
    if ((retval = decode_krb5_priv(inbuf, &privmsg)))
	return retval;
    
    if (i_vector) {
	if ((retval = krb5_c_block_size(context, keyblock->enctype,
					&blocksize)))
	    goto cleanup_privmsg;

	ivdata.length = blocksize;
	ivdata.data = i_vector;
    }

    scratch.length = privmsg->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(scratch.length))) {
	retval = ENOMEM;
	goto cleanup_privmsg;
    }

    if ((retval = krb5_c_decrypt(context, keyblock,
				 KRB5_KEYUSAGE_KRB_PRIV_ENCPART, 
				 i_vector?&ivdata:0,
				 &privmsg->enc_part, &scratch)))
	goto cleanup_scratch;

    /*  now decode the decrypted stuff */
    if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
        goto cleanup_scratch;

    if (!krb5_address_compare(context,remote_addr,privmsg_enc_part->s_address)){
	retval = KRB5KRB_AP_ERR_BADADDR;
	goto cleanup_data;
    }
    
    if (privmsg_enc_part->r_address) {
	if (local_addr) {
	    if (!krb5_address_compare(context, local_addr,
				      privmsg_enc_part->r_address)) {
		retval = KRB5KRB_AP_ERR_BADADDR;
		goto cleanup_data;
	    }
	} else {
	    krb5_address **our_addrs;
	
	    if ((retval = krb5_os_localaddr(context, &our_addrs))) {
		goto cleanup_data;
	    }
	    if (!krb5_address_search(context, privmsg_enc_part->r_address, 
				     our_addrs)) {
		krb5_free_addresses(context, our_addrs);
		retval =  KRB5KRB_AP_ERR_BADADDR;
		goto cleanup_data;
	    }
	    krb5_free_addresses(context, our_addrs);
	}
    }

    replaydata->timestamp = privmsg_enc_part->timestamp;
    replaydata->usec = privmsg_enc_part->usec;
    replaydata->seq = privmsg_enc_part->seq_number;

    /* everything is ok - return data to the user */
    *outbuf = privmsg_enc_part->user_data;
    retval = 0;

cleanup_data:;
    if (retval == 0)
	privmsg_enc_part->user_data.data = 0;
    krb5_free_priv_enc_part(context, privmsg_enc_part);

cleanup_scratch:;
    memset(scratch.data, 0, scratch.length); 
    free(scratch.data);

cleanup_privmsg:;
    free(privmsg->enc_part.ciphertext.data); 
    free(privmsg);

    return retval;
}
Beispiel #17
0
static int
rxkad_keytab_decrypt(int kvno, int et, void *in, size_t inlen,
		     void *out, size_t *outlen)
{
    krb5_error_code code;
    /* use heimdal api if available, since heimdal's interface to
       krb5_c_decrypt is non-standard and annoying to use */
#ifdef HAVE_KRB5_CRYPTO_INIT
    krb5_crypto kcrypto;
#else
    krb5_enc_data ind;
#endif
    krb5_data outd;
    int i, foundkey;
    MUTEX_ENTER(&krb5_lock);
    reload_keys();
    if (have_keytab_keys == 0) {
	MUTEX_EXIT(&krb5_lock);
	return RXKADUNKNOWNKEY;
    }
    foundkey = 0;
    code = -1;
    for (i = 0; i < nkeys; i++) {
	/* foundkey determines what error code we return for failure */
	if (ktent[i].vno == kvno)
	    foundkey = 1;
	/* but check against all keys if the enctype matches, for robustness */
	if (kb_enctype(kte_keyblock(ktent[i])) == et) {
#ifdef HAVE_KRB5_CRYPTO_INIT
	    code = krb5_crypto_init(k5ctx, kte_keyblock(ktent[i]), et,
				    &kcrypto);
	    if (code == 0) {
		code = krb5_decrypt(k5ctx, kcrypto,
				    KRB5_KEYUSAGE_KDC_REP_TICKET, in, inlen,
				    &outd);
		krb5_crypto_destroy(k5ctx, kcrypto);
	    }
	    if (code == 0) {
		if (outd.length > *outlen) {
		    /* This should never happen, but don't assert since we may
		     * be dealing with untrusted user data. */
		    code = EINVAL;
		    krb5_data_free(&outd);
		    outd.data = NULL;
		}
	    }
	    if (code == 0) {
		/* heimdal allocates new memory for the decrypted data; put
		 * the data back into the requested 'out' buffer */
		*outlen = outd.length;
		memcpy(out, outd.data, outd.length);
		krb5_data_free(&outd);
		break;
	    }
#else
	    outd.length = *outlen;
	    outd.data = out;
	    ind.ciphertext.length = inlen;
	    ind.ciphertext.data = in;
	    ind.enctype = et;
	    ind.kvno = kvno;
	    code = krb5_c_decrypt(k5ctx, kte_keyblock(ktent[i]),
				  KRB5_KEYUSAGE_KDC_REP_TICKET, NULL, &ind,
				  &outd);
	    if (code == 0) {
		*outlen = outd.length;
		break;
	    }
#endif
	}
    }
    MUTEX_EXIT(&krb5_lock);
    if (code == 0)
	return 0;
    if (foundkey != 0)
	return RXKADBADTICKET;
    return RXKADUNKNOWNKEY;
}
Beispiel #18
0
krb5_error_code
krb5_dbekd_decrypt_key_data( krb5_context 	  context,
			     const krb5_keyblock	* mkey,
			     const krb5_key_data	* key_data,
			     krb5_keyblock 	* dbkey,
			     krb5_keysalt 	* keysalt)
{
    krb5_error_code 	  retval = 0;
    krb5_int16		  tmplen;
    krb5_octet		* ptr;
    krb5_enc_data	  cipher;
    krb5_data		  plain;

    ptr = key_data->key_data_contents[0];

    if (ptr) {
	krb5_kdb_decode_int16(ptr, tmplen);
	ptr += 2;

	cipher.enctype = ENCTYPE_UNKNOWN;
	cipher.ciphertext.length = key_data->key_data_length[0]-2;
	cipher.ciphertext.data = (char *)ptr;  /* SUNWresync121 XXX */
	plain.length = key_data->key_data_length[0]-2;
	if ((plain.data = (char *) malloc(plain.length)) == NULL)
	    return(ENOMEM);
	(void) memset(plain.data, 0, plain.length);

	if ((retval = krb5_c_decrypt(context, mkey, 0 /* XXX */, 0,
				     &cipher, &plain))) {
	    krb5_xfree(plain.data);
	    return retval;
	}

	/* tmplen is the true length of the key.  plain.data is the
	   plaintext data length, but it may be padded, since the
	   old-style etypes didn't store the real length.  I can check
	   to make sure that there are enough bytes, but I can't do
	   any better than that. */

	if (tmplen > plain.length) {
	    krb5_xfree(plain.data);
	    return(KRB5_CRYPTO_INTERNAL);
	}

	dbkey->magic = KV5M_KEYBLOCK;
	dbkey->enctype = key_data->key_data_type[0];
	dbkey->length = tmplen;
	dbkey->contents = (unsigned char *) plain.data;  /* SUNWresync121 XXX */
	dbkey->dk_list = NULL;
	dbkey->hKey = CK_INVALID_HANDLE;
    }

    /* Decode salt data */
    if (keysalt) {
	if (key_data->key_data_ver == 2) {
	    keysalt->type = key_data->key_data_type[1];
	    if ((keysalt->data.length = key_data->key_data_length[1])) {
		if (!(keysalt->data.data=(char *)malloc(keysalt->data.length))){
		    if (key_data->key_data_contents[0]) {
			krb5_xfree(dbkey->contents);
			dbkey->contents = 0;
			dbkey->length = 0;
		    }
		    return ENOMEM;
		}
		memcpy(keysalt->data.data, key_data->key_data_contents[1],
		       (size_t) keysalt->data.length);
	    } else
		keysalt->data.data = (char *) NULL;
	} else {
	    keysalt->type = KRB5_KDB_SALTTYPE_NORMAL;
	    keysalt->data.data = (char *) NULL;
	    keysalt->data.length = 0;
	}
    }

    return retval;
}
Beispiel #19
0
static void
enc_ts_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
              krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
              krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
              krb5_kdcpreauth_moddata moddata,
              krb5_kdcpreauth_verify_respond_fn respond, void *arg)
{
    krb5_pa_enc_ts *            pa_enc = 0;
    krb5_error_code             retval;
    krb5_data                   scratch;
    krb5_data                   enc_ts_data;
    krb5_enc_data               *enc_data = 0;
    krb5_keyblock               key;
    krb5_key_data *             client_key;
    krb5_int32                  start;
    krb5_timestamp              timenow;
    krb5_error_code             decrypt_err = 0;

    scratch.data = (char *)pa->contents;
    scratch.length = pa->length;

    enc_ts_data.data = 0;

    if ((retval = decode_krb5_enc_data(&scratch, &enc_data)) != 0)
        goto cleanup;

    enc_ts_data.length = enc_data->ciphertext.length;
    if ((enc_ts_data.data = (char *) malloc(enc_ts_data.length)) == NULL)
        goto cleanup;

    start = 0;
    decrypt_err = 0;
    while (1) {
        if ((retval = krb5_dbe_search_enctype(context, rock->client,
                                              &start, enc_data->enctype,
                                              -1, 0, &client_key)))
            goto cleanup;

        if ((retval = krb5_dbe_decrypt_key_data(context, NULL, client_key,
                                                &key, NULL)))
            goto cleanup;

        key.enctype = enc_data->enctype;

        retval = krb5_c_decrypt(context, &key, KRB5_KEYUSAGE_AS_REQ_PA_ENC_TS,
                                0, enc_data, &enc_ts_data);
        krb5_free_keyblock_contents(context, &key);
        if (retval == 0)
            break;
        else
            decrypt_err = retval;
    }

    if ((retval = decode_krb5_pa_enc_ts(&enc_ts_data, &pa_enc)) != 0)
        goto cleanup;

    if ((retval = krb5_timeofday(context, &timenow)) != 0)
        goto cleanup;

    if (labs(timenow - pa_enc->patimestamp) > context->clockskew) {
        retval = KRB5KRB_AP_ERR_SKEW;
        goto cleanup;
    }

    setflag(enc_tkt_reply->flags, TKT_FLG_PRE_AUTH);

    retval = 0;

cleanup:
    if (enc_data) {
        krb5_free_data_contents(context, &enc_data->ciphertext);
        free(enc_data);
    }
    krb5_free_data_contents(context, &enc_ts_data);
    if (pa_enc)
        free(pa_enc);
    /*
     * If we get NO_MATCHING_KEY and decryption previously failed, and
     * we failed to find any other keys of the correct enctype after
     * that failed decryption, it probably means that the password was
     * incorrect.
     */
    if (retval == KRB5_KDB_NO_MATCHING_KEY && decrypt_err != 0)
        retval = decrypt_err;

    (*respond)(arg, retval, NULL, NULL, NULL);
}
Beispiel #20
0
int
main ()
{
    krb5_context context = 0;
    krb5_data  in, in2, out, out2, check, check2, state, signdata;
    krb5_crypto_iov iov[5];
    int i, j, pos;
    unsigned int dummy;
    size_t len;
    krb5_enc_data enc_out, enc_out2;
    krb5_keyblock *keyblock;
    krb5_key key;

    memset(iov, 0, sizeof(iov));

    in.data = "This is a test.\n";
    in.length = strlen (in.data);
    in2.data = "This is another test.\n";
    in2.length = strlen (in2.data);

    test ("Seeding random number generator",
          krb5_c_random_seed (context, &in));

    /* Set up output buffers. */
    out.data = malloc(2048);
    out2.data = malloc(2048);
    check.data = malloc(2048);
    check2.data = malloc(2048);
    if (out.data == NULL || out2.data == NULL
        || check.data == NULL || check2.data == NULL)
        abort();
    out.magic = KV5M_DATA;
    out.length = 2048;
    out2.magic = KV5M_DATA;
    out2.length = 2048;
    check.length = 2048;
    check2.length = 2048;

    for (i = 0; interesting_enctypes[i]; i++) {
        krb5_enctype enctype = interesting_enctypes [i];

        printf ("Testing enctype %d\n", enctype);
        test ("Initializing a keyblock",
              krb5_init_keyblock (context, enctype, 0, &keyblock));
        test ("Generating random keyblock",
              krb5_c_make_random_key (context, enctype, keyblock));
        test ("Creating opaque key from keyblock",
              krb5_k_create_key (context, keyblock, &key));

        enc_out.ciphertext = out;
        enc_out2.ciphertext = out2;
        /* We use an intermediate `len' because size_t may be different size
           than `int' */
        krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len);
        enc_out.ciphertext.length = len;

        /* Encrypt, decrypt, and see if we got the plaintext back again. */
        test ("Encrypting (c)",
              krb5_c_encrypt (context, keyblock, 7, 0, &in, &enc_out));
        display ("Enc output", &enc_out.ciphertext);
        test ("Decrypting",
              krb5_c_decrypt (context, keyblock, 7, 0, &enc_out, &check));
        test ("Comparing", compare_results (&in, &check));

        /* Try again with the opaque-key-using variants. */
        memset(out.data, 0, out.length);
        test ("Encrypting (k)",
              krb5_k_encrypt (context, key, 7, 0, &in, &enc_out));
        display ("Enc output", &enc_out.ciphertext);
        test ("Decrypting",
              krb5_k_decrypt (context, key, 7, 0, &enc_out, &check));
        test ("Comparing", compare_results (&in, &check));

        /* Check if this enctype supports IOV encryption. */
        if ( krb5_c_crypto_length(context, keyblock->enctype,
                                  KRB5_CRYPTO_TYPE_HEADER, &dummy) == 0 ){
            /* Set up iovecs for stream decryption. */
            memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length);
            iov[0].flags= KRB5_CRYPTO_TYPE_STREAM;
            iov[0].data.data = out2.data;
            iov[0].data.length = enc_out.ciphertext.length;
            iov[1].flags = KRB5_CRYPTO_TYPE_DATA;

            /* Decrypt the encrypted data from above and check it. */
            test("IOV stream decrypting (c)",
                 krb5_c_decrypt_iov( context, keyblock, 7, 0, iov, 2));
            test("Comparing results",
                 compare_results(&in, &iov[1].data));

            /* Try again with the opaque-key-using variant. */
            memcpy(out2.data, enc_out.ciphertext.data, enc_out.ciphertext.length);
            test("IOV stream decrypting (k)",
                 krb5_k_decrypt_iov( context, key, 7, 0, iov, 2));
            test("Comparing results",
                 compare_results(&in, &iov[1].data));

            /* Set up iovecs for AEAD encryption. */
            signdata.magic = KV5M_DATA;
            signdata.data = (char *) "This should be signed";
            signdata.length = strlen(signdata.data);
            iov[0].flags = KRB5_CRYPTO_TYPE_HEADER;
            iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
            iov[1].data = in; /*We'll need to copy memory before encrypt*/
            iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY;
            iov[2].data = signdata;
            iov[3].flags = KRB5_CRYPTO_TYPE_PADDING;
            iov[4].flags = KRB5_CRYPTO_TYPE_TRAILER;

            /* "Allocate" data for the iovec buffers from the "out" buffer. */
            test("Setting up iov lengths",
                 krb5_c_crypto_length_iov(context, keyblock->enctype, iov, 5));
            for (j=0,pos=0; j <= 4; j++ ){
                if (iov[j].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY)
                    continue;
                iov[j].data.data = &out.data[pos];
                pos += iov[j].data.length;
            }
            assert (iov[1].data.length == in.length);
            memcpy(iov[1].data.data, in.data, in.length);

            /* Encrypt and decrypt in place, and check the result. */
            test("iov encrypting (c)",
                 krb5_c_encrypt_iov(context, keyblock, 7, 0, iov, 5));
            assert(iov[1].data.length == in.length);
            display("Header", &iov[0].data);
            display("Data", &iov[1].data);
            display("Padding", &iov[3].data);
            display("Trailer", &iov[4].data);
            test("iov decrypting",
                 krb5_c_decrypt_iov(context, keyblock, 7, 0, iov, 5));
            test("Comparing results",
                 compare_results(&in, &iov[1].data));

            /* Try again with opaque-key-using variants. */
            test("iov encrypting (k)",
                 krb5_k_encrypt_iov(context, key, 7, 0, iov, 5));
            assert(iov[1].data.length == in.length);
            display("Header", &iov[0].data);
            display("Data", &iov[1].data);
            display("Padding", &iov[3].data);
            display("Trailer", &iov[4].data);
            test("iov decrypting",
                 krb5_k_decrypt_iov(context, key, 7, 0, iov, 5));
            test("Comparing results",
                 compare_results(&in, &iov[1].data));
        }

        enc_out.ciphertext.length = out.length;
        check.length = 2048;

        test ("init_state",
              krb5_c_init_state (context, keyblock, 7, &state));
        test ("Encrypting with state",
              krb5_c_encrypt (context, keyblock, 7, &state, &in, &enc_out));
        display ("Enc output", &enc_out.ciphertext);
        test ("Encrypting again with state",
              krb5_c_encrypt (context, keyblock, 7, &state, &in2, &enc_out2));
        display ("Enc output", &enc_out2.ciphertext);
        test ("free_state",
              krb5_c_free_state (context, keyblock, &state));
        test ("init_state",
              krb5_c_init_state (context, keyblock, 7, &state));
        test ("Decrypting with state",
              krb5_c_decrypt (context, keyblock, 7, &state, &enc_out, &check));
        test ("Decrypting again with state",
              krb5_c_decrypt (context, keyblock, 7, &state, &enc_out2, &check2));
        test ("free_state",
              krb5_c_free_state (context, keyblock, &state));
        test ("Comparing",
              compare_results (&in, &check));
        test ("Comparing",
              compare_results (&in2, &check2));

        krb5_free_keyblock (context, keyblock);
        krb5_k_free_key (context, key);
    }

    /* Test the RC4 decrypt fallback from key usage 9 to 8. */
    test ("Initializing an RC4 keyblock",
          krb5_init_keyblock (context, ENCTYPE_ARCFOUR_HMAC, 0, &keyblock));
    test ("Generating random RC4 key",
          krb5_c_make_random_key (context, ENCTYPE_ARCFOUR_HMAC, keyblock));
    enc_out.ciphertext = out;
    krb5_c_encrypt_length (context, keyblock->enctype, in.length, &len);
    enc_out.ciphertext.length = len;
    check.length = 2048;
    test ("Encrypting with RC4 key usage 8",
          krb5_c_encrypt (context, keyblock, 8, 0, &in, &enc_out));
    display ("Enc output", &enc_out.ciphertext);
    test ("Decrypting with RC4 key usage 9",
          krb5_c_decrypt (context, keyblock, 9, 0, &enc_out, &check));
    test ("Comparing", compare_results (&in, &check));

    krb5_free_keyblock (context, keyblock);
    free(out.data);
    free(out2.data);
    free(check.data);
    free(check2.data);
    return 0;
}
Beispiel #21
0
static krb5_error_code
ec_process(krb5_context context, krb5_clpreauth_moddata moddata,
           krb5_clpreauth_modreq modreq, krb5_get_init_creds_opt *opt,
           krb5_clpreauth_callbacks cb,
           krb5_clpreauth_rock rock, krb5_kdc_req *request,
           krb5_data *encoded_request_body,
           krb5_data *encoded_previous_request, krb5_pa_data *padata,
           krb5_prompter_fct prompter, void *prompter_data,
           krb5_pa_data ***out_padata)
{
    krb5_error_code retval = 0;
    krb5_keyblock *challenge_key = NULL, *armor_key, *as_key;

    armor_key = cb->fast_armor(context, rock);
    if (armor_key == NULL)
        return ENOENT;
    retval = cb->get_as_key(context, rock, &as_key);
    if (retval == 0 && padata->length) {
        krb5_enc_data *enc = NULL;
        krb5_data scratch;
        scratch.length = padata->length;
        scratch.data = (char *) padata->contents;
        retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor",
                                      as_key, "challengelongterm",
                                      &challenge_key);
        if (retval == 0)
            retval = decode_krb5_enc_data(&scratch, &enc);
        scratch.data = NULL;
        if (retval == 0) {
            scratch.data = malloc(enc->ciphertext.length);
            scratch.length = enc->ciphertext.length;
            if (scratch.data == NULL)
                retval = ENOMEM;
        }
        if (retval == 0)
            retval = krb5_c_decrypt(context, challenge_key,
                                    KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL,
                                    enc, &scratch);
        /*
         * Per draft 11 of the preauth framework, the client MAY but is not
         * required to actually check the timestamp from the KDC other than to
         * confirm it decrypts. This code does not perform that check.
         */
        if (scratch.data)
            krb5_free_data_contents(context, &scratch);
        /* If we had a callback to assert that the KDC is verified, we would
         * call it here. */
        if (enc)
            krb5_free_enc_data(context, enc);
    } else if (retval == 0) { /*No padata; we send*/
        krb5_enc_data enc;
        krb5_pa_data **pa = NULL;
        krb5_data *encoded_ts = NULL;
        krb5_pa_enc_ts ts;
        enc.ciphertext.data = NULL;
        /* Use the timestamp from the preauth-required error if possible.
         * This time should always be secured by the FAST channel. */
        retval = cb->get_preauth_time(context, rock, FALSE, &ts.patimestamp,
                                      &ts.pausec);
        if (retval == 0)
            retval = encode_krb5_pa_enc_ts(&ts, &encoded_ts);
        if (retval == 0)
            retval = krb5_c_fx_cf2_simple(context,
                                          armor_key, "clientchallengearmor",
                                          as_key, "challengelongterm",
                                          &challenge_key);
        if (retval == 0)
            retval = krb5_encrypt_helper(context, challenge_key,
                                         KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
                                         encoded_ts, &enc);
        if (encoded_ts)
            krb5_free_data(context, encoded_ts);
        encoded_ts = NULL;
        if (retval == 0) {
            retval = encode_krb5_enc_data(&enc, &encoded_ts);
            krb5_free_data_contents(context, &enc.ciphertext);
        }
        if (retval == 0) {
            pa = calloc(2, sizeof(krb5_pa_data *));
            if (pa == NULL)
                retval = ENOMEM;
        }
        if (retval == 0) {
            pa[0] = calloc(1, sizeof(krb5_pa_data));
            if (pa[0] == NULL)
                retval = ENOMEM;
        }
        if (retval == 0) {
            pa[0]->length = encoded_ts->length;
            pa[0]->contents = (unsigned char *) encoded_ts->data;
            pa[0]->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE;
            encoded_ts->data = NULL;
            *out_padata = pa;
            pa = NULL;
        }
        free(pa);
        krb5_free_data(context, encoded_ts);
    }
    if (challenge_key)
        krb5_free_keyblock(context, challenge_key);
    return retval;
}
static krb5_error_code
process_preauth(krb5_context context, void *plugin_context,
                void *request_context, krb5_get_init_creds_opt *opt,
                preauth_get_client_data_proc get_data_proc,
                struct _krb5_preauth_client_rock *rock, krb5_kdc_req *request,
                krb5_data *encoded_request_body,
                krb5_data *encoded_previous_request, krb5_pa_data *padata,
                krb5_prompter_fct prompter, void *prompter_data,
                preauth_get_as_key_proc gak_fct, void *gak_data,
                krb5_data *salt, krb5_data *s2kparams, krb5_keyblock *as_key,
                krb5_pa_data ***out_padata)
{
    krb5_error_code retval = 0;
    krb5_enctype enctype = 0;
    krb5_keyblock *challenge_key = NULL, *armor_key = NULL;
    krb5_data *etype_data = NULL;
    krb5int_access kaccess;

    if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0)
        return 0;
    retval = fast_get_armor_key(context, get_data_proc, rock, &armor_key);
    if (retval || armor_key == NULL)
        return 0;
    retval = get_data_proc(context, rock, krb5plugin_preauth_client_get_etype, &etype_data);
    if (retval == 0) {
        enctype = *((krb5_enctype *)etype_data->data);
        if (as_key->length == 0 ||as_key->enctype != enctype)
            retval = gak_fct(context, request->client,
                             enctype, prompter, prompter_data,
                             salt, s2kparams,
                             as_key, gak_data);
    }
    if (retval == 0 && padata->length) {
        krb5_enc_data *enc = NULL;
        krb5_data scratch;
        scratch.length = padata->length;
        scratch.data = (char *) padata->contents;
        retval = krb5_c_fx_cf2_simple(context,armor_key, "kdcchallengearmor",
                                      as_key, "challengelongterm",
                                      &challenge_key);
        if (retval == 0)
            retval =kaccess.decode_enc_data(&scratch, &enc);
        scratch.data = NULL;
        if (retval == 0) {
            scratch.data = malloc(enc->ciphertext.length);
            scratch.length = enc->ciphertext.length;
            if (scratch.data == NULL)
                retval = ENOMEM;
        }
        if (retval == 0)
            retval = krb5_c_decrypt(context, challenge_key,
                                    KRB5_KEYUSAGE_ENC_CHALLENGE_KDC, NULL,
                                    enc, &scratch);
        /*
         * Per draft 11 of the preauth framework, the client MAY but is not
         * required to actually check the timestamp from the KDC other than to
         * confirm it decrypts. This code does not perform that check.
         */
        if (scratch.data)
            krb5_free_data_contents(context, &scratch);
        if (retval == 0)
            fast_set_kdc_verified(context, get_data_proc, rock);
        if (enc)
            kaccess.free_enc_data(context, enc);
    } else if (retval == 0) { /*No padata; we send*/
        krb5_enc_data enc;
        krb5_pa_data *pa = NULL;
        krb5_pa_data **pa_array = NULL;
        krb5_data *encoded_ts = NULL;
        krb5_pa_enc_ts ts;
        enc.ciphertext.data = NULL;
        retval = krb5_us_timeofday(context, &ts.patimestamp, &ts.pausec);
        if (retval == 0)
            retval = kaccess.encode_enc_ts(&ts, &encoded_ts);
        if (retval == 0)
            retval = krb5_c_fx_cf2_simple(context,
                                          armor_key, "clientchallengearmor",
                                          as_key, "challengelongterm",
                                          &challenge_key);
        if (retval == 0)
            retval = kaccess.encrypt_helper(context, challenge_key,
                                            KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
                                            encoded_ts, &enc);
        if (encoded_ts)
            krb5_free_data(context, encoded_ts);
        encoded_ts = NULL;
        if (retval == 0) {
            retval = kaccess.encode_enc_data(&enc, &encoded_ts);
            krb5_free_data_contents(context, &enc.ciphertext);
        }
        if (retval == 0) {
            pa = calloc(1, sizeof(krb5_pa_data));
            if (pa == NULL)
                retval = ENOMEM;
        }
        if (retval == 0) {
            pa_array = calloc(2, sizeof(krb5_pa_data *));
            if (pa_array == NULL)
                retval = ENOMEM;
        }
        if (retval == 0) {
            pa->length = encoded_ts->length;
            pa->contents = (unsigned char *) encoded_ts->data;
            pa->pa_type = KRB5_PADATA_ENCRYPTED_CHALLENGE;
            free(encoded_ts);
            encoded_ts = NULL;
            pa_array[0] = pa;
            pa = NULL;
            *out_padata = pa_array;
            pa_array = NULL;
        }
        if (pa)
            free(pa);
        if (encoded_ts)
            krb5_free_data(context, encoded_ts);
        if (pa_array)
            free(pa_array);
    }
    if (challenge_key)
        krb5_free_keyblock(context, challenge_key);
    if (armor_key)
        krb5_free_keyblock(context, armor_key);
    if (etype_data != NULL)
        get_data_proc(context, rock, krb5plugin_preauth_client_free_etype,
                      &etype_data);
    return retval;
}
static krb5_error_code
kdc_verify_preauth(krb5_context context, struct _krb5_db_entry_new *client,
                   krb5_data *req_pkt, krb5_kdc_req *request,
                   krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
                   preauth_get_entry_data_proc get_entry_proc,
                   void *pa_module_context, void **pa_request_context,
                   krb5_data **e_data, krb5_authdata ***authz_data)
{
    krb5_error_code retval = 0;
    krb5_timestamp now;
    krb5_enc_data *enc = NULL;
    krb5_data scratch, plain;
    krb5_keyblock *armor_key = NULL;
    krb5_pa_enc_ts *ts = NULL;
    krb5int_access kaccess;
    krb5_keyblock *client_keys = NULL;
    krb5_data *client_data = NULL;
    krb5_keyblock *challenge_key = NULL;
    int i = 0;

    plain.data = NULL;
    if (krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION) != 0)
        return 0;

    retval = fast_kdc_get_armor_key(context, get_entry_proc, request, client, &armor_key);
    if (retval == 0 &&armor_key == NULL) {
        retval = ENOENT;
        krb5_set_error_message(context, ENOENT, "Encrypted Challenge used outside of FAST tunnel");
    }
    scratch.data = (char *) data->contents;
    scratch.length = data->length;
    if (retval == 0)
        retval = kaccess.decode_enc_data(&scratch, &enc);
    if (retval == 0) {
        plain.data =  malloc(enc->ciphertext.length);
        plain.length = enc->ciphertext.length;
        if (plain.data == NULL)
            retval = ENOMEM;
    }
    if (retval == 0)
        retval = get_entry_proc(context, request, client,
                                krb5plugin_preauth_keys, &client_data);
    if (retval == 0) {
        client_keys = (krb5_keyblock *) client_data->data;
        for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) {
            retval = krb5_c_fx_cf2_simple(context,
                                          armor_key, "clientchallengearmor",
                                          &client_keys[i], "challengelongterm",
                                          &challenge_key);
            if (retval == 0)
                retval  = krb5_c_decrypt(context, challenge_key,
                                         KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
                                         NULL, enc, &plain);
            if (challenge_key)
                krb5_free_keyblock(context, challenge_key);
            challenge_key = NULL;
            if (retval == 0)
                break;
            /*We failed to decrypt. Try next key*/
            retval = 0;
            krb5_free_keyblock_contents(context, &client_keys[i]);
        }
        if (client_keys[i].enctype == 0) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval, "Incorrect password  in encrypted challenge");
        } else { /*not run out of keys*/
            int j;
            assert (retval == 0);
            for (j = i+1; client_keys[j].enctype; j++)
                krb5_free_keyblock_contents(context, &client_keys[j]);
        }

    }
    if (retval == 0)
        retval = kaccess.decode_enc_ts(&plain, &ts);
    if (retval == 0)
        retval = krb5_timeofday(context, &now);
    if (retval == 0) {
        if (labs(now-ts->patimestamp) < context->clockskew) {
            enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
            /*
             * If this fails, we won't generate a reply to the client.  That
             * may cause the client to fail, but at this point the KDC has
             * considered this a success, so the return value is ignored.
             */
            fast_kdc_replace_reply_key(context, get_entry_proc, request);
            krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor",
                                 &client_keys[i], "challengelongterm",
                                 (krb5_keyblock **) pa_request_context);
        } else { /*skew*/
            retval = KRB5KRB_AP_ERR_SKEW;
        }
    }
    if (client_keys) {
        if (client_keys[i].enctype)
            krb5_free_keyblock_contents(context, &client_keys[i]);
        krb5_free_data(context, client_data);
    }
    if (armor_key)
        krb5_free_keyblock(context, armor_key);
    if (plain.data)
        free(plain.data);
    if (enc)
        kaccess.free_enc_data(context, enc);
    if (ts)
        kaccess.free_enc_ts(context, ts);
    return retval;
}
Beispiel #24
0
krb5_error_code
verify_securid_data_2(krb5_context context, krb5_db_entry *client,
                      krb5_sam_response_2 *sr2,
                      krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *pa,
                      krb5_sam_challenge_2 **sc2_out)
{
    krb5_error_code retval;
    int new_pin = 0;
    krb5_key_data *client_key_data = NULL;
    krb5_keyblock client_key;
    krb5_data scratch;
    krb5_enc_sam_response_enc_2 *esre2 = NULL;
    struct securid_track_data sid_track_data, *trackp = NULL;
    krb5_data tmp_data;
    SDI_HANDLE sd_handle = SDI_HANDLE_NONE;
    krb5_sam_challenge_2 *sc2p = NULL;
    char *cp, *user = NULL;
    char *securid_user = NULL;
    char passcode[LENPRNST+1];
    char max_pin_len, min_pin_len, alpha_pin;

    memset(&client_key, 0, sizeof(client_key));
    memset(&scratch, 0, sizeof(scratch));
    *sc2_out = NULL;

    retval = krb5_unparse_name(context, client->princ, &user);
    if (retval != 0) {
        com_err("krb5kdc", retval,
                "while unparsing client name in verify_securid_data_2");
        return retval;
    }

    if ((sr2->sam_enc_nonce_or_sad.ciphertext.data == NULL) ||
        (sr2->sam_enc_nonce_or_sad.ciphertext.length <= 0)) {
          retval = KRB5KDC_ERR_PREAUTH_FAILED;
          krb5_set_error_message(context, retval,
                                 "No preauth data supplied in "
                                 "verify_securid_data_2 (%s)", user);
          goto cleanup;
    }

    retval = krb5_dbe_find_enctype(context, client,
                                   sr2->sam_enc_nonce_or_sad.enctype,
                                   KRB5_KDB_SALTTYPE_NORMAL,
                                   sr2->sam_enc_nonce_or_sad.kvno,
                                   &client_key_data);
    if (retval) {
        com_err("krb5kdc", retval,
                "while getting client key in verify_securid_data_2 (%s)",
                user);
        goto cleanup;
    }

    retval = krb5_dbe_decrypt_key_data(context, NULL, client_key_data,
                                       &client_key, NULL);
    if (retval != 0) {
        com_err("krb5kdc", retval,
                "while decrypting client key in verify_securid_data_2 (%s)",
                user);
        goto cleanup;
    }

    scratch.length = sr2->sam_enc_nonce_or_sad.ciphertext.length;
    scratch.data = k5alloc(scratch.length, &retval);
    if (retval)
        goto cleanup;
    retval = krb5_c_decrypt(context, &client_key,
                            KRB5_KEYUSAGE_PA_SAM_RESPONSE, 0,
                            &sr2->sam_enc_nonce_or_sad, &scratch);
    if (retval) {
        com_err("krb5kdc", retval,
                "while decrypting SAD in verify_securid_data_2 (%s)", user);
        goto cleanup;
    }

    retval = decode_krb5_enc_sam_response_enc_2(&scratch, &esre2);
    if (retval) {
        com_err("krb5kdc", retval,
                "while decoding SAD in verify_securid_data_2 (%s)", user);
        esre2 = NULL;
        goto cleanup;
    }

    if (sr2->sam_nonce != esre2->sam_nonce) {
        com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
                "while checking nonce in verify_securid_data_2 (%s)", user);
        retval = KRB5KDC_ERR_PREAUTH_FAILED;
        goto cleanup;
    }

    if (esre2->sam_sad.length == 0 || esre2->sam_sad.data == NULL) {
        com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
                "No SecurID passcode in verify_securid_data_2 (%s)", user);
        retval = KRB5KDC_ERR_PREAUTH_FAILED;
        goto cleanup;
    }

    /* Copy out SAD to null-terminated buffer */
    memset(passcode, 0, sizeof(passcode));
    if (esre2->sam_sad.length > (sizeof(passcode) - 1)) {
        retval = KRB5KDC_ERR_PREAUTH_FAILED;
        com_err("krb5kdc", retval,
                "SecurID passcode/PIN too long (%d bytes) in "
                "verify_securid_data_2 (%s)",
                esre2->sam_sad.length, user);
        goto cleanup;
    }
    memcpy(passcode, esre2->sam_sad.data, esre2->sam_sad.length);

    securid_user = strdup(user);
    if (!securid_user) {
        retval = ENOMEM;
        com_err("krb5kdc", ENOMEM,
                "while copying user name in verify_securid_data_2 (%s)", user);
        goto cleanup;
    }
    cp = strchr(securid_user, '@');
    if (cp != NULL)
        *cp = '\0';

    /* Check for any track_id data that may have state from a previous attempt
     * at SecurID authentication. */

    if (sr2->sam_track_id.data && (sr2->sam_track_id.length > 0)) {
        krb5_data track_id_data;

        memset(&track_id_data, 0, sizeof(track_id_data));
        retval = securid_decrypt_track_data_2(context, client,
                                              &sr2->sam_track_id,
                                              &track_id_data);
        if (retval) {
            com_err("krb5kdc", retval,
                    "while decrypting SecurID trackID in "
                    "verify_securid_data_2 (%s)", user);
           goto cleanup;
        }
        if (track_id_data.length < sizeof (struct securid_track_data)) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            com_err("krb5kdc", retval, "Length of track data incorrect");
            goto cleanup;
        }
        trackp = (struct securid_track_data *)track_id_data.data;

        if(trackp->hostid != gethostid()) {
            krb5_klog_syslog(LOG_INFO, "Unexpected challenge response");
            retval = KRB5KDC_ERR_DISCARD;
            goto cleanup;
        }

        switch(trackp->state) {
        case SECURID_STATE_INITIAL:
            goto initial;
            break;
        case SECURID_STATE_NEW_PIN_AGAIN:
        {
            int pin1_len, pin2_len;

            trackp->handle = ntohl(trackp->handle);
            pin2_len = strlen(passcode);
            pin1_len = strlen(trackp->passcode);

            if ((pin1_len != pin2_len) ||
                (memcmp(passcode, trackp->passcode, pin1_len) != 0)) {
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
                krb5_klog_syslog(LOG_INFO, "New SecurID PIN Failed for user "
                                 "%s: PIN mis-match", user);
                break;
            }
            retval = SD_Pin(trackp->handle, passcode);
            SD_Close(trackp->handle);
            if (retval == ACM_NEW_PIN_ACCEPTED) {
                enc_tkt_reply->flags|=  TKT_FLG_HW_AUTH;
                enc_tkt_reply->flags|=  TKT_FLG_PRE_AUTH;
                krb5_klog_syslog(LOG_INFO, "SecurID PIN Accepted for %s in "
                                 "verify_securid_data_2",
                                 securid_user);
                retval = 0;
            } else {
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
                krb5_klog_syslog(LOG_INFO,
                                 "SecurID PIN Failed for user %s (AceServer "
                                 "returns %d) in verify_securid_data_2",
                                 user, retval);
            }
            break;
        }
        case SECURID_STATE_NEW_PIN: {
            krb5_sam_challenge_2_body sc2b;
            sc2p = k5alloc(sizeof *sc2p, &retval);
            if (retval)
                goto cleanup;
            memset(sc2p, 0, sizeof(*sc2p));
            memset(&sc2b, 0, sizeof(sc2b));
            sc2b.sam_type = PA_SAM_TYPE_SECURID;
            sc2b.sam_response_prompt.data = NEW_PIN_AGAIN_message;
            sc2b.sam_response_prompt.length =
                strlen(sc2b.sam_response_prompt.data);
            sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
            sc2b.sam_etype = client_key.enctype;

            tmp_data.data = (char *)&sc2b.sam_nonce;
            tmp_data.length = sizeof(sc2b.sam_nonce);
            if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {
                com_err("krb5kdc", retval,
                        "while making nonce for SecurID new "
                        "PIN2 SAM_CHALLENGE_2 (%s)", user);
                goto cleanup;
            }
            sid_track_data.state = SECURID_STATE_NEW_PIN_AGAIN;
            sid_track_data.handle = trackp->handle;
            sid_track_data.hostid = gethostid();
            /* Should we complain if sizes don't work ??  */
            memcpy(sid_track_data.passcode, passcode,
                   sizeof(sid_track_data.passcode));
            tmp_data.data = (char *)&sid_track_data;
            tmp_data.length = sizeof(sid_track_data);
            if ((retval = securid_encrypt_track_data_2(context, client,
                                                       &tmp_data,
                                                       &sc2b.sam_track_id))) {
                com_err("krb5kdc", retval,
                        "while encrypting NEW PIN2 SecurID "
                        "track data for SAM_CHALLENGE_2 (%s)",
                        securid_user);
                goto cleanup;
            }
            retval = securid_make_sam_challenge_2_and_cksum(context, sc2p,
                                                            &sc2b,
                                                            &client_key);
            if (retval) {
                com_err("krb5kdc", retval,
                        "while making cksum for "
                        "SAM_CHALLENGE_2 (new PIN2) (%s)", securid_user);
                goto cleanup;
            }
            krb5_klog_syslog(LOG_INFO,
                             "Requesting verification of new PIN for user %s",
                             securid_user);
            *sc2_out = sc2p;
            sc2p = NULL;
            /*sc2_out may be set even on error path*/
            retval = KRB5KDC_ERR_PREAUTH_REQUIRED;
            goto cleanup;
        }
        case SECURID_STATE_NEXT_CODE:
            trackp->handle = ntohl(trackp->handle);
            retval = SD_Next(trackp->handle, passcode);
            SD_Close(trackp->handle);
            if (retval == ACM_OK) {
                enc_tkt_reply->flags |=  TKT_FLG_HW_AUTH | TKT_FLG_PRE_AUTH;

                krb5_klog_syslog(LOG_INFO, "Next SecurID Code Accepted for "
                                 "user %s", securid_user);
                retval = 0;
            } else {
                krb5_klog_syslog(LOG_INFO, "Next SecurID Code Failed for user "
                                 "%s (AceServer returns %d) in "
                                 "verify_securid_data_2", user, retval);
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
            }
            break;
        }
    } else {            /* No track data, this is first of N attempts */
    initial:
        retval = SD_Init(&sd_handle);
        if (retval) {
            com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
                    "SD_Init() returns error %d in verify_securid_data_2 (%s)",
                    retval, securid_user);
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            goto cleanup;
        }

        retval = SD_Lock(sd_handle, securid_user);
        if (retval != ACM_OK) {
            SD_Close(sd_handle);
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_klog_syslog(LOG_INFO,
                             "SD_Lock() failed (AceServer returns %d) for %s",
                             retval, securid_user);
            goto cleanup;
        }

        retval = SD_Check(sd_handle, passcode, securid_user);
        switch (retval) {
        case ACM_OK:
            SD_Close(sd_handle);
            enc_tkt_reply->flags|=  TKT_FLG_HW_AUTH;
            enc_tkt_reply->flags|=  TKT_FLG_PRE_AUTH;
            krb5_klog_syslog(LOG_INFO, "SecurID passcode accepted for user %s",
                             user);
            retval = 0;
            break;
        case ACM_ACCESS_DENIED:
            SD_Close(sd_handle);
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_klog_syslog(LOG_INFO, "AceServer returns Access Denied for "
                             "user %s (SAM2)", user);
            goto cleanup;
        case ACM_NEW_PIN_REQUIRED:
            new_pin = 1;
            /*fall through*/
        case ACM_NEXT_CODE_REQUIRED: {
            krb5_sam_challenge_2_body sc2b;
            sc2p = k5alloc(sizeof *sc2p, &retval);
            if (retval)
                goto cleanup;

            memset(sc2p, 0, sizeof(*sc2p));
            memset(&sc2b, 0, sizeof(sc2b));

            sc2b.sam_type = PA_SAM_TYPE_SECURID;
            sc2b.sam_response_prompt.data = NEXT_PASSCODE_message;
            sc2b.sam_response_prompt.length =
                strlen(sc2b.sam_response_prompt.data);
            sc2b.sam_flags = KRB5_SAM_SEND_ENCRYPTED_SAD;
            sc2b.sam_etype = client_key.enctype;
            if (new_pin) {
                if ((AceGetMaxPinLen(sd_handle, &max_pin_len) == ACE_SUCCESS)
                    && (AceGetMinPinLen(sd_handle,
                                        &min_pin_len) == ACE_SUCCESS)
                    && (AceGetAlphanumeric(sd_handle,
                                           &alpha_pin) == ACE_SUCCESS)) {
                    sprintf(PIN_message,
                            "New PIN must contain %d to %d %sdigits",
                            min_pin_len, max_pin_len,
                            (alpha_pin == 0) ? "" : "alphanumeric ");
                    sc2b.sam_challenge_label.data = PIN_message;
                    sc2b.sam_challenge_label.length =
                        strlen(sc2b.sam_challenge_label.data);
                } else {
                    sc2b.sam_challenge_label.length = 0;
                }
            }

            tmp_data.data = (char *)&sc2b.sam_nonce;
            tmp_data.length = sizeof(sc2b.sam_nonce);
            if ((retval = krb5_c_random_make_octets(context, &tmp_data))) {
                com_err("krb5kdc", retval,
                        "while making nonce for SecurID SAM_CHALLENGE_2 (%s)",
                        user);
                goto cleanup;
            }
            if (new_pin)
                sid_track_data.state = SECURID_STATE_NEW_PIN;
            else
                sid_track_data.state = SECURID_STATE_NEXT_CODE;
            sid_track_data.handle = htonl(sd_handle);
            sid_track_data.hostid = gethostid();
            tmp_data.data = (char *)&sid_track_data;
            tmp_data.length = sizeof(sid_track_data);
            retval = securid_encrypt_track_data_2(context, client, &tmp_data,
                                                  &sc2b.sam_track_id);
            if (retval) {
                   com_err("krb5kdc", retval,
                           "while encrypting SecurID track "
                           "data for SAM_CHALLENGE_2 (%s)",
                           securid_user);
                   goto cleanup;
            }
            retval = securid_make_sam_challenge_2_and_cksum(context, sc2p,
                                                            &sc2b,
                                                            &client_key);
            if (retval) {
                com_err("krb5kdc", retval,
                        "while making cksum for SAM_CHALLENGE_2 (%s)",
                        securid_user);
            }
            if (new_pin)
                krb5_klog_syslog(LOG_INFO, "New SecurID PIN required for "
                                 "user %s", securid_user);
            else
                krb5_klog_syslog(LOG_INFO, "Next SecurID passcode required "
                                 "for user %s", securid_user);
            *sc2_out = sc2p;
            sc2p = NULL;
            retval = KRB5KDC_ERR_PREAUTH_REQUIRED;
            /*sc2_out is permitted as an output on error path*/
            goto cleanup;
        }
        default:
            com_err("krb5kdc", KRB5KDC_ERR_PREAUTH_FAILED,
                    "AceServer returns unknown error code %d "
                    "in verify_securid_data_2\n", retval);
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            goto cleanup;
        }
    }   /* no track_id data */

cleanup:
    krb5_free_keyblock_contents(context, &client_key);
    free(scratch.data);
    krb5_free_enc_sam_response_enc_2(context, esre2);
    free(user);
    free(securid_user);
    free(trackp);
    krb5_free_sam_challenge_2(context, sc2p);
    return retval;
}
Beispiel #25
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 #26
0
static void
ec_verify(krb5_context context, krb5_data *req_pkt, krb5_kdc_req *request,
          krb5_enc_tkt_part *enc_tkt_reply, krb5_pa_data *data,
          krb5_kdcpreauth_callbacks cb, krb5_kdcpreauth_rock rock,
          krb5_kdcpreauth_moddata moddata,
          krb5_kdcpreauth_verify_respond_fn respond, void *arg)
{
    krb5_error_code retval = 0;
    krb5_timestamp now;
    krb5_enc_data *enc = NULL;
    krb5_data scratch, plain;
    krb5_keyblock *armor_key = cb->fast_armor(context, rock);
    krb5_pa_enc_ts *ts = NULL;
    krb5_keyblock *client_keys = NULL;
    krb5_keyblock *challenge_key = NULL;
    krb5_keyblock *kdc_challenge_key;
    krb5_kdcpreauth_modreq modreq = NULL;
    int i = 0;

    plain.data = NULL;

    if (armor_key == NULL) {
        retval = ENOENT;
        krb5_set_error_message(context, ENOENT,
                               _("Encrypted Challenge used outside of FAST "
                                 "tunnel"));
    }
    scratch.data = (char *) data->contents;
    scratch.length = data->length;
    if (retval == 0)
        retval = decode_krb5_enc_data(&scratch, &enc);
    if (retval == 0) {
        plain.data =  malloc(enc->ciphertext.length);
        plain.length = enc->ciphertext.length;
        if (plain.data == NULL)
            retval = ENOMEM;
    }
    if (retval == 0)
        retval = cb->client_keys(context, rock, &client_keys);
    if (retval == 0) {
        for (i = 0; client_keys[i].enctype&& (retval == 0); i++ ) {
            retval = krb5_c_fx_cf2_simple(context,
                                          armor_key, "clientchallengearmor",
                                          &client_keys[i], "challengelongterm",
                                          &challenge_key);
            if (retval == 0)
                retval  = krb5_c_decrypt(context, challenge_key,
                                         KRB5_KEYUSAGE_ENC_CHALLENGE_CLIENT,
                                         NULL, enc, &plain);
            if (challenge_key)
                krb5_free_keyblock(context, challenge_key);
            challenge_key = NULL;
            if (retval == 0)
                break;
            /*We failed to decrypt. Try next key*/
            retval = 0;
        }
        if (client_keys[i].enctype == 0) {
            retval = KRB5KDC_ERR_PREAUTH_FAILED;
            krb5_set_error_message(context, retval,
                                   _("Incorrect password in encrypted "
                                     "challenge"));
        }
    }
    if (retval == 0)
        retval = decode_krb5_pa_enc_ts(&plain, &ts);
    if (retval == 0)
        retval = krb5_timeofday(context, &now);
    if (retval == 0) {
        if (labs(now-ts->patimestamp) < context->clockskew) {
            enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
            /*
             * If this fails, we won't generate a reply to the client.  That
             * may cause the client to fail, but at this point the KDC has
             * considered this a success, so the return value is ignored.
             */
            if (krb5_c_fx_cf2_simple(context, armor_key, "kdcchallengearmor",
                                     &client_keys[i], "challengelongterm",
                                     &kdc_challenge_key) == 0)
                modreq = (krb5_kdcpreauth_modreq)kdc_challenge_key;
        } else { /*skew*/
            retval = KRB5KRB_AP_ERR_SKEW;
        }
    }
    cb->free_keys(context, rock, client_keys);
    if (plain.data)
        free(plain.data);
    if (enc)
        krb5_free_enc_data(context, enc);
    if (ts)
        krb5_free_pa_enc_ts(context, ts);

    (*respond)(arg, retval, modreq, NULL, NULL);
}
Beispiel #27
0
OM_uint32
gss_krb5int_unseal_token_v3(krb5_context *contextptr,
			    OM_uint32 *minor_status,
			    krb5_gss_ctx_id_rec *ctx,
			    unsigned char *ptr, int bodysize,
			    gss_buffer_t message_buffer,
			    int *conf_state, int *qop_state, int toktype)
{
    krb5_context context = *contextptr;
    krb5_data plain;
    gssint_uint64 seqnum;
    size_t ec, rrc;
    int key_usage;
    unsigned char acceptor_flag;
    krb5_checksum sum;
    krb5_error_code err;
    krb5_boolean valid;
    krb5_keyblock *key;

    ASSERT(toktype != KG_TOK_SEAL_MSG || ctx->enc != 0);
    ASSERT(ctx->big_endian == 0);
    ASSERT(ctx->proto == 1);

    if (qop_state)
	*qop_state = GSS_C_QOP_DEFAULT;

    acceptor_flag = ctx->initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
    key_usage = (toktype == KG_TOK_WRAP_MSG
		 ? (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SEAL
		    : KG_USAGE_ACCEPTOR_SEAL)
		 : (!ctx->initiate
		    ? KG_USAGE_INITIATOR_SIGN
		    : KG_USAGE_ACCEPTOR_SIGN));

    /* Oops.  I wrote this code assuming ptr would be at the start of
       the token header.  */
    ptr -= 2;
    bodysize += 2;

    if (bodysize < 16) {
    defective:
	*minor_status = 0;
	return GSS_S_DEFECTIVE_TOKEN;
    }
    if ((ptr[2] & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
	*minor_status = (OM_uint32)G_BAD_DIRECTION;
	return GSS_S_BAD_SIG;
    }

    /* Two things to note here.

       First, we can't really enforce the use of the acceptor's subkey,
       if we're the acceptor; the initiator may have sent messages
       before getting the subkey.  We could probably enforce it if
       we're the initiator.

       Second, if someone tweaks the code to not set the flag telling
       the krb5 library to generate a new subkey in the AP-REP
       message, the MIT library may include a subkey anyways --
       namely, a copy of the AP-REQ subkey, if it was provided.  So
       the initiator may think we wanted a subkey, and set the flag,
       even though we weren't trying to set the subkey.  The "other"
       key, the one not asserted by the acceptor, will have the same
       value in that case, though, so we can just ignore the flag.  */
    if (ctx->have_acceptor_subkey && (ptr[2] & FLAG_ACCEPTOR_SUBKEY)) {
	key = ctx->acceptor_subkey;
    } else {
	key = ctx->enc;
    }

#ifdef _KERNEL
    context->kef_cipher_mt = get_cipher_mech_type(context, key);
    context->kef_hash_mt = get_hash_mech_type(context, key);

    if ((err = init_key_kef(context->kef_cipher_mt, key))) {
	return (GSS_S_FAILURE);
    }
#endif /* _KERNEL */

    if (toktype == KG_TOK_WRAP_MSG) {
	if (load_16_be(ptr) != 0x0504)
	    goto defective;
	if (ptr[3] != 0xff)
	    goto defective;
	ec = load_16_be(ptr+4);
	rrc = load_16_be(ptr+6);
	seqnum = load_64_be(ptr+8);
	if (!rotate_left(ptr+16, bodysize-16, rrc)) {
	no_mem:
	    *minor_status = ENOMEM;
	    return GSS_S_FAILURE;
	}
	if (ptr[2] & FLAG_WRAP_CONFIDENTIAL) {
	    /* confidentiality */
	    krb5_enc_data cipher;
	    unsigned char *althdr;
            size_t plainlen;

	    if (conf_state)
		*conf_state = 1;
	    /* Do we have no decrypt_size function?

	       For all current cryptosystems, the ciphertext size will
	       be larger than the plaintext size.  */
	    cipher.enctype = key->enctype;
	    cipher.ciphertext.length = bodysize - 16;
	    cipher.ciphertext.data = (char *)ptr + 16;
	    plain.length = plainlen = bodysize - 16;
	    plain.data = MALLOC(plain.length);
	    if (plain.data == NULL)
		goto no_mem;
	    err = krb5_c_decrypt(context, key, key_usage, 0,
				 &cipher, &plain);
	    if (err) {
		goto error;
	    }
	    /* Don't use bodysize here!  Use the fact that
	       plain.length has been adjusted to the
	       correct length.  */
	    althdr = (uchar_t *)plain.data + plain.length - 16;
	    if (load_16_be(althdr) != 0x0504
		|| althdr[2] != ptr[2]
		|| althdr[3] != ptr[3]
		|| memcmp(althdr+8, ptr+8, 8)) {
		FREE(plain.data, plainlen);
		goto defective;
	    }
	    message_buffer->length = plain.length - ec - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL) {
		FREE(plain.data, plainlen);
		goto no_mem;
	    }
	    (void) memcpy(message_buffer->value, plain.data,
			message_buffer->length);
	    FREE(plain.data, plainlen);
	} else {
	    /* no confidentiality */
	    if (conf_state)
		*conf_state = 0;
	    if (ec + 16 < ec)
		/* overflow check */
		goto defective;
	    if (ec + 16 > bodysize)
		goto defective;
	    /* We have: header | msg | cksum.
	       We need cksum(msg | header).
	       Rotate the first two.  */
	    store_16_be(0, ptr+4);
	    store_16_be(0, ptr+6);
	    plain.length = bodysize-ec;
	    plain.data = (char *)ptr;
	    if (!rotate_left(ptr, bodysize-ec, 16))
		goto no_mem;
	    sum.length = ec;
	    if (sum.length != ctx->cksum_size) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    sum.contents = ptr+bodysize-ec;
	    sum.checksum_type = ctx->cksumtype;
	    err = krb5_c_verify_checksum(context, key, key_usage,
					 &plain, &sum, &valid);
	    if (err) {
		*minor_status = err;
		return GSS_S_BAD_SIG;
	    }
	    if (!valid) {
		*minor_status = 0;
		return GSS_S_BAD_SIG;
	    }
	    message_buffer->length = plain.length - 16;
	    message_buffer->value = MALLOC(message_buffer->length);
	    if (message_buffer->value == NULL)
		goto no_mem;
	    (void) memcpy(message_buffer->value,
		plain.data, message_buffer->length);

		/*
		 * Solaris Kerberos: Restore the original token.
		 * This allows the token to be detected as a duplicate if it
		 * is passed in to gss_unwrap() again.
		 */
		if (!rotate_left(ptr, bodysize-ec, bodysize - ec - 16))
			goto no_mem;
		store_16_be(ec, ptr+4);
		store_16_be(rrc, ptr+6);
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_MIC_MSG) {
	/* wrap token, no confidentiality */
	if (load_16_be(ptr) != 0x0404)
	    goto defective;
    verify_mic_1:
	if (ptr[3] != 0xff)
	    goto defective;
	if (load_32_be(ptr+4) != (ulong_t)0xffffffffU)
	    goto defective;
	seqnum = load_64_be(ptr+8);
	plain.length = message_buffer->length + 16;
	plain.data = MALLOC(plain.length);
	if (plain.data == NULL)
	    goto no_mem;
	if (message_buffer->length)
	    (void) memcpy(plain.data,
		message_buffer->value, message_buffer->length);
	(void) memcpy(plain.data + message_buffer->length, ptr, 16);
	sum.length = bodysize - 16;
	sum.contents = ptr + 16;
	sum.checksum_type = ctx->cksumtype;
	err = krb5_c_verify_checksum(context, key, key_usage,
				     &plain, &sum, &valid);
	if (err) {
	error:
	    FREE(plain.data, plain.length);
	    *minor_status = err;
	    save_error_info(*minor_status, context);
	    return GSS_S_BAD_SIG; /* XXX */
	}
	FREE(plain.data, plain.length);
	if (!valid) {
	    *minor_status = 0;
	    return GSS_S_BAD_SIG;
	}
	err = g_order_check(&ctx->seqstate, seqnum);
	*minor_status = 0;
	return err;
    } else if (toktype == KG_TOK_DEL_CTX) {
	if (load_16_be(ptr) != 0x0405)
	    goto defective;
	message_buffer = (gss_buffer_t)&empty_message;
	goto verify_mic_1;
    } else {
	goto defective;
    }
}
Beispiel #28
0
krb5_error_code KRB5_CALLCONV
krb5_rd_rep(krb5_context context, krb5_auth_context auth_context,
	    const krb5_data *inbuf, krb5_ap_rep_enc_part **repl)
{
    krb5_error_code 	  retval;
    krb5_ap_rep 	* reply;
    krb5_data 	 	  scratch;

    if (!krb5_is_ap_rep(inbuf))
	return KRB5KRB_AP_ERR_MSG_TYPE;

    /* decode it */

    if ((retval = decode_krb5_ap_rep(inbuf, &reply)))
	return retval;

    /* put together an eblock for this encryption */

    scratch.length = reply->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(scratch.length))) {
	krb5_free_ap_rep(context, reply);
	return(ENOMEM);
    }

    if ((retval = krb5_c_decrypt(context, auth_context->keyblock,
				 KRB5_KEYUSAGE_AP_REP_ENCPART, 0,
				 &reply->enc_part, &scratch)))
	goto clean_scratch;

    /* now decode the decrypted stuff */
    retval = decode_krb5_ap_rep_enc_part(&scratch, repl);
    if (retval)
	goto clean_scratch;

    /* Check reply fields */
    if (((*repl)->ctime != auth_context->authentp->ctime) ||
      ((*repl)->cusec != auth_context->authentp->cusec)) {
	retval = KRB5_MUTUAL_FAILED;
	goto clean_scratch;
    }

    /* Set auth subkey */
    if ((*repl)->subkey) {
	if (auth_context->recv_subkey) {
	    krb5_free_keyblock(context, auth_context->recv_subkey);
	    auth_context->recv_subkey = NULL;
	}
	retval = krb5_copy_keyblock(context, (*repl)->subkey,
				    &auth_context->recv_subkey);
	if (retval)
	    goto clean_scratch;
	if (auth_context->send_subkey) {
	    krb5_free_keyblock(context, auth_context->send_subkey);
	    auth_context->send_subkey = NULL;
	}
	retval = krb5_copy_keyblock(context, (*repl)->subkey,
				    &auth_context->send_subkey);
	if (retval) {
	    krb5_free_keyblock(context, auth_context->send_subkey);
	    auth_context->send_subkey = NULL;
	}
	/* not used for anything yet */
	auth_context->negotiated_etype = (*repl)->subkey->enctype;
    }

    /* Get remote sequence number */
    auth_context->remote_seq_number = (*repl)->seq_number;

clean_scratch:
    memset(scratch.data, 0, scratch.length); 

    krb5_free_ap_rep(context, reply);
    free(scratch.data);
    return retval;
}
Beispiel #29
0
krb5_error_code
kdc_find_fast(krb5_kdc_req **requestptr,
              krb5_data *checksummed_data,
              krb5_keyblock *tgs_subkey,
              krb5_keyblock *tgs_session,
              struct kdc_request_state *state,
              krb5_data **inner_body_out)
{
    krb5_error_code retval = 0;
    krb5_pa_data *fast_padata, *cookie_padata = NULL;
    krb5_data scratch, *inner_body = NULL;
    krb5_fast_req * fast_req = NULL;
    krb5_kdc_req *request = *requestptr;
    krb5_fast_armored_req *fast_armored_req = NULL;
    krb5_checksum *cksum;
    krb5_boolean cksum_valid;
    krb5_keyblock empty_keyblock;
    kdc_realm_t *kdc_active_realm = state->realm_data;

    if (inner_body_out != NULL)
        *inner_body_out = NULL;
    scratch.data = NULL;
    krb5_clear_error_message(kdc_context);
    memset(&empty_keyblock, 0, sizeof(krb5_keyblock));
    fast_padata = krb5int_find_pa_data(kdc_context,
                                       request->padata, KRB5_PADATA_FX_FAST);
    if (fast_padata !=  NULL){
        scratch.length = fast_padata->length;
        scratch.data = (char *) fast_padata->contents;
        retval = decode_krb5_pa_fx_fast_request(&scratch, &fast_armored_req);
        if (retval == 0 &&fast_armored_req->armor) {
            switch (fast_armored_req->armor->armor_type) {
            case KRB5_FAST_ARMOR_AP_REQUEST:
                if (tgs_subkey) {
                    retval = KRB5KDC_ERR_PREAUTH_FAILED;
                    krb5_set_error_message(kdc_context, retval,
                                           _("Ap-request armor not permitted "
                                             "with TGS"));
                    break;
                }
                retval = armor_ap_request(state, fast_armored_req->armor);
                break;
            default:
                krb5_set_error_message(kdc_context, KRB5KDC_ERR_PREAUTH_FAILED,
                                       _("Unknown FAST armor type %d"),
                                       fast_armored_req->armor->armor_type);
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
            }
        }
        if (retval == 0 && !state->armor_key) {
            if (tgs_subkey)
                retval = krb5_c_fx_cf2_simple(kdc_context,
                                              tgs_subkey, "subkeyarmor",
                                              tgs_session, "ticketarmor",
                                              &state->armor_key);
            else {
                retval = KRB5KDC_ERR_PREAUTH_FAILED;
                krb5_set_error_message(kdc_context, retval,
                                       _("No armor key but FAST armored "
                                         "request present"));
            }
        }
        if (retval == 0) {
            krb5_data plaintext;
            plaintext.length = fast_armored_req->enc_part.ciphertext.length;
            plaintext.data = malloc(plaintext.length);
            if (plaintext.data == NULL)
                retval = ENOMEM;
            retval = krb5_c_decrypt(kdc_context,
                                    state->armor_key,
                                    KRB5_KEYUSAGE_FAST_ENC, NULL,
                                    &fast_armored_req->enc_part,
                                    &plaintext);
            if (retval == 0)
                retval = decode_krb5_fast_req(&plaintext, &fast_req);
            if (retval == 0 && inner_body_out != NULL) {
                retval = fetch_asn1_field((unsigned char *)plaintext.data,
                                          1, 2, &scratch);
                if (retval == 0) {
                    retval = krb5_copy_data(kdc_context, &scratch,
                                            &inner_body);
                }
            }
            if (plaintext.data)
                free(plaintext.data);
        }
        cksum = &fast_armored_req->req_checksum;
        if (retval == 0)
            retval = krb5_c_verify_checksum(kdc_context, state->armor_key,
                                            KRB5_KEYUSAGE_FAST_REQ_CHKSUM,
                                            checksummed_data, cksum,
                                            &cksum_valid);
        if (retval == 0 && !cksum_valid) {
            retval = KRB5KRB_AP_ERR_MODIFIED;
            krb5_set_error_message(kdc_context, retval,
                                   _("FAST req_checksum invalid; request "
                                     "modified"));
        }
        if (retval == 0) {
            if (!krb5_c_is_keyed_cksum(cksum->checksum_type)) {
                retval = KRB5KDC_ERR_POLICY;
                krb5_set_error_message(kdc_context, retval,
                                       _("Unkeyed checksum used in fast_req"));
            }
        }
        if (retval == 0) {
            if ((fast_req->fast_options & UNSUPPORTED_CRITICAL_FAST_OPTIONS) != 0)
                retval = KRB5KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTION;
        }
        if (retval == 0)
            cookie_padata = krb5int_find_pa_data(kdc_context,
                                                 fast_req->req_body->padata,
                                                 KRB5_PADATA_FX_COOKIE);
        if (retval == 0) {
            state->fast_options = fast_req->fast_options;
            krb5_free_kdc_req( kdc_context, request);
            *requestptr = fast_req->req_body;
            fast_req->req_body = NULL;
        }
    }
    else {
        cookie_padata = krb5int_find_pa_data(kdc_context,
                                             request->padata,
                                             KRB5_PADATA_FX_COOKIE);
    }
    if (retval == 0 && cookie_padata != NULL) {
        krb5_pa_data *new_padata = malloc(sizeof (krb5_pa_data));
        if (new_padata == NULL) {
            retval = ENOMEM;
        } else {
            new_padata->pa_type = KRB5_PADATA_FX_COOKIE;
            new_padata->length = cookie_padata->length;
            new_padata->contents = malloc(new_padata->length);
            if (new_padata->contents == NULL) {
                retval = ENOMEM;
                free(new_padata);
            } else {
                memcpy(new_padata->contents, cookie_padata->contents,
                       new_padata->length);
                state->cookie = new_padata;
            }
        }
    }
    if (retval == 0 && inner_body_out != NULL) {
        *inner_body_out = inner_body;
        inner_body = NULL;
    }
    krb5_free_data(kdc_context, inner_body);
    if (fast_req)
        krb5_free_fast_req( kdc_context, fast_req);
    if (fast_armored_req)
        krb5_free_fast_armored_req(kdc_context, fast_armored_req);
    return retval;
}
/*ARGSUSED*/
krb5_error_code
process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
		krb5_data **response)
{
    krb5_keyblock * subkey = 0;
    krb5_kdc_req *request = 0;
    krb5_db_entry server;
    krb5_kdc_rep reply;
    krb5_enc_kdc_rep_part reply_encpart;
    krb5_ticket ticket_reply, *header_ticket = 0;
    int st_idx = 0;
    krb5_enc_tkt_part enc_tkt_reply;
    krb5_transited enc_tkt_transited;
    int newtransited = 0;
    krb5_error_code retval = 0;
    int nprincs = 0;
    krb5_boolean more;
    krb5_timestamp kdc_time, authtime=0;
    krb5_keyblock session_key;
    krb5_timestamp until, rtime;
    krb5_keyblock encrypting_key;
    krb5_key_data  *server_key;
    char *cname = 0, *sname = 0, *tmp = 0;
    const char *fromstring = 0;
    krb5_last_req_entry *nolrarray[2], nolrentry;
/*    krb5_address *noaddrarray[1]; */
    krb5_enctype useenctype;
    int	errcode, errcode2;
    register int i;
    int firstpass = 1;
    const char	*status = 0;
    char ktypestr[128];
    char rep_etypestr[128];
    char fromstringbuf[70];

    session_key.contents = 0;
    
    retval = decode_krb5_tgs_req(pkt, &request);
    if (retval)
	return retval;
    if (request->msg_type != KRB5_TGS_REQ)
        return KRB5_BADMSGTYPE;

    ktypes2str(ktypestr, sizeof(ktypestr),
	       request->nktypes, request->ktype);
    /*
     * setup_server_realm() sets up the global realm-specific data pointer.
     */
    if ((retval = setup_server_realm(request->server))) {
	krb5_free_kdc_req(kdc_context, request);
	return retval;
    }

    fromstring = inet_ntop(ADDRTYPE2FAMILY(from->address->addrtype),
			   from->address->contents,
			   fromstringbuf, sizeof(fromstringbuf));
    if (!fromstring)
	fromstring = "<unknown>";

    if ((errcode = krb5_unparse_name(kdc_context, request->server, &sname))) {
	status = "UNPARSING SERVER";
	goto cleanup;
    }
    limit_string(sname);

   /* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */
    errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey);

    if (header_ticket && header_ticket->enc_part2 &&
	(errcode2 = krb5_unparse_name(kdc_context, 
				      header_ticket->enc_part2->client,
				      &cname))) {
	status = "UNPARSING CLIENT";
	errcode = errcode2;
	goto cleanup;
    }
    limit_string(cname);
    
    if (errcode) {
	status = "PROCESS_TGS";
	goto cleanup;
    }

    if (!header_ticket) {
	errcode = KRB5_NO_TKT_SUPPLIED;	/* XXX? */
	status="UNEXPECTED NULL in header_ticket";
	goto cleanup;
    }
    
    /*
     * We've already dealt with the AP_REQ authentication, so we can
     * use header_ticket freely.  The encrypted part (if any) has been
     * decrypted with the session key.
     */

    authtime = header_ticket->enc_part2->times.authtime;

    /* XXX make sure server here has the proper realm...taken from AP_REQ
       header? */

    nprincs = 1;
    if ((errcode = get_principal(kdc_context, request->server, &server,
				 &nprincs, &more))) {
	status = "LOOKING_UP_SERVER";
	nprincs = 0;
	goto cleanup;
    }
tgt_again:
    if (more) {
	status = "NON_UNIQUE_PRINCIPAL";
	errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
	goto cleanup;
    } else if (nprincs != 1) {
	/*
	 * might be a request for a TGT for some other realm; we
	 * should do our best to find such a TGS in this db
	 */
	if (firstpass && krb5_is_tgs_principal(request->server) == TRUE) {
	    if (krb5_princ_size(kdc_context, request->server) == 2) {
		krb5_data *server_1 =
		    krb5_princ_component(kdc_context, request->server, 1);
		krb5_data *tgs_1 =
		    krb5_princ_component(kdc_context, tgs_server, 1);

		if (!tgs_1 || !data_eq(*server_1, *tgs_1)) {
		    krb5_db_free_principal(kdc_context, &server, nprincs);
		    find_alternate_tgs(request, &server, &more, &nprincs);
		    firstpass = 0;
		    goto tgt_again;
		}
	    }
	}
	krb5_db_free_principal(kdc_context, &server, nprincs);
	status = "UNKNOWN_SERVER";
	errcode = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
	goto cleanup;
    }

    if ((errcode = krb5_timeofday(kdc_context, &kdc_time))) {
	status = "TIME_OF_DAY";
	goto cleanup;
    }
    
    if ((retval = validate_tgs_request(request, server, header_ticket,
				      kdc_time, &status))) {
	if (!status)
	    status = "UNKNOWN_REASON";
	errcode = retval + ERROR_TABLE_BASE_krb5;
	goto cleanup;
    }

    /*
     * We pick the session keytype here....
     * 
     * Some special care needs to be taken in the user-to-user
     * case, since we don't know what keytypes the application server
     * which is doing user-to-user authentication can support.  We
     * know that it at least must be able to support the encryption
     * type of the session key in the TGT, since otherwise it won't be
     * able to decrypt the U2U ticket!  So we use that in preference
     * to anything else.
     */
    useenctype = 0;
    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
	krb5_keyblock *	st_sealing_key;
	krb5_kvno 	st_srv_kvno;
	krb5_enctype	etype;

	/*
	 * Get the key for the second ticket, and decrypt it.
	 */
	if ((errcode = kdc_get_server_key(request->second_ticket[st_idx],
					 &st_sealing_key,
					 &st_srv_kvno))) {
	    status = "2ND_TKT_SERVER";
	    goto cleanup;
	}
	errcode = krb5_decrypt_tkt_part(kdc_context, st_sealing_key,
				       request->second_ticket[st_idx]);
	krb5_free_keyblock(kdc_context, st_sealing_key);
	if (errcode) {
	    status = "2ND_TKT_DECRYPT";
	    goto cleanup;
	}
	
	etype = request->second_ticket[st_idx]->enc_part2->session->enctype;
	if (!krb5_c_valid_enctype(etype)) {
	    status = "BAD_ETYPE_IN_2ND_TKT";
	    errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
	    goto cleanup;
	}
	
	for (i = 0; i < request->nktypes; i++) {
	    if (request->ktype[i] == etype) {
		useenctype = etype;
		break;
	    }
	}
    }

    /*
     * Select the keytype for the ticket session key.
     */
    if ((useenctype == 0) &&
	(useenctype = select_session_keytype(kdc_context, &server,
					     request->nktypes,
					     request->ktype)) == 0) {
	/* unsupported ktype */
	status = "BAD_ENCRYPTION_TYPE";
	errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
	goto cleanup;
    }
    
    errcode = krb5_c_make_random_key(kdc_context, useenctype, &session_key);

    if (errcode) {
	/* random key failed */
	status = "RANDOM_KEY_FAILED";
	goto cleanup;
    }

    ticket_reply.server = request->server; /* XXX careful for realm... */

    enc_tkt_reply.flags = 0;
    enc_tkt_reply.times.starttime = 0;

    /*
     * Fix header_ticket's starttime; if it's zero, fill in the
     * authtime's value.
     */
    if (!(header_ticket->enc_part2->times.starttime))
	header_ticket->enc_part2->times.starttime =
	    header_ticket->enc_part2->times.authtime;

    /* don't use new addresses unless forwarded, see below */

    enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs;
    /* noaddrarray[0] = 0; */
    reply_encpart.caddrs = 0;		/* optional...don't put it in */

    /* It should be noted that local policy may affect the  */
    /* processing of any of these flags.  For example, some */
    /* realms may refuse to issue renewable tickets         */

    if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
	setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);

    if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
	setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);

	/* include new addresses in ticket & reply */

	enc_tkt_reply.caddrs = request->addresses;
	reply_encpart.caddrs = request->addresses;
    }	
    if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED))
	setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);

    if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
	setflag(enc_tkt_reply.flags, TKT_FLG_PROXIABLE);

    if (isflagset(request->kdc_options, KDC_OPT_PROXY)) {
	setflag(enc_tkt_reply.flags, TKT_FLG_PROXY);

	/* include new addresses in ticket & reply */

	enc_tkt_reply.caddrs = request->addresses;
	reply_encpart.caddrs = request->addresses;
    }

    if (isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE))
	setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);

    if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
	setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
	setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
	enc_tkt_reply.times.starttime = request->from;
    } else
	enc_tkt_reply.times.starttime = kdc_time;

    if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
	/* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
	   to the caller */
	ticket_reply = *(header_ticket);
	enc_tkt_reply = *(header_ticket->enc_part2);
        enc_tkt_reply.authorization_data = NULL;
	clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
    }

    if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
	krb5_deltat old_life;

	/* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
	   to the caller */
	ticket_reply = *(header_ticket);
	enc_tkt_reply = *(header_ticket->enc_part2);
        enc_tkt_reply.authorization_data = NULL;

	old_life = enc_tkt_reply.times.endtime - enc_tkt_reply.times.starttime;

	enc_tkt_reply.times.starttime = kdc_time;
	enc_tkt_reply.times.endtime =
	    min(header_ticket->enc_part2->times.renew_till,
		kdc_time + old_life);
    } else {
	/* not a renew request */
	enc_tkt_reply.times.starttime = kdc_time;
	until = (request->till == 0) ? kdc_infinity : request->till;
	enc_tkt_reply.times.endtime =
	    min(until, min(enc_tkt_reply.times.starttime + server.max_life,
			   min(enc_tkt_reply.times.starttime + max_life_for_realm,
			       header_ticket->enc_part2->times.endtime)));
	if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
	    (enc_tkt_reply.times.endtime < request->till) &&
	    isflagset(header_ticket->enc_part2->flags,
		  TKT_FLG_RENEWABLE)) {
	    setflag(request->kdc_options, KDC_OPT_RENEWABLE);
	    request->rtime =
		min(request->till,
		    header_ticket->enc_part2->times.renew_till);
	}
    }
    rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;

    if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE)) {
	/* already checked above in policy check to reject request for a
	   renewable ticket using a non-renewable ticket */
	setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
	enc_tkt_reply.times.renew_till =
	    min(rtime,
		min(header_ticket->enc_part2->times.renew_till,
		    enc_tkt_reply.times.starttime +
		    min(server.max_renewable_life,
			max_renewable_life_for_realm)));
    } else {
	enc_tkt_reply.times.renew_till = 0;
    }
    
    /*
     * Set authtime to be the same as header_ticket's
     */
    enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime;
    
    /*
     * Propagate the preauthentication flags through to the returned ticket.
     */
    if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH))
	setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);

    if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH))
	setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
    
    /* starttime is optional, and treated as authtime if not present.
       so we can nuke it if it matches */
    if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
	enc_tkt_reply.times.starttime = 0;

    /* assemble any authorization data */
    if (request->authorization_data.ciphertext.data) {
	krb5_data scratch;

	scratch.length = request->authorization_data.ciphertext.length;
	if (!(scratch.data =
	      malloc(request->authorization_data.ciphertext.length))) {
	    status = "AUTH_NOMEM";
	    errcode = ENOMEM;
	    goto cleanup;
	}

	if ((errcode = krb5_c_decrypt(kdc_context,
				      header_ticket->enc_part2->session,
				      KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
				      0, &request->authorization_data,
				      &scratch))) {
	    status = "AUTH_ENCRYPT_FAIL";
	    free(scratch.data);
	    goto cleanup;
	}

	/* scratch now has the authorization data, so we decode it */
	errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata));
	free(scratch.data);
	if (errcode) {
	    status = "AUTH_DECODE";
	    goto cleanup;
	}

	if ((errcode =
	     concat_authorization_data(request->unenc_authdata,
				       header_ticket->enc_part2->authorization_data, 
				       &enc_tkt_reply.authorization_data))) {
	    status = "CONCAT_AUTH";
	    goto cleanup;
	}
    } else
	enc_tkt_reply.authorization_data =
	    header_ticket->enc_part2->authorization_data;

    enc_tkt_reply.session = &session_key;
    enc_tkt_reply.client = header_ticket->enc_part2->client;
    enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
    enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */

    /*
     * Only add the realm of the presented tgt to the transited list if 
     * it is different than the local realm (cross-realm) and it is different
     * than the realm of the client (since the realm of the client is already
     * implicitly part of the transited list and should not be explicitly
     * listed).
     */

    /* realm compare is like strcmp, but knows how to deal with these args */
    if (realm_compare(header_ticket->server, tgs_server) ||
	realm_compare(header_ticket->server, enc_tkt_reply.client)) {
	/* tgt issued by local realm or issued by realm of client */
	enc_tkt_reply.transited = header_ticket->enc_part2->transited;
    } else {
	/* tgt issued by some other realm and not the realm of the client */
	/* assemble new transited field into allocated storage */
	if (header_ticket->enc_part2->transited.tr_type !=
	    KRB5_DOMAIN_X500_COMPRESS) {
	    status = "BAD_TRTYPE";
	    errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
	    goto cleanup;
	}
	enc_tkt_transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
	enc_tkt_transited.magic = 0;
	enc_tkt_transited.tr_contents.magic = 0;
	enc_tkt_transited.tr_contents.data = 0;
	enc_tkt_transited.tr_contents.length = 0;
	enc_tkt_reply.transited = enc_tkt_transited;
	if ((errcode =
	     add_to_transited(&header_ticket->enc_part2->transited.tr_contents,
			      &enc_tkt_reply.transited.tr_contents,
			      header_ticket->server,
			      enc_tkt_reply.client,
			      request->server))) {
	    status = "ADD_TR_FAIL";
	    goto cleanup;
	}
	newtransited = 1;
    }
    if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
	unsigned int tlen;
	char *tdots;

	errcode = krb5_check_transited_list (kdc_context,
					     &enc_tkt_reply.transited.tr_contents,
					     krb5_princ_realm (kdc_context, header_ticket->enc_part2->client),
					     krb5_princ_realm (kdc_context, request->server));
	tlen = enc_tkt_reply.transited.tr_contents.length;
	tdots = tlen > 125 ? "..." : "";
	tlen = tlen > 125 ? 125 : tlen;

	if (errcode == 0) {
	    setflag (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED);
	} else if (errcode == KRB5KRB_AP_ERR_ILL_CR_TKT)
	    krb5_klog_syslog (LOG_INFO,
			      "bad realm transit path from '%s' to '%s' "
			      "via '%.*s%s'",
			      cname ? cname : "<unknown client>",
			      sname ? sname : "<unknown server>",
			      tlen,
			      enc_tkt_reply.transited.tr_contents.data,
			      tdots);
	else {
	    const char *emsg = krb5_get_error_message(kdc_context, errcode);
	    krb5_klog_syslog (LOG_ERR,
			      "unexpected error checking transit from "
			      "'%s' to '%s' via '%.*s%s': %s",
			      cname ? cname : "<unknown client>",
			      sname ? sname : "<unknown server>",
			      tlen,
			      enc_tkt_reply.transited.tr_contents.data,
			      tdots, emsg);
	    krb5_free_error_message(kdc_context, emsg);
	}
    } else
	krb5_klog_syslog (LOG_INFO, "not checking transit path");
    if (reject_bad_transit
	&& !isflagset (enc_tkt_reply.flags, TKT_FLG_TRANSIT_POLICY_CHECKED)) {
	errcode = KRB5KDC_ERR_POLICY;
	status = "BAD_TRANSIT";
	goto cleanup;
    }

    ticket_reply.enc_part2 = &enc_tkt_reply;

    /*
     * If we are doing user-to-user authentication, then make sure
     * that the client for the second ticket matches the request
     * server, and then encrypt the ticket using the session key of
     * the second ticket.
     */
    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
	/*
	 * Make sure the client for the second ticket matches
	 * requested server.
	 */
	krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
	krb5_principal client2 = t2enc->client;
	if (!krb5_principal_compare(kdc_context, request->server, client2)) {
		if ((errcode = krb5_unparse_name(kdc_context, client2, &tmp)))
			tmp = 0;
		if (tmp != NULL)
		    limit_string(tmp);

		krb5_klog_syslog(LOG_INFO,
				 "TGS_REQ %s: 2ND_TKT_MISMATCH: "
				 "authtime %d, %s for %s, 2nd tkt client %s",
				 fromstring, authtime,
				 cname ? cname : "<unknown client>",
				 sname ? sname : "<unknown server>",
				 tmp ? tmp : "<unknown>");
		errcode = KRB5KDC_ERR_SERVER_NOMATCH;
		goto cleanup;
	}
	    
	ticket_reply.enc_part.kvno = 0;
	ticket_reply.enc_part.enctype = t2enc->session->enctype;
	if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session,
					     &ticket_reply))) {
	    status = "2ND_TKT_ENCRYPT";
	    goto cleanup;
	}
	st_idx++;
    } else {
	/*
	 * Find the server key
	 */
	if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
					     -1, /* ignore keytype */
					     -1, /* Ignore salttype */
					     0,		/* Get highest kvno */
					     &server_key))) {
	    status = "FINDING_SERVER_KEY";
	    goto cleanup;
	}
	/* convert server.key into a real key (it may be encrypted
	 *        in the database) */
	if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
						   &master_keyblock, 
						   server_key, &encrypting_key,
						   NULL))) {
	    status = "DECRYPT_SERVER_KEY";
	    goto cleanup;
	}
	errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
					&ticket_reply);
	krb5_free_keyblock_contents(kdc_context, &encrypting_key);
	if (errcode) {
	    status = "TKT_ENCRYPT";
	    goto cleanup;
	}
	ticket_reply.enc_part.kvno = server_key->key_data_kvno;
    }

    /* Start assembling the response */
    reply.msg_type = KRB5_TGS_REP;
    reply.padata = 0;		/* always */
    reply.client = header_ticket->enc_part2->client;
    reply.enc_part.kvno = 0;		/* We are using the session key */
    reply.ticket = &ticket_reply;

    reply_encpart.session = &session_key;
    reply_encpart.nonce = request->nonce;

    /* copy the time fields EXCEPT for authtime; its location
       is used for ktime */
    reply_encpart.times = enc_tkt_reply.times;
    reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime;

    /* starttime is optional, and treated as authtime if not present.
       so we can nuke it if it matches */
    if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
	enc_tkt_reply.times.starttime = 0;

    nolrentry.lr_type = KRB5_LRQ_NONE;
    nolrentry.value = 0;
    nolrarray[0] = &nolrentry;
    nolrarray[1] = 0;
    reply_encpart.last_req = nolrarray;	/* not available for TGS reqs */
    reply_encpart.key_exp = 0;		/* ditto */
    reply_encpart.flags = enc_tkt_reply.flags;
    reply_encpart.server = ticket_reply.server;
    
    /* use the session key in the ticket, unless there's a subsession key
       in the AP_REQ */

    reply.enc_part.enctype = subkey ? subkey->enctype :
		    header_ticket->enc_part2->session->enctype;
    errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart, 
				  subkey ? 1 : 0,
				  subkey ? subkey :
				  header_ticket->enc_part2->session,
				  &reply, response);
    if (errcode) {
	status = "ENCODE_KDC_REP";
    } else {
	status = "ISSUE";
    }

    memset(ticket_reply.enc_part.ciphertext.data, 0,
	   ticket_reply.enc_part.ciphertext.length);
    free(ticket_reply.enc_part.ciphertext.data);
    /* these parts are left on as a courtesy from krb5_encode_kdc_rep so we
       can use them in raw form if needed.  But, we don't... */
    memset(reply.enc_part.ciphertext.data, 0,
	   reply.enc_part.ciphertext.length);
    free(reply.enc_part.ciphertext.data);
    
cleanup:
    if (status) {
	const char * emsg = NULL;
	if (!errcode)
	    rep_etypes2str(rep_etypestr, sizeof(rep_etypestr), &reply);
	if (errcode) 
	    emsg = krb5_get_error_message (kdc_context, errcode);
	krb5_klog_syslog(LOG_INFO,
			 "TGS_REQ (%s) %s: %s: authtime %d, "
			 "%s%s %s for %s%s%s",
			 ktypestr,
			 fromstring, status, authtime,
			 !errcode ? rep_etypestr : "",
			 !errcode ? "," : "",
			 cname ? cname : "<unknown client>",
			 sname ? sname : "<unknown server>",
			 errcode ? ", " : "",
			 errcode ? emsg : "");
	if (errcode)
	    krb5_free_error_message (kdc_context, emsg);
    }
    
    if (errcode) {
        int got_err = 0;
	if (status == 0) {
	    status = krb5_get_error_message (kdc_context, errcode);
	    got_err = 1;
	}
	errcode -= ERROR_TABLE_BASE_krb5;
	if (errcode < 0 || errcode > 128)
	    errcode = KRB_ERR_GENERIC;
	    
	retval = prepare_error_tgs(request, header_ticket, errcode,
				   fromstring, response, status);
	if (got_err) {
	    krb5_free_error_message (kdc_context, status);
	    status = 0;
	}
    }
    
    if (header_ticket)
	krb5_free_ticket(kdc_context, header_ticket);
    if (request)
	krb5_free_kdc_req(kdc_context, request);
    if (cname)
	free(cname);
    if (sname)
	free(sname);
    if (nprincs)
	krb5_db_free_principal(kdc_context, &server, 1);
    if (session_key.contents)
	krb5_free_keyblock_contents(kdc_context, &session_key);
    if (newtransited)
	free(enc_tkt_reply.transited.tr_contents.data);
    if (subkey)
	krb5_free_keyblock(kdc_context, subkey);

    return retval;
}