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

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/eb5d2c9afa4eba05f44e66d0e843b94be74d53e7
commit eb5d2c9afa4eba05f44e66d0e843b94be74d53e7
Author: Greg Hudson <[email protected]>
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['[email protected]']).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')
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.