#include "k5-int.h"
#include <stdio.h>
#include "int-proto.h"
#define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
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;
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;
*tgts = NULL;
memset((char *)&tgtq, 0, sizeof(tgtq));
memset((char *)&tgt, 0, sizeof(tgt));
if ((retval = krb5_copy_principal(context, in_cred->client, &tgtq.client)))
goto cleanup;
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;
}
context->use_conf_ktypes = 1;
if ((retval = krb5_cc_retrieve_cred(context, ccache,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt))) {
if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
goto cleanup;
}
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_cc_retrieve_cred(context, ccache,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt))) {
goto cleanup;
}
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++)
;
if (!(ret_tgts = (krb5_creds **) calloc(nservers+1, sizeof(krb5_creds)))) {
retval = ENOMEM;
goto cleanup;
}
*tgts = ret_tgts;
for (top_server = tgs_list;
top_server < tgs_list + nservers;
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, 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_cc_retrieve_cred(context, ccache,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt))) {
if (retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE) {
goto cleanup;
}
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) {
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_cc_retrieve_cred(context, ccache,
KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt))) {
if (retval != KRB5_CC_NOTFOUND) {
goto cleanup;
}
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;
if ((retval = krb5_copy_creds(context, tgtr,
&ret_tgts[ntgts]))) {
goto cleanup;
}
krb5_free_creds(context, tgtr);
tgtr = NULL;
tgt = *ret_tgts[ntgts++];
}
break;
}
if (next_server == top_server) {
goto cleanup;
}
continue;
}
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++];
if (!*next_server++) break;
}
}
}
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:
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);
}
krb5_error_code
krb5_get_cred_from_kdc(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts)
{
return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts,
0);
}
krb5_error_code
krb5_get_cred_from_kdc_validate(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts)
{
return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts,
KDC_OPT_VALIDATE);
}
krb5_error_code
krb5_get_cred_from_kdc_renew(krb5_context context, krb5_ccache ccache, krb5_creds *in_cred, krb5_creds **out_cred, krb5_creds ***tgts)
{
return krb5_get_cred_from_kdc_opt(context, ccache, in_cred, out_cred, tgts,
KDC_OPT_RENEW);
}