krb5 commit: Add ktutil addent option to fetch salt from KDC

Greg Hudson ghudson at mit.edu
Thu Oct 4 16:56:11 EDT 2018


https://github.com/krb5/krb5/commit/eb5d2c9afa4eba05f44e66d0e843b94be74d53e7
commit eb5d2c9afa4eba05f44e66d0e843b94be74d53e7
Author: Greg Hudson <ghudson at mit.edu>
Date:   Thu Sep 27 18:32:24 2018 -0400

    Add ktutil addent option to fetch salt from KDC
    
    Add a -f flag to ktutil addent.  If specified, the enctype need not be
    specified (although it can be) and ktutil will request etype-info from
    the KDC to produce the string-to-key parameters.
    
    ticket: 8587

 doc/admin/admin_commands/ktutil.rst |   11 +++-
 src/kadmin/ktutil/ktutil.c          |   19 +++++---
 src/kadmin/ktutil/ktutil.h          |    1 +
 src/kadmin/ktutil/ktutil_funcs.c    |   87 ++++++++++++++++++++++++++++-------
 src/tests/t_keytab.py               |   43 +++++++++++++++++
 5 files changed, 134 insertions(+), 27 deletions(-)

diff --git a/doc/admin/admin_commands/ktutil.rst b/doc/admin/admin_commands/ktutil.rst
index 2eb19de..9f8e53e 100644
--- a/doc/admin/admin_commands/ktutil.rst
+++ b/doc/admin/admin_commands/ktutil.rst
@@ -87,9 +87,14 @@ add_entry
 ~~~~~~~~~
 
     **add_entry** {**-key**\|\ **-password**} **-p** *principal*
-    **-k** *kvno* **-e** *enctype* [**-s** *salt*]
-
-Add *principal* to keylist using key or password.
+    **-k** *kvno* [**-e** *enctype*] [**-f**\|\ **-s** *salt*]
+
+Add *principal* to keylist using key or password.  If the **-f** flag
+is specified, salt information will be fetched from the KDC; in this
+case the **-e** flag may be omitted, or it may be supplied to force a
+particular enctype.  If the **-f** flag is not specified, the **-e**
+flag must be specified, and the default salt will be used unless
+overridden with the **-s** option.
 
 Alias: **addent**
 
diff --git a/src/kadmin/ktutil/ktutil.c b/src/kadmin/ktutil/ktutil.c
index 198cb13..196f207 100644
--- a/src/kadmin/ktutil/ktutil.c
+++ b/src/kadmin/ktutil/ktutil.c
@@ -140,7 +140,7 @@ void ktutil_add_entry(argc, argv)
     char *princ = NULL;
     char *enctype = NULL;
     krb5_kvno kvno = 0;
-    int use_pass = 0, use_key = 0, use_kvno = 0, i;
+    int use_pass = 0, use_key = 0, use_kvno = 0, fetch = 0, i;
     char *salt = NULL;
 
     for (i = 1; i < argc; i++) {
@@ -169,18 +169,23 @@ void ktutil_add_entry(argc, argv)
             salt = argv[++i];
             continue;
         }
+        if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-f", 2))
+            fetch++;
     }
 
-    if (!((argc == 8 && princ && use_kvno && enctype) ||
-          (argc == 10 && princ && use_kvno && enctype && salt)) ||
-        use_pass + use_key != 1) {
+    if (princ == NULL || use_pass + use_key != 1 || !use_kvno ||
+        (fetch && salt != NULL)) {
         fprintf(stderr, _("usage: %s (-key | -password) -p principal "
-                          "-k kvno -e enctype [-s salt]\n"), argv[0]);
+                          "-k kvno [-e enctype] [-f|-s salt]\n"), argv[0]);
+        return;
+    }
+    if (!fetch && enctype == NULL) {
+        fprintf(stderr, _("enctype must be specified if not using -f\n"));
         return;
     }
 
-    retval = ktutil_add(kcontext, &ktlist, princ, kvno, enctype, use_pass,
-                        salt);
+    retval = ktutil_add(kcontext, &ktlist, princ, fetch, kvno, enctype,
+                        use_pass, salt);
     if (retval)
         com_err(argv[0], retval, _("while adding new entry"));
 }
diff --git a/src/kadmin/ktutil/ktutil.h b/src/kadmin/ktutil/ktutil.h
index 8bf4915..ddb754b 100644
--- a/src/kadmin/ktutil/ktutil.h
+++ b/src/kadmin/ktutil/ktutil.h
@@ -36,6 +36,7 @@ krb5_error_code ktutil_delete (krb5_context, krb5_kt_list *, int);
 krb5_error_code ktutil_add (krb5_context,
                             krb5_kt_list *,
                             char *,
+                            int,
                             krb5_kvno,
                             char *,
                             int,
diff --git a/src/kadmin/ktutil/ktutil_funcs.c b/src/kadmin/ktutil/ktutil_funcs.c
index 5843e24..2daf814 100644
--- a/src/kadmin/ktutil/ktutil_funcs.c
+++ b/src/kadmin/ktutil/ktutil_funcs.c
@@ -82,16 +82,68 @@ krb5_error_code ktutil_delete(context, list, idx)
 }
 
 /*
+ * Determine the enctype, salt, and s2kparams for princ based on the presence
+ * of the -f flag (fetch), the optionally specified salt string, and the
+ * optionally specified enctype.  If the fetch flag is used, salt_str must not
+ * be given; if the fetch flag is not used, the enctype must be given.
+ */
+static krb5_error_code
+get_etype_info(krb5_context context, krb5_principal princ, int fetch,
+               char *salt_str, krb5_enctype *enctype_inout,
+               krb5_data *salt_out, krb5_data *s2kparams_out)
+{
+    krb5_error_code retval;
+    krb5_enctype enctype;
+    krb5_get_init_creds_opt *opt = NULL;
+    krb5_data salt;
+
+    *salt_out = empty_data();
+    *s2kparams_out = empty_data();
+
+    if (!fetch) {
+        /* Use the specified enctype and either the specified or default salt.
+         * Do not produce s2kparams. */
+        assert(*enctype_inout != ENCTYPE_NULL);
+        if (salt_str != NULL) {
+            salt = string2data(salt_str);
+            return krb5int_copy_data_contents(context, &salt, salt_out);
+        } else {
+            return krb5_principal2salt(context, princ, salt_out);
+        }
+    }
+
+    /* Get etype-info from the KDC. */
+    assert(salt_str == NULL);
+    if (*enctype_inout != ENCTYPE_NULL) {
+        retval = krb5_get_init_creds_opt_alloc(context, &opt);
+        if (retval)
+            return retval;
+        krb5_get_init_creds_opt_set_etype_list(opt, enctype_inout, 1);
+    }
+    retval = krb5_get_etype_info(context, princ, opt, &enctype, salt_out,
+                                 s2kparams_out);
+    krb5_get_init_creds_opt_free(context, opt);
+    if (retval)
+        return retval;
+    if (enctype == ENCTYPE_NULL)
+        return KRB5KDC_ERR_ETYPE_NOSUPP;
+
+    *enctype_inout = enctype;
+    return 0;
+}
+
+/*
  * Create a new keytab entry and add it to the keytab list.
  * Based on the value of use_pass, either prompt the user for a
  * password or key.  If the keytab list is NULL, allocate a new
  * one first.
  */
-krb5_error_code ktutil_add(context, list, princ_str, kvno,
+krb5_error_code ktutil_add(context, list, princ_str, fetch, kvno,
                            enctype_str, use_pass, salt_str)
     krb5_context context;
     krb5_kt_list *list;
     char *princ_str;
+    int fetch;
     krb5_kvno kvno;
     char *enctype_str;
     int use_pass;
@@ -100,10 +152,10 @@ krb5_error_code ktutil_add(context, list, princ_str, kvno,
     krb5_keytab_entry *entry;
     krb5_kt_list lp = NULL, prev = NULL;
     krb5_principal princ;
-    krb5_enctype enctype;
+    krb5_enctype enctype = ENCTYPE_NULL;
     krb5_timestamp now;
     krb5_error_code retval;
-    krb5_data password, salt, defsalt = empty_data();
+    krb5_data password, salt = empty_data(), params = empty_data(), *s2kparams;
     krb5_keyblock key;
     char buf[BUFSIZ];
     char promptstr[1024];
@@ -119,9 +171,11 @@ krb5_error_code ktutil_add(context, list, princ_str, kvno,
     retval = krb5_unparse_name(context, princ, &princ_str);
     if (retval)
         return retval;
-    retval = krb5_string_to_enctype(enctype_str, &enctype);
-    if (retval)
-        return KRB5_BAD_ENCTYPE;
+    if (enctype_str != NULL) {
+        retval = krb5_string_to_enctype(enctype_str, &enctype);
+        if (retval)
+            return KRB5_BAD_ENCTYPE;
+    }
     retval = krb5_timeofday(context, &now);
     if (retval)
         return retval;
@@ -166,16 +220,14 @@ krb5_error_code ktutil_add(context, list, princ_str, kvno,
                                     &password.length);
         if (retval)
             goto cleanup;
-        if (salt_str != NULL) {
-            salt = string2data(salt_str);
-        } else {
-            retval = krb5_principal2salt(context, princ, &defsalt);
-            if (retval)
-                goto cleanup;
-            salt = defsalt;
-        }
-        retval = krb5_c_string_to_key(context, enctype, &password,
-                                      &salt, &key);
+
+        retval = get_etype_info(context, princ, fetch, salt_str,
+                                &enctype, &salt, &params);
+        if (retval)
+            goto cleanup;
+        s2kparams = (params.length > 0) ? &params : NULL;
+        retval = krb5_c_string_to_key_with_params(context, enctype, &password,
+                                                  &salt, s2kparams, &key);
         if (retval)
             goto cleanup;
         memset(password.data, 0, password.length);
@@ -225,7 +277,8 @@ cleanup:
     if (prev)
         prev->next = NULL;
     ktutil_free_kt_list(context, lp);
-    krb5_free_data_contents(context, &defsalt);
+    krb5_free_data_contents(context, &salt);
+    krb5_free_data_contents(context, &params);
     return retval;
 }
 
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index 950517d..72e09da 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -148,4 +148,47 @@ out = realm.run([klist, '-k'], expected_code=1, expected_msg=msg)
 msg = 'FILE:testdir/xyz%s' % uidstr
 out = realm.run([klist, '-ki'], expected_code=1, expected_msg=msg)
 
+conf = {'libdefaults': {'allow_weak_crypto': 'true'}}
+realm = K5Realm(create_user=False, create_host=False, krb5_conf=conf)
+
+realm.run([kadminl, 'ank', '-pw', 'pw', 'default'])
+realm.run([kadminl, 'ank', '-e', 'aes256-cts:special', '-pw', 'pw', 'exp'])
+realm.run([kadminl, 'ank', '-e', 'aes256-cts:special', '-pw', 'pw', '+preauth',
+           'pexp'])
+realm.run([kadminl, 'ank', '-e', 'des-cbc-crc:afs3', '-pw', 'pw', 'afs'])
+realm.run([kadminl, 'ank', '-e', 'des-cbc-crc:afs3', '-pw', 'pw', '+preauth',
+           'pafs'])
+
+# Extract one of the explicit salt values from the database.
+out = realm.run([kdb5_util, 'tabdump', 'keyinfo'])
+salt_dict = {f[0]: f[5] for f in [l.split('\t') for l in out.splitlines()]}
+exp_salt = bytes.fromhex(salt_dict['exp at KRBTEST.COM']).decode('ascii')
+
+# Create a keytab using ktutil addent with the specified options and
+# password "pw".  Test that we can use it to get initial tickets.
+# Remove the keytab afterwards.
+def test_addent(realm, princ, opts):
+    realm.run([ktutil], input=('addent -password -p %s -k 1 %s\npw\nwkt %s\n' %
+                               (princ, opts, realm.keytab)))
+    realm.kinit(princ, flags=['-k'])
+    os.remove(realm.keytab)
+
+mark('ktutil addent')
+
+# Test with default salt.
+test_addent(realm, 'default', '-e aes128-cts')
+test_addent(realm, 'default', '-e aes256-cts')
+
+# Test with a salt specified to ktutil addent.
+test_addent(realm, 'exp', '-e aes256-cts -s %s' % exp_salt)
+
+# Test etype-info fetching.
+test_addent(realm, 'default', '-f')
+test_addent(realm, 'default', '-f -e aes128-cts')
+test_addent(realm, 'exp', '-f')
+test_addent(realm, 'pexp', '-f')
+test_addent(realm, 'afs', '-f')
+test_addent(realm, 'pafs', '-f')
+
+success('Keytab-related tests')
 success('Keytab-related tests')


More information about the cvs-krb5 mailing list