Add --hostbased and --canonical kgetcred options

This commit is contained in:
Nicolas Williams
2015-03-25 11:16:37 -05:00
parent 0306d70a91
commit 50615d2a37
2 changed files with 160 additions and 37 deletions

View File

@@ -40,6 +40,7 @@
.Sh SYNOPSIS
.Nm
.Op Fl Fl canonicalize
.Op Fl Fl canonical
.Oo Fl c cache \*(Ba Xo
.Fl Fl cache= Ns Ar cache
.Xc
@@ -49,6 +50,10 @@
.Xc
.Oc
.Op Fl Fl debug
.Oo Fl H \*(Ba Xo
.Fl Fl hostbased
.Xc
.Oc
.Op Fl name-type= Ns Ar name-type
.Op Fl Fl no-transit-check
.Op Fl Fl version
@@ -78,6 +83,9 @@ requests that the KDC canonicalize the principal.
turns off local canonicalization of the principal.
.It Fl Fl name-type= Ns Ar name-type
the name-type to use when parsing the principal name.
.It Fl Fl hostbased
is short for
.Fl Fl name-type=srv_hst .
.It Fl c Ar cache , Fl Fl cache= Ns Ar cache
the credential cache to use.
.It Fl Fl delegation-credential-cache= Ns Ar cache
@@ -92,6 +100,29 @@ enables debug output to stderr.
.It Fl Fl version
.It Fl Fl help
.El
.Pp
If the
.Fl Fl canonical
option is used, then no further canonicalization should be done locally
by the client (for example, DNS), but if
.Fl Fl canonicalize
is used, then the client will ask that the KDC canonicalize the name.
.Pp
If the
.Fl Fl canonicalize
option is used with
.Fl Fl hostbased
a host-based name-type, and
.Fl Fl canonical
is not used, then the hostname will be canonicalized according to the
name canonicalization rules in
.Va krb5.conf .
.Pp
GSS-API initiator applications with host-based services will get the
same behavior as using the
.Fl Fl canonicalize
.Fl Fl hostbased
options here.
.Sh SEE ALSO
.Xr kinit 1 ,
.Xr klist 1 ,

View File

@@ -40,6 +40,8 @@ static char *etype_str;
static int transit_flag = 1;
static int forwardable_flag;
static int canonicalize_flag;
static int is_hostbased_flag;
static int is_canonical_flag;
static char *impersonate_str;
static char *nametype_str;
static int debug;
@@ -54,22 +56,27 @@ struct getargs args[] = {
{ "delegation-credential-cache",0,arg_string, &delegation_cred_str,
NP_("where to find the ticket use for delegation", ""), "cache"},
{ "canonicalize", 0, arg_flag, &canonicalize_flag,
NP_("canonicalize the principal", ""), NULL },
{ "forwardable", 0, arg_flag, &forwardable_flag,
NP_("canonicalize the principal (chase referrals)", ""), NULL },
{ "canonical", 0, arg_flag, &is_canonical_flag,
NP_("the name components are canonical", ""), NULL },
{ "forwardable", 0, arg_flag, &forwardable_flag,
NP_("forwardable ticket requested", ""), NULL},
{ "transit-check", 0, arg_negative_flag, &transit_flag, NULL, NULL },
{ "enctype", 'e', arg_string, &etype_str,
NP_("encryption type to use", ""), "enctype"},
{ "impersonate", 0, arg_string, &impersonate_str,
NP_("client to impersonate", ""), "principal"},
{ "name-type", 0, arg_string, &nametype_str, NULL, NULL },
{ "name-type", 0, arg_string, &nametype_str,
NP_("Kerberos name type", ""), NULL },
{ "hostbased", 'H', arg_flag, &is_hostbased_flag,
NP_("indicate that the name is a host-based service name", ""), NULL },
{ "debug", 0, arg_flag, &debug, NULL, NULL },
{ "version", 0, arg_flag, &version_flag, NULL, NULL },
{ "help", 0, arg_flag, &help_flag, NULL, NULL }
};
static void
usage (int ret)
usage(int ret)
{
arg_printusage(args,
sizeof(args)/sizeof(*args),
@@ -86,6 +93,7 @@ main(int argc, char **argv)
krb5_ccache cache;
krb5_creds *out;
int optidx = 0;
int32_t nametype = KRB5_NT_UNKNOWN;
krb5_get_creds_opt opt;
krb5_principal server = NULL;
krb5_principal impersonate;
@@ -167,88 +175,172 @@ main(int argc, char **argv)
krb5_cc_clear_mcred(&mc);
ret = krb5_cc_get_principal(context, cache, &mc.server);
if (ret)
krb5_err (context, 1, ret, "krb5_cc_get_principal");
krb5_err(context, 1, ret, "krb5_cc_get_principal");
ret = krb5_cc_resolve(context, delegation_cred_str, &id);
if(ret)
krb5_err (context, 1, ret, "krb5_cc_resolve");
krb5_err(context, 1, ret, "krb5_cc_resolve");
ret = krb5_cc_retrieve_cred(context, id, 0, &mc, &c);
if(ret)
krb5_err (context, 1, ret, "krb5_cc_retrieve_cred");
krb5_err(context, 1, ret, "krb5_cc_retrieve_cred");
ret = decode_Ticket(c.ticket.data, c.ticket.length, &ticket, NULL);
if (ret) {
krb5_clear_error_message(context);
krb5_err (context, 1, ret, "decode_Ticket");
krb5_err(context, 1, ret, "decode_Ticket");
}
krb5_free_cred_contents(context, &c);
ret = krb5_get_creds_opt_set_ticket(context, opt, &ticket);
if(ret)
krb5_err (context, 1, ret, "krb5_get_creds_opt_set_ticket");
krb5_err(context, 1, ret, "krb5_get_creds_opt_set_ticket");
free_Ticket(&ticket);
krb5_cc_close (context, id);
krb5_cc_close(context, id);
krb5_free_principal(context, mc.server);
krb5_get_creds_opt_add_options(context, opt,
KRB5_GC_CONSTRAINED_DELEGATION);
}
if (nametype_str) {
int32_t nametype;
char *sname = NULL;
char *hname = NULL;
if (nametype_str != NULL) {
ret = krb5_parse_nametype(context, nametype_str, &nametype);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_nametype");
}
ret = krb5_parse_nametype(context, nametype_str, &nametype);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_nametype");
if (nametype == KRB5_NT_SRV_HST ||
nametype == KRB5_NT_SRV_HST_NEEDS_CANON)
is_hostbased_flag = 1;
if (is_hostbased_flag) {
const char *sname = NULL;
const char *hname = NULL;
if (nametype_str != NULL &&
nametype != KRB5_NT_SRV_HST &&
nametype != KRB5_NT_SRV_HST_NEEDS_CANON)
krb5_errx(context, 1, "--hostbased not compatible with "
"non-hostbased --name-type");
if (is_canonical_flag)
nametype = KRB5_NT_SRV_HST;
else
nametype = KRB5_NT_SRV_HST_NEEDS_CANON;
/*
* Host-based service names can have more than one component.
*
* RFC5179 did not, but should have, assign a Kerberos name-type
* corresponding to GSS_C_NT_DOMAINBASED. But it's basically a
* host-based service name type with one additional component.
*
* So that's how we're treating host-based service names here:
* two or more components.
*/
if (argc == 0) {
usage(1);
} else if (argc == 1) {
krb5_principal server2;
/*
* In this case the one argument is a principal name, not the
* service name.
*
* We parse the argument as a principal name, extract the service
* and hostname components, use krb5_sname_to_principal(), then
* extract the service and hostname components from that.
*/
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]);
sname = krb5_principal_get_comp_string(context, server, 0);
/*
* If a single-component principal name is given, then we'll
* default the hostname, as krb5_principal_get_comp_string()
* returns NULL in this case.
*/
hname = krb5_principal_get_comp_string(context, server, 1);
if (nametype == KRB5_NT_SRV_HST && argc == 2) {
sname = argv[0];
hname = argv[1];
ret = krb5_sname_to_principal(context, hname, sname,
KRB5_NT_SRV_HST, &server);
KRB5_NT_SRV_HST, &server2);
sname = krb5_principal_get_comp_string(context, server2, 0);
hname = krb5_principal_get_comp_string(context, server2, 1);
/*
* Modify the original with the new sname/hname. This way we
* retain any additional principal name components from the given
* principal name.
*
* The name-type is set further below.
*/
ret = krb5_principal_set_comp_string(context, server, 0, sname);
if (ret)
krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]);
ret = krb5_principal_set_comp_string(context, server, 1, hname);
if (ret)
krb5_err(context, 1, ret, "krb5_principal_set_comp_string %s", argv[0]);
krb5_free_principal(context, server2);
} else {
size_t i;
/*
* In this case the arguments are principal name components.
*
* The service and hostname components can be defaulted by passing
* empty strings.
*/
sname = argv[0];
if (*sname == '\0')
sname = NULL;
hname = argv[1];
if (hname == NULL || *hname == '\0')
hname = NULL;
ret = krb5_sname_to_principal(context, hname, sname,
KRB5_NT_SRV_HST, &server);
if (ret)
krb5_err(context, 1, ret, "krb5_sname_to_principal %s/%s",
(sname && *sname) ? sname : "<default>",
(hname && *hname) ? hname : "<default>");
} else {
if (argc != 1)
usage(1);
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
server->name.name_type = (NAME_TYPE)nametype;
krb5_err(context, 1, ret, "krb5_sname_to_principal");
for (i = 2; i < argc; i++) {
ret = krb5_principal_set_comp_string(context, server, i, argv[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_principal_set_comp_string");
}
}
} else if (argc == 1) {
ret = krb5_parse_name(context, argv[0], &server);
if (ret)
krb5_err (context, 1, ret, "krb5_parse_name %s", argv[0]);
krb5_err(context, 1, ret, "krb5_parse_name %s", argv[0]);
} else {
usage(1);
}
if (nametype != KRB5_NT_UNKNOWN)
server->name.name_type = (NAME_TYPE)nametype;
ret = krb5_get_creds(context, opt, cache, server, &out);
if (ret)
krb5_err (context, 1, ret, "krb5_get_creds");
krb5_err(context, 1, ret, "krb5_get_creds");
if (out_cache_str) {
krb5_ccache id;
ret = krb5_cc_resolve(context, out_cache_str, &id);
if(ret)
krb5_err (context, 1, ret, "krb5_cc_resolve");
krb5_err(context, 1, ret, "krb5_cc_resolve");
ret = krb5_cc_initialize(context, id, out->client);
if(ret)
krb5_err (context, 1, ret, "krb5_cc_initialize");
krb5_err(context, 1, ret, "krb5_cc_initialize");
ret = krb5_cc_store_cred(context, id, out);
if(ret)
krb5_err (context, 1, ret, "krb5_cc_store_cred");
krb5_cc_close (context, id);
krb5_err(context, 1, ret, "krb5_cc_store_cred");
krb5_cc_close(context, id);
}
krb5_free_creds(context, out);