From: Greg Hudson Date: Thu, 27 Sep 2018 22:32:24 +0000 (-0400) Subject: Add ktutil addent option to fetch salt from KDC X-Git-Tag: krb5-1.17-beta1~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eb5d2c9afa4eba05f44e66d0e843b94be74d53e7;p=thirdparty%2Fkrb5.git 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 --- diff --git a/doc/admin/admin_commands/ktutil.rst b/doc/admin/admin_commands/ktutil.rst index 2eb19ded27..9f8e53efb7 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 198cb1317e..196f207865 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 8bf4915250..ddb754bae4 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 5843e24b79..2daf814f3f 100644 --- a/src/kadmin/ktutil/ktutil_funcs.c +++ b/src/kadmin/ktutil/ktutil_funcs.c @@ -81,17 +81,69 @@ krb5_error_code ktutil_delete(context, list, idx) return EINVAL; } +/* + * 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, ¶ms); + if (retval) + goto cleanup; + s2kparams = (params.length > 0) ? ¶ms : 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, ¶ms); return retval; } diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py index 950517db64..72e09daac3 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@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')