Implement forwarding of leaf TGTs to selected realms.

Refactor and enhance TGT forwarding to allow forwarding of leaf
(destination) TGTs for selected destination realms.

Enhance kinit(1) to renew non-origin realm tickets

Document delegate-destination-tgt

Use the newly implemented _krb5_mk_1cred().
This commit is contained in:
Viktor Dukhovni
2019-10-29 20:02:14 +00:00
committed by Nico Williams
parent d81118cc1f
commit 5bbe7c8dc6
6 changed files with 326 additions and 310 deletions

View File

@@ -115,10 +115,18 @@ like
.It Fl p , Fl Fl proxiable
Request tickets with the proxiable flag set.
.It Fl R , Fl Fl renew
Try to renew ticket.
Try to renew a ticket.
The ticket must have the
.Sq renewable
flag set, and must not be expired.
flag set, and must not be expired. If the
.Oo Fl S Ar principal Oc
option is specified, the ticket for the indicated service is renewed.
If no service is explicitly specified, an attempt is made to renew the
TGT for the client realm. If no TGT for the client realm is found in the
credential cache, an attempt is made to renew the TGT for the defaualt
realm (if that is found in the credential cache), or else the first
TGT found. This makes it easier for users to renew forwarded tickets
that are not issued by the origin realm.
.It Fl Fl renewable
The same as
.Fl Fl renewable-life ,

View File

@@ -61,6 +61,7 @@ int anonymous_flag = 0;
char *lifetime = NULL;
char *renew_life = NULL;
char *server_str = NULL;
static krb5_principal tgs_service;
char *cred_cache = NULL;
char *start_str = NULL;
static int switch_cache_flags = 1;
@@ -217,19 +218,125 @@ usage(int ret)
exit(ret);
}
static krb5_error_code
tgs_principal(krb5_context context,
krb5_ccache cache,
krb5_principal client,
krb5_const_realm tgs_realm,
krb5_principal *out_princ)
{
krb5_error_code ret;
krb5_principal tgs_princ;
krb5_creds creds;
krb5_creds *tick;
krb5_flags options;
ret = krb5_make_principal(context, &tgs_princ, tgs_realm,
KRB5_TGS_NAME, tgs_realm, NULL);
if (ret)
return ret;
/*
* Don't fail-over to a different realm just because a TGT expired
*/
options = KRB5_GC_CACHED | KRB5_GC_EXPIRED_OK;
memset(&creds, 0, sizeof(creds));
creds.client = client;
creds.server = tgs_princ;
ret = krb5_get_credentials(context, options, cache, &creds, &tick);
if (ret == 0) {
krb5_free_creds(context, tick);
*out_princ = tgs_princ;
} else {
krb5_free_principal(context, tgs_princ);
}
return ret;
}
/*
* Try TGS specified with '-S',
* then TGS of client realm,
* then if fallback is FALSE: fail,
* otherwise try TGS of default realm,
* and finally first TGT in ccache.
*/
static krb5_error_code
get_server(krb5_context context,
krb5_ccache cache,
krb5_principal client,
const char *server,
krb5_boolean fallback,
krb5_principal *princ)
{
krb5_error_code ret = 0;
krb5_const_realm realm;
if (server)
return krb5_parse_name(context, server, princ);
krb5_realm def_realm;
krb5_cc_cursor cursor;
krb5_creds creds;
const char *pcomp;
if (tgs_service)
goto done;
if (server) {
ret = krb5_parse_name(context, server, &tgs_service);
goto done;
}
/* Try the client realm first */
realm = krb5_principal_get_realm(context, client);
return krb5_make_principal(context, princ, realm,
KRB5_TGS_NAME, realm, NULL);
ret = tgs_principal(context, cache, client, realm, &tgs_service);
if (ret == 0 || ret != KRB5_CC_NOTFOUND)
goto done;
if (!fallback)
return ret;
/* Next try the default realm */
ret = krb5_get_default_realm(context, &def_realm);
if (ret)
return ret;
ret = tgs_principal(context, cache, client, def_realm, &tgs_service);
free(def_realm);
if (ret == 0 || ret != KRB5_CC_NOTFOUND)
goto done;
/* Finally try the first TGT with instance == realm in the cache */
ret = krb5_cc_start_seq_get(context, cache, &cursor);
if (ret)
return ret;
for (/**/; ret == 0; krb5_free_cred_contents (context, &creds)) {
ret = krb5_cc_next_cred(context, cache, &cursor, &creds);
if (ret)
break;
if (creds.server->name.name_string.len != 2)
continue;
pcomp = krb5_principal_get_comp_string(context, creds.server, 0);
if (strcmp(pcomp, KRB5_TGS_NAME) != 0)
continue;
realm = krb5_principal_get_realm(context, creds.server);
pcomp = krb5_principal_get_comp_string(context, creds.server, 1);
if (strcmp(realm, pcomp) != 0)
continue;
ret = krb5_copy_principal(context, creds.server, &tgs_service);
break;
}
if (ret == KRB5_CC_END) {
ret = KRB5_CC_NOTFOUND;
krb5_set_error_message(context, ret,
N_("Credential cache contains no TGTs", ""));
}
krb5_cc_end_seq_get(context, cache, &cursor);
done:
if (!ret)
ret = krb5_copy_principal(context, tgs_service, princ);
return ret;
}
static krb5_error_code
@@ -333,7 +440,7 @@ renew_validate(krb5_context context,
KRB5_ANON_MATCH_UNAUTHENTICATED))
ret = get_anon_pkinit_tgs_name(context, cache, &in.server);
else
ret = get_server(context, in.client, server, &in.server);
ret = get_server(context, cache, in.client, server, TRUE, &in.server);
if (ret) {
krb5_warn(context, ret, "get_server");
goto out;
@@ -855,7 +962,10 @@ ticket_lifetime(krb5_context context, krb5_ccache cache, krb5_principal client,
krb5_warn(context, ret, "krb5_cc_get_principal");
return 0;
}
ret = get_server(context, in_cred.client, server, &in_cred.server);
/* Determine TGS principal without fallback */
ret = get_server(context, cache, in_cred.client, server, FALSE,
&in_cred.server);
if (ret) {
krb5_free_principal(context, in_cred.client);
krb5_warn(context, ret, "get_server");