From 0e39f8a3ad915eeb0131fb4a87b0fef304101cfd Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Sun, 13 Sep 2009 02:52:23 +0000 Subject: Implement s4u extensions Merge Luke's users/lhoward/s4u branch to trunk. Implements S4U2Self and S4U2Proxy extensions. ticket: 6563 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@22736 dc483132-0cff-0310-8789-dd5450dbe970 --- src/clients/kvno/kvno.M | 14 ++++ src/clients/kvno/kvno.c | 166 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 133 insertions(+), 47 deletions(-) (limited to 'src/clients/kvno') diff --git a/src/clients/kvno/kvno.M b/src/clients/kvno/kvno.M index b7e4d46a0d..37b0bcbd53 100644 --- a/src/clients/kvno/kvno.M +++ b/src/clients/kvno/kvno.M @@ -51,6 +51,13 @@ suppress printing .B \-h prints a usage statement and exits .TP +.B \-P +specifies that the +.B service1 service2 ... +arguments are to be treated as services for which credentials should +be acquired using constrained delegation. This option is only valid +when used in conjunction with protocol transition. +.TP .B \-S sname specifies that krb5_sname_to_principal() will be used to build principal names. If this flag is specified, the @@ -59,6 +66,13 @@ arguments are interpreted as hostnames (rather than principal names), and .B sname is interpreted as the service name. +.TP +.B \-U for_user +specifies that protocol transition (S4U2Self) is to be used to acquire +a ticket on behalf of +.B for_user. +If constrained delegation is not requested, the service name +must match the credentials cache client principal. .SH ENVIRONMENT .B Kvno uses the following environment variable: diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c index b98b85d304..58702525f6 100644 --- a/src/clients/kvno/kvno.c +++ b/src/clients/kvno/kvno.c @@ -39,8 +39,9 @@ static char *prog; static void xusage() { - fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype] [-k keytab] [-S sname] service1 service2 ...\n", - prog); + fprintf(stderr, "usage: %s [-C] [-u] [-c ccache] [-e etype]\n", prog); + fprintf(stderr, "\t[-k keytab] [-S sname] [-U for_user [-P]]\n"); + fprintf(stderr, "\tservice1 service2 ...\n"); exit(1); } @@ -48,7 +49,8 @@ int quiet = 0; static void do_v5_kvno (int argc, char *argv[], char *ccachestr, char *etypestr, char *keytab_name, - char *sname, int canon, int unknown); + char *sname, int canon, int unknown, + char *for_user, int proxy); #include static void extended_com_err_fn (const char *, errcode_t, const char *, @@ -58,8 +60,8 @@ int main(int argc, char *argv[]) { int option; char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL; - char *sname = NULL; - int canon = 0, unknown = 0; + char *sname = NULL, *for_user = NULL; + int canon = 0, unknown = 0, proxy = 0; set_com_err_hook (extended_com_err_fn); @@ -67,7 +69,7 @@ int main(int argc, char *argv[]) prog = strrchr(argv[0], '/'); prog = prog ? (prog + 1) : argv[0]; - while ((option = getopt(argc, argv, "uCc:e:hk:qS:")) != -1) { + while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) { switch (option) { case 'C': canon = 1; @@ -87,6 +89,9 @@ int main(int argc, char *argv[]) case 'q': quiet = 1; break; + case 'P': + proxy = 1; /* S4U2Proxy - constrained delegation */ + break; case 'S': sname = optarg; if (unknown == 1){ @@ -101,21 +106,37 @@ int main(int argc, char *argv[]) xusage(); } break; + case 'U': + for_user = optarg; /* S4U2Self - protocol transition */ + break; default: xusage(); break; } } + if (proxy) { + if (keytab_name == NULL) { + fprintf(stderr, "Option -P (constrained delegation) " + "requires keytab to be specified\n"); + xusage(); + } else if (for_user == NULL) { + fprintf(stderr, "Option -P (constrained delegation) requires " + "option -U (protocol transition)\n"); + xusage(); + } + } + if ((argc - optind) < 1) xusage(); do_v5_kvno(argc - optind, argv + optind, - ccachestr, etypestr, keytab_name, sname, canon, unknown); + ccachestr, etypestr, keytab_name, sname, + canon, unknown, for_user, proxy); return 0; } -#include +#include static krb5_context context; static void extended_com_err_fn (const char *myprog, errcode_t code, const char *fmt, va_list args) @@ -130,17 +151,18 @@ static void extended_com_err_fn (const char *myprog, errcode_t code, static void do_v5_kvno (int count, char *names[], char * ccachestr, char *etypestr, char *keytab_name, - char *sname, int canon, int unknown) + char *sname, int canon, int unknown, char *for_user, + int proxy) { krb5_error_code ret; int i, errors; krb5_enctype etype; krb5_ccache ccache; krb5_principal me; - krb5_creds in_creds, *out_creds; - krb5_ticket *ticket; - char *princ; + krb5_creds in_creds; krb5_keytab keytab = NULL; + krb5_principal for_user_princ = NULL; + krb5_flags options; ret = krb5_init_context(&context); if (ret) { @@ -175,6 +197,16 @@ static void do_v5_kvno (int count, char *names[], } } + if (for_user) { + ret = krb5_parse_name_flags(context, for_user, + KRB5_PRINCIPAL_PARSE_ENTERPRISE, + &for_user_princ); + if (ret) { + com_err(prog, ret, "while parsing principal name %s", for_user); + exit(1); + } + } + ret = krb5_cc_get_principal(context, ccache, &me); if (ret) { com_err(prog, ret, "while getting client principal name"); @@ -183,91 +215,131 @@ static void do_v5_kvno (int count, char *names[], errors = 0; + options = 0; + if (canon) + options |= KRB5_GC_CANONICALIZE; + for (i = 0; i < count; i++) { - memset(&in_creds, 0, sizeof(in_creds)); + krb5_principal server = NULL; + krb5_ticket *ticket = NULL; + krb5_creds *out_creds = NULL; + char *princ = NULL; - in_creds.client = me; + memset(&in_creds, 0, sizeof(in_creds)); if (sname != NULL) { ret = krb5_sname_to_principal(context, names[i], sname, KRB5_NT_SRV_HST, - &in_creds.server); + &server); } else { - ret = krb5_parse_name(context, names[i], &in_creds.server); + ret = krb5_parse_name(context, names[i], &server); } if (ret) { if (!quiet) com_err(prog, ret, "while parsing principal name %s", names[i]); - errors++; - continue; + goto error; } if (unknown == 1) { - krb5_princ_type(context, in_creds.server) = KRB5_NT_UNKNOWN; + krb5_princ_type(context, server) = KRB5_NT_UNKNOWN; } - ret = krb5_unparse_name(context, in_creds.server, &princ); + ret = krb5_unparse_name(context, server, &princ); if (ret) { com_err(prog, ret, "while formatting parsed principal name for '%s'", names[i]); - errors++; - continue; + goto error; } in_creds.keyblock.enctype = etype; - ret = krb5_get_credentials(context, canon ? KRB5_GC_CANONICALIZE : 0, - ccache, &in_creds, &out_creds); + if (for_user) { + if (!proxy && + !krb5_principal_compare(context, me, server)) { + com_err(prog, EINVAL, + "client and server principal names must match"); + goto error; + } - krb5_free_principal(context, in_creds.server); + in_creds.client = for_user_princ; + in_creds.server = me; + + ret = krb5_get_credentials_for_user(context, options, ccache, + &in_creds, NULL, &out_creds); + } else { + in_creds.client = me; + in_creds.server = server; + ret = krb5_get_credentials(context, options, ccache, + &in_creds, &out_creds); + } if (ret) { com_err(prog, ret, "while getting credentials for %s", princ); - - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } /* we need a native ticket */ ret = krb5_decode_ticket(&out_creds->ticket, &ticket); if (ret) { com_err(prog, ret, "while decoding ticket for %s", princ); - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } - + if (keytab) { ret = krb5_server_decrypt_ticket_keytab(context, keytab, ticket); if (ret) { - if (!quiet) - printf("%s: kvno = %d, keytab entry invalid", princ, ticket->enc_part.kvno); + if (!quiet) { + fprintf(stderr, "%s: kvno = %d, keytab entry invalid\n", + princ, ticket->enc_part.kvno); + } com_err(prog, ret, "while decrypting ticket for %s", princ); - krb5_free_ticket(context, ticket); - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); - - errors++; - continue; + goto error; } if (!quiet) - printf("%s: kvno = %d, keytab entry valid\n", princ, ticket->enc_part.kvno); + printf("%s: kvno = %d, keytab entry valid\n", + princ, ticket->enc_part.kvno); + if (proxy) { + krb5_free_creds(context, out_creds); + out_creds = NULL; + + in_creds.client = ticket->enc_part2->client; + in_creds.server = server; + + ret = krb5_get_credentials_for_proxy(context, + KRB5_GC_CANONICALIZE, + ccache, + &in_creds, + ticket, + &out_creds); + if (ret) { + com_err(prog, ret, + "%s: constrained delegation failed", princ); + goto error; + } + } } else { if (!quiet) printf("%s: kvno = %d\n", princ, ticket->enc_part.kvno); } - krb5_free_creds(context, out_creds); - krb5_free_unparsed_name(context, princ); + continue; + +error: + if (server != NULL) + krb5_free_principal(context, server); + if (ticket != NULL) + krb5_free_ticket(context, ticket); + if (out_creds != NULL) + krb5_free_creds(context, out_creds); + if (princ != NULL) + krb5_free_unparsed_name(context, princ); + errors++; } if (keytab) krb5_kt_close(context, keytab); krb5_free_principal(context, me); + krb5_free_principal(context, for_user_princ); krb5_cc_close(context, ccache); krb5_free_context(context); -- cgit