#include "k5-int.h"
static krb5_error_code
krb5_get_credentials_core(krb5_context context, krb5_flags options,
krb5_creds *in_creds, krb5_creds *mcreds,
krb5_flags *fields)
{
if (!in_creds || !in_creds->server || !in_creds->client)
return EINVAL;
memset((char *)mcreds, 0, sizeof(krb5_creds));
mcreds->magic = KV5M_CREDS;
if (in_creds->times.endtime != 0) {
mcreds->times.endtime = in_creds->times.endtime;
} else {
krb5_error_code retval;
retval = krb5_timeofday(context, &mcreds->times.endtime);
if (retval != 0) return retval;
}
mcreds->keyblock = in_creds->keyblock;
mcreds->authdata = in_creds->authdata;
mcreds->server = in_creds->server;
mcreds->client = in_creds->client;
*fields = KRB5_TC_MATCH_TIMES
| KRB5_TC_MATCH_AUTHDATA
| KRB5_TC_SUPPORTED_KTYPES;
if (mcreds->keyblock.enctype) {
krb5_enctype *ktypes;
krb5_error_code ret;
int i;
*fields |= KRB5_TC_MATCH_KTYPE;
ret = krb5_get_tgs_ktypes (context, mcreds->server, &ktypes);
for (i = 0; ktypes[i]; i++)
if (ktypes[i] == mcreds->keyblock.enctype)
break;
if (ktypes[i] == 0)
ret = KRB5_CC_NOT_KTYPE;
free (ktypes);
if (ret)
return ret;
}
if (options & KRB5_GC_USER_USER) {
*fields |= KRB5_TC_MATCH_2ND_TKT|KRB5_TC_MATCH_IS_SKEY;
mcreds->is_skey = TRUE;
mcreds->second_ticket = in_creds->second_ticket;
if (!in_creds->second_ticket.length)
return KRB5_NO_2ND_TKT;
}
return 0;
}
krb5_error_code KRB5_CALLCONV
krb5_get_credentials(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds)
{
krb5_error_code retval;
krb5_creds mcreds;
krb5_creds *ncreds;
krb5_creds **tgts;
krb5_flags fields;
int not_ktype;
retval = krb5_get_credentials_core(context, options,
in_creds,
&mcreds, &fields);
if (retval) return retval;
if ((ncreds = (krb5_creds *)malloc(sizeof(krb5_creds))) == NULL)
return ENOMEM;
memset((char *)ncreds, 0, sizeof(krb5_creds));
ncreds->magic = KV5M_CREDS;
if ((retval = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds,
ncreds))) {
krb5_xfree(ncreds);
ncreds = in_creds;
} else {
*out_creds = ncreds;
}
if ((retval != KRB5_CC_NOTFOUND && retval != KRB5_CC_NOT_KTYPE)
|| options & KRB5_GC_CACHED)
return retval;
if (retval == KRB5_CC_NOT_KTYPE)
not_ktype = 1;
else
not_ktype = 0;
if (!not_ktype) {
krb5_data neg;
retval = krb5_cc_get_config(context, ccache, in_creds->server, "negative-cache", &neg);
if (retval == 0) {
int32_t t32 = time(NULL);
int32_t rv = KRB5_CC_NOTFOUND;
if (neg.length >= 4) {
memcpy(&t32, neg.data, sizeof(t32));
t32 = ntohl(t32);
}
if (neg.length >= 8) {
memcpy(&rv, ((unsigned char *)neg.data) + 4, sizeof(rv));
retval = ntohl(rv);
}
krb5_free_data_contents(context, &neg);
if (abs(time(NULL) - t32) < 7200) {
char *str = NULL;
krb5_unparse_name(context, in_creds->server, &str);
krb5_set_error_message(context, retval,
"Negative cache rejected lookup for '%s'",
str ? str : "unknown");
free(str);
return retval;
}
}
}
retval = krb5_get_cred_from_kdc(context, ccache, ncreds, out_creds, &tgts);
if (tgts) {
register int i = 0;
krb5_error_code rv2;
while (tgts[i]) {
if ((rv2 = krb5_cc_store_cred(context, ccache, tgts[i]))) {
retval = rv2;
break;
}
i++;
}
krb5_free_tgt_creds(context, tgts);
}
if ((retval == KRB5_CC_NOTFOUND || retval == KRB5_CC_NOT_KTYPE)
&& not_ktype)
retval = KRB5_CC_NOT_KTYPE;
if (!retval) {
krb5_cc_store_cred(context, ccache, *out_creds);
} else if (!not_ktype &&
(retval == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN || retval == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN))
{
uint32_t t32[2];
krb5_data neg;
t32[0] = htonl(time(NULL));
t32[1] = htonl(retval);
neg.data = (void *)t32;
neg.length = sizeof(t32);
(void)krb5_cc_set_config(context, ccache, in_creds->server, "negative-cache", &neg);
}
return retval;
}
#define INT_GC_VALIDATE 1
#define INT_GC_RENEW 2
static krb5_error_code
krb5_get_credentials_val_renew_core(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds, int which)
{
krb5_error_code retval;
krb5_principal tmp;
krb5_creds **tgts = 0;
switch(which) {
case INT_GC_VALIDATE:
retval = krb5_get_cred_from_kdc_validate(context, ccache,
in_creds, out_creds, &tgts);
break;
case INT_GC_RENEW:
retval = krb5_get_cred_from_kdc_renew(context, ccache,
in_creds, out_creds, &tgts);
break;
default:
retval = 255;
break;
}
if (tgts) krb5_free_tgt_creds(context, tgts);
if (retval) return retval;
retval = krb5_cc_get_principal(context, ccache, &tmp);
if (retval) return retval;
retval = krb5_cc_initialize(context, ccache, tmp);
if (retval) return retval;
retval = krb5_cc_store_cred(context, ccache, *out_creds);
return retval;
}
krb5_error_code KRB5_CALLCONV
krb5_get_credentials_validate(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds)
{
return(krb5_get_credentials_val_renew_core(context, options, ccache,
in_creds, out_creds,
INT_GC_VALIDATE));
}
krb5_error_code KRB5_CALLCONV
krb5_get_credentials_renew(krb5_context context, krb5_flags options,
krb5_ccache ccache, krb5_creds *in_creds,
krb5_creds **out_creds)
{
return(krb5_get_credentials_val_renew_core(context, options, ccache,
in_creds, out_creds,
INT_GC_RENEW));
}
static krb5_error_code
krb5_validate_or_renew_creds(krb5_context context, krb5_creds *creds,
krb5_principal client, krb5_ccache ccache,
char *in_tkt_service, int validate)
{
krb5_error_code ret;
krb5_creds in_creds;
krb5_creds *out_creds = 0;
krb5_creds **tgts;
memset((char *)&in_creds, 0, sizeof(krb5_creds));
in_creds.server = NULL;
tgts = NULL;
in_creds.client = client;
if (in_tkt_service) {
if ((ret = krb5_parse_name(context, in_tkt_service, &in_creds.server)))
goto cleanup;
if (in_creds.server->realm.length < in_creds.client->realm.length)
if ((in_creds.server->realm.data =
(char *) realloc(in_creds.server->realm.data,
in_creds.client->realm.length)) == NULL) {
ret = ENOMEM;
goto cleanup;
}
in_creds.server->realm.length = in_creds.client->realm.length;
memcpy(in_creds.server->realm.data, in_creds.client->realm.data,
in_creds.client->realm.length);
} else {
if ((ret = krb5_build_principal_ext(context, &in_creds.server,
in_creds.client->realm.length,
in_creds.client->realm.data,
KRB5_TGS_NAME_SIZE,
KRB5_TGS_NAME,
in_creds.client->realm.length,
in_creds.client->realm.data,
0)))
goto cleanup;
}
if (validate)
ret = krb5_get_cred_from_kdc_validate(context, ccache,
&in_creds, &out_creds, &tgts);
else
ret = krb5_get_cred_from_kdc_renew(context, ccache,
&in_creds, &out_creds, &tgts);
if (out_creds) {
*creds = *out_creds;
krb5_xfree(out_creds);
}
cleanup:
if (in_creds.server)
krb5_free_principal(context, in_creds.server);
if (tgts)
krb5_free_tgt_creds(context, tgts);
return(ret);
}
krb5_error_code KRB5_CALLCONV
krb5_get_validated_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
{
return(krb5_validate_or_renew_creds(context, creds, client, ccache,
in_tkt_service, 1));
}
krb5_error_code KRB5_CALLCONV
krb5_get_renewed_creds(krb5_context context, krb5_creds *creds, krb5_principal client, krb5_ccache ccache, char *in_tkt_service)
{
return(krb5_validate_or_renew_creds(context, creds, client, ccache,
in_tkt_service, 0));
}