#include "k5-int.h"
#include "auth_con.h"
#include <locale.h>
#include <syslog.h>
static krb5_error_code decrypt_authenticator
(krb5_context, const krb5_ap_req *, krb5_authenticator **,
int);
static krb5_error_code
krb5_rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req, krb5_keytab keytab)
{
krb5_error_code retval;
krb5_enctype enctype;
krb5_keytab_entry ktent;
enctype = req->ticket->enc_part.enctype;
memset(&ktent, 0, sizeof(krb5_keytab_entry));
if ((retval = krb5_kt_get_entry(context, keytab, req->ticket->server,
req->ticket->enc_part.kvno,
enctype, &ktent)))
return retval;
ktent.key.enctype = enctype;
retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
if (retval == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
krb5_principal princ = (krb5_principal) req->ticket->server;
char *s_name = NULL;
int kret = krb5_unparse_name(context, princ, &s_name);
if (kret == 0) {
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"AP Request ticket decrypt fail for principal '%s' (kvno=%d, enctype=%d)"),
s_name,
req->ticket->enc_part.kvno,
enctype);
krb5_free_unparsed_name(context, s_name);
}
}
(void) krb5_kt_free_entry(context, &ktent);
return retval;
}
static krb5_error_code
krb5int_check_clockskew2(krb5_context context,
krb5_timestamp date,
krb5_timestamp *ret_skew)
{
krb5_timestamp currenttime, skew;
krb5_error_code retval;
retval = krb5_timeofday(context, ¤ttime);
if (retval)
return retval;
skew = labs((date)-currenttime);
if (!(skew < context->clockskew)) {
*ret_skew = skew;
return KRB5KRB_AP_ERR_SKEW;
}
return 0;
}
static krb5_error_code
krb5_rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
const krb5_ap_req *req, krb5_const_principal server,
krb5_keytab keytab, krb5_flags *ap_req_options,
krb5_ticket **ticket, int check_valid_flag)
{
krb5_error_code retval = 0;
krb5_principal_data princ_data;
krb5_timestamp skew = 0;
req->ticket->enc_part2 == NULL;
if (server && krb5_is_referral_realm(&server->realm)) {
char *realm;
princ_data = *server;
server = &princ_data;
retval = krb5_get_default_realm(context, &realm);
if (retval)
return retval;
princ_data.realm.data = realm;
princ_data.realm.length = strlen(realm);
}
if (server && !krb5_principal_compare(context, server, req->ticket->server)) {
char *found_name = 0, *wanted_name = 0;
if (krb5_unparse_name(context, server, &wanted_name) == 0
&& krb5_unparse_name(context, req->ticket->server, &found_name) == 0)
krb5_set_error_message(context, KRB5KRB_AP_WRONG_PRINC,
dgettext(TEXT_DOMAIN,
"Wrong principal in request (found %s, wanted %s)"),
found_name, wanted_name);
krb5_free_unparsed_name(context, wanted_name);
krb5_free_unparsed_name(context, found_name);
retval = KRB5KRB_AP_WRONG_PRINC;
goto cleanup;
}
if ((*auth_context)->keyblock) {
if ((retval = krb5_decrypt_tkt_part(context, (*auth_context)->keyblock,
req->ticket)))
goto cleanup;
krb5_free_keyblock(context, (*auth_context)->keyblock);
(*auth_context)->keyblock = NULL;
} else {
if ((retval = krb5_rd_req_decrypt_tkt_part(context, req, keytab)))
goto cleanup;
}
if ((retval = decrypt_authenticator(context, req,
&((*auth_context)->authentp),
check_valid_flag)))
goto cleanup;
if (!krb5_principal_compare(context, (*auth_context)->authentp->client,
req->ticket->enc_part2->client)) {
retval = KRB5KRB_AP_ERR_BADMATCH;
goto cleanup;
}
if ((*auth_context)->remote_addr &&
!krb5_address_search(context, (*auth_context)->remote_addr,
req->ticket->enc_part2->caddrs)) {
retval = KRB5KRB_AP_ERR_BADADDR;
goto cleanup;
}
#if defined(_SINGLE_HOP_ONLY)
{
krb5_transited *trans = &(req->ticket->enc_part2->transited);
if (trans->tr_contents.data && trans->tr_contents.data[0])
retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
}
#elif defined(_NO_CROSS_REALM)
{
char * lrealm;
krb5_data * realm;
krb5_transited * trans;
realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
trans = &(req->ticket->enc_part2->transited);
krb5_get_default_realm(context, &lrealm);
if ((trans->tr_contents.data && trans->tr_contents.data[0]) ||
strlen(lrealm) != realm->length ||
memcmp(lrealm, realm->data, strlen(lrealm))) {
retval = KRB5KRB_AP_ERR_ILL_CR_TKT;
}
free(lrealm);
}
#else
{
krb5_data * realm;
krb5_transited * trans;
realm = krb5_princ_realm(context, req->ticket->enc_part2->client);
trans = &(req->ticket->enc_part2->transited);
if (trans->tr_contents.data && trans->tr_contents.data[0]) {
retval = krb5_check_transited_list(context, &(trans->tr_contents),
realm,
krb5_princ_realm (context,
server));
}
}
#endif
if (retval) goto cleanup;
if ((*auth_context)->rcache) {
krb5_donot_replay rep;
krb5_tkt_authent tktauthent;
tktauthent.ticket = req->ticket;
tktauthent.authenticator = (*auth_context)->authentp;
if (!(retval = krb5_auth_to_rep(context, &tktauthent, &rep))) {
retval = krb5_rc_store(context, (*auth_context)->rcache, &rep);
krb5_xfree(rep.server);
krb5_xfree(rep.client);
}
if (retval == KRB5KRB_AP_ERR_SKEW)
goto err_skew;
if (retval)
goto cleanup;
}
retval = krb5_validate_times(context, &req->ticket->enc_part2->times);
if (retval != 0)
goto cleanup;
err_skew:
if ((retval = krb5int_check_clockskew2(context,
(*auth_context)->authentp->ctime,
&skew))) {
char *s_name = NULL;
char *c_name = NULL;
krb5_error_code serr, cerr;
serr = krb5_unparse_name(context, req->ticket->server, &s_name);
cerr = krb5_unparse_name(context, req->ticket->enc_part2->client,
&c_name);
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Clock skew too great: client '%s' AP request with ticket for '%s'. Skew is %dm (allowable %dm)."),
cerr == 0 ? c_name : "unknown",
serr == 0 ? s_name : "unknown",
skew > 0 ? skew/60 : 0,
context->clockskew > 0 ? context->clockskew/60 : 0);
if (s_name)
krb5_free_unparsed_name(context, s_name);
if (c_name)
krb5_free_unparsed_name(context, c_name);
goto cleanup;
}
if (check_valid_flag) {
if (req->ticket->enc_part2->flags & TKT_FLG_INVALID) {
char *s_name = NULL;
int err = krb5_unparse_name(context, req->ticket->server, &s_name);
retval = KRB5KRB_AP_ERR_TKT_INVALID;
if (!err) {
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Ticket has invalid flag set for server '%s'"),
s_name);
krb5_free_unparsed_name(context, s_name);
}
goto cleanup;
}
}
if ((*auth_context)->auth_context_flags & KRB5_AUTH_CONTEXT_PERMIT_ALL) {
;
} else if ((*auth_context)->permitted_etypes == NULL) {
int etype;
if ((!krb5_is_permitted_enctype(context,
etype = req->ticket->enc_part.enctype)) ||
(!krb5_is_permitted_enctype(context,
etype = req->ticket->enc_part2->session->enctype)) ||
(((*auth_context)->authentp->subkey) &&
!krb5_is_permitted_enctype(context,
etype = (*auth_context)->authentp->subkey->enctype))) {
char enctype_name[30];
retval = KRB5_NOPERM_ETYPE;
if (krb5_enctype_to_string(etype, enctype_name, sizeof(enctype_name)) == 0)
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Encryption type %s not permitted"),
enctype_name);
goto cleanup;
}
} else {
int i;
for (i=0; (*auth_context)->permitted_etypes[i]; i++)
if ((*auth_context)->permitted_etypes[i] ==
req->ticket->enc_part.enctype)
break;
if (!(*auth_context)->permitted_etypes[i]) {
char enctype_name[30];
retval = KRB5_NOPERM_ETYPE;
if (krb5_enctype_to_string(req->ticket->enc_part.enctype,
enctype_name, sizeof(enctype_name)) == 0)
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Encryption type %s not permitted"),
enctype_name);
goto cleanup;
}
for (i=0; (*auth_context)->permitted_etypes[i]; i++)
if ((*auth_context)->permitted_etypes[i] ==
req->ticket->enc_part2->session->enctype)
break;
if (!(*auth_context)->permitted_etypes[i]) {
char enctype_name[30];
retval = KRB5_NOPERM_ETYPE;
if (krb5_enctype_to_string(req->ticket->enc_part2->session->enctype,
enctype_name, sizeof(enctype_name)) == 0)
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Encryption type %s not permitted"),
enctype_name);
goto cleanup;
}
if ((*auth_context)->authentp->subkey) {
for (i=0; (*auth_context)->permitted_etypes[i]; i++)
if ((*auth_context)->permitted_etypes[i] ==
(*auth_context)->authentp->subkey->enctype)
break;
if (!(*auth_context)->permitted_etypes[i]) {
char enctype_name[30];
retval = KRB5_NOPERM_ETYPE;
if (krb5_enctype_to_string((*auth_context)->authentp->subkey->enctype,
enctype_name,
sizeof(enctype_name)) == 0)
krb5_set_error_message(context, retval,
dgettext(TEXT_DOMAIN,
"Encryption type %s not permitted"),
enctype_name);
goto cleanup;
}
}
}
(*auth_context)->remote_seq_number = (*auth_context)->authentp->seq_number;
if ((*auth_context)->authentp->subkey) {
if ((*auth_context)->recv_subkey != NULL) {
krb5_free_keyblock(context, (*auth_context)->recv_subkey);
(*auth_context)->recv_subkey = NULL;
}
if ((retval = krb5_copy_keyblock(context,
(*auth_context)->authentp->subkey,
&((*auth_context)->recv_subkey))))
goto cleanup;
if ((*auth_context)->send_subkey != NULL) {
krb5_free_keyblock(context, (*auth_context)->send_subkey);
(*auth_context)->send_subkey = NULL;
}
retval = krb5_copy_keyblock(context, (*auth_context)->authentp->subkey,
&((*auth_context)->send_subkey));
if (retval) {
krb5_free_keyblock(context, (*auth_context)->recv_subkey);
(*auth_context)->recv_subkey = NULL;
goto cleanup;
}
} else {
(*auth_context)->recv_subkey = 0;
(*auth_context)->send_subkey = 0;
}
if ((*auth_context)->keyblock != NULL) {
krb5_free_keyblock(context, (*auth_context)->keyblock);
(*auth_context)->keyblock = NULL;
}
if ((retval = krb5_copy_keyblock(context, req->ticket->enc_part2->session,
&((*auth_context)->keyblock))))
goto cleanup;
if ((!(req->ap_options & AP_OPTS_MUTUAL_REQUIRED)) &&
(*auth_context)->remote_seq_number) {
(*auth_context)->local_seq_number ^=
(*auth_context)->remote_seq_number;
}
if (ticket)
if ((retval = krb5_copy_ticket(context, req->ticket, ticket)))
goto cleanup;
if (ap_req_options)
*ap_req_options = req->ap_options;
retval = 0;
cleanup:
if (server == &princ_data)
krb5_free_default_realm(context, princ_data.realm.data);
if (retval) {
if (req->ticket->enc_part2)
krb5_free_enc_tkt_part(context, req->ticket->enc_part2);
req->ticket->enc_part2 = NULL;
}
return retval;
}
krb5_error_code
krb5_rd_req_decoded(krb5_context context, krb5_auth_context *auth_context,
const krb5_ap_req *req, krb5_const_principal server,
krb5_keytab keytab, krb5_flags *ap_req_options,
krb5_ticket **ticket)
{
krb5_error_code retval;
retval = krb5_rd_req_decoded_opt(context, auth_context,
req, server, keytab,
ap_req_options, ticket,
1);
return retval;
}
krb5_error_code
krb5_rd_req_decoded_anyflag(krb5_context context,
krb5_auth_context *auth_context,
const krb5_ap_req *req,
krb5_const_principal server, krb5_keytab keytab,
krb5_flags *ap_req_options, krb5_ticket **ticket)
{
krb5_error_code retval;
retval = krb5_rd_req_decoded_opt(context, auth_context,
req, server, keytab,
ap_req_options, ticket,
0);
return retval;
}
static krb5_error_code
decrypt_authenticator(krb5_context context, const krb5_ap_req *request,
krb5_authenticator **authpp, int is_ap_req)
{
krb5_authenticator *local_auth;
krb5_error_code retval;
krb5_data scratch;
krb5_keyblock *sesskey;
sesskey = request->ticket->enc_part2->session;
scratch.length = request->authenticator.ciphertext.length;
if (!(scratch.data = malloc(scratch.length)))
return(ENOMEM);
if ((retval = krb5_c_decrypt(context, sesskey,
is_ap_req?KRB5_KEYUSAGE_AP_REQ_AUTH:
KRB5_KEYUSAGE_TGS_REQ_AUTH, 0,
&request->authenticator, &scratch))) {
free(scratch.data);
return(retval);
}
#define clean_scratch() {memset(scratch.data, 0, scratch.length); \
free(scratch.data);}
if (!(retval = decode_krb5_authenticator(&scratch, &local_auth))) {
*authpp = local_auth;
}
clean_scratch();
return retval;
}
krb5_error_code
krb5int_check_clockskew(krb5_context context, krb5_timestamp date)
{
krb5_timestamp currenttime;
krb5_error_code retval;
retval = krb5_timeofday(context, ¤ttime);
if (retval)
return retval;
if (!(labs((date)-currenttime) < context->clockskew))
return KRB5KRB_AP_ERR_SKEW;
return 0;
}