krb5 commit: Add kvno option for user-to-user

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/409e0657f8a859d7f3a342ebc1e15755180fef61
commit 409e0657f8a859d7f3a342ebc1e15755180fef61
Author: Greg Hudson <[email protected]>
Date:   Fri Aug 24 11:40:39 2018 -0400

    Add kvno option for user-to-user
    
    Add a --u2u option to kvno, with an argument to specify a credential
    cache containing a krbtgt for the server principal.  Move the
    -allow_svr test from appl/user_to_user to a new test script and add
    additional tests.  Suggested by Chris Hecker.
    
    ticket: 8730 (new)

 doc/user/user_commands/kvno.rst   |    7 +++
 src/appl/user_user/t_user2user.py |    6 ---
 src/clients/kvno/kvno.c           |   89 ++++++++++++++++++++++++++++++++-----
 src/tests/Makefile.in             |    1 +
 src/tests/t_u2u.py                |   27 +++++++++++
 5 files changed, 113 insertions(+), 17 deletions(-)

diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst
index 31ca244..369ca79 100644
--- a/doc/user/user_commands/kvno.rst
+++ b/doc/user/user_commands/kvno.rst
@@ -14,6 +14,7 @@ SYNOPSIS
 [**-P**]
 [**-S** *sname*]
 [**-U** *for_user*]
+[**--u2u** *ccache*]
 *service1 service2* ...
 
 
@@ -63,6 +64,12 @@ OPTIONS
     delegation is not requested, the service name must match the
     credentials cache client principal.
 
+**--u2u** *ccache*
+    Requests a user-to-user ticket.  *ccache* must contain a local
+    krbtgt ticket for the server principal.  The reported version
+    number will typically be 0, as the resulting ticket is not
+    encrypted in the server's long-term key.
+
 
 ENVIRONMENT
 -----------
diff --git a/src/appl/user_user/t_user2user.py b/src/appl/user_user/t_user2user.py
index 0d50d66..2c054f1 100755
--- a/src/appl/user_user/t_user2user.py
+++ b/src/appl/user_user/t_user2user.py
@@ -4,12 +4,6 @@ from k5test import *
 debug_compiled=1
 
 for realm in multipass_realms():
-    # Verify that -allow_svr denies regular TGS requests, but allows
-    # user-to-user TGS requests.
-    realm.run([kadminl, 'modprinc', '-allow_svr', realm.user_princ])
-    realm.run([kvno, realm.user_princ], expected_code=1,
-               expected_msg='Server principal valid for user2user only')
-
     if debug_compiled == 0:
         realm.start_in_inetd(['./uuserver', 'uuserver'], port=9999)
     else:
diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c
index 57f7a78..f4fa048 100644
--- a/src/clients/kvno/kvno.c
+++ b/src/clients/kvno/kvno.c
@@ -40,13 +40,13 @@ xusage()
 {
     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"));
+    fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n"));
     exit(1);
 }
 
 static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr,
                        char *keytab_name, char *sname, int canon, int unknown,
-                       char *for_user, int proxy);
+                       char *for_user, int proxy, const char *u2u_ccname);
 
 #include <com_err.h>
 static void extended_com_err_fn(const char *myprog, errcode_t code,
@@ -55,9 +55,15 @@ static void extended_com_err_fn(const char *myprog, errcode_t code,
 int
 main(int argc, char *argv[])
 {
+    enum { OPTION_U2U = 256 };
+    struct option lopts[] = {
+        { "u2u", 1, NULL, OPTION_U2U },
+        { NULL, 0, NULL, 0 }
+    };
+    const char *shopts = "uCc:e:hk:qPS:U:";
     int option;
     char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL;
-    char *sname = NULL, *for_user = NULL;
+    char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL;
     int canon = 0, unknown = 0, proxy = 0;
 
     setlocale(LC_ALL, "");
@@ -66,7 +72,7 @@ main(int argc, char *argv[])
     prog = strrchr(argv[0], '/');
     prog = prog ? (prog + 1) : argv[0];
 
-    while ((option = getopt(argc, argv, "uCc:e:hk:qPS:U:")) != -1) {
+    while ((option = getopt_long(argc, argv, shopts, lopts, NULL)) != -1) {
         switch (option) {
         case 'C':
             canon = 1;
@@ -108,12 +114,20 @@ main(int argc, char *argv[])
         case 'U':
             for_user = optarg; /* S4U2Self - protocol transition */
             break;
+        case OPTION_U2U:
+            u2u_ccname = optarg;
+            break;
         default:
             xusage();
             break;
         }
     }
 
+    if (u2u_ccname != NULL && for_user != NULL) {
+        fprintf(stderr, _("Options --u2u and -P are mutually exclusive\n"));
+        xusage();
+    }
+
     if (proxy) {
         if (keytab_name == NULL) {
             fprintf(stderr, _("Option -P (constrained delegation) "
@@ -130,7 +144,7 @@ main(int argc, char *argv[])
         xusage();
 
     do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name,
-               sname, canon, unknown, for_user, proxy);
+               sname, canon, unknown, for_user, proxy, u2u_ccname);
     return 0;
 }
 
@@ -153,7 +167,8 @@ static void extended_com_err_fn(const char *myprog, errcode_t code,
 static krb5_error_code
 kvno(const char *name, krb5_ccache ccache, krb5_principal me,
      krb5_enctype etype, krb5_keytab keytab, const char *sname,
-     krb5_flags options, int unknown, krb5_principal for_user_princ, int proxy)
+     krb5_flags options, int unknown, krb5_principal for_user_princ, int proxy,
+     krb5_data *u2u_ticket)
 {
     krb5_error_code ret;
     krb5_principal server = NULL;
@@ -186,6 +201,9 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me,
 
     in_creds.keyblock.enctype = etype;
 
+    if (u2u_ticket != NULL)
+        in_creds.second_ticket = *u2u_ticket;
+
     if (for_user_princ != NULL) {
         if (!proxy && !krb5_principal_compare(context, me, server)) {
             ret = EINVAL;
@@ -260,10 +278,49 @@ cleanup:
     return ret;
 }
 
+/* Fetch the encoded local TGT for ccname's default client principal. */
+static krb5_error_code
+get_u2u_ticket(const char *ccname, krb5_data **ticket_out)
+{
+    krb5_error_code ret;
+    krb5_ccache cc = NULL;
+    krb5_creds mcred, *creds = NULL;
+
+    *ticket_out = NULL;
+    memset(&mcred, 0, sizeof(mcred));
+
+    ret = krb5_cc_resolve(context, ccname, &cc);
+    if (ret)
+        goto cleanup;
+    ret = krb5_cc_get_principal(context, cc, &mcred.client);
+    if (ret)
+        goto cleanup;
+    ret = krb5_build_principal_ext(context, &mcred.server,
+                                   mcred.client->realm.length,
+                                   mcred.client->realm.data,
+                                   KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME,
+                                   mcred.client->realm.length,
+                                   mcred.client->realm.data, 0);
+    if (ret)
+        goto cleanup;
+    ret = krb5_get_credentials(context, KRB5_GC_CACHED, cc, &mcred, &creds);
+    if (ret)
+        goto cleanup;
+
+    ret = krb5_copy_data(context, &creds->ticket, ticket_out);
+
+cleanup:
+    if (cc != NULL)
+        krb5_cc_close(context, cc);
+    krb5_free_cred_contents(context, &mcred);
+    krb5_free_creds(context, creds);
+    return ret;
+}
+
 static void
 do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
            char *keytab_name, char *sname, int canon, int unknown,
-           char *for_user, int proxy)
+           char *for_user, int proxy, const char *u2u_ccname)
 {
     krb5_error_code ret;
     int i, errors;
@@ -272,7 +329,8 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
     krb5_principal me;
     krb5_keytab keytab = NULL;
     krb5_principal for_user_princ = NULL;
-    krb5_flags options;
+    krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0;
+    krb5_data *u2u_ticket = NULL;
 
     ret = krb5_init_context(&context);
     if (ret) {
@@ -317,18 +375,26 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
         }
     }
 
+    if (u2u_ccname != NULL) {
+        ret = get_u2u_ticket(u2u_ccname, &u2u_ticket);
+        if (ret) {
+            com_err(prog, ret, _("while getting user-to-user ticket from %s"),
+                    u2u_ccname);
+            exit(1);
+        }
+        options |= KRB5_GC_USER_USER;
+    }
+
     ret = krb5_cc_get_principal(context, ccache, &me);
     if (ret) {
         com_err(prog, ret, _("while getting client principal name"));
         exit(1);
     }
 
-    options = canon ? KRB5_GC_CANONICALIZE : 0;
-
     errors = 0;
     for (i = 0; i < count; i++) {
         if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown,
-                 for_user_princ, proxy) != 0)
+                 for_user_princ, proxy, u2u_ticket) != 0)
             errors++;
     }
 
@@ -337,6 +403,7 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr,
     krb5_free_principal(context, me);
     krb5_free_principal(context, for_user_princ);
     krb5_cc_close(context, ccache);
+    krb5_free_data(context, u2u_ticket);
     krb5_free_context(context);
 
     if (errors)
diff --git a/src/tests/Makefile.in b/src/tests/Makefile.in
index aed23e5..e27617e 100644
--- a/src/tests/Makefile.in
+++ b/src/tests/Makefile.in
@@ -176,6 +176,7 @@ check-pytests: unlockiter
 	$(RUNPYTEST) $(srcdir)/t_certauth.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_y2038.py $(PYTESTFLAGS)
 	$(RUNPYTEST) $(srcdir)/t_kdcpolicy.py $(PYTESTFLAGS)
+	$(RUNPYTEST) $(srcdir)/t_u2u.py $(PYTESTFLAGS)
 
 clean:
 	$(RM) adata etinfo forward gcred hist hooks hrealm icinterleave icred
diff --git a/src/tests/t_u2u.py b/src/tests/t_u2u.py
new file mode 100644
index 0000000..8905dc2
--- /dev/null
+++ b/src/tests/t_u2u.py
@@ -0,0 +1,27 @@
+from k5test import *
+
+realm = K5Realm(create_host=False)
+
+# Create a second user principal and get tickets for it.
+u2u_ccache = 'FILE:' + os.path.join(realm.testdir, 'ccu2u')
+realm.addprinc('alice', password('alice'))
+realm.kinit('alice', password('alice'), ['-c', u2u_ccache])
+
+# Verify that -allow_dup_skey denies u2u requests.
+realm.run([kadminl, 'modprinc', '-allow_dup_skey', 'alice'])
+realm.run([kvno, '--u2u', u2u_ccache, 'alice'], expected_code=1,
+          expected_msg='KDC policy rejects request')
+realm.run([kadminl, 'modprinc', '+allow_dup_skey', 'alice'])
+
+# Verify that -allow_svr denies regular TGS requests, but allows
+# user-to-user TGS requests.
+realm.run([kadminl, 'modprinc', '-allow_svr', 'alice'])
+realm.run([kvno, 'alice'], expected_code=1,
+          expected_msg='Server principal valid for user2user only')
+realm.run([kvno, '--u2u', u2u_ccache, 'alice'], expected_msg='kvno = 0')
+realm.run([kadminl, 'modprinc', '+allow_svr', 'alice'])
+
+# Try u2u against the client user.
+realm.run([kvno, '--u2u', realm.ccache, realm.user_princ])
+
+realm.run([klist])
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.