From 3051db0d5dbf52c9dbdb6ed357373ba78750304f Mon Sep 17 00:00:00 2001 From: Luke Howard Date: Fri, 3 May 2019 14:45:58 +1000 Subject: [PATCH] kuser: support authenticated anonymous AS-REQs in kinit Allow kinit to request anonymous tickets with authenticated clients, not just anonymous PKINIT. --- kuser/kinit.1 | 12 +++++++----- kuser/kinit.c | 36 ++++++++++++++++++++++++++---------- tests/kdc/check-kdc.in | 26 +++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/kuser/kinit.1 b/kuser/kinit.1 index 697f61b0a..65d733d96 100644 --- a/kuser/kinit.1 +++ b/kuser/kinit.1 @@ -82,7 +82,7 @@ .Op Fl Fl password-file= Ns Ar filename .Op Fl Fl fcache-version= Ns Ar version-number .Op Fl A | Fl Fl no-addresses -.Op Fl Fl anonymous +.Op Fl n | Fl Fl anonymous .Op Fl Fl enterprise .Op Fl Fl version .Op Fl Fl help @@ -165,10 +165,12 @@ in .Xr krb5.conf 5 . .It Fl A , Fl Fl no-addresses Request a ticket with no addresses. -.It Fl Fl anonymous -Request an anonymous ticket (which means that the ticket will be -issued to an anonymous principal, typically -.Dq anonymous@REALM ) . +.It Fl n , Fl Fl anonymous +Request an anonymous ticket. If the principal is specified as @REALM, then +anonymous PKINIT will be used to acquire an unauthenticated anonymous ticket +and both the client name and realm in the returned ticket will be anonymized. +Otherwise, authentication proceeds as normal and the anonymous ticket will have +only the client name anonymized. .It Fl Fl enterprise Parse principal as a enterprise (KRB5-NT-ENTERPRISE) name. Enterprise names are email like principals that are stored in the name part of diff --git a/kuser/kinit.c b/kuser/kinit.c index 04980e409..8cd5f3aa6 100644 --- a/kuser/kinit.c +++ b/kuser/kinit.c @@ -155,7 +155,7 @@ static struct getargs args[] = { { "extra-addresses",'a', arg_strings, &extra_addresses, NP_("include these extra addresses", ""), "addresses" }, - { "anonymous", 0, arg_flag, &anonymous_flag, + { "anonymous", 'n', arg_flag, &anonymous_flag, NP_("request an anonymous ticket", ""), NULL }, { "request-pac", 0, arg_flag, &pac_flag, @@ -425,12 +425,25 @@ store_ntlmkey(krb5_context context, krb5_ccache id, } #endif +static krb5_boolean +is_anonymous_princ_p(krb5_const_principal principal) +{ + if ((principal->name.name_type != KRB5_NT_WELLKNOWN && + principal->name.name_type != KRB5_NT_UNKNOWN) || + principal->name.name_string.len != 2 || + strcmp(principal->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 || + strcmp(principal->name.name_string.val[1], KRB5_ANON_NAME) != 0) + return 0; + return 1; +} + static krb5_error_code get_new_tickets(krb5_context context, krb5_principal principal, krb5_ccache ccache, krb5_deltat ticket_life, - int interactive) + int interactive, + int anonymous_pkinit) { krb5_error_code ret; krb5_creds cred; @@ -529,7 +542,7 @@ get_new_tickets(krb5_context context, krb5_get_init_creds_opt_set_canonicalize(context, opt, TRUE); if (pk_enterprise_flag || enterprise_flag || canonicalize_flag || windows_flag) krb5_get_init_creds_opt_set_win2k(context, opt, TRUE); - if (pk_user_id || ent_user_id || anonymous_flag) { + if (pk_user_id || ent_user_id || anonymous_pkinit) { ret = krb5_get_init_creds_opt_set_pkinit(context, opt, principal, pk_user_id, @@ -537,7 +550,7 @@ get_new_tickets(krb5_context context, NULL, NULL, pk_use_enckey ? 2 : 0 | - anonymous_flag ? 4 : 0, + anonymous_pkinit ? 4 : 0, prompter, NULL, passwd); @@ -629,7 +642,7 @@ get_new_tickets(krb5_context context, krb5_warn(context, ret, "krb5_init_creds_set_keytab"); goto out; } - } else if (pk_user_id || ent_user_id || anonymous_flag) { + } else if (pk_user_id || ent_user_id || is_anonymous_princ_p(principal)) { } else if (!interactive && passwd[0] == '\0') { static int already_warned = 0; @@ -924,7 +937,7 @@ renew_func(void *ptr) server_str, ctx->ticket_life); } else { ret = get_new_tickets(ctx->context, ctx->principal, ctx->ccache, - ctx->ticket_life, 0); + ctx->ticket_life, 0, 0); } expire = ticket_lifetime(ctx->context, ctx->ccache, ctx->principal, server_str, &renew_expire); @@ -1224,6 +1237,7 @@ main(int argc, char **argv) struct sigaction sa; #endif krb5_boolean unique_ccache = FALSE; + int anonymous_pkinit = FALSE; setprogname(argv[0]); @@ -1273,15 +1287,16 @@ main(int argc, char **argv) pk_user_id = NULL; - } else if (anonymous_flag) { + } else if (anonymous_flag && argc && argv[0][0] == '@') { + /* If principal argument as @REALM, try anonymous PKINIT */ - ret = krb5_make_principal(context, &principal, argv[0], + ret = krb5_make_principal(context, &principal, &argv[0][1], KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); if (ret) krb5_err(context, 1, ret, "krb5_make_principal"); krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); - + anonymous_pkinit = TRUE; } else if (use_keytab || keytab_str) { get_princ_kt(context, &principal, argv[0]); } else { @@ -1388,7 +1403,8 @@ main(int argc, char **argv) exit(ret != 0); } - ret = get_new_tickets(context, principal, ccache, ticket_life, 1); + ret = get_new_tickets(context, principal, ccache, ticket_life, + 1, anonymous_pkinit); if (ret) { if (unique_ccache) krb5_cc_destroy(context, ccache); diff --git a/tests/kdc/check-kdc.in b/tests/kdc/check-kdc.in index 763a789d0..0f291551c 100644 --- a/tests/kdc/check-kdc.in +++ b/tests/kdc/check-kdc.in @@ -487,6 +487,30 @@ for a in $enctypes; do done ${kdestroy} +echo "Getting client authenticated anonymous initial tickets"; > messages.log +${kinit} -n --password-file=${objdir}/foopassword foo@$R || \ + { ec=1 ; eval "${testfailed}"; } +for a in $enctypes; do + echo "Getting tickets ($a)"; > messages.log + ${kgetcred} -e $a ${server}@${R} || { ec=1 ; eval "${testfailed}"; } + ${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ + { ec=1 ; eval "${testfailed}"; } + ${kdestroy} --credential=${server}@${R} +done +${kdestroy} + +echo "Getting client anonymous service tickets"; > messages.log +${kinit} --password-file=${objdir}/foopassword foo@$R || \ + { ec=1 ; eval "${testfailed}"; } +for a in $enctypes; do + echo "Getting tickets ($a)"; > messages.log + ${kgetcred} -n -e $a ${server}@${R} || { ec=1 ; eval "${testfailed}"; } + ${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ + { ec=1 ; eval "${testfailed}"; } + ${kdestroy} --credential=${server}@${R} +done +${kdestroy} + echo "Getting client initial tickets for cross realm case"; > messages.log ${kinit} --password-file=${objdir}/foopassword foo@$R || { ec=1 ; eval "${testfailed}"; } for a in $enctypes; do @@ -713,7 +737,7 @@ fi if test "$pkinit" = yes -a "$rsa" = yes ; then echo "try anonymous pkinit"; > messages.log - ${kinit} --anonymous ${R} || \ + ${kinit} -n @${R} || \ { ec=1 ; eval "${testfailed}"; } ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } ${kdestroy}