Example #1
0
krb5_error_code
krb5int_decode_tgs_rep(krb5_context context, krb5_data *enc_rep, const krb5_keyblock *key,
                       krb5_keyusage usage, krb5_kdc_rep **dec_rep)
{
    krb5_error_code retval;
    krb5_kdc_rep *local_dec_rep;

    if (krb5_is_as_rep(enc_rep)) {
        retval = decode_krb5_as_rep(enc_rep, &local_dec_rep);
    } else if (krb5_is_tgs_rep(enc_rep)) {
        retval = decode_krb5_tgs_rep(enc_rep, &local_dec_rep);
    } else {
        return KRB5KRB_AP_ERR_MSG_TYPE;
    }

    if (retval)
        return retval;

    if ((retval = krb5_kdc_rep_decrypt_proc(context, key, &usage,
                                            local_dec_rep)))
        krb5_free_kdc_rep(context, local_dec_rep);
    else
        *dec_rep = local_dec_rep;
    return(retval);
}
Example #2
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 #3
0
krb5_error_code
krb5int_process_tgs_reply(krb5_context context,
                          struct krb5int_fast_request_state *fast_state,
                          krb5_data *response_data,
                          krb5_creds *tkt,
                          krb5_flags kdcoptions,
                          krb5_address *const *address,
                          krb5_pa_data **in_padata,
                          krb5_creds *in_cred,
                          krb5_timestamp timestamp,
                          krb5_int32 nonce,
                          krb5_keyblock *subkey,
                          krb5_pa_data ***out_padata,
                          krb5_pa_data ***out_enc_padata,
                          krb5_creds **out_cred)
{
    krb5_error_code retval;
    krb5_kdc_rep *dec_rep = NULL;
    krb5_error *err_reply = NULL;
    krb5_boolean s4u2self;

    s4u2self = krb5int_find_pa_data(context, in_padata,
                                    KRB5_PADATA_S4U_X509_USER) ||
        krb5int_find_pa_data(context, in_padata,
                             KRB5_PADATA_FOR_USER);

    if (krb5_is_krb_error(response_data)) {
        retval = decode_krb5_error(response_data, &err_reply);
        if (retval != 0)
            goto cleanup;
        retval = krb5int_fast_process_error(context, fast_state,
                                            &err_reply, NULL, NULL);
        if (retval)
            goto cleanup;
        retval = (krb5_error_code) err_reply->error + ERROR_TABLE_BASE_krb5;
        if (err_reply->text.length > 0) {
            switch (err_reply->error) {
            case KRB_ERR_GENERIC:
                k5_setmsg(context, retval,
                          _("KDC returned error string: %.*s"),
                          err_reply->text.length, err_reply->text.data);
                break;
            case KDC_ERR_S_PRINCIPAL_UNKNOWN:
            {
                char *s_name;
                if (err_reply->server &&
                    krb5_unparse_name(context, err_reply->server, &s_name) == 0) {
                    k5_setmsg(context, retval,
                              _("Server %s not found in Kerberos database"),
                              s_name);
                    krb5_free_unparsed_name(context, s_name);
                } else
                    /* In case there's a stale S_PRINCIPAL_UNKNOWN
                       report already noted.  */
                    krb5_clear_error_message(context);
            }
            break;
            }
        }
        krb5_free_error(context, err_reply);
        goto cleanup;
    } else if (!krb5_is_tgs_rep(response_data)) {
        retval = KRB5KRB_AP_ERR_MSG_TYPE;
        goto cleanup;
    }

    /* Unfortunately, Heimdal at least up through 1.2  encrypts using
       the session key not the subsession key.  So we try both. */
    retval = krb5int_decode_tgs_rep(context, fast_state, response_data, subkey,
                                    KRB5_KEYUSAGE_TGS_REP_ENCPART_SUBKEY,
                                    &dec_rep);
    if (retval) {
        TRACE_TGS_REPLY_DECODE_SESSION(context, &tkt->keyblock);
        if ((krb5int_decode_tgs_rep(context, fast_state, response_data,
                                    &tkt->keyblock,
                                    KRB5_KEYUSAGE_TGS_REP_ENCPART_SESSKEY, &dec_rep)) == 0)
            retval = 0;
        else
            goto cleanup;
    }

    if (dec_rep->msg_type != KRB5_TGS_REP) {
        retval = KRB5KRB_AP_ERR_MSG_TYPE;
        goto cleanup;
    }

    /*
     * Don't trust the ok-as-delegate flag from foreign KDCs unless the
     * cross-realm TGT also had the ok-as-delegate flag set.
     */
    if (!tgt_is_local_realm(tkt)
        && !(tkt->ticket_flags & TKT_FLG_OK_AS_DELEGATE))
        dec_rep->enc_part2->flags &= ~TKT_FLG_OK_AS_DELEGATE;

    /* make sure the response hasn't been tampered with..... */
    retval = 0;

    if (s4u2self && !IS_TGS_PRINC(dec_rep->ticket->server)) {
        /* Final hop, check whether KDC supports S4U2Self */
        if (krb5_principal_compare(context, dec_rep->client, in_cred->server))
            retval = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
    } else if ((kdcoptions & KDC_OPT_CNAME_IN_ADDL_TKT) == 0) {
        /* XXX for constrained delegation this check must be performed by caller
         * as we don't have access to the key to decrypt the evidence ticket.
         */
        if (!krb5_principal_compare(context, dec_rep->client, tkt->client))
            retval = KRB5_KDCREP_MODIFIED;
    }

    if (retval == 0)
        retval = check_reply_server(context, kdcoptions, in_cred, dec_rep);

    if (dec_rep->enc_part2->nonce != nonce)
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_POSTDATED) &&
        (in_cred->times.starttime != 0) &&
        (in_cred->times.starttime != dec_rep->enc_part2->times.starttime))
        retval = KRB5_KDCREP_MODIFIED;

    if ((in_cred->times.endtime != 0) &&
        (dec_rep->enc_part2->times.endtime > in_cred->times.endtime))
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_RENEWABLE) &&
        (in_cred->times.renew_till != 0) &&
        (dec_rep->enc_part2->times.renew_till > in_cred->times.renew_till))
        retval = KRB5_KDCREP_MODIFIED;

    if ((kdcoptions & KDC_OPT_RENEWABLE_OK) &&
        (dec_rep->enc_part2->flags & KDC_OPT_RENEWABLE) &&
        (in_cred->times.endtime != 0) &&
        (dec_rep->enc_part2->times.renew_till > in_cred->times.endtime))
        retval = KRB5_KDCREP_MODIFIED;

    if (retval != 0)
        goto cleanup;

    if (!in_cred->times.starttime &&
        !in_clock_skew(dec_rep->enc_part2->times.starttime,
                       timestamp)) {
        retval = KRB5_KDCREP_SKEW;
        goto cleanup;
    }

    if (out_padata != NULL) {
        *out_padata = dec_rep->padata;
        dec_rep->padata = NULL;
    }
    if (out_enc_padata != NULL) {
        *out_enc_padata = dec_rep->enc_part2->enc_padata;
        dec_rep->enc_part2->enc_padata = NULL;
    }

    retval = kdcrep2creds(context, dec_rep, address,
                          &in_cred->second_ticket, out_cred);
    if (retval != 0)
        goto cleanup;

cleanup:
    if (dec_rep != NULL) {
        memset(dec_rep->enc_part2->session->contents, 0,
               dec_rep->enc_part2->session->length);
        krb5_free_kdc_rep(context, dec_rep);
    }

    return retval;
}