Example #1
0
/*
 * Set the desired default ktypes, making sure they are valid.
 */
krb5_error_code
krb5_set_default_in_tkt_ktypes(krb5_context context, const krb5_enctype *ktypes)
{
    krb5_enctype * new_ktypes;
    int i;

    if (ktypes) {
	for (i = 0; ktypes[i]; i++) {
	    if (!krb5_c_valid_enctype(ktypes[i])) 
		return KRB5_PROG_ETYPE_NOSUPP;
	    if (!context->allow_weak_crypto && krb5_c_weak_enctype(ktypes[i]))
		return KRB5_PROG_ETYPE_NOSUPP;
	}

	/* Now copy the default ktypes into the context pointer */
	if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
	    memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
	else
	    return ENOMEM;

    } else {
	i = 0;
	new_ktypes = 0;
    }

    if (context->in_tkt_ktypes) 
        free(context->in_tkt_ktypes);
    context->in_tkt_ktypes = new_ktypes;
    context->in_tkt_ktype_count = i;
    return 0;
}
Example #2
0
/*
 * Set up the request given by ctx->tgs_in_creds, using ctx->cur_tgt.  KDC
 * options for the requests are determined by ctx->cur_tgt->ticket_flags and
 * extra_options.
 */
static krb5_error_code
make_request(krb5_context context, krb5_tkt_creds_context ctx,
             int extra_options)
{
    krb5_error_code code;
    krb5_data request = empty_data();

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

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

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

    krb5_free_data_contents(context, &ctx->previous_request);
    ctx->previous_request = request;
    return set_caller_request(context, ctx);
}
Example #3
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;
}
Example #4
0
/*
 * try_kdc()
 *
 * Using CUR_TGT, attempt to get desired NXT_TGT.  Update NXT_KDC if
 * successful.
 */
static krb5_error_code
try_kdc(struct tr_state *ts, krb5_creds *tgtq)
{
    krb5_error_code retval;
    krb5_creds ltgtq;

    TR_DBG(ts, "try_kdc");
    /* This check should probably be in gc_via_tkt. */
    if (!krb5_c_valid_enctype(ts->cur_tgt->keyblock.enctype))
	return KRB5_PROG_ETYPE_NOSUPP;

    ltgtq = *tgtq;
    ltgtq.is_skey = FALSE;
    ltgtq.ticket_flags = ts->cur_tgt->ticket_flags;
    retval = krb5_get_cred_via_tkt(ts->ctx, ts->cur_tgt,
				   FLAGS2OPTS(ltgtq.ticket_flags),
				   ts->cur_tgt->addresses,
				   &ltgtq, &ts->kdc_tgts[ts->ntgts++]);
    if (retval) {
	ts->ntgts--;
	ts->nxt_tgt = ts->cur_tgt;
	TR_DBG_RET(ts, "try_kdc", retval);
	return retval;
    }
    ts->nxt_tgt = ts->kdc_tgts[ts->ntgts-1];
    retval = find_nxt_kdc(ts);
    TR_DBG_RET(ts, "try_kdc", retval);
    return retval;
}
Example #5
0
/*
 * Find the key in client for the most preferred enctype in req_enctypes.  Fill
 * in *kb_out with the decrypted keyblock (which the caller must free) and set
 * *kd_out to an alias to that key data entry.  Set *kd_out to NULL and leave
 * *kb_out zeroed if no key is found for any of the requested enctypes.
 * kb_out->enctype may differ from the enctype of *kd_out for DES enctypes; in
 * this case, kb_out->enctype is the requested enctype used to match the key
 * data entry.
 */
static krb5_error_code
select_client_key(krb5_context context, krb5_db_entry *client,
                  krb5_enctype *req_enctypes, int n_req_enctypes,
                  krb5_keyblock *kb_out, krb5_key_data **kd_out)
{
    krb5_error_code ret;
    krb5_key_data *kd;
    krb5_enctype etype;
    int i;

    memset(kb_out, 0, sizeof(*kb_out));
    *kd_out = NULL;

    for (i = 0; i < n_req_enctypes; i++) {
        etype = req_enctypes[i];
        if (!krb5_c_valid_enctype(etype))
            continue;
        if (krb5_dbe_find_enctype(context, client, etype, -1, 0, &kd) == 0) {
            /* Decrypt the client key data and set its enctype to the request
             * enctype (which may differ from the key data enctype for DES). */
            ret = krb5_dbe_decrypt_key_data(context, NULL, kd, kb_out, NULL);
            if (ret)
                return ret;
            kb_out->enctype = etype;
            *kd_out = kd;
            return 0;
        }
    }
    return 0;
}
Example #6
0
krb5_error_code KRB5_CALLCONV
krb5_set_default_tgs_enctypes (krb5_context context, const krb5_enctype *ktypes)
{
    krb5_enctype * new_ktypes;
    int i;

    if (ktypes) {
	for (i = 0; ktypes[i]; i++) {
	    if (!krb5_c_valid_enctype(ktypes[i])) 
		return KRB5_PROG_ETYPE_NOSUPP;
	}

	/* Now copy the default ktypes into the context pointer */
	if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * i)))
	    memcpy(new_ktypes, ktypes, sizeof(krb5_enctype) * i);
	else
	    return ENOMEM;

    } else {
	i = 0;
	new_ktypes = (krb5_enctype *)NULL;
    }

    if (context->tgs_ktypes) 
        krb5_free_ktypes(context, context->tgs_ktypes);
    context->tgs_ktypes = new_ktypes;
    context->tgs_ktype_count = i;
    return 0;
}
Example #7
0
/*
 * try_kdc()
 *
 * Using CUR_TGT, attempt to get desired NXT_TGT.  Update NXT_KDC if
 * successful.
 */
static krb5_error_code
try_kdc(struct tr_state *ts, krb5_creds *tgtq)
{
    krb5_error_code retval;
    krb5_creds ltgtq;
    krb5_creds *tmp_out_cred;

    TR_DBG(ts, "try_kdc");
    /* This check should probably be in gc_via_tkt. */
    if (!krb5_c_valid_enctype(ts->cur_tgt->keyblock.enctype))
	return KRB5_PROG_ETYPE_NOSUPP;

    ltgtq = *tgtq;
    ltgtq.is_skey = FALSE;
    ltgtq.ticket_flags = ts->cur_tgt->ticket_flags;
    /*
     * Solaris Kerberos:
     * Store credential in a temporary ticket as we may not
     * want to add it to ts->kdc_tgts if it is already in
     * the cache.
     */
    retval = krb5_get_cred_via_tkt(ts->ctx, ts->cur_tgt,
				   FLAGS2OPTS(ltgtq.ticket_flags),
				   ts->cur_tgt->addresses,
				   &ltgtq, &tmp_out_cred);
    if (retval) {
	ts->ntgts--;
	ts->nxt_tgt = ts->cur_tgt;
	TR_DBG_RET(ts, "try_kdc", retval);
	return retval;
    }

    /*
     * Solaris Kerberos:
     * See if the returned creds are different to the requested creds.
     * This can happen when the server returns a TGT "closer" to the
     * desired realm.
     */ 
    if (!(krb5_principal_compare(ts->ctx, tgtq->server, tmp_out_cred->server))) {
	    /* Not equal, ticket may already be in the cache */
	    retval = try_ccache(ts, tmp_out_cred);
	    if (!retval) {
	        krb5_free_creds(ts->ctx, tmp_out_cred);
	        retval = find_nxt_kdc(ts);
	        return retval;
	    }
	}

    ts->kdc_tgts[ts->ntgts++] = tmp_out_cred;
    ts->nxt_tgt = ts->kdc_tgts[ts->ntgts-1];
    retval = find_nxt_kdc(ts);
    TR_DBG_RET(ts, "try_kdc", retval);
    return retval;
}
Example #8
0
static krb5_error_code
get_as_key_keytab(krb5_context context,
                  krb5_principal client,
                  krb5_enctype etype,
                  krb5_prompter_fct prompter,
                  void *prompter_data,
                  krb5_data *salt,
                  krb5_data *params,
                  krb5_keyblock *as_key,
                  void *gak_data,
                  k5_response_items *ritems)
{
    krb5_keytab keytab = (krb5_keytab) gak_data;
    krb5_error_code ret;
    krb5_keytab_entry kt_ent;
    krb5_keyblock *kt_key;

    /* We don't need the password from the responder to create the AS key. */
    if (as_key == NULL)
        return 0;

    /* if there's already a key of the correct etype, we're done.
       if the etype is wrong, free the existing key, and make
       a new one. */

    if (as_key->length) {
        if (as_key->enctype == etype)
            return(0);

        krb5_free_keyblock_contents(context, as_key);
        as_key->length = 0;
    }

    if (!krb5_c_valid_enctype(etype))
        return(KRB5_PROG_ETYPE_NOSUPP);

    if ((ret = krb5_kt_get_entry(context, keytab, client,
                                 0, /* don't have vno available */
                                 etype, &kt_ent)))
        return(ret);

    ret = krb5_copy_keyblock(context, &kt_ent.key, &kt_key);

    /* again, krb5's memory management is lame... */

    *as_key = *kt_key;
    free(kt_key);

    (void) krb5_kt_free_entry(context, &kt_ent);

    return(ret);
}
Example #9
0
/* A krb5_gic_get_as_key_fct shim for copying a caller-provided keyblock into
 * the AS keyblock. */
static krb5_error_code
get_as_key_skey(krb5_context context, krb5_principal client,
                krb5_enctype etype, krb5_prompter_fct prompter,
                void *prompter_data, krb5_data *salt, krb5_data *params,
                krb5_keyblock *as_key, void *gak_data)
{
    const krb5_keyblock *key = gak_data;

    if (!krb5_c_valid_enctype(etype))
        return(KRB5_PROG_ETYPE_NOSUPP);
    if (as_key->length)
        krb5_free_keyblock_contents(context, as_key);
    return krb5int_c_copy_keyblock_contents(context, key, as_key);
}
Example #10
0
File: hooks.c Project: INNOAUS/krb5
/* Verify that reply is an AS-REP with kvno 1 and a valid enctype. */
static krb5_error_code
test_recv_as_rep(krb5_context context, void *data, krb5_error_code code,
                 const krb5_data *realm, const krb5_data *message,
                 const krb5_data *reply, krb5_data **new_reply)
{
    krb5_kdc_rep *as_rep;

    assert(code == 0);
    assert(krb5_is_as_rep(reply));
    check(decode_krb5_as_rep(reply, &as_rep));

    assert(as_rep->msg_type == KRB5_AS_REP);
    assert(as_rep->ticket->enc_part.kvno == 1);
    assert(krb5_c_valid_enctype(as_rep->ticket->enc_part.enctype));

    krb5_free_kdc_rep(context, as_rep);
    return 0;
}
Example #11
0
static krb5_error_code
get_2ndtkt_enctype(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req,
                   krb5_enctype *useenctype, const char **status)
{
    krb5_enctype etype;
    krb5_ticket *stkt = req->second_ticket[0];
    int i;

    etype = stkt->enc_part2->session->enctype;
    if (!krb5_c_valid_enctype(etype)) {
        *status = "BAD_ETYPE_IN_2ND_TKT";
        return KRB5KDC_ERR_ETYPE_NOSUPP;
    }
    for (i = 0; i < req->nktypes; i++) {
        if (req->ktype[i] == etype) {
            *useenctype = etype;
            break;
        }
    }
    return 0;
}
Example #12
0
File: init_ctx.c Project: PADL/krb5
/*
 * Set the desired default ktypes, making sure they are valid.
 */
static krb5_error_code
set_default_etype_var(krb5_context context, const krb5_enctype *etypes,
                      krb5_enctype **var)
{
    krb5_error_code code;
    krb5_enctype *list;
    size_t src, dst;

    if (etypes) {
        /* Empty list passed in. */
        if (etypes[0] == 0)
            return EINVAL;
        code = k5_copy_etypes(etypes, &list);
        if (code)
            return code;

        /* Filter list in place to exclude invalid and (optionally) weak
         * enctypes. */
        for (src = dst = 0; list[src]; src++) {
            if (!krb5_c_valid_enctype(list[src]))
                continue;
            if (!context->allow_weak_crypto
                && krb5int_c_weak_enctype(list[src]))
                continue;
            list[dst++] = list[src];
        }
        list[dst] = 0;          /* Zero-terminate. */
        if (dst == 0) {
            free(list);
            return KRB5_CONFIG_ETYPE_NOSUPP;
        }
    } else {
        list = NULL;
    }

    free(*var);
    *var = list;
    return 0;
}
Example #13
0
static PyObject *
k5_c_valid_enctype(PyObject *self, PyObject *args)
{
    char *name;
    krb5_context ctx;
    krb5_enctype type;
    krb5_error_code code;
    krb5_boolean valid;
    PyObject *ret;

    if (!PyArg_ParseTuple( args, "s", &name))
	return NULL;

    code = krb5_init_context(&ctx);
    RETURN_ON_ERROR("krb5_init_context()", code);
    code = krb5_string_to_enctype(name, &type);
    RETURN_ON_ERROR("krb5_string_to_enctype()", code);
    valid = krb5_c_valid_enctype(type);
    ret = PyBool_FromLong((long) valid);
    krb5_free_context(ctx);

    return ret;
}
OM_uint32 KRB5_CALLCONV
gss_krb5int_set_allowable_enctypes(OM_uint32 *minor_status,
                                   gss_cred_id_t cred_handle,
                                   const gss_OID desired_oid,
                                   const gss_buffer_t value)
{
    unsigned int i;
    krb5_enctype * new_ktypes;
    OM_uint32 major_status;
    krb5_gss_cred_id_t cred;
    krb5_error_code kerr = 0;
    OM_uint32 temp_status;
    struct krb5_gss_set_allowable_enctypes_req *req;

    /* Assume a failure */
    *minor_status = 0;
    major_status = GSS_S_FAILURE;

    assert(value->length == sizeof(*req));
    req = (struct krb5_gss_set_allowable_enctypes_req *)value->value;

    /* verify and valildate cred handle */
    if (cred_handle == GSS_C_NO_CREDENTIAL) {
        kerr = KRB5_NOCREDS_SUPPLIED;
        goto error_out;
    }
    major_status = krb5_gss_validate_cred(&temp_status, cred_handle);
    if (GSS_ERROR(major_status)) {
        kerr = temp_status;
        goto error_out;
    }
    cred = (krb5_gss_cred_id_t) cred_handle;

    if (req->ktypes) {
        for (i = 0; i < req->num_ktypes && req->ktypes[i]; i++) {
            if (!krb5_c_valid_enctype(req->ktypes[i])) {
                kerr = KRB5_PROG_ETYPE_NOSUPP;
                goto error_out;
            }
        }
    } else {
        kerr = k5_mutex_lock(&cred->lock);
        if (kerr)
            goto error_out;
        if (cred->req_enctypes)
            free(cred->req_enctypes);
        cred->req_enctypes = NULL;
        k5_mutex_unlock(&cred->lock);
        return GSS_S_COMPLETE;
    }

    /* Copy the requested ktypes into the cred structure */
    if ((new_ktypes = (krb5_enctype *)malloc(sizeof(krb5_enctype) * (i + 1)))) {
        memcpy(new_ktypes, req->ktypes, sizeof(krb5_enctype) * i);
        new_ktypes[i] = 0;      /* "null-terminate" the list */
    }
    else {
        kerr = ENOMEM;
        goto error_out;
    }
    kerr = k5_mutex_lock(&cred->lock);
    if (kerr) {
        free(new_ktypes);
        goto error_out;
    }
    if (cred->req_enctypes)
        free(cred->req_enctypes);
    cred->req_enctypes = new_ktypes;
    k5_mutex_unlock(&cred->lock);

    /* Success! */
    return GSS_S_COMPLETE;

error_out:
    *minor_status = kerr;
    return(major_status);
}
Example #15
0
static krb5_error_code
pkinit_server_return_padata(krb5_context context,
			    krb5_pa_data * padata,
			    struct _krb5_db_entry_new * client,
			    krb5_data *req_pkt,
			    krb5_kdc_req * request,
			    krb5_kdc_rep * reply,
			    struct _krb5_key_data * client_key,
			    krb5_keyblock * encrypting_key,
			    krb5_pa_data ** send_pa,
			    preauth_get_entry_data_proc server_get_entry_data,
			    void *pa_plugin_context,
			    void **pa_request_context)
{
    krb5_error_code retval = 0;
    krb5_data scratch = {0, 0, NULL};
    krb5_pa_pk_as_req *reqp = NULL;
    krb5_pa_pk_as_req_draft9 *reqp9 = NULL;
    int i = 0;

    unsigned char *subjectPublicKey = NULL;
    unsigned char *dh_pubkey = NULL, *server_key = NULL;
    unsigned int subjectPublicKey_len = 0;
    unsigned int server_key_len = 0, dh_pubkey_len = 0;

    krb5_kdc_dh_key_info dhkey_info;
    krb5_data *encoded_dhkey_info = NULL;
    krb5_pa_pk_as_rep *rep = NULL;
    krb5_pa_pk_as_rep_draft9 *rep9 = NULL;
    krb5_data *out_data = NULL;

    krb5_enctype enctype = -1;

    krb5_reply_key_pack *key_pack = NULL;
    krb5_reply_key_pack_draft9 *key_pack9 = NULL;
    krb5_data *encoded_key_pack = NULL;
    unsigned int num_types;
    krb5_cksumtype *cksum_types = NULL;

    pkinit_kdc_context plgctx;
    pkinit_kdc_req_context reqctx;

    int fixed_keypack = 0;

    *send_pa = NULL;
    if (padata == NULL || padata->length <= 0 || padata->contents == NULL)
	return 0;

    if (pa_request_context == NULL || *pa_request_context == NULL) {
	pkiDebug("missing request context \n");
	return EINVAL;
    }
    
    plgctx = pkinit_find_realm_context(context, pa_plugin_context,
				       request->server);
    if (plgctx == NULL) {
	pkiDebug("Unable to locate correct realm context\n");
	return ENOENT;
    }

    pkiDebug("pkinit_return_padata: entered!\n");
    reqctx = (pkinit_kdc_req_context)*pa_request_context;

    if (encrypting_key->contents) {
	free(encrypting_key->contents);
	encrypting_key->length = 0;
	encrypting_key->contents = NULL;
    }

    for(i = 0; i < request->nktypes; i++) {
	enctype = request->ktype[i];
	if (!krb5_c_valid_enctype(enctype))
	    continue;
	else {
	    pkiDebug("KDC picked etype = %d\n", enctype);
	    break;
	}
    }

    if (i == request->nktypes) {
	retval = KRB5KDC_ERR_ETYPE_NOSUPP;
	goto cleanup;
    }

    switch((int)reqctx->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    init_krb5_pa_pk_as_rep(&rep);
	    if (rep == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    /* let's assume it's RSA. we'll reset it to DH if needed */
	    rep->choice = choice_pa_pk_as_rep_encKeyPack;
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    init_krb5_pa_pk_as_rep_draft9(&rep9);
	    if (rep9 == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
	    break;
	default:
	    retval = KRB5KDC_ERR_PREAUTH_FAILED;
	    goto cleanup;
    }

    if (reqctx->rcv_auth_pack != NULL &&
	    reqctx->rcv_auth_pack->clientPublicValue != NULL) {
	subjectPublicKey =
	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.data;
	subjectPublicKey_len =
	    reqctx->rcv_auth_pack->clientPublicValue->subjectPublicKey.length;
	rep->choice = choice_pa_pk_as_rep_dhInfo;
    } else if (reqctx->rcv_auth_pack9 != NULL &&
		reqctx->rcv_auth_pack9->clientPublicValue != NULL) {
	subjectPublicKey =
	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.data;
	subjectPublicKey_len =
	    reqctx->rcv_auth_pack9->clientPublicValue->subjectPublicKey.length;
	rep9->choice = choice_pa_pk_as_rep_draft9_dhSignedData;
    }

    /* if this DH, then process finish computing DH key */
    if (rep != NULL && (rep->choice == choice_pa_pk_as_rep_dhInfo ||
	    rep->choice == choice_pa_pk_as_rep_draft9_dhSignedData)) {
	pkiDebug("received DH key delivery AS REQ\n");
	retval = server_process_dh(context, plgctx->cryptoctx,
	    reqctx->cryptoctx, plgctx->idctx, subjectPublicKey,
	    subjectPublicKey_len, &dh_pubkey, &dh_pubkey_len, 
	    &server_key, &server_key_len);
	if (retval) {
	    pkiDebug("failed to process/create dh paramters\n");
	    goto cleanup;
	}
    }
	
    if ((rep9 != NULL &&
	    rep9->choice == choice_pa_pk_as_rep_draft9_dhSignedData) ||
	(rep != NULL && rep->choice == choice_pa_pk_as_rep_dhInfo)) {
	retval = pkinit_octetstring2key(context, enctype, server_key,
					server_key_len, encrypting_key);
	if (retval) {
	    pkiDebug("pkinit_octetstring2key failed: %s\n",
		     error_message(retval));
	    goto cleanup;
	}

	dhkey_info.subjectPublicKey.length = dh_pubkey_len;
	dhkey_info.subjectPublicKey.data = dh_pubkey;
	dhkey_info.nonce = request->nonce;
	dhkey_info.dhKeyExpiration = 0;

	retval = k5int_encode_krb5_kdc_dh_key_info(&dhkey_info,
						   &encoded_dhkey_info);
	if (retval) {
	    pkiDebug("encode_krb5_kdc_dh_key_info failed\n");
	    goto cleanup;
	}
#ifdef DEBUG_ASN1
	print_buffer_bin((unsigned char *)encoded_dhkey_info->data,
			 encoded_dhkey_info->length,
			 "/tmp/kdc_dh_key_info");
#endif

	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		retval = cms_signeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_SERVER, 1,
		    (unsigned char *)encoded_dhkey_info->data,
		    encoded_dhkey_info->length,
		    &rep->u.dh_Info.dhSignedData.data,
		    &rep->u.dh_Info.dhSignedData.length);
		if (retval) {
		    pkiDebug("failed to create pkcs7 signed data\n");
		    goto cleanup;
		}
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		retval = cms_signeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, CMS_SIGN_DRAFT9, 1,
		    (unsigned char *)encoded_dhkey_info->data,
		    encoded_dhkey_info->length,
		    &rep9->u.dhSignedData.data,
		    &rep9->u.dhSignedData.length);
		if (retval) {
		    pkiDebug("failed to create pkcs7 signed data\n");
		    goto cleanup;
		}
		break;
	}
    } else {
	pkiDebug("received RSA key delivery AS REQ\n");

	retval = krb5_c_make_random_key(context, enctype, encrypting_key);
	if (retval) {
	    pkiDebug("unable to make a session key\n");
	    goto cleanup;
	}

	/* check if PA_TYPE of 132 is present which means the client is
	 * requesting that a checksum is send back instead of the nonce
	 */
	for (i = 0; request->padata[i] != NULL; i++) {
	    pkiDebug("%s: Checking pa_type 0x%08x\n",
		     __FUNCTION__, request->padata[i]->pa_type);
	    if (request->padata[i]->pa_type == 132)
		fixed_keypack = 1;
	}
	pkiDebug("%s: return checksum instead of nonce = %d\n",
		 __FUNCTION__, fixed_keypack);

	/* if this is an RFC reply or draft9 client requested a checksum 
	 * in the reply instead of the nonce, create an RFC-style keypack
	 */
	if ((int)padata->pa_type == KRB5_PADATA_PK_AS_REQ || fixed_keypack) {
	    init_krb5_reply_key_pack(&key_pack);
	    if (key_pack == NULL) {
		retval = ENOMEM;
		goto cleanup;
	    }
	    /* retrieve checksums for a given enctype of the reply key */
	    retval = krb5_c_keyed_checksum_types(context,
		encrypting_key->enctype, &num_types, &cksum_types);
	    if (retval)
		goto cleanup;

	    /* pick the first of acceptable enctypes for the checksum */
	    retval = krb5_c_make_checksum(context, cksum_types[0],
		    encrypting_key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
		    req_pkt, &key_pack->asChecksum);
	    if (retval) {
		pkiDebug("unable to calculate AS REQ checksum\n");
		goto cleanup;
	    }
#ifdef DEBUG_CKSUM
	    pkiDebug("calculating checksum on buf size = %d\n", req_pkt->length);
	    print_buffer(req_pkt->data, req_pkt->length);
	    pkiDebug("checksum size = %d\n", key_pack->asChecksum.length);
	    print_buffer(key_pack->asChecksum.contents, 
			 key_pack->asChecksum.length);
	    pkiDebug("encrypting key (%d)\n", encrypting_key->length);
	    print_buffer(encrypting_key->contents, encrypting_key->length);
#endif

	    krb5_copy_keyblock_contents(context, encrypting_key,
					&key_pack->replyKey);

	    retval = k5int_encode_krb5_reply_key_pack(key_pack,
						      &encoded_key_pack);
	    if (retval) {
		pkiDebug("failed to encode reply_key_pack\n");
		goto cleanup;
	    }
	}

	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		rep->choice = choice_pa_pk_as_rep_encKeyPack;
		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 
		    (unsigned char *)encoded_key_pack->data,
		    encoded_key_pack->length,
		    &rep->u.encKeyPack.data, &rep->u.encKeyPack.length);
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		/* if the request is from the broken draft9 client that
		 * expects back a nonce, create it now 
		 */
		if (!fixed_keypack) {
		    init_krb5_reply_key_pack_draft9(&key_pack9);
		    if (key_pack9 == NULL) {
			retval = ENOMEM;
			goto cleanup;
		    }
		    key_pack9->nonce = reqctx->rcv_auth_pack9->pkAuthenticator.nonce;
		    krb5_copy_keyblock_contents(context, encrypting_key,
						&key_pack9->replyKey);

		    retval = k5int_encode_krb5_reply_key_pack_draft9(key_pack9,
							   &encoded_key_pack);
		    if (retval) {
			pkiDebug("failed to encode reply_key_pack\n");
			goto cleanup;
		    }
		} 

		rep9->choice = choice_pa_pk_as_rep_draft9_encKeyPack;
		retval = cms_envelopeddata_create(context, plgctx->cryptoctx,
		    reqctx->cryptoctx, plgctx->idctx, padata->pa_type, 1, 
		    (unsigned char *)encoded_key_pack->data,
		    encoded_key_pack->length,
		    &rep9->u.encKeyPack.data, &rep9->u.encKeyPack.length);
		break;
	}
	if (retval) {
	    pkiDebug("failed to create pkcs7 enveloped data: %s\n",
		     error_message(retval));
	    goto cleanup;
	}
#ifdef DEBUG_ASN1
	print_buffer_bin((unsigned char *)encoded_key_pack->data,
			 encoded_key_pack->length,
			 "/tmp/kdc_key_pack");
	switch ((int)padata->pa_type) {
	    case KRB5_PADATA_PK_AS_REQ:
		print_buffer_bin(rep->u.encKeyPack.data,
				 rep->u.encKeyPack.length,
				 "/tmp/kdc_enc_key_pack");
		break;
	    case KRB5_PADATA_PK_AS_REP_OLD:
	    case KRB5_PADATA_PK_AS_REQ_OLD:
		print_buffer_bin(rep9->u.encKeyPack.data,
				 rep9->u.encKeyPack.length,
				 "/tmp/kdc_enc_key_pack");
		break;
	}
#endif
    }

    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    retval = k5int_encode_krb5_pa_pk_as_rep(rep, &out_data);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    retval = k5int_encode_krb5_pa_pk_as_rep_draft9(rep9, &out_data);
	    break;
    }
    if (retval) {
	pkiDebug("failed to encode AS_REP\n");
	goto cleanup;
    }
#ifdef DEBUG_ASN1
    if (out_data != NULL)
	print_buffer_bin((unsigned char *)out_data->data, out_data->length,
			 "/tmp/kdc_as_rep");
#endif

    *send_pa = (krb5_pa_data *) malloc(sizeof(krb5_pa_data));
    if (*send_pa == NULL) {
	retval = ENOMEM;
	free(out_data->data);
	free(out_data);
	out_data = NULL;
	goto cleanup;
    }
    (*send_pa)->magic = KV5M_PA_DATA;
    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP;
	    break;
	case KRB5_PADATA_PK_AS_REQ_OLD:
	case KRB5_PADATA_PK_AS_REP_OLD:
	    (*send_pa)->pa_type = KRB5_PADATA_PK_AS_REP_OLD;
	    break;
    }
    (*send_pa)->length = out_data->length;
    (*send_pa)->contents = (krb5_octet *) out_data->data;


  cleanup:
    pkinit_fini_kdc_req_context(context, reqctx);
    if (scratch.data != NULL)
	free(scratch.data);
    if (out_data != NULL)
	free(out_data);
    if (encoded_dhkey_info != NULL)
	krb5_free_data(context, encoded_dhkey_info);
    if (encoded_key_pack != NULL)
	krb5_free_data(context, encoded_key_pack);
    if (dh_pubkey != NULL)
	free(dh_pubkey);
    if (server_key != NULL)
	free(server_key);
    if (cksum_types != NULL)
	free(cksum_types);

    switch ((int)padata->pa_type) {
	case KRB5_PADATA_PK_AS_REQ:
	    free_krb5_pa_pk_as_req(&reqp);
	    free_krb5_pa_pk_as_rep(&rep);
	    free_krb5_reply_key_pack(&key_pack);
	    break;
	case KRB5_PADATA_PK_AS_REP_OLD:
	case KRB5_PADATA_PK_AS_REQ_OLD:
	    free_krb5_pa_pk_as_req_draft9(&reqp9);
	    free_krb5_pa_pk_as_rep_draft9(&rep9);
	    if (!fixed_keypack)
		free_krb5_reply_key_pack_draft9(&key_pack9);
	    else
		free_krb5_reply_key_pack(&key_pack);
	    break;
    }

    if (retval)
	pkiDebug("pkinit_verify_padata failure");

    return retval;
}
Example #16
0
/*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;
    krb5_keyblock encrypting_key;
    int nprincs = 0;
    krb5_boolean more;
    krb5_timestamp kdc_time, authtime=0;
    krb5_keyblock session_key;
    krb5_timestamp until, rtime;
    krb5_keyblock *reply_key = NULL;
    krb5_keyblock *mkey_ptr;
    krb5_key_data  *server_key;
    char *cname = 0, *sname = 0, *altcname = 0;
    krb5_last_req_entry *nolrarray[2], nolrentry;
    krb5_enctype useenctype;
    int errcode, errcode2;
    register int i;
    int firstpass = 1;
    const char        *status = 0;
    krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
    krb5_db_entry client, krbtgt;
    int c_nprincs = 0, k_nprincs = 0;
    krb5_pa_s4u_x509_user *s4u_x509_user = NULL; /* protocol transition request */
    krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
    unsigned int c_flags = 0, s_flags = 0;       /* client/server KDB flags */
    char *s4u_name = NULL;
    krb5_boolean is_referral, db_ref_done = FALSE;
    const char *emsg = NULL;
    krb5_data *tgs_1 =NULL, *server_1 = NULL;
    krb5_principal krbtgt_princ;
    krb5_kvno ticket_kvno = 0;
    struct kdc_request_state *state = NULL;
    krb5_pa_data *pa_tgs_req; /*points into request*/
    krb5_data scratch;

    session_key.contents = NULL;

    retval = decode_krb5_tgs_req(pkt, &request);
    if (retval)
        return retval;

    /*
     * 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;
    }
    errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket,
                                  &krbtgt, &k_nprincs, &subkey, &pa_tgs_req);
    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;
    }
    errcode = kdc_make_rstate(&state);
    if (errcode !=0) {
	status = "making state";
	goto cleanup;
    }
    scratch.length = pa_tgs_req->length;
    scratch.data = (char *) pa_tgs_req->contents;
    errcode = kdc_find_fast(&request, &scratch, subkey, header_ticket->enc_part2->session, state);
    if (errcode !=0) {
	status = "kdc_find_fast";
		goto cleanup;
    }


    /*
     * Pointer to the encrypted part of the header ticket, which may be
     * replaced to point to the encrypted part of the evidence ticket
     * if constrained delegation is used. This simplifies the number of
     * special cases for constrained delegation.
     */
    header_enc_tkt = header_ticket->enc_part2;
    
    /*
     * 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.
     */

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

    if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
        setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
        setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
    }

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

    errcode = krb5_db_get_principal_ext(kdc_context,
                                        request->server,
                                        s_flags,
                                        &server,
                                        &nprincs,
                                        &more);
    if (errcode) {
        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 ) {

            if ( krb5_is_tgs_principal(request->server) == TRUE) { /* Principal is a name of krb ticket service */
                if (krb5_princ_size(kdc_context, request->server) == 2) {

                    server_1 = krb5_princ_component(kdc_context, request->server, 1);
                    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;

            } else if ( db_ref_done == FALSE) {
                retval = prep_reprocess_req(request, &krbtgt_princ);
                if (!retval) {
                    krb5_free_principal(kdc_context, request->server);
                    retval = krb5_copy_principal(kdc_context, krbtgt_princ, &(request->server));
                    if (!retval) {
                        db_ref_done = TRUE;
                        if (sname != NULL)
                            free(sname);
                        goto ref_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;
    }

    if (!is_local_principal(header_enc_tkt->client))
        setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);

    is_referral = krb5_is_tgs_principal(server.princ) &&
        !krb5_principal_compare(kdc_context, tgs_server, server.princ);

    /* Check for protocol transition */
    errcode = kdc_process_s4u2self_req(kdc_context,
				       request,
				       header_enc_tkt->client,
                                       &server,
				       subkey,
				       header_enc_tkt->session,
				       kdc_time,
                                       &s4u_x509_user,
				       &client,
				       &c_nprincs,
				       &status);
    if (errcode)
        goto cleanup;
    if (s4u_x509_user != NULL)
        setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);

    /*
     * 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 |
                        KDC_OPT_CNAME_IN_ADDL_TKT)) {
        krb5_keyblock *        st_sealing_key;
        krb5_kvno       st_srv_kvno;
        krb5_enctype    etype;
        krb5_db_entry    st_client;
        int             st_nprincs = 0;

        /*
         * Get the key for the second ticket, and decrypt it.
         */
        if ((errcode = kdc_get_server_key(request->second_ticket[st_idx],
                                          c_flags,
                                          TRUE, /* match_enctype */
                                          &st_client,
                                          &st_nprincs,
                                          &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";
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
            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;
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
            goto cleanup;
        }

        for (i = 0; i < request->nktypes; i++) {
            if (request->ktype[i] == etype) {
                useenctype = etype;
                break;
            }
        }

        if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
            /* Do constrained delegation protocol and authorization checks */
            errcode = kdc_process_s4u2proxy_req(kdc_context,
                                                request,
                                                request->second_ticket[st_idx]->enc_part2,
                                                &st_client,
                                                header_ticket->enc_part2->client,
                                                request->server,
                                                &status);
            if (errcode)
                goto cleanup;

            setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);

            assert(krb5_is_tgs_principal(header_ticket->server));

            /* From now on, use evidence ticket as header ticket */
            header_enc_tkt = request->second_ticket[st_idx]->enc_part2;

            assert(c_nprincs == 0); /* assured by kdc_process_s4u2self_req() */

            client = st_client;
            c_nprincs = st_nprincs;
        } else {
            /* "client" is not used for user2user */
            krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
        }
    }

    /*
     * 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;
    }

    authtime = header_enc_tkt->times.authtime;

    if (is_referral)
        ticket_reply.server = server.princ;
    else
        ticket_reply.server = request->server; /* XXX careful for realm... */

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

    if (isflagset(server.attributes, KRB5_KDB_OK_AS_DELEGATE))
        setflag(enc_tkt_reply.flags, TKT_FLG_OK_AS_DELEGATE);

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

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

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

    /* 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(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
            /*
             * If S4U2Self principal is not forwardable, then mark ticket as
             * unforwardable. This behaviour matches Windows, but it is
             * different to the MIT AS-REQ path, which returns an error
             * (KDC_ERR_POLICY) if forwardable tickets cannot be issued.
             *
             * Consider this block the S4U2Self equivalent to
             * validate_forwardable().
             */
            if (c_nprincs &&
                isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
                clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
            /*
             * OK_TO_AUTH_AS_DELEGATE must be set on the service requesting
             * S4U2Self in order for forwardable tickets to be returned.
             */
            else if (!is_referral &&
                !isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
                clear(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_enc_tkt->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)) {
        assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
        /* 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);
        clear(enc_tkt_reply.flags, TKT_FLG_INVALID);
    }

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

        assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
        /* 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);

        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_enc_tkt->times.endtime)));
        if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
            (enc_tkt_reply.times.endtime < request->till) &&
            isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) {
            setflag(request->kdc_options, KDC_OPT_RENEWABLE);
            request->rtime =
                min(request->till, header_enc_tkt->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_enc_tkt->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_enc_tkt->times.authtime;
    
    /*
     * Propagate the preauthentication flags through to the returned ticket.
     */
    if (isflagset(header_enc_tkt->flags, TKT_FLG_PRE_AUTH))
        setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);

    if (isflagset(header_enc_tkt->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;

    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
        errcode = krb5_unparse_name(kdc_context, s4u_x509_user->user_id.user, &s4u_name);
    } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
        errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
    } else {
        errcode = 0;
    }
    if (errcode) {
        status = "UNPARSING S4U CLIENT";
        goto cleanup;
    }

    if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
        krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
        encrypting_key = *(t2enc->session);
    } 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;
        }

        if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist, &server,
                                          &mkey_ptr))) {
            krb5_keylist_node *tmp_mkey_list;
            /* try refreshing master key list */
            /* XXX it would nice if we had the mkvno here for optimization */
            if (krb5_db_fetch_mkey_list(kdc_context, master_princ,
                                        &master_keyblock, 0, &tmp_mkey_list) == 0) {
                krb5_dbe_free_key_list(kdc_context, master_keylist);
                master_keylist = tmp_mkey_list;
                if ((errcode = krb5_dbe_find_mkey(kdc_context, master_keylist,
                                                  &server, &mkey_ptr))) {
                    status = "FINDING_MASTER_KEY";
                    goto cleanup;
                }
            } else {
                status = "FINDING_MASTER_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,
                                                   mkey_ptr,
                                                   server_key, &encrypting_key,
                                                   NULL))) {
            status = "DECRYPT_SERVER_KEY";
            goto cleanup;
        }
    }

    if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
        /*
         * Don't allow authorization data to be disabled if constrained
         * delegation is requested. We don't want to deny the server
         * the ability to validate that delegation was used.
         */
        clear(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
    }
    if (isflagset(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
        /*
         * If we are not doing protocol transition/constrained delegation
         * and there was no authorization data included, try to lookup
         * the client principal as it may be mapped to a local account.
         *
         * Always validate authorization data for constrained delegation
         * because we must validate the KDC signatures.
         */
        if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U) &&
            header_enc_tkt->authorization_data == NULL) {

            /* Generate authorization data so we can include it in ticket */
            setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
            /* Map principals from foreign (possibly non-AD) realms */
            setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);

            assert(c_nprincs == 0); /* should not have been looked up already */

            c_nprincs = 1;
            errcode = krb5_db_get_principal_ext(kdc_context,
                                                header_enc_tkt->client,
                                                c_flags,
                                                &client,
                                                &c_nprincs,
                                                &more);
            /*
             * We can ignore errors because the principal may be a
             * valid cross-realm principal for which we have no local
             * mapping. But we do want to check that at most one entry
             * was returned.
             */
            if (errcode == 0 && (more || c_nprincs > 1)) {
                errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
                goto cleanup;
            } else if (errcode) {
                c_nprincs = 0;
            }
        }
    }

    enc_tkt_reply.authorization_data = NULL;

    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
        !isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM))
        enc_tkt_reply.client = s4u_x509_user->user_id.user;
    else
        enc_tkt_reply.client = header_enc_tkt->client;

    enc_tkt_reply.session = &session_key;
    enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
    enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */

    errcode = handle_authdata(kdc_context,
                              c_flags,
                              (c_nprincs != 0) ? &client : NULL,
                              &server,
                              (k_nprincs != 0) ? &krbtgt : NULL,
                              subkey != NULL ? subkey :
                              header_ticket->enc_part2->session,
                              &encrypting_key, /* U2U or server key */
                              pkt,
                              request,
                              s4u_x509_user ?
				s4u_x509_user->user_id.user : NULL,
                              header_enc_tkt,
                              &enc_tkt_reply);
    if (errcode) {
        krb5_klog_syslog(LOG_INFO, "TGS_REQ : handle_authdata (%d)", errcode);
        status = "HANDLE_AUTHDATA";
        goto cleanup;
    }

    if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
        errcode = return_svr_referral_data(kdc_context,
                                           &server, &reply_encpart);
        if (errcode) {
            status = "KDC_RETURN_ENC_PADATA";
            goto cleanup;
        }
    }

    /*
     * 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_enc_tkt->transited;
    } else {
        /* tgt issued by some other realm and not the realm of the client */
        /* assemble new transited field into allocated storage */
        if (header_enc_tkt->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_enc_tkt->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(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) {
        errcode = validate_transit_path(kdc_context, header_enc_tkt->client,
        &server,
        (k_nprincs != 0) ? &krbtgt : NULL);
        if (errcode) {
            status = "NON_TRANSITIVE";
            goto cleanup;
        }
    }
    if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
        unsigned int tlen;
        char *tdots;

        errcode = kdc_check_transited_list (kdc_context,
                                            &enc_tkt_reply.transited.tr_contents,
                                            krb5_princ_realm (kdc_context, header_enc_tkt->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 {
            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);
            emsg = NULL;
        }
    } 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, &altcname)))
                altcname = 0;
            if (altcname != NULL)
                limit_string(altcname);

            errcode = KRB5KDC_ERR_SERVER_NOMATCH;
            status = "2ND_TKT_MISMATCH";
            goto cleanup;
        }

        ticket_kvno = 0;
        ticket_reply.enc_part.enctype = t2enc->session->enctype;
        st_idx++;
    } else {
        ticket_kvno = server_key->key_data_kvno;
    }

    errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
                    &ticket_reply);
    if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
        krb5_free_keyblock_contents(kdc_context, &encrypting_key);
    if (errcode) {
        status = "TKT_ENCRYPT";
        goto cleanup;
    }
    ticket_reply.enc_part.kvno = ticket_kvno;
    /* Start assembling the response */
    reply.msg_type = KRB5_TGS_REP;
    reply.padata = 0;/* always */
    if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
        find_pa_data(request->padata, KRB5_PADATA_S4U_X509_USER) != NULL) {
        errcode = kdc_make_s4u2self_rep(kdc_context,
                                        subkey,
                                        header_ticket->enc_part2->session,
                                        s4u_x509_user,
                                        &reply,
                                        &reply_encpart);
        if (errcode) {
            status = "KDC_RETURN_S4U2SELF_PADATA";
            goto cleanup;
        }
    }

    reply.client = enc_tkt_reply.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_enc_tkt->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  = kdc_fast_response_handle_padata(state, request, &reply,
					       subkey?subkey->enctype:header_ticket->enc_part2->session->enctype);
    if (errcode !=0 ) {
	status = "Preparing FAST padata";
	goto cleanup;
    }
    errcode =kdc_fast_handle_reply_key(state, subkey?subkey:header_ticket->enc_part2->session, &reply_key);
    if (errcode) {
      status  = "generating reply key";
      goto cleanup;
    }
            errcode = krb5_encode_kdc_rep(kdc_context, KRB5_TGS_REP, &reply_encpart,
                  subkey ? 1 : 0,
					  reply_key,
                  &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:
    assert(status != NULL);
    if (reply_key)
      krb5_free_keyblock(kdc_context, reply_key);
    if (errcode)
        emsg = krb5_get_error_message (kdc_context, errcode);
    log_tgs_req(from, request, &reply, cname, sname, altcname, authtime,
                c_flags, s4u_name, status, errcode, emsg);
    if (errcode) {
        krb5_free_error_message (kdc_context, emsg);
        emsg = NULL;
    }

    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(state, request, header_ticket, errcode,
        nprincs ? server.princ : NULL,
                   response, status);
        if (got_err) {
            krb5_free_error_message (kdc_context, status);
            status = 0;
        }
    }
    
    if (header_ticket != NULL)
        krb5_free_ticket(kdc_context, header_ticket);
    if (request != NULL)
        krb5_free_kdc_req(kdc_context, request);
    if (state)
	kdc_free_rstate(state);
    if (cname != NULL)
        free(cname);
    if (sname != NULL)
        free(sname);
    if (nprincs != 0)
        krb5_db_free_principal(kdc_context, &server, 1);
    if (session_key.contents != NULL)
        krb5_free_keyblock_contents(kdc_context, &session_key);
    if (newtransited)
        free(enc_tkt_reply.transited.tr_contents.data);
    if (k_nprincs)
        krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
    if (c_nprincs)
        krb5_db_free_principal(kdc_context, &client, c_nprincs);
    if (s4u_x509_user != NULL)
        krb5_free_pa_s4u_x509_user(kdc_context, s4u_x509_user);
    if (kdc_issued_auth_data != NULL)
        krb5_free_authdata(kdc_context, kdc_issued_auth_data);
    if (s4u_name != NULL)
        free(s4u_name);
    if (subkey != NULL)
        krb5_free_keyblock(kdc_context, subkey);
    if (reply.padata)
        krb5_free_pa_data(kdc_context, reply.padata);
    if (reply_encpart.enc_padata)
        krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);

    return retval;
}
Example #17
0
static krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache,
			   krb5_creds *in_cred, krb5_creds **out_cred,
			   krb5_creds ***tgts, int kdcopt)
{
    krb5_error_code retval, subretval;
    krb5_principal client, server, supplied_server, out_supplied_server;
    krb5_creds tgtq, cc_tgt, *tgtptr, *referral_tgts[KRB5_REFERRAL_MAXHOPS];
    krb5_creds *otgtptr = NULL;
    int tgtptr_isoffpath = 0;
    krb5_boolean old_use_conf_ktypes;
    char **hrealms;
    unsigned int referral_count, i;

    /* 
     * Set up client and server pointers.  Make a fresh and modifyable
     * copy of the in_cred server and save the supplied version.
     */
    client = in_cred->client;
    if ((retval=krb5_copy_principal(context, in_cred->server, &server)))
        return retval;
    /* We need a second copy for the output creds. */
    if ((retval = krb5_copy_principal(context, server,
				      &out_supplied_server)) != 0 ) {
	krb5_free_principal(context, server);
	return retval;
    }
    supplied_server = in_cred->server;
    in_cred->server=server;

    DUMP_PRINC("gc_from_kdc initial client", client);
    DUMP_PRINC("gc_from_kdc initial server", server);
    memset(&cc_tgt, 0, sizeof(cc_tgt));
    memset(&tgtq, 0, sizeof(tgtq));
    memset(&referral_tgts, 0, sizeof(referral_tgts));

    tgtptr = NULL;
    *tgts = NULL;
    *out_cred=NULL;
    old_use_conf_ktypes = context->use_conf_ktypes;

    /* Copy client realm to server if no hint. */
    if (krb5_is_referral_realm(&server->realm)) {
        /* Use the client realm. */
        DPRINTF(("gc_from_kdc: no server realm supplied, "
		 "using client realm.\n"));
	krb5_free_data_contents(context, &server->realm);
	if (!( server->realm.data = (char *)malloc(client->realm.length+1)))
	    return ENOMEM;
	memcpy(server->realm.data, client->realm.data, client->realm.length);
	server->realm.length = client->realm.length;
	server->realm.data[server->realm.length] = 0;
    }
    /*
     * Retreive initial TGT to match the specified server, either for the
     * local realm in the default (referral) case or for the remote
     * realm if we're starting someplace non-local.
     */
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
        DPRINTF(("gc_from_kdc: starting do_traversal to find "
		 "initial TGT for referral\n"));
	tgtptr_isoffpath = 0;
	otgtptr = NULL;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval) {
        DPRINTF(("gc_from_kdc: failed to find initial TGT for referral\n"));
        goto cleanup;
    }

    DUMP_PRINC("gc_from_kdc: server as requested", supplied_server);

    /*
     * Try requesting a service ticket from our local KDC with referrals
     * turned on.  If the first referral succeeds, follow a referral-only
     * path, otherwise fall back to old-style assumptions.
     */

    /*
     * Save TGTPTR because we rewrite it in the referral loop, and
     * we might need to explicitly free it later.
     */
    otgtptr = tgtptr;
    for (referral_count = 0;
	 referral_count < KRB5_REFERRAL_MAXHOPS;
	 referral_count++) {
#if 0
        DUMP_PRINC("gc_from_kdc: referral loop: tgt in use", tgtptr->server);
        DUMP_PRINC("gc_from_kdc: referral loop: request is for", server);
#endif
        retval = krb5_get_cred_via_tkt(context, tgtptr,
				       KDC_OPT_CANONICALIZE | 
				       FLAGS2OPTS(tgtptr->ticket_flags) |  
				       kdcopt |
				       (in_cred->second_ticket.length ?
					KDC_OPT_ENC_TKT_IN_SKEY : 0),
				       tgtptr->addresses, in_cred, out_cred);
	if (retval) {
	    DPRINTF(("gc_from_kdc: referral TGS-REQ request failed: <%s>\n",
		     error_message(retval)));
	    /* If we haven't gone anywhere yet, fail through to the
	       non-referral case. */
	    if (referral_count==0) {
	        DPRINTF(("gc_from_kdc: initial referral failed; "
			 "punting to fallback.\n"));
	        break;
	    }
	    /* Otherwise, try the same query without canonicalization
	       set, and fail hard if that doesn't work. */
	    DPRINTF(("gc_from_kdc: referral #%d failed; "
		     "retrying without option.\n", referral_count + 1));
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    /* Whether or not that succeeded, we're done. */
	    goto cleanup;
	}
	/* Referral request succeeded; let's see what it is. */
	if (krb5_principal_compare(context, in_cred->server,
				   (*out_cred)->server)) {
	    DPRINTF(("gc_from_kdc: request generated ticket "
		     "for requested server principal\n"));
	    DUMP_PRINC("gc_from_kdc final referred reply",
		       in_cred->server);

	    /*
	     * Check if the return enctype is one that we requested if
	     * needed.
	     */
	    if (old_use_conf_ktypes || context->tgs_ktype_count == 0)
		goto cleanup;
	    for (i = 0; i < context->tgs_ktype_count; i++) {
		if ((*out_cred)->keyblock.enctype == context->tgs_ktypes[i]) {
		    /* Found an allowable etype, so we're done */
		    goto cleanup;
		}
	    }
	    /*
	     *  We need to try again, but this time use the
	     *  tgs_ktypes in the context. At this point we should
	     *  have all the tgts to succeed.
	     */

	    /* Free "wrong" credential */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    /* Re-establish tgs etypes */
	    context->use_conf_ktypes = old_use_conf_ktypes;
	    retval = krb5_get_cred_via_tkt(context, tgtptr,
					   KDC_OPT_CANONICALIZE | 
					   FLAGS2OPTS(tgtptr->ticket_flags) |  
					   kdcopt |
					   (in_cred->second_ticket.length ?
					    KDC_OPT_ENC_TKT_IN_SKEY : 0),
					   tgtptr->addresses,
					   in_cred, out_cred);
	    goto cleanup;
	}
	else if (IS_TGS_PRINC(context, (*out_cred)->server)) {
	    krb5_data *r1, *r2;

	    DPRINTF(("gc_from_kdc: request generated referral tgt\n"));
	    DUMP_PRINC("gc_from_kdc credential received",
		       (*out_cred)->server);

	    if (referral_count == 0)
		r1 = &tgtptr->server->data[1];
	    else
		r1 = &referral_tgts[referral_count-1]->server->data[1];

	    r2 = &(*out_cred)->server->data[1];
	    if (data_eq(*r1, *r2)) {
		DPRINTF(("gc_from_kdc: referred back to "
			 "previous realm; fall back\n"));
		krb5_free_creds(context, *out_cred);
		*out_cred = NULL;
		break;
	    }
	    /* Check for referral routing loop. */
	    for (i=0;i<referral_count;i++) {
#if 0
		DUMP_PRINC("gc_from_kdc: loop compare #1",
			   (*out_cred)->server);
		DUMP_PRINC("gc_from_kdc: loop compare #2",
			   referral_tgts[i]->server);
#endif
		if (krb5_principal_compare(context,
					   (*out_cred)->server,
					   referral_tgts[i]->server)) {
		    DFPRINTF((stderr,
			      "krb5_get_cred_from_kdc_opt: "
			      "referral routing loop - "
			      "got referral back to hop #%d\n", i));
		    retval=KRB5_KDC_UNREACH;
		    goto cleanup;
		}
	    }
	    /* Point current tgt pointer at newly-received TGT. */
	    if (tgtptr == &cc_tgt)
		krb5_free_cred_contents(context, tgtptr);
	    tgtptr=*out_cred;
	    /* Save pointer to tgt in referral_tgts. */
	    referral_tgts[referral_count]=*out_cred;
	    *out_cred = NULL;
	    /* Copy krbtgt realm to server principal. */
	    krb5_free_data_contents(context, &server->realm);
	    retval = krb5int_copy_data_contents(context,
						&tgtptr->server->data[1],
						&server->realm);
	    if (retval)
		return retval;
	    /*
	     * Future work: rewrite server principal per any
	     * supplied padata.
	     */
	} else {
	    /* Not a TGT; punt to fallback. */
	    krb5_free_creds(context, *out_cred);
	    *out_cred = NULL;
	    break;
	}
    }

    DUMP_PRINC("gc_from_kdc client at fallback", client);
    DUMP_PRINC("gc_from_kdc server at fallback", server);

    /*
     * At this point referrals have been tried and have failed.  Go
     * back to the server principal as originally issued and try the
     * conventional path.
     */

    /*
     * Referrals have failed.  Look up fallback realm if not
     * originally provided.
     */
    if (krb5_is_referral_realm(&supplied_server->realm)) {
        if (server->length >= 2) {
	    retval=krb5_get_fallback_host_realm(context, &server->data[1],
						&hrealms);
	    if (retval) goto cleanup;
#if 0
	    DPRINTF(("gc_from_kdc: using fallback realm of %s\n",
		     hrealms[0]));
#endif
	    krb5_free_data_contents(context,&in_cred->server->realm);
	    server->realm.data=hrealms[0];
	    server->realm.length=strlen(hrealms[0]);
	    free(hrealms);
	}
	else {
	    /*
	     * Problem case: Realm tagged for referral but apparently not
	     * in a <type>/<host> format that
	     * krb5_get_fallback_host_realm can deal with.
	     */
	    DPRINTF(("gc_from_kdc: referral specified "
		     "but no fallback realm avaiable!\n"));
	    return KRB5_ERR_HOST_REALM_UNKNOWN;
	}
    }

    DUMP_PRINC("gc_from_kdc server at fallback after fallback rewrite",
	       server);

    /*
     * Get a TGT for the target realm.
     */

    krb5_free_cred_contents(context, &tgtq);
    retval = tgt_mcred(context, client, server, client, &tgtq);
    if (retval)
	goto cleanup;

    /* Fast path: Is it in the ccache? */
    /* Free tgtptr data if reused from above. */
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    tgtptr = NULL;
    /* Free saved TGT in OTGTPTR if it was off-path. */
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    otgtptr = NULL;
    /* Free TGTS if previously filled by do_traversal() */
    if (*tgts != NULL) {
	for (i = 0; (*tgts)[i] != NULL; i++) {
	    krb5_free_creds(context, (*tgts)[i]);
	}
	free(*tgts);
	*tgts = NULL;
    }
    context->use_conf_ktypes = 1;
    retval = krb5_cc_retrieve_cred(context, ccache, RETR_FLAGS,
				   &tgtq, &cc_tgt);
    if (!retval) {
	tgtptr = &cc_tgt;
    } else if (!HARD_CC_ERR(retval)) {
	tgtptr_isoffpath = 0;
	retval = do_traversal(context, ccache, client, server,
			      &cc_tgt, &tgtptr, tgts, &tgtptr_isoffpath);
    }
    if (retval)
	goto cleanup;
    otgtptr = tgtptr;

    /*
     * Finally have TGT for target realm!  Try using it to get creds.
     */

    if (!krb5_c_valid_enctype(tgtptr->keyblock.enctype)) {
	retval = KRB5_PROG_ETYPE_NOSUPP;
	goto cleanup;
    }

    context->use_conf_ktypes = old_use_conf_ktypes;
    retval = krb5_get_cred_via_tkt(context, tgtptr,
				   FLAGS2OPTS(tgtptr->ticket_flags) |
				   kdcopt |
				   (in_cred->second_ticket.length ?
				    KDC_OPT_ENC_TKT_IN_SKEY : 0),
				   tgtptr->addresses, in_cred, out_cred);

cleanup:
    krb5_free_cred_contents(context, &tgtq);
    if (tgtptr == &cc_tgt)
	krb5_free_cred_contents(context, tgtptr);
    if (tgtptr_isoffpath)
	krb5_free_creds(context, otgtptr);
    context->use_conf_ktypes = old_use_conf_ktypes;
    /* Drop the original principal back into in_cred so that it's cached
       in the expected format. */
    DUMP_PRINC("gc_from_kdc: final hacked server principal at cleanup",
	       server);
    krb5_free_principal(context, server);
    in_cred->server = supplied_server;
    if (*out_cred && !retval) {
        /* Success: free server, swap supplied server back in. */
        krb5_free_principal (context, (*out_cred)->server);
	(*out_cred)->server= out_supplied_server;
    }
    else {
        /* 
	 * Failure: free out_supplied_server.  Don't free out_cred here
	 * since it's either null or a referral TGT that we free below,
	 * and we may need it to return.
	 */
        krb5_free_principal (context, out_supplied_server);
    }
    DUMP_PRINC("gc_from_kdc: final server after reversion", in_cred->server);
    /*
     * Deal with ccache TGT management: If tgts has been set from
     * initial non-referral TGT discovery, leave it alone.  Otherwise, if
     * referral_tgts[0] exists return it as the only entry in tgts.
     * (Further referrals are never cached, only the referral from the
     * local KDC.)  This is part of cleanup because useful received TGTs
     * should be cached even if the main request resulted in failure.
     */

    if (*tgts == NULL) {
        if (referral_tgts[0]) {
#if 0
  	    /*
	     * This should possibly be a check on the candidate return
	     * credential against the cache, in the circumstance where we
	     * don't want to clutter the cache with near-duplicate
	     * credentials on subsequent iterations.  For now, it is
	     * disabled.
	     */
	    subretval=...?;
	    if (subretval) {
#endif
	        /* Allocate returnable TGT list. */
	        if (!(*tgts=calloc(sizeof (krb5_creds *), 2)))
		    return ENOMEM;
		subretval=krb5_copy_creds(context, referral_tgts[0], &((*tgts)[0]));
		if(subretval)
		    return subretval;
		(*tgts)[1]=NULL;
		DUMP_PRINC("gc_from_kdc: returning referral TGT for ccache",
			   (*tgts)[0]->server);
#if 0
	    }
#endif
	}
    }

    /* Free referral TGTs list. */
    for (i=0;i<KRB5_REFERRAL_MAXHOPS;i++) {
        if(referral_tgts[i]) {
	    krb5_free_creds(context, referral_tgts[i]);
	}
    }
    DPRINTF(("gc_from_kdc finishing with %s\n",
	     retval ? error_message(retval) : "no error"));
    return retval;
}
Example #18
0
/* Return the list of etypes available for client in keytab. */
static krb5_error_code
lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
                         krb5_principal client, krb5_enctype **etypes_out)
{
    krb5_kt_cursor cursor;
    krb5_keytab_entry entry;
    krb5_enctype *p, *etypes = NULL;
    krb5_kvno max_kvno = 0;
    krb5_error_code ret;
    size_t count = 0;

    *etypes_out = NULL;

    if (keytab->ops->start_seq_get == NULL)
        return EINVAL;
    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    if (ret != 0)
        return ret;

    for (;;) {
        ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
        if (ret == KRB5_KT_END)
            break;
        if (ret)
            goto cleanup;

        if (!krb5_c_valid_enctype(entry.key.enctype))
            continue;
        if (!krb5_principal_compare(context, entry.principal, client))
            continue;
        /* Make sure our list is for the highest kvno found for client. */
        if (entry.vno > max_kvno) {
            free(etypes);
            etypes = NULL;
            count = 0;
            max_kvno = entry.vno;
        } else if (entry.vno != max_kvno)
            continue;

        /* Leave room for the terminator and possibly a second entry. */
        p = realloc(etypes, (count + 3) * sizeof(*etypes));
        if (p == NULL) {
            ret = ENOMEM;
            goto cleanup;
        }
        etypes = p;
        etypes[count++] = entry.key.enctype;
        /* All DES key types work with des-cbc-crc, which is more likely to be
         * accepted by the KDC (since MIT KDCs refuse des-cbc-md5). */
        if (entry.key.enctype == ENCTYPE_DES_CBC_MD5 ||
            entry.key.enctype == ENCTYPE_DES_CBC_MD4)
            etypes[count++] = ENCTYPE_DES_CBC_CRC;
        etypes[count] = 0;
    }

    ret = 0;
    *etypes_out = etypes;
    etypes = NULL;
cleanup:
    krb5_kt_end_seq_get(context, keytab, &cursor);
    free(etypes);
    return ret;
}
Example #19
0
/* Return the list of etypes available for client in keytab. */
static krb5_error_code
lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
                         krb5_principal client, krb5_enctype **etypes_out)
{
    krb5_kt_cursor cursor;
    krb5_keytab_entry entry;
    krb5_enctype *p, *etypes = NULL, etype;
    krb5_kvno max_kvno = 0, vno;
    krb5_error_code ret;
    krb5_boolean match;
    size_t count = 0;

    *etypes_out = NULL;

    if (keytab->ops->start_seq_get == NULL)
        return EINVAL;
    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    if (ret != 0)
        return ret;

    while (!(ret = krb5_kt_next_entry(context, keytab, &entry, &cursor))) {
        /* Extract what we need from the entry and free it. */
        etype = entry.key.enctype;
        vno = entry.vno;
        match = krb5_principal_compare(context, entry.principal, client);
        krb5_free_keytab_entry_contents(context, &entry);

        /* Filter out old or non-matching entries and invalid enctypes. */
        if (vno < max_kvno || !match || !krb5_c_valid_enctype(etype))
            continue;

        /* Update max_kvno and reset the list if we find a newer kvno. */
        if (vno > max_kvno) {
            max_kvno = vno;
            free(etypes);
            etypes = NULL;
            count = 0;
        }

        /* Leave room for the terminator and possibly a second entry. */
        p = realloc(etypes, (count + 3) * sizeof(*etypes));
        if (p == NULL) {
            ret = ENOMEM;
            goto cleanup;
        }
        etypes = p;
        etypes[count++] = etype;
        /* All DES key types work with des-cbc-crc, which is more likely to be
         * accepted by the KDC (since MIT KDCs refuse des-cbc-md5). */
        if (etype == ENCTYPE_DES_CBC_MD5 || etype == ENCTYPE_DES_CBC_MD4)
            etypes[count++] = ENCTYPE_DES_CBC_CRC;
        etypes[count] = 0;
    }
    if (ret != KRB5_KT_END)
        goto cleanup;
    ret = 0;

    *etypes_out = etypes;
    etypes = NULL;

cleanup:
    krb5_kt_end_seq_get(context, keytab, &cursor);
    free(etypes);
    return ret;
}
Example #20
0
static krb5_error_code
krb5_get_cred_from_kdc_opt(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts, int kdcopt)
{
  krb5_creds      **ret_tgts = NULL;
  int             ntgts = 0;

  krb5_creds      tgt, tgtq, *tgtr = NULL;
  krb5_error_code retval;
  krb5_principal  int_server = NULL;    /* Intermediate server for request */

  krb5_principal  *tgs_list = NULL;
  krb5_principal  *top_server = NULL;
  krb5_principal  *next_server = NULL;
  unsigned int             nservers = 0;
  krb5_boolean	  old_use_conf_ktypes = context->use_conf_ktypes;

  /* in case we never get a TGT, zero the return */

  *tgts = NULL;

  memset((char *)&tgtq, 0, sizeof(tgtq));
  memset((char *)&tgt, 0, sizeof(tgt));

  /*
   * we know that the desired credentials aren't in the cache yet.
   *
   * To get them, we first need a tgt for the realm of the server.
   * first, we see if we have such a TGT in cache.  if not, then
   * we ask the kdc to give us one.  if that doesn't work, then
   * we try to get a tgt for a realm that is closest to the target.
   * once we have that, then we ask that realm if it can give us
   * tgt for the target.  if not, we do the process over with this
   * new tgt.
   */

  /*
   * (the ticket may be issued by some other intermediate
   *  realm's KDC; so we use KRB5_TC_MATCH_SRV_NAMEONLY)
   */
  if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client)))
      goto cleanup;

  /* get target tgt from cache */
  if ((retval = krb5_tgtname(context, krb5_princ_realm(context, in_cred->server),
			     krb5_princ_realm(context, in_cred->client),
			     &int_server))) {
      goto cleanup;
  }

  if ((retval = krb5_copy_principal(context, int_server, &tgtq.server))) {
      goto cleanup;
  }

  /* set endtime to now so krb5_cc_retrieve_cred won't return an expired tik */
  if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	goto cleanup;
  }

  context->use_conf_ktypes = 1;
  if ((retval = krb5_cc_retrieve_cred(context, ccache,
				    KRB5_TC_MATCH_SRV_NAMEONLY |
				    KRB5_TC_SUPPORTED_KTYPES |
				    KRB5_TC_MATCH_TIMES,
				    &tgtq, &tgt)) != 0) {

    if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
	goto cleanup;
    }

    /*
     * Note that we want to request a TGT from our local KDC, even
     * if we already have a TGT for some intermediate realm.  The
     * reason is that our local KDC may have a shortcut to the
     * destination realm, and if it does we want to use the
     * shortcut because it will provide greater security. - bcn
     */

    /*
     * didn't find it in the cache so it is time to get a local
     * tgt and walk the realms tree.
     */
    krb5_free_principal(context, int_server);
    int_server = NULL;
    if ((retval = krb5_tgtname(context,
			       krb5_princ_realm(context, in_cred->client),
			       krb5_princ_realm(context, in_cred->client),
			       &int_server))) {
	goto cleanup;
    }

    krb5_free_cred_contents(context, &tgtq);
    memset((char *)&tgtq, 0, sizeof(tgtq));
    if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client)))
	goto cleanup;
    if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	goto cleanup;

    if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	goto cleanup;
    }

    if ((retval = krb5_cc_retrieve_cred(context, ccache,
					KRB5_TC_MATCH_SRV_NAMEONLY |
					KRB5_TC_SUPPORTED_KTYPES |
					KRB5_TC_MATCH_TIMES,
					&tgtq, &tgt)) != 0) {
	goto cleanup;
    }

    /* get a list of realms to consult */

    if ((retval = krb5_walk_realm_tree(context,
				       krb5_princ_realm(context,in_cred->client),
				       krb5_princ_realm(context,in_cred->server),
				       &tgs_list,
				       KRB5_REALM_BRANCH_CHAR))) {
	goto cleanup;
    }

    for (nservers = 0; tgs_list[nservers]; nservers++)
      ;

    /* allocate storage for TGT pointers. */

    if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) {
      retval = ENOMEM;
      goto cleanup;
    }
    *tgts = ret_tgts;

    /*
     * step one is to take the current tgt and see if there is a tgt for
     * krbtgt/realmof(target)@realmof(tgt).  if not, try to get one with
     * the tgt.
     *
     * if we don't get a tgt for the target, then try to find a tgt as
     * close to the target realm as possible. at each step if there isn't
     * a tgt in the cache we have to try and get one with our latest tgt.
     * once we have a tgt for a closer realm, we go back to step one.
     *
     * once we have a tgt for the target, we go try and get credentials.
     */

    for (top_server = tgs_list;
         top_server < tgs_list + nservers;
         top_server = next_server) {

      /* look in cache for a tgt for the destination */

      krb5_free_cred_contents(context, &tgtq);
      memset(&tgtq, 0, sizeof(tgtq));
      if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client)))
	  goto cleanup;

      krb5_free_principal(context, int_server);
      int_server = NULL;
      if ((retval = krb5_tgtname(context,
				 krb5_princ_realm(context, in_cred->server),
				 krb5_princ_realm(context, *top_server),
				 &int_server))) {
	  goto cleanup;
      }

      if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	  goto cleanup;

      if ((retval = krb5_timeofday(context, &(tgtq.times.endtime))) != 0) {
	    goto cleanup;
      }

      if ((retval = krb5_cc_retrieve_cred(context, ccache,
					KRB5_TC_MATCH_SRV_NAMEONLY |
					KRB5_TC_SUPPORTED_KTYPES |
					KRB5_TC_MATCH_TIMES,
					  &tgtq, &tgt)) != 0) {

	if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
	    goto cleanup;
	}

	/* didn't find it in the cache so try and get one */
	/* with current tgt.                              */

	if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
	    retval = KRB5_PROG_ETYPE_NOSUPP;
	    goto cleanup;
	}

	krb5_free_cred_contents(context, &tgtq);
	memset(&tgtq, 0, sizeof(tgtq));
	tgtq.times        = tgt.times;

	if ((retval = krb5_copy_principal(context, tgt.client, &tgtq.client)))
	    goto cleanup;
	if ((retval = krb5_copy_principal(context, int_server, &tgtq.server)))
	    goto cleanup;
	tgtq.is_skey      = FALSE;
	tgtq.ticket_flags = tgt.ticket_flags;
	retval = krb5_get_cred_via_tkt(context, &tgt,
					    FLAGS2OPTS(tgtq.ticket_flags),
				            tgt.addresses, &tgtq, &tgtr);
	if (retval) {

       /*
	* couldn't get one so now loop backwards through the realms
	* list and try and get a tgt for a realm as close to the
	* target as possible. the kdc should give us a tgt for the
	* closest one it knows about, but not all kdc's do this yet.
	*/

	  for (next_server = tgs_list + nservers - 1;
	       next_server > top_server;
	       next_server--) {
	    krb5_free_cred_contents(context, &tgtq);
	    memset(&tgtq, 0, sizeof(tgtq));
	    if ((retval = krb5_copy_principal(context, tgt.client,
					      &tgtq.client)))
		goto cleanup;

	    krb5_free_principal(context, int_server);
	    int_server = NULL;
	    if ((retval = krb5_tgtname(context,
				       krb5_princ_realm(context, *next_server),
				       krb5_princ_realm(context, *top_server),
				       &int_server))) {
		goto cleanup;
	    }

	    if ((retval = krb5_copy_principal(context, int_server,
					      &tgtq.server)))
		goto cleanup;

	    if ((retval = krb5_timeofday(context,
					&(tgtq.times.endtime))) != 0) {
		goto cleanup;
	    }

	    if ((retval = krb5_cc_retrieve_cred(context, ccache,
						KRB5_TC_MATCH_SRV_NAMEONLY |
						KRB5_TC_SUPPORTED_KTYPES |
						KRB5_TC_MATCH_TIMES,
						&tgtq, &tgt)) != 0) {
	      if (retval != KRB5_CC_NOTFOUND) {
		  goto cleanup;
	      }

	      /* not in the cache so try and get one with our current tgt. */

	      if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
		  retval = KRB5_PROG_ETYPE_NOSUPP;
		  goto cleanup;
	      }

	      krb5_free_cred_contents(context, &tgtq);
	      memset(&tgtq, 0, sizeof(tgtq));
	      tgtq.times        = tgt.times;
	      if ((retval = krb5_copy_principal(context, tgt.client,
						&tgtq.client)))
		  goto cleanup;
	      if ((retval = krb5_copy_principal(context, int_server,
						&tgtq.server)))
		  goto cleanup;
	      tgtq.is_skey      = FALSE;
	      tgtq.ticket_flags = tgt.ticket_flags;
	      retval = krb5_get_cred_via_tkt(context, &tgt,
					     FLAGS2OPTS(tgtq.ticket_flags),
					     tgt.addresses,
					     &tgtq, &tgtr);
	      if (retval)
		  continue;
	
	      /* save tgt in return array */
	      if ((retval = krb5_copy_creds(context, tgtr,
					    &ret_tgts[ntgts]))) {
		  goto cleanup;
	      }
	      krb5_free_creds(context, tgtr);
	      tgtr = NULL;
	
	      tgt = *ret_tgts[ntgts++];
	    }

	    /* got one as close as possible, now start all over */

	    break;
	  }

	  if (next_server == top_server) {
	      goto cleanup;
	  }
	  continue;
        }

	/*
	 * Got a tgt.  If it is for the target realm we can go try for the
	 * credentials.  If it is not for the target realm, then make sure it
	 * is in the realms hierarchy and if so, save it and start the loop
	 * over from there.  Note that we only need to compare the instance
	 * names since that is the target realm of the tgt.
	 */

	for (next_server = top_server; *next_server; next_server++) {
            krb5_data *realm_1 = krb5_princ_component(context, next_server[0], 1);
            krb5_data *realm_2 = krb5_princ_component(context, tgtr->server, 1);
            if (realm_1 != NULL &&
		realm_2 != NULL &&
		realm_1->length == realm_2->length &&
                !memcmp(realm_1->data, realm_2->data, realm_1->length)) {
		break;
            }
	}

	if (!next_server) {
	    retval = KRB5_KDCREP_MODIFIED;
	    goto cleanup;
	}

	if ((retval = krb5_copy_creds(context, tgtr, &ret_tgts[ntgts]))) {
	    goto cleanup;
	}
	krb5_free_creds(context, tgtr);
	tgtr = NULL;

        tgt = *ret_tgts[ntgts++];

        /* we're done if it is the target */

        if (!*next_server++) break;
      }
    }
  }

  /* got/finally have tgt!  try for the creds */

  if (!krb5_c_valid_enctype(tgt.keyblock.enctype)) {
    retval = KRB5_PROG_ETYPE_NOSUPP;
    goto cleanup;
  }

  context->use_conf_ktypes = old_use_conf_ktypes;
  retval = krb5_get_cred_via_tkt(context, &tgt,
				 FLAGS2OPTS(tgt.ticket_flags) |
				 kdcopt |
  				 (in_cred->second_ticket.length ?
				  KDC_OPT_ENC_TKT_IN_SKEY : 0),
				 tgt.addresses, in_cred, out_cred);

  /* cleanup and return */

cleanup:

  if (tgtr) krb5_free_creds(context, tgtr);
  if(tgs_list)  krb5_free_realm_tree(context, tgs_list);
  krb5_free_cred_contents(context, &tgtq);
  if (int_server) krb5_free_principal(context, int_server);
  if (ntgts == 0) {
      *tgts = NULL;
      if (ret_tgts)  free(ret_tgts);
      krb5_free_cred_contents(context, &tgt);
  }
  context->use_conf_ktypes = old_use_conf_ktypes;
  return(retval);
}
Example #21
0
/* due to argument promotion rules, we need to use the DECLARG/OLDDECLARG
   stuff... */
krb5_error_code
krb5_encode_kdc_rep(krb5_context context, krb5_msgtype type,
		    const krb5_enc_kdc_rep_part *encpart,
		    int using_subkey, const krb5_keyblock *client_key,
		    krb5_kdc_rep *dec_rep, krb5_data **enc_rep)
{
    krb5_data *scratch;
    krb5_error_code retval;
    krb5_enc_kdc_rep_part tmp_encpart;
    krb5_keyusage usage;

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

    switch (type) {
    case KRB5_AS_REP:
	usage = KRB5_KEYUSAGE_AS_REP_ENCPART;
	break;
    case KRB5_TGS_REP:
	if (using_subkey)
	    usage = KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY;
	else
	    usage = KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY;
	break;
    default:
	return KRB5_BADMSGTYPE;
    }

    /*
     * We don't want to modify encpart, but we need to be able to pass
     * in the message type to the encoder, so it can set the ASN.1
     * type correct.
     * 
     * Although note that it may be doing nothing with the message
     * type, to be compatible with old versions of Kerberos that always
     * encode this as a TGS_REP regardly of what it really should be;
     * also note that the reason why we are passing it in a structure
     * instead of as an argument to encode_krb5_enc_kdc_rep_part (the
     * way we should) is for compatibility with the ISODE version of
     * this fuction.  Ah, compatibility....
     */
    tmp_encpart = *encpart;
    tmp_encpart.msg_type = type;
    retval = encode_krb5_enc_kdc_rep_part(&tmp_encpart, &scratch);
    if (retval) {
	return retval;
    }
    memset(&tmp_encpart, 0, sizeof(tmp_encpart));

#define cleanup_scratch() { (void) memset(scratch->data, 0, scratch->length); \
krb5_free_data(context, scratch); }

    retval = krb5_encrypt_helper(context, client_key, usage, scratch,
				 &dec_rep->enc_part);

#define cleanup_encpart() { \
(void) memset(dec_rep->enc_part.ciphertext.data, 0, \
	     dec_rep->enc_part.ciphertext.length); \
free(dec_rep->enc_part.ciphertext.data); \
dec_rep->enc_part.ciphertext.length = 0; \
dec_rep->enc_part.ciphertext.data = 0;}

    cleanup_scratch();

    if (retval)
	return(retval);

    /* now it's ready to be encoded for the wire! */

    switch (type) {
    case KRB5_AS_REP:
	retval = encode_krb5_as_rep(dec_rep, enc_rep);
	break;
    case KRB5_TGS_REP:
	retval = encode_krb5_tgs_rep(dec_rep, enc_rep);
	break;
    }

    if (retval)
	cleanup_encpart();

    return retval;
}
Example #22
0
/*
 * Solaris Kerberos
 * Same as krb5_send_tgs plus an extra arg to return the FQDN
 * of the KDC sent the request.
 */
krb5_error_code
krb5_send_tgs2(krb5_context context, krb5_flags kdcoptions,
	      const krb5_ticket_times *timestruct, const krb5_enctype *ktypes,
	      krb5_const_principal sname, krb5_address *const *addrs,
	      krb5_authdata *const *authorization_data,
	      krb5_pa_data *const *padata, const krb5_data *second_ticket,
	    krb5_creds *in_cred, krb5_response *rep, char **hostname_used)
{
    krb5_error_code retval;
    krb5_kdc_req tgsreq;
    krb5_data *scratch, scratch2;
    krb5_ticket *sec_ticket = 0;
    krb5_ticket *sec_ticket_arr[2];
    krb5_timestamp time_now;
    krb5_pa_data **combined_padata;
    krb5_pa_data ap_req_padata;
    int tcp_only = 0, use_master;

    /* 
     * in_creds MUST be a valid credential NOT just a partially filled in
     * place holder for us to get credentials for the caller.
     */
    if (!in_cred->ticket.length)
        return(KRB5_NO_TKT_SUPPLIED);

    memset((char *)&tgsreq, 0, sizeof(tgsreq));

    tgsreq.kdc_options = kdcoptions;
    tgsreq.server = (krb5_principal) sname;

    tgsreq.from = timestruct->starttime;
    tgsreq.till = timestruct->endtime ? timestruct->endtime :
	    in_cred->times.endtime;
    tgsreq.rtime = timestruct->renew_till;
    if ((retval = krb5_timeofday(context, &time_now)))
	return(retval);
    /* XXX we know they are the same size... */
    rep->expected_nonce = tgsreq.nonce = (krb5_int32) time_now;
    rep->request_time = time_now;

    tgsreq.addresses = (krb5_address **) addrs;

    if (authorization_data) {
	/* need to encrypt it in the request */

	if ((retval = encode_krb5_authdata(authorization_data,
					   &scratch)))
	    return(retval);

	if ((retval = krb5_encrypt_helper(context, &in_cred->keyblock,
					  KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
					  scratch,
					  &tgsreq.authorization_data))) {
	    krb5_xfree(tgsreq.authorization_data.ciphertext.data);
	    krb5_free_data(context, scratch);
	    return retval;
	}

	krb5_free_data(context, scratch);
    }

    /* Get the encryption types list */
    if (ktypes) {
	/* Check passed ktypes and make sure they're valid. */
   	for (tgsreq.nktypes = 0; ktypes[tgsreq.nktypes]; tgsreq.nktypes++) {
    	    if (!krb5_c_valid_enctype(ktypes[tgsreq.nktypes]))
		return KRB5_PROG_ETYPE_NOSUPP;
	}
    	tgsreq.ktype = (krb5_enctype *)ktypes;
    } else {
        /* Get the default ktypes */
	/* Solaris Kerberos */
	if ((retval = krb5_get_tgs_ktypes(context, sname, &(tgsreq.ktype))))
		goto send_tgs_error_2;
	for(tgsreq.nktypes = 0; tgsreq.ktype[tgsreq.nktypes]; tgsreq.nktypes++);
    }

    if (second_ticket) {
	if ((retval = decode_krb5_ticket(second_ticket, &sec_ticket)))
	    goto send_tgs_error_1;
	sec_ticket_arr[0] = sec_ticket;
	sec_ticket_arr[1] = 0;
	tgsreq.second_ticket = sec_ticket_arr;
    } else
	tgsreq.second_ticket = 0;

    /* encode the body; then checksum it */
    if ((retval = encode_krb5_kdc_req_body(&tgsreq, &scratch)))
	goto send_tgs_error_2;

    /*
     * Get an ap_req.
     */
    if ((retval = krb5_send_tgs_basic(context, scratch, in_cred, &scratch2))) {
        krb5_free_data(context, scratch);
	goto send_tgs_error_2;
    }
    krb5_free_data(context, scratch);

    ap_req_padata.pa_type = KRB5_PADATA_AP_REQ;
    ap_req_padata.length = scratch2.length;
    ap_req_padata.contents = (krb5_octet *)scratch2.data;

    /* combine in any other supplied padata */
    if (padata) {
	krb5_pa_data * const * counter;
	register unsigned int i = 0;
	for (counter = padata; *counter; counter++, i++);
	combined_padata = malloc((i+2) * sizeof(*combined_padata));
	if (!combined_padata) {
	    krb5_xfree(ap_req_padata.contents);
	    retval = ENOMEM;
	    goto send_tgs_error_2;
	}
	combined_padata[0] = &ap_req_padata;
	for (i = 1, counter = padata; *counter; counter++, i++)
	    combined_padata[i] = (krb5_pa_data *) *counter;
	combined_padata[i] = 0;
    } else {
	combined_padata = (krb5_pa_data **)malloc(2*sizeof(*combined_padata));
	if (!combined_padata) {
	    krb5_xfree(ap_req_padata.contents);
	    retval = ENOMEM;
	    goto send_tgs_error_2;
	}
	combined_padata[0] = &ap_req_padata;
	combined_padata[1] = 0;
    }
    tgsreq.padata = combined_padata;

    /* the TGS_REQ is assembled in tgsreq, so encode it */
    if ((retval = encode_krb5_tgs_req(&tgsreq, &scratch))) {
	krb5_xfree(ap_req_padata.contents);
	krb5_xfree(combined_padata);
	goto send_tgs_error_2;
    }
    krb5_xfree(ap_req_padata.contents);
    krb5_xfree(combined_padata);

    /* now send request & get response from KDC */
send_again:
    use_master = 0;
    retval = krb5_sendto_kdc2(context, scratch, 
			    krb5_princ_realm(context, sname),
			    &rep->response, &use_master, tcp_only,
			    hostname_used);
    if (retval == 0) {
	if (krb5_is_krb_error(&rep->response)) {
	    if (!tcp_only) {
		krb5_error *err_reply;
		retval = decode_krb5_error(&rep->response, &err_reply);
		/* Solaris Kerberos */
		if (retval == 0) {
		    if (err_reply->error == KRB_ERR_RESPONSE_TOO_BIG) {
			tcp_only = 1;
			krb5_free_error(context, err_reply);
			free(rep->response.data);
			rep->response.data = 0;
			goto send_again;
		    }
		    krb5_free_error(context, err_reply);
		}
	    }
	} else if (krb5_is_tgs_rep(&rep->response))
	    rep->message_type = KRB5_TGS_REP;
        else /* XXX: assume it's an error */
	    rep->message_type = KRB5_ERROR;
    }

    krb5_free_data(context, scratch);
    
send_tgs_error_2:;
    if (sec_ticket) 
	krb5_free_ticket(context, sec_ticket);

send_tgs_error_1:;
    if (ktypes == NULL)
	krb5_xfree(tgsreq.ktype);
    if (tgsreq.authorization_data.ciphertext.data) {
	memset(tgsreq.authorization_data.ciphertext.data, 0,
               tgsreq.authorization_data.ciphertext.length); 
	krb5_xfree(tgsreq.authorization_data.ciphertext.data);
    }

    return retval;
}
Example #23
0
krb5_boolean KRB5_CALLCONV
valid_enctype(krb5_enctype etype)
{
    return krb5_c_valid_enctype (etype);
}
Example #24
0
static void
finish_process_as_req(struct as_req_state *state, krb5_error_code errcode)
{
    krb5_key_data *server_key;
    krb5_key_data *client_key;
    krb5_keyblock *as_encrypting_key = NULL;
    krb5_data *response = NULL;
    const char *emsg = 0;
    int did_log = 0;
    register int i;
    krb5_enctype useenctype;
    loop_respond_fn oldrespond;
    void *oldarg;
    kdc_realm_t *kdc_active_realm = state->active_realm;

    assert(state);
    oldrespond = state->respond;
    oldarg = state->arg;

    if (errcode)
        goto egress;

    if ((errcode = validate_forwardable(state->request, *state->client,
                                        *state->server, state->kdc_time,
                                        &state->status))) {
        errcode += ERROR_TABLE_BASE_krb5;
        goto egress;
    }

    state->ticket_reply.enc_part2 = &state->enc_tkt_reply;

    /*
     * Find the server key
     */
    if ((errcode = krb5_dbe_find_enctype(kdc_context, state->server,
                                         -1, /* ignore keytype   */
                                         -1, /* Ignore salttype  */
                                         0,  /* Get highest kvno */
                                         &server_key))) {
        state->status = "FINDING_SERVER_KEY";
        goto egress;
    }

    /*
     * Convert server->key into a real key
     * (it may be encrypted in the database)
     *
     *  server_keyblock is later used to generate auth data signatures
     */
    if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
                                             server_key,
                                             &state->server_keyblock,
                                             NULL))) {
        state->status = "DECRYPT_SERVER_KEY";
        goto egress;
    }

    /*
     * Find the appropriate client key.  We search in the order specified
     * by request keytype list.
     */
    client_key = NULL;
    useenctype = 0;
    for (i = 0; i < state->request->nktypes; i++) {
        useenctype = state->request->ktype[i];
        if (!krb5_c_valid_enctype(useenctype))
            continue;

        if (!krb5_dbe_find_enctype(kdc_context, state->client,
                                   useenctype, -1, 0, &client_key))
            break;
    }
    if (!(client_key)) {
        /* Cannot find an appropriate key */
        state->status = "CANT_FIND_CLIENT_KEY";
        errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
        goto egress;
    }
    state->rock.client_key = client_key;

    /* convert client.key_data into a real key */
    if ((errcode = krb5_dbe_decrypt_key_data(kdc_context, NULL,
                                             client_key,
                                             &state->client_keyblock,
                                             NULL))) {
        state->status = "DECRYPT_CLIENT_KEY";
        goto egress;
    }
    state->client_keyblock.enctype = useenctype;

    /* Start assembling the response */
    state->reply.msg_type = KRB5_AS_REP;
    state->reply.client = state->enc_tkt_reply.client; /* post canonization */
    state->reply.ticket = &state->ticket_reply;
    state->reply_encpart.session = &state->session_key;
    if ((errcode = fetch_last_req_info(state->client,
                                       &state->reply_encpart.last_req))) {
        state->status = "FETCH_LAST_REQ";
        goto egress;
    }
    state->reply_encpart.nonce = state->request->nonce;
    state->reply_encpart.key_exp = get_key_exp(state->client);
    state->reply_encpart.flags = state->enc_tkt_reply.flags;
    state->reply_encpart.server = state->ticket_reply.server;

    /* copy the time fields EXCEPT for authtime; it's location
     *  is used for ktime
     */
    state->reply_encpart.times = state->enc_tkt_reply.times;
    state->reply_encpart.times.authtime = state->authtime = state->kdc_time;

    state->reply_encpart.caddrs = state->enc_tkt_reply.caddrs;
    state->reply_encpart.enc_padata = NULL;

    /* Fetch the padata info to be returned (do this before
     *  authdata to handle possible replacement of reply key
     */
    errcode = return_padata(kdc_context, &state->rock, state->req_pkt,
                            state->request, &state->reply,
                            &state->client_keyblock, &state->pa_context);
    if (errcode) {
        state->status = "KDC_RETURN_PADATA";
        goto egress;
    }

    errcode = handle_authdata(kdc_context,
                              state->c_flags,
                              state->client,
                              state->server,
                              state->server,
                              &state->client_keyblock,
                              &state->server_keyblock,
                              &state->server_keyblock,
                              state->req_pkt,
                              state->request,
                              NULL, /* for_user_princ */
                              NULL, /* enc_tkt_request */
                              &state->enc_tkt_reply);
    if (errcode) {
        krb5_klog_syslog(LOG_INFO, _("AS_REQ : handle_authdata (%d)"),
                         errcode);
        state->status = "HANDLE_AUTHDATA";
        goto egress;
    }

    errcode = krb5_encrypt_tkt_part(kdc_context, &state->server_keyblock,
                                    &state->ticket_reply);
    if (errcode) {
        state->status = "ENCRYPTING_TICKET";
        goto egress;
    }
    state->ticket_reply.enc_part.kvno = server_key->key_data_kvno;
    errcode = kdc_fast_response_handle_padata(state->rstate,
                                              state->request,
                                              &state->reply,
                                              state->client_keyblock.enctype);
    if (errcode) {
        state->status = "fast response handling";
        goto egress;
    }

    /* now encode/encrypt the response */

    state->reply.enc_part.enctype = state->client_keyblock.enctype;

    errcode = kdc_fast_handle_reply_key(state->rstate, &state->client_keyblock,
                                        &as_encrypting_key);
    if (errcode) {
        state->status = "generating reply key";
        goto egress;
    }
    errcode = return_enc_padata(kdc_context, state->req_pkt, state->request,
                                as_encrypting_key, state->server,
                                &state->reply_encpart, FALSE);
    if (errcode) {
        state->status = "KDC_RETURN_ENC_PADATA";
        goto egress;
    }

    errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP,
                                  &state->reply_encpart, 0,
                                  as_encrypting_key,
                                  &state->reply, &response);
    state->reply.enc_part.kvno = client_key->key_data_kvno;
    if (errcode) {
        state->status = "ENCODE_KDC_REP";
        goto egress;
    }

    /* 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(state->reply.enc_part.ciphertext.data, 0,
           state->reply.enc_part.ciphertext.length);
    free(state->reply.enc_part.ciphertext.data);

    log_as_req(kdc_context, state->from, state->request, &state->reply,
               state->client, state->cname, state->server,
               state->sname, state->authtime, 0, 0, 0);
    did_log = 1;

egress:
    if (errcode != 0)
        assert (state->status != 0);
    free_padata_context(kdc_context, state->pa_context);
    if (as_encrypting_key)
        krb5_free_keyblock(kdc_context, as_encrypting_key);
    if (errcode)
        emsg = krb5_get_error_message(kdc_context, errcode);

    if (state->status) {
        log_as_req(kdc_context,
                   state->from, state->request, &state->reply, state->client,
                   state->cname, state->server, state->sname, state->authtime,
                   state->status, errcode, emsg);
        did_log = 1;
    }
    if (errcode) {
        if (state->status == 0) {
            state->status = emsg;
        }
        if (errcode != KRB5KDC_ERR_DISCARD) {
            errcode -= ERROR_TABLE_BASE_krb5;
            if (errcode < 0 || errcode > 128)
                errcode = KRB_ERR_GENERIC;

            errcode = prepare_error_as(state->rstate, state->request,
                                       errcode, state->e_data,
                                       state->typed_e_data,
                                       ((state->client != NULL) ?
                                        state->client->princ : NULL),
                                       &response, state->status);
            state->status = 0;
        }
    }

    if (emsg)
        krb5_free_error_message(kdc_context, emsg);
    if (state->enc_tkt_reply.authorization_data != NULL)
        krb5_free_authdata(kdc_context,
                           state->enc_tkt_reply.authorization_data);
    if (state->server_keyblock.contents != NULL)
        krb5_free_keyblock_contents(kdc_context, &state->server_keyblock);
    if (state->client_keyblock.contents != NULL)
        krb5_free_keyblock_contents(kdc_context, &state->client_keyblock);
    if (state->reply.padata != NULL)
        krb5_free_pa_data(kdc_context, state->reply.padata);
    if (state->reply_encpart.enc_padata)
        krb5_free_pa_data(kdc_context, state->reply_encpart.enc_padata);

    if (state->cname != NULL)
        free(state->cname);
    if (state->sname != NULL)
        free(state->sname);
    krb5_db_free_principal(kdc_context, state->client);
    krb5_db_free_principal(kdc_context, state->server);
    if (state->session_key.contents != NULL)
        krb5_free_keyblock_contents(kdc_context, &state->session_key);
    if (state->ticket_reply.enc_part.ciphertext.data != NULL) {
        memset(state->ticket_reply.enc_part.ciphertext.data , 0,
               state->ticket_reply.enc_part.ciphertext.length);
        free(state->ticket_reply.enc_part.ciphertext.data);
    }

    krb5_free_pa_data(kdc_context, state->e_data);
    krb5_free_data(kdc_context, state->inner_body);
    kdc_free_rstate(state->rstate);
    krb5_free_kdc_req(kdc_context, state->request);
    assert(did_log != 0);

    free(state);
    (*oldrespond)(oldarg, errcode, response);
}
Example #25
0
/*
 * Construct a TGS request and return its ASN.1 encoding as well as the
 * timestamp, nonce, and subkey used.  The pacb_fn callback allows the caller
 * to amend the request padata after the nonce and subkey are determined.
 */
krb5_error_code
k5_make_tgs_req(krb5_context context,
                struct krb5int_fast_request_state *fast_state,
                krb5_creds *tgt, krb5_flags kdcoptions,
                krb5_address *const *addrs, krb5_pa_data **in_padata,
                krb5_creds *desired, k5_pacb_fn pacb_fn, void *pacb_data,
                krb5_data *req_asn1_out, krb5_timestamp *timestamp_out,
                krb5_int32 *nonce_out, krb5_keyblock **subkey_out)
{
    krb5_error_code ret;
    krb5_kdc_req req;
    krb5_data *authdata_asn1 = NULL, *req_body_asn1 = NULL;
    krb5_data *ap_req_asn1 = NULL, *tgs_req_asn1 = NULL;
    krb5_ticket *sec_ticket = NULL;
    krb5_ticket *sec_ticket_arr[2];
    krb5_timestamp time_now;
    krb5_pa_data **padata = NULL, *pa;
    krb5_keyblock *subkey = NULL;
    krb5_enc_data authdata_enc;
    krb5_enctype enctypes[2], *defenctypes = NULL;
    size_t count, i;

    *req_asn1_out = empty_data();
    *timestamp_out = 0;
    *nonce_out = 0;
    *subkey_out = NULL;
    memset(&req, 0, sizeof(req));
    memset(&authdata_enc, 0, sizeof(authdata_enc));

    /* tgt's client principal must match the desired client principal. */
    if (!krb5_principal_compare(context, tgt->client, desired->client))
        return KRB5_PRINC_NOMATCH;

    /* tgt must be an actual credential, not a template. */
    if (!tgt->ticket.length)
        return KRB5_NO_TKT_SUPPLIED;

    req.kdc_options = kdcoptions;
    req.server = desired->server;
    req.from = desired->times.starttime;
    req.till = desired->times.endtime ? desired->times.endtime :
        tgt->times.endtime;
    req.rtime = desired->times.renew_till;
    ret = krb5_timeofday(context, &time_now);
    if (ret)
        return ret;
    *nonce_out = req.nonce = (krb5_int32)time_now;
    *timestamp_out = time_now;

    req.addresses = (krb5_address **)addrs;

    /* Generate subkey. */
    ret = krb5_generate_subkey(context, &tgt->keyblock, &subkey);
    if (ret)
        return ret;
    TRACE_SEND_TGS_SUBKEY(context, subkey);

    ret = krb5int_fast_tgs_armor(context, fast_state, subkey, &tgt->keyblock,
                                 NULL, NULL);
    if (ret)
        goto cleanup;

    if (desired->authdata != NULL) {
        ret = encode_krb5_authdata(desired->authdata, &authdata_asn1);
        if (ret)
            goto cleanup;
        ret = krb5_encrypt_helper(context, subkey,
                                  KRB5_KEYUSAGE_TGS_REQ_AD_SUBKEY,
                                  authdata_asn1, &authdata_enc);
        if (ret)
            goto cleanup;
        req.authorization_data = authdata_enc;
    }

    if (desired->keyblock.enctype != ENCTYPE_NULL) {
        if (!krb5_c_valid_enctype(desired->keyblock.enctype)) {
            ret = KRB5_PROG_ETYPE_NOSUPP;
            goto cleanup;
        }
        enctypes[0] = desired->keyblock.enctype;
        enctypes[1] = ENCTYPE_NULL;
        req.ktype = enctypes;
        req.nktypes = 1;
    } else {
        /* Get the default TGS enctypes. */
        ret = krb5_get_tgs_ktypes(context, desired->server, &defenctypes);
        if (ret)
            goto cleanup;
        for (count = 0; defenctypes[count]; count++);
        req.ktype = defenctypes;
        req.nktypes = count;
    }
    TRACE_SEND_TGS_ETYPES(context, req.ktype);

    if (kdcoptions & (KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
        if (desired->second_ticket.length == 0) {
            ret = KRB5_NO_2ND_TKT;
            goto cleanup;
        }
        ret = decode_krb5_ticket(&desired->second_ticket, &sec_ticket);
        if (ret)
            goto cleanup;
        sec_ticket_arr[0] = sec_ticket;
        sec_ticket_arr[1] = NULL;
        req.second_ticket = sec_ticket_arr;
    }

    /* Encode the request body. */
    ret = krb5int_fast_prep_req_body(context, fast_state, &req,
                                     &req_body_asn1);
    if (ret)
        goto cleanup;

    ret = tgs_construct_ap_req(context, req_body_asn1, tgt, subkey,
                               &ap_req_asn1);
    if (ret)
        goto cleanup;

    for (count = 0; in_padata != NULL && in_padata[count] != NULL; count++);

    /* Construct a padata array for the request, beginning with the ap-req. */
    padata = k5calloc(count + 2, sizeof(krb5_pa_data *), &ret);
    if (padata == NULL)
        goto cleanup;
    padata[0] = k5alloc(sizeof(krb5_pa_data), &ret);
    if (padata[0] == NULL)
        goto cleanup;
    padata[0]->pa_type = KRB5_PADATA_AP_REQ;
    padata[0]->contents = k5memdup(ap_req_asn1->data, ap_req_asn1->length,
                                   &ret);
    if (padata[0] == NULL)
        goto cleanup;
    padata[0]->length = ap_req_asn1->length;

    /* Append copies of any other supplied padata. */
    for (i = 0; in_padata != NULL && in_padata[i] != NULL; i++) {
        pa = k5alloc(sizeof(krb5_pa_data), &ret);
        if (pa == NULL)
            goto cleanup;
        pa->pa_type = in_padata[i]->pa_type;
        pa->length = in_padata[i]->length;
        pa->contents = k5memdup(in_padata[i]->contents, in_padata[i]->length,
                                &ret);
        if (pa->contents == NULL)
            goto cleanup;
        padata[i + 1] = pa;
    }
    req.padata = padata;

    if (pacb_fn != NULL) {
        ret = (*pacb_fn)(context, subkey, &req, pacb_data);
        if (ret)
            goto cleanup;
    }

    /* Encode the TGS-REQ.  Discard the krb5_data container. */
    ret = krb5int_fast_prep_req(context, fast_state, &req, ap_req_asn1,
                                encode_krb5_tgs_req, &tgs_req_asn1);
    if (ret)
        goto cleanup;
    *req_asn1_out = *tgs_req_asn1;
    free(tgs_req_asn1);
    tgs_req_asn1 = NULL;

    *subkey_out = subkey;
    subkey = NULL;

cleanup:
    krb5_free_data(context, authdata_asn1);
    krb5_free_data(context, req_body_asn1);
    krb5_free_data(context, ap_req_asn1);
    krb5_free_pa_data(context, req.padata);
    krb5_free_ticket(context, sec_ticket);
    krb5_free_data_contents(context, &authdata_enc.ciphertext);
    krb5_free_keyblock(context, subkey);
    free(defenctypes);
    return ret;
}
/*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;
}
Example #27
0
static krb5_error_code
sam2_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;
    krb5_sam_challenge_2 *sc2 = NULL;
    krb5_sam_challenge_2_body *sc2b = NULL;
    krb5_data tmp_data;
    krb5_data response_data;
    char name[100], banner[100], prompt[100], response[100];
    krb5_prompt kprompt;
    krb5_prompt_type prompt_type;
    krb5_data defsalt, *salt;
    krb5_checksum **cksum;
    krb5_data *scratch = NULL;
    krb5_boolean valid_cksum = 0;
    krb5_enc_sam_response_enc_2 enc_sam_response_enc_2;
    krb5_sam_response_2 sr2;
    size_t ciph_len;
    krb5_pa_data **sam_padata;

    if (prompter == NULL)
        return KRB5_LIBOS_CANTREADPWD;

    tmp_data.length = padata->length;
    tmp_data.data = (char *)padata->contents;

    if ((retval = decode_krb5_sam_challenge_2(&tmp_data, &sc2)))
        return(retval);

    retval = decode_krb5_sam_challenge_2_body(&sc2->sam_challenge_2_body, &sc2b);

    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        return(retval);
    }

    if (!sc2->sam_cksum || ! *sc2->sam_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_NO_CHECKSUM);
    }

    if (sc2b->sam_flags & KRB5_SAM_MUST_PK_ENCRYPT_SAD) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_UNSUPPORTED);
    }

    if (!krb5_c_valid_enctype(sc2b->sam_etype)) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(KRB5_SAM_INVALID_ETYPE);
    }

    /* All of the above error checks are KDC-specific, that is, they     */
    /* assume a failure in the KDC reply.  By returning anything other   */
    /* than KRB5_KDC_UNREACH, KRB5_PREAUTH_FAILED,               */
    /* KRB5_LIBOS_PWDINTR, or KRB5_REALM_CANT_RESOLVE, the client will   */
    /* most likely go on to try the AS_REQ against master KDC            */

    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* We will need the password to obtain the key used for */
        /* the checksum, and encryption of the sam_response.    */
        /* Go ahead and get it now, preserving the ordering of  */
        /* prompts for the user.                                */

        retval = (*rock->gak_fct)(context, request->client, sc2b->sam_etype,
				  prompter, prompter_data, rock->salt,
				  rock->s2kparams, rock->as_key,
				  *rock->gak_data);
        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    snprintf(name, sizeof(name), "%.*s",
             SAMDATA(sc2b->sam_type_name, _("SAM Authentication"),
                     sizeof(name) - 1));

    snprintf(banner, sizeof(banner), "%.*s",
             SAMDATA(sc2b->sam_challenge_label,
                     sam_challenge_banner(sc2b->sam_type),
                     sizeof(banner)-1));

    snprintf(prompt, sizeof(prompt), "%s%.*s%s%.*s",
             sc2b->sam_challenge.length?"Challenge is [":"",
             SAMDATA(sc2b->sam_challenge, "", 20),
             sc2b->sam_challenge.length?"], ":"",
             SAMDATA(sc2b->sam_response_prompt, "passcode", 55));

    response_data.data = response;
    response_data.length = sizeof(response);
    kprompt.prompt = prompt;
    kprompt.hidden = 1;
    kprompt.reply = &response_data;

    prompt_type = KRB5_PROMPT_TYPE_PREAUTH;
    krb5int_set_prompt_types(context, &prompt_type);

    if ((retval = ((*prompter)(context, prompter_data, name,
                               banner, 1, &kprompt)))) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5int_set_prompt_types(context, 0);
        return(retval);
    }

    krb5int_set_prompt_types(context, (krb5_prompt_type *)NULL);

    /* Generate salt used by string_to_key() */
    salt = rock->salt;
    if (((int) salt->length == -1) && (salt->data == NULL)) {
        if ((retval =
             krb5_principal2salt(context, request->client, &defsalt))) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        salt = &defsalt;
    } else {
        defsalt.length = 0;
    }

    /* Get encryption key to be used for checksum and sam_response */
    if (!(sc2b->sam_flags & KRB5_SAM_USE_SAD_AS_KEY)) {
        /* as_key = string_to_key(password) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
				      (krb5_data *)*rock->gak_data, salt,
				      rock->as_key);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            if (defsalt.length) free(defsalt.data);
            return(retval);
        }

        if (!(sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD)) {
            /* as_key = combine_key (as_key, string_to_key(SAD)) */
            krb5_keyblock tmp_kb;

            retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                          &response_data, salt, &tmp_kb);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }

            /* This should be a call to the crypto library some day */
            /* key types should already match the sam_etype */
            retval = krb5int_c_combine_keys(context, rock->as_key, &tmp_kb,
					    rock->as_key);

            if (retval) {
                krb5_free_sam_challenge_2(context, sc2);
                krb5_free_sam_challenge_2_body(context, sc2b);
                if (defsalt.length) free(defsalt.data);
                return(retval);
            }
            krb5_free_keyblock_contents(context, &tmp_kb);
        }

        if (defsalt.length)
            free(defsalt.data);

    } else {
        /* as_key = string_to_key(SAD) */

        if (rock->as_key->length) {
            krb5_free_keyblock_contents(context, rock->as_key);
            rock->as_key->length = 0;
        }

        /* generate a key using the supplied password */
        retval = krb5_c_string_to_key(context, sc2b->sam_etype,
                                      &response_data, salt, rock->as_key);

        if (defsalt.length)
            free(defsalt.data);

        if (retval) {
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
    }

    /* Now we have a key, verify the checksum on the sam_challenge */

    cksum = sc2->sam_cksum;

    for (; *cksum; cksum++) {
        if (!krb5_c_is_keyed_cksum((*cksum)->checksum_type))
            continue;
        /* Check this cksum */
        retval = krb5_c_verify_checksum(context, rock->as_key,
                                        KRB5_KEYUSAGE_PA_SAM_CHALLENGE_CKSUM,
                                        &sc2->sam_challenge_2_body,
                                        *cksum, &valid_cksum);
        if (retval) {
            krb5_free_data(context, scratch);
            krb5_free_sam_challenge_2(context, sc2);
            krb5_free_sam_challenge_2_body(context, sc2b);
            return(retval);
        }
        if (valid_cksum)
            break;
    }

    if (!valid_cksum) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        /*
         * Note: We return AP_ERR_BAD_INTEGRITY so upper-level applications
         * can interpret that as "password incorrect", which is probably
         * the best error we can return in this situation.
         */
        return(KRB5KRB_AP_ERR_BAD_INTEGRITY);
    }

    /* fill in enc_sam_response_enc_2 */
    enc_sam_response_enc_2.magic = KV5M_ENC_SAM_RESPONSE_ENC_2;
    enc_sam_response_enc_2.sam_nonce = sc2b->sam_nonce;
    if (sc2b->sam_flags & KRB5_SAM_SEND_ENCRYPTED_SAD) {
        enc_sam_response_enc_2.sam_sad = response_data;
    } else {
        enc_sam_response_enc_2.sam_sad.data = NULL;
        enc_sam_response_enc_2.sam_sad.length = 0;
    }

    /* encode and encrypt enc_sam_response_enc_2 with as_key */
    retval = encode_krb5_enc_sam_response_enc_2(&enc_sam_response_enc_2,
                                                &scratch);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        return(retval);
    }

    /* Fill in sam_response_2 */
    memset(&sr2, 0, sizeof(sr2));
    sr2.sam_type = sc2b->sam_type;
    sr2.sam_flags = sc2b->sam_flags;
    sr2.sam_track_id = sc2b->sam_track_id;
    sr2.sam_nonce = sc2b->sam_nonce;

    /* Now take care of sr2.sam_enc_nonce_or_sad by encrypting encoded   */
    /* enc_sam_response_enc_2 from above */

    retval = krb5_c_encrypt_length(context, rock->as_key->enctype,
				   scratch->length, &ciph_len);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(retval);
    }
    sr2.sam_enc_nonce_or_sad.ciphertext.length = ciph_len;

    sr2.sam_enc_nonce_or_sad.ciphertext.data =
        (char *)malloc(sr2.sam_enc_nonce_or_sad.ciphertext.length);

    if (!sr2.sam_enc_nonce_or_sad.ciphertext.data) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }

    retval = krb5_c_encrypt(context, rock->as_key,
			    KRB5_KEYUSAGE_PA_SAM_RESPONSE, NULL, scratch,
			    &sr2.sam_enc_nonce_or_sad);
    if (retval) {
        krb5_free_sam_challenge_2(context, sc2);
        krb5_free_sam_challenge_2_body(context, sc2b);
        krb5_free_data(context, scratch);
        krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);
        return(retval);
    }
    krb5_free_data(context, scratch);
    scratch = NULL;

    /* Encode the sam_response_2 */
    retval = encode_krb5_sam_response_2(&sr2, &scratch);
    krb5_free_sam_challenge_2(context, sc2);
    krb5_free_sam_challenge_2_body(context, sc2b);
    krb5_free_data_contents(context, &sr2.sam_enc_nonce_or_sad.ciphertext);

    if (retval) {
        return (retval);
    }

    /* Almost there, just need to make padata !  */
    sam_padata = malloc(2 * sizeof(*sam_padata));
    if (sam_padata == NULL) {
        krb5_free_data(context, scratch);
        return(ENOMEM);
    }
    sam_padata[0] = malloc(sizeof(krb5_pa_data));
    if (sam_padata[0] == NULL) {
        krb5_free_data(context, scratch);
	free(sam_padata);
        return(ENOMEM);
    }

    sam_padata[0]->magic = KV5M_PA_DATA;
    sam_padata[0]->pa_type = KRB5_PADATA_SAM_RESPONSE_2;
    sam_padata[0]->length = scratch->length;
    sam_padata[0]->contents = (krb5_octet *) scratch->data;
    free(scratch);
    sam_padata[1] = NULL;

    *out_padata = sam_padata;

    return(0);
}
Example #28
0
/* Set etype info parameters in rock based on padata. */
static krb5_error_code
get_etype_info(krb5_context context, krb5_pa_data **padata,
               krb5_kdc_req *request, krb5_clpreauth_rock rock)
{
    krb5_error_code ret = 0;
    krb5_pa_data *pa;
    krb5_data d;
    krb5_etype_info etype_info = NULL, e;
    krb5_etype_info_entry *entry;
    krb5_boolean valid_found;
    int i;

    /* Find an etype-info2 or etype-info element in padata. */
    pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO2);
    if (pa != NULL) {
        d = padata2data(*pa);
        (void)decode_krb5_etype_info2(&d, &etype_info);
    } else {
        pa = krb5int_find_pa_data(context, padata, KRB5_PADATA_ETYPE_INFO);
        if (pa != NULL) {
            d = padata2data(*pa);
            (void)decode_krb5_etype_info(&d, &etype_info);
        }
    }

    /* Fall back to pw-salt/afs3-salt if no etype-info element is present. */
    if (etype_info == NULL)
        return get_salt(context, padata, request, rock);

    /* Search entries in order of the request's enctype preference. */
    entry = NULL;
    valid_found = FALSE;
    for (i = 0; i < request->nktypes && entry == NULL; i++) {
        for (e = etype_info; *e != NULL && entry == NULL; e++) {
            if ((*e)->etype == request->ktype[i])
                entry = *e;
            if (krb5_c_valid_enctype((*e)->etype))
                valid_found = TRUE;
        }
    }
    if (entry == NULL) {
        ret = (valid_found) ? KRB5_CONFIG_ETYPE_NOSUPP :
            KRB5_PROG_ETYPE_NOSUPP;
        goto cleanup;
    }

    /* Set rock fields based on the entry we selected. */
    *rock->etype = entry->etype;
    krb5_free_data_contents(context, rock->salt);
    if (entry->length != KRB5_ETYPE_NO_SALT) {
        *rock->salt = make_data(entry->salt, entry->length);
        entry->salt = NULL;
        *rock->default_salt = FALSE;
    } else {
        *rock->salt = empty_data();
        *rock->default_salt = TRUE;
    }
    krb5_free_data_contents(context, rock->s2kparams);
    *rock->s2kparams = entry->s2kparams;
    entry->s2kparams = empty_data();
    TRACE_PREAUTH_ETYPE_INFO(context, *rock->etype, rock->salt,
                             rock->s2kparams);

cleanup:
    krb5_free_etype_info(context, etype_info);
    return ret;
}