diff options
author | Greg Hudson <ghudson@mit.edu> | 2013-08-05 23:47:52 -0400 |
---|---|---|
committer | Greg Hudson <ghudson@mit.edu> | 2013-08-12 11:48:30 -0400 |
commit | 2e956074b228ff4df3b7462037ab69e4e88ffffe (patch) | |
tree | dc5b984c1ac87bdd44f464e478302fd7bab39306 /src/tests | |
parent | 941f3d999ad403f327c0a7ccc5c1f71347a6221a (diff) | |
download | krb5-2e956074b228ff4df3b7462037ab69e4e88ffffe.zip krb5-2e956074b228ff4df3b7462037ab69e4e88ffffe.tar.gz krb5-2e956074b228ff4df3b7462037ab69e4e88ffffe.tar.bz2 |
Fix gss_krb5_set_allowable_enctypes for acceptor
The acceptor implementation of gss_krb5_set_allowable_enctypes (added
in 1.9.1) is intended to restrict the acceptor subkey negotiated by
krb5_rd_req(). It uses the same approach as the initiator, calling
krb5_set_default_tgs_enctypes on the context. This has the unwanted
side effect of restricting the encryption key of the ticket, because
krb5_decrypt_tkt_part has checked krb5_is_permitted_enctype on the
ticket encryption key since 1.8.
Instead, use krb5_auth_con_setpermetypes on the auth context. This
list is only used for session key enctype negotiation. Also add
automated tests to verify that gss_krb5_set_allowable_enctypes works
as desired.
ticket: 7688 (new)
target_version: 1.11.4
tags: pullup
Diffstat (limited to 'src/tests')
-rw-r--r-- | src/tests/gssapi/Makefile.in | 3 | ||||
-rw-r--r-- | src/tests/gssapi/t_enctypes.c | 229 | ||||
-rw-r--r-- | src/tests/gssapi/t_enctypes.py | 149 |
3 files changed, 381 insertions, 0 deletions
diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in index c53bda5..da6f534 100644 --- a/src/tests/gssapi/Makefile.in +++ b/src/tests/gssapi/Makefile.in @@ -32,6 +32,7 @@ check-pytests:: ccinit ccrefresh t_accname t_ccselect t_credstore \ $(RUNPYTEST) $(srcdir)/t_gssapi.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_ccselect.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_client_keytab.py $(PYTESTFLAGS) + $(RUNPYTEST) $(srcdir)/t_enctypes.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_export_cred.py $(PYTESTFLAGS) $(RUNPYTEST) $(srcdir)/t_s4u.py $(PYTESTFLAGS) @@ -45,6 +46,8 @@ t_ccselect: t_ccselect.o $(COMMON_DEPS) $(CC_LINK) -o $@ t_ccselect.o $(COMMON_LIBS) t_credstore: t_credstore.o $(COMMON_DEPS) $(CC_LINK) -o $@ t_credstore.o $(COMMON_LIBS) +t_enctypes: t_enctypes.o $(COMMON_DEPS) + $(CC_LINK) -o $@ t_enctypes.o $(COMMON_LIBS) t_export_cred: t_export_cred.o $(COMMON_DEPS) $(CC_LINK) -o $@ t_export_cred.o $(COMMON_LIBS) t_export_name: t_export_name.o $(COMMON_DEPS) diff --git a/src/tests/gssapi/t_enctypes.c b/src/tests/gssapi/t_enctypes.c new file mode 100644 index 0000000..c1e02fa --- /dev/null +++ b/src/tests/gssapi/t_enctypes.c @@ -0,0 +1,229 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* tests/gssapi/t_enctypes.c - gss_krb5_set_allowable_enctypes test */ +/* + * Copyright (C) 2013 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include "k5-int.h" +#include "common.h" + +/* + * This test program performs a gss_init_sec_context/gss_accept_sec_context + * exchange with the krb5 mech, the default initiator name, a specified + * principal name as target name, and the default acceptor name. Before the + * exchange, gss_set_allowable_enctypes is called for the initiator and the + * acceptor cred if requested. If the exchange is successful, the resulting + * contexts are exported with gss_krb5_export_lucid_sec_context, checked for + * mismatches, and the GSS protocol and keys are displayed. Exits with status + * 0 if all operations are successful, or 1 if not. + * + * Usage: ./t_enctypes [-i initenctypes] [-a accenctypes] targetname + */ + +static void +usage() +{ + errout("Usage: t_enctypes [-i initenctypes] [-a accenctypes] " + "targetname"); +} + +/* Error out if ikey is not the same as akey. */ +static void +check_key_match(gss_krb5_lucid_key_t *ikey, gss_krb5_lucid_key_t *akey) +{ + if (ikey->type != akey->type || ikey->length != akey->length || + memcmp(ikey->data, akey->data, ikey->length) != 0) + errout("Initiator and acceptor keys do not match"); +} + +/* Display the name of enctype. */ +static void +display_enctype(krb5_enctype enctype) +{ + char ename[128]; + + if (krb5_enctype_to_name(enctype, FALSE, ename, sizeof(ename)) == 0) + fputs(ename, stdout); + else + fputs("(unknown)", stdout); +} + +int +main(int argc, char *argv[]) +{ + krb5_error_code ret; + krb5_context kctx = NULL; + krb5_enctype *ienc = NULL, *aenc = NULL, zero = 0; + OM_uint32 minor, major, flags; + gss_name_t tname; + gss_cred_id_t icred = GSS_C_NO_CREDENTIAL, acred = GSS_C_NO_CREDENTIAL; + gss_ctx_id_t ictx = GSS_C_NO_CONTEXT, actx = GSS_C_NO_CONTEXT; + gss_buffer_desc itok, atok, tmp; + gss_krb5_lucid_context_v1_t *ilucid, *alucid; + gss_krb5_rfc1964_keydata_t *i1964, *a1964; + gss_krb5_cfx_keydata_t *icfx, *acfx; + size_t count; + void *lptr; + int c; + + ret = krb5_init_context(&kctx); + check_k5err(kctx, "krb5_init_context", ret); + + /* Parse arguments. */ + while ((c = getopt(argc, argv, "i:a:")) != -1) { + switch (c) { + case 'i': + ret = krb5int_parse_enctype_list(kctx, "", optarg, &zero, &ienc); + check_k5err(kctx, "krb5_parse_enctype_list(initiator)", ret); + break; + case 'a': + ret = krb5int_parse_enctype_list(kctx, "", optarg, &zero, &aenc); + check_k5err(kctx, "krb5_parse_enctype_list(acceptor)", ret); + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc != 1) + usage(); + tname = import_name(*argv); + + if (ienc != NULL) { + major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, + &mechset_krb5, GSS_C_INITIATE, &icred, NULL, + NULL); + check_gsserr("gss_acquire_cred(initiator)", major, minor); + + for (count = 0; ienc[count]; count++); + major = gss_krb5_set_allowable_enctypes(&minor, icred, count, ienc); + check_gsserr("gss_krb5_set_allowable_enctypes(init)", major, minor); + } + if (aenc != NULL) { + major = gss_acquire_cred(&minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, + &mechset_krb5, GSS_C_ACCEPT, &acred, NULL, + NULL); + check_gsserr("gss_acquire_cred(acceptor)", major, minor); + + for (count = 0; aenc[count]; count++); + major = gss_krb5_set_allowable_enctypes(&minor, acred, count, aenc); + check_gsserr("gss_krb5_set_allowable_enctypes(acc)", major, minor); + } + + /* Create initiator context and get the first token. */ + itok.value = NULL; + itok.length = 0; + flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG; + major = gss_init_sec_context(&minor, icred, &ictx, tname, &mech_krb5, + flags, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, GSS_C_NO_BUFFER, + NULL, &itok, NULL, NULL); + check_gsserr("gss_init_sec_context(1)", major, minor); + if (major != GSS_S_CONTINUE_NEEDED) + errout("gss_init_sec_context(1) unexpected complete"); + + /* Pass the initiator token to gss_accept_sec_context. */ + atok.value = NULL; + atok.length = 0; + major = gss_accept_sec_context(&minor, &actx, acred, &itok, + GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, + &atok, NULL, NULL, NULL); + check_gsserr("gss_accept_sec_context", major, minor); + if (major != GSS_S_COMPLETE) + errout("gss_accept_sec_context unexpected continue"); + + /* Pass the return token to gss_init_sec_context again. */ + tmp.value = NULL; + tmp.length = 0; + major = gss_init_sec_context(&minor, icred, &ictx, tname, &mech_krb5, + flags, GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, &atok, NULL, &tmp, + NULL, NULL); + check_gsserr("gss_init_sec_context(2)", major, minor); + if (major != GSS_S_COMPLETE) + errout("gss_init_sec_context(2) unexpected continue"); + + /* Export to lucid contexts. */ + major = gss_krb5_export_lucid_sec_context(&minor, &ictx, 1, &lptr); + check_gsserr("gss_export_lucid_sec_context(initiator)", major, minor); + ilucid = lptr; + major = gss_krb5_export_lucid_sec_context(&minor, &actx, 1, &lptr); + check_gsserr("gss_export_lucid_sec_context(acceptor)", major, minor); + alucid = lptr; + + /* Grab the session keys and make sure they match. */ + if (ilucid->protocol != alucid->protocol) + errout("Initiator/acceptor protocol mismatch"); + if (ilucid->protocol) { + icfx = &ilucid->cfx_kd; + acfx = &alucid->cfx_kd; + if (icfx->have_acceptor_subkey != acfx->have_acceptor_subkey) + errout("Initiator/acceptor have_acceptor_subkey mismatch"); + check_key_match(&icfx->ctx_key, &acfx->ctx_key); + if (icfx->have_acceptor_subkey) + check_key_match(&icfx->acceptor_subkey, &acfx->acceptor_subkey); + fputs("cfx ", stdout); + display_enctype(icfx->ctx_key.type); + if (icfx->have_acceptor_subkey) { + fputs(" ", stdout); + display_enctype(icfx->acceptor_subkey.type); + } + fputs("\n", stdout); + } else { + i1964 = &ilucid->rfc1964_kd; + a1964 = &alucid->rfc1964_kd; + if (i1964->sign_alg != a1964->sign_alg || + i1964->seal_alg != a1964->seal_alg) + errout("Initiator/acceptor sign or seal alg mismatch"); + check_key_match(&i1964->ctx_key, &a1964->ctx_key); + fputs("rfc1964 ", stdout); + display_enctype(i1964->ctx_key.type); + fputs("\n", stdout); + } + + krb5_free_context(kctx); + free(ienc); + free(aenc); + (void)gss_release_name(&minor, &tname); + (void)gss_release_cred(&minor, &icred); + (void)gss_release_cred(&minor, &acred); + (void)gss_delete_sec_context(&minor, &ictx, NULL); + (void)gss_delete_sec_context(&minor, &actx, NULL); + (void)gss_release_buffer(&minor, &itok); + (void)gss_release_buffer(&minor, &atok); + (void)gss_release_buffer(&minor, &tmp); + (void)gss_krb5_free_lucid_sec_context(&minor, ilucid); + (void)gss_krb5_free_lucid_sec_context(&minor, alucid); + return 0; +} diff --git a/src/tests/gssapi/t_enctypes.py b/src/tests/gssapi/t_enctypes.py new file mode 100644 index 0000000..d7577bf --- /dev/null +++ b/src/tests/gssapi/t_enctypes.py @@ -0,0 +1,149 @@ +#!/usr/bin/python +from k5test import * + +# Define some convenience abbreviations for enctypes we will see in +# test program output. For background, aes256 and aes128 are "CFX +# enctypes", meaning that they imply support for RFC 4121, while des3 +# and rc4 are not. DES3 keys will appear as 'des3-cbc-raw' in +# t_enctypes output because that's how GSSAPI does raw triple-DES +# encryption without the RFC3961 framing. +aes256 = 'aes256-cts-hmac-sha1-96' +aes128 = 'aes128-cts-hmac-sha1-96' +des3 = 'des3-cbc-sha1' +des3raw = 'des3-cbc-raw' +rc4 = 'arcfour-hmac' + +# These tests make assumptions about the default enctype lists, so set +# them explicitly rather than relying on the library defaults. +enctypes='aes des3 rc4' +supp='aes256-cts:normal aes128-cts:normal des3-cbc-sha1:normal rc4-hmac:normal' +conf = {'libdefaults': { + 'default_tgs_enctypes': enctypes, + 'default_tkt_enctypes': enctypes, + 'permitted_enctypes': enctypes}, + 'realms': {'$realm': {'supported_enctypes': supp}}} +realm = K5Realm(krb5_conf=conf) +shutil.copyfile(realm.ccache, os.path.join(realm.testdir, 'save')) + +# Return an argument list for running t_enctypes with optional initiator +# and acceptor enctype lists. +def cmdline(ienc, aenc): + iflags = ienc and ['-i', ienc] or [] + aflags = aenc and ['-a', aenc] or [] + return ['./t_enctypes'] + iflags + aflags + ['p:' + realm.host_princ] + + +# Run t_enctypes with optional initiator and acceptor enctype lists, +# and check that it succeeds with the expected output. Also check +# that the ticket we got has the expected encryption key and session +# key. +def test(msg, ienc, aenc, tktenc='', tktsession='', proto='', isubkey='', + asubkey=None): + shutil.copyfile(os.path.join(realm.testdir, 'save'), realm.ccache) + # Run the test program and check its output. + out = realm.run(cmdline(ienc, aenc)).split() + if out[0] != proto or out[1] != isubkey: + fail(msg) + if asubkey is not None and (len(out) < 3 or out[2] != asubkey): + fail(msg) + lines = realm.run([klist, '-e']).splitlines() + for ind, line in enumerate(lines): + if realm.host_princ in line: + if lines[ind + 1].strip() != ('Etype (skey, tkt): %s, %s' % + (tktsession, tktenc)): + fail(msg) + break + +# Run t_enctypes with optional initiator and acceptor enctype lists, +# and check that it fails with the expected error message. +def test_err(msg, ienc, aenc, expected_err): + shutil.copyfile(os.path.join(realm.testdir, 'save'), realm.ccache) + out = realm.run(cmdline(ienc, aenc), expected_code=1) + if expected_err not in out: + fail(msg) + + +# By default, all of the key enctypes should be aes256. +test('noargs', None, None, + tktenc=aes256, tktsession=aes256, + proto='cfx', isubkey=aes256, asubkey=aes256) + +# When the initiator constrains the permitted session enctypes to +# aes128, the ticket encryption key should remain aes256. The client +# initiator will not send an RFC 4537 upgrade list because it sees no +# other permitted enctypes, so the acceptor subkey will not be +# upgraded from aes128. +test('init aes128', 'aes128-cts', None, + tktenc=aes256, tktsession=aes128, + proto='cfx', isubkey=aes128, asubkey=aes128) + +# If the initiator and acceptor both constrain the permitted session +# enctypes to aes128, we should see the same keys as above. This +# tests that the acceptor does not mistakenly contrain the ticket +# encryption key. +test('both aes128', 'aes128-cts', 'aes128-cts', + tktenc=aes256, tktsession=aes128, + proto='cfx', isubkey=aes128, asubkey=aes128) + +# If only the acceptor constrains the permitted session enctypes to +# aes128, subkey negotiation fails because the acceptor considers the +# aes256 session key to be non-permitted. +test_err('acc aes128', None, 'aes128-cts', 'Encryption type not permitted') + +# If the initiator constrains the permitted session enctypes to des3, +# no acceptor subkey will be generated because we can't upgrade to a +# CFX enctype. +test('init des3', 'des3', None, + tktenc=aes256, tktsession=des3, + proto='rfc1964', isubkey=des3raw, asubkey=None) + +# Force the ticket session key to be rc4, so we can test some subkey +# upgrade cases. The ticket encryption key remains aes256. +realm.run_kadminl('setstr %s session_enctypes rc4' % realm.host_princ) + +# With no arguments, the initiator should send an upgrade list of +# [aes256 aes128 des3] and the acceptor should upgrade to an aes256 +# subkey. +test('upgrade noargs', None, None, + tktenc=aes256, tktsession=rc4, + proto='cfx', isubkey=rc4, asubkey=aes256) + +# If the initiator won't permit rc4 as a session key, it won't be able +# to get a ticket. +test_err('upgrade init aes', 'aes', None, 'no support for encryption type') + +# If the initiator permits rc4 but prefers aes128, it will send an +# upgrade list of [aes128] and the acceptor will upgrade to aes128. +test('upgrade init aes128+rc4', 'aes128-cts rc4', None, + tktenc=aes256, tktsession=rc4, + proto='cfx', isubkey=rc4, asubkey=aes128) + +# If the initiator permits rc4 but prefers des3, it will send an +# upgrade list of [des3], but the acceptor won't generate a subkey +# because des3 isn't a CFX enctype. +test('upgrade init des3+rc4', 'des3 rc4', None, + tktenc=aes256, tktsession=rc4, + proto='rfc1964', isubkey=rc4, asubkey=None) + +# If the acceptor permits only aes128, subkey negotiation will fail +# because the ticket session key and initiator subkey are +# non-permitted. (This is unfortunate if the acceptor's restriction +# is only for the sake of the kernel, since we could upgrade to an +# aes128 subkey, but it's the current semantics.) +test_err('upgrade acc aes128', None, 'aes128-cts', + 'Encryption type ArcFour with HMAC/md5 not permitted') + +# If the acceptor permits rc4 but prefers aes128, it will negotiate an +# upgrade to aes128. +test('upgrade acc aes128 rc4', None, 'aes128-cts rc4', + tktenc=aes256, tktsession=rc4, + proto='cfx', isubkey=rc4, asubkey=aes128) + +# In this test, the initiator and acceptor each prefer an AES enctype +# to rc4, but they can't agree on which one, so no subkey is +# generated. +test('upgrade mismatch', 'aes128-cts rc4', 'aes256-cts rc4', + tktenc=aes256, tktsession=rc4, + proto='rfc1964', isubkey=rc4, asubkey=None) + +success('gss_krb5_set_allowable_enctypes tests') |