From 5d03cb6b235f0ee0e30b34630f95f208d6acd3d0 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Sat, 28 Sep 2013 16:29:36 -0400 Subject: Conditionally test KEYRING ccache type If the keyctl command is found and klist recognizes the KEYRING credential cache type, then run several tests against keyring ccaches: the collection test program in lib/krb5/ccache, the command-line collection tests in tests/t_ccache.py, and some new tests to verify legacy session cache behavior. Much of the Python code in t_ccache.py is moved into a new function named "collection_test" so we can run it once against a DIR collection and once against a KEYRING collection. Also: fix a memory leak in the collection test program; add a test for iteration when the default cache name is a subsidiary name; use a process keyring ccache in t_cc.c to avoid leaving behind empty collections in the session keyring after each test run. Adapted from a patch by simo@redhat.com. ticket: 7711 --- src/lib/krb5/ccache/t_cc.c | 4 +- src/lib/krb5/ccache/t_cccol.c | 10 ++++ src/lib/krb5/ccache/t_cccol.py | 58 ++++++++++++++++++- src/tests/t_ccache.py | 128 ++++++++++++++++++++++++++++------------- 4 files changed, 156 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/lib/krb5/ccache/t_cc.c b/src/lib/krb5/ccache/t_cc.c index 991cef0253..6069cabd33 100644 --- a/src/lib/krb5/ccache/t_cc.c +++ b/src/lib/krb5/ccache/t_cc.c @@ -426,8 +426,8 @@ main(void) test_misc(context); do_test(context, ""); - if(check_registered(context, "KEYRING:")) - do_test(context, "KEYRING:"); + if (check_registered(context, "KEYRING:process:")) + do_test(context, "KEYRING:process:"); else printf("Skiping KEYRING: test - unregistered type\n"); diff --git a/src/lib/krb5/ccache/t_cccol.c b/src/lib/krb5/ccache/t_cccol.c index 3b4d3b3c29..444806e5a2 100644 --- a/src/lib/krb5/ccache/t_cccol.c +++ b/src/lib/krb5/ccache/t_cccol.c @@ -293,6 +293,15 @@ main(int argc, char **argv) check_princ(collection_name, princ3); check_collection(initial_primary_name, 2, unique1_name, unique2_name); + /* + * Temporarily set the context default ccache to a subsidiary name, and + * check that iterating over the collection yields that subsidiary cache + * and no others. + */ + check(krb5_cc_set_default_name(ctx, unique1_name)); + check_collection(unique1_name, 0); + check(krb5_cc_set_default_name(ctx, collection_name)); + /* * Destroy the primary cache. Make sure this causes both the initial * primary name and the collection name to resolve to an uninitialized @@ -349,5 +358,6 @@ main(int argc, char **argv) krb5_free_principal(ctx, princ1); krb5_free_principal(ctx, princ2); krb5_free_principal(ctx, princ3); + krb5_free_context(ctx); return 0; } diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py index 8b70470df2..e762625662 100644 --- a/src/lib/krb5/ccache/t_cccol.py +++ b/src/lib/krb5/ccache/t_cccol.py @@ -1,9 +1,43 @@ #!/usr/bin/python from k5test import * -# Run the collection test program against each collection-enabled type. realm = K5Realm(create_kdb=False) + +keyctl = which('keyctl') +out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1) +test_keyring = (keyctl is not None and + 'Unknown credential cache type' not in out) + +# Run the collection test program against each collection-enabled type. realm.run(['./t_cccol', 'DIR:' + os.path.join(realm.testdir, 'cc')]) +if test_keyring: + # Use the test directory as the collection name to avoid colliding + # with other build trees. + cname = realm.testdir + + # Remove any keys left behind by previous failed test runs. + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) + realm.run(['keyctl', 'purge', 'keyring', cname]) + out = realm.run(['keyctl', 'list', '@u']) + if ('keyring: _krb_' + cname + '\n') in out: + id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname]) + realm.run(['keyctl', 'unlink', id.strip(), '@u']) + + # Run test program over each subtype, cleaning up as we go. Don't + # test the persistent subtype, since it supports only one + # collection and might be in actual use. + realm.run(['./t_cccol', 'KEYRING:' + cname]) + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) + realm.run(['./t_cccol', 'KEYRING:legacy:' + cname]) + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) + realm.run(['./t_cccol', 'KEYRING:session:' + cname]) + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) + realm.run(['./t_cccol', 'KEYRING:user:' + cname]) + id = realm.run(['keyctl', 'search', '@u', 'keyring', '_krb_' + cname]) + realm.run(['keyctl', 'unlink', id.strip(), '@u']) + realm.run(['./t_cccol', 'KEYRING:process:abcd']) + realm.run(['./t_cccol', 'KEYRING:thread:abcd']) + realm.stop() # Test cursor semantics using real ccaches. @@ -22,6 +56,18 @@ realm.kinit('user', password('user'), flags=['-c', duser]) realm.kinit('alice', password('alice'), flags=['-c', dalice]) realm.kinit('bob', password('bob'), flags=['-c', dbob]) +if test_keyring: + cname = realm.testdir + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) + krccname = 'KEYRING:session:' + cname + kruser = '%s:tkt1' % krccname + kralice = '%s:tkt2' % krccname + krbob = '%s:tkt3' % krccname + krnoent = '%s:noent' % krccname + realm.kinit('user', password('user'), flags=['-c', kruser]) + realm.kinit('alice', password('alice'), flags=['-c', kralice]) + realm.kinit('bob', password('bob'), flags=['-c', krbob]) + def cursor_test(testname, args, expected): outlines = realm.run(['./t_cccursor'] + args).splitlines() outlines.sort() @@ -40,16 +86,26 @@ cursor_test('dir', [dccname], [duser, dalice, dbob]) cursor_test('dir-subsidiary', [duser], [duser]) cursor_test('dir-nofile', [dnoent], []) +if test_keyring: + cursor_test('keyring', [krccname], [kruser, kralice, krbob]) + cursor_test('keyring-subsidiary', [kruser], [kruser]) + cursor_test('keyring-noent', [krnoent], []) + mfoo = 'MEMORY:foo' mbar = 'MEMORY:bar' cursor_test('filemem', [fccname, mfoo, mbar], [fccname, mfoo, mbar]) cursor_test('dirmem', [dccname, mfoo], [duser, dalice, dbob, mfoo]) +if test_keyring: + cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob, mfoo]) # Test krb5_cccol_have_content. realm.run(['./t_cccursor', dccname, 'CONTENT']) realm.run(['./t_cccursor', fccname, 'CONTENT']) realm.run(['./t_cccursor', realm.ccache, 'CONTENT']) realm.run(['./t_cccursor', mfoo, 'CONTENT'], expected_code=1) +if test_keyring: + realm.run(['./t_cccursor', krccname, 'CONTENT']) + realm.run(['keyctl', 'purge', 'keyring', '_krb_' + cname]) # Make sure FILE doesn't yield a nonexistent default cache. realm.run([kdestroy]) diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py index a761d48fbe..15d8141f02 100644 --- a/src/tests/t_ccache.py +++ b/src/tests/t_ccache.py @@ -25,58 +25,104 @@ from k5test import * realm = K5Realm(create_host=False) +keyctl = which('keyctl') +out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1) +test_keyring = (keyctl is not None and + 'Unknown credential cache type' not in out) + # Test kdestroy and klist of a non-existent ccache. realm.run([kdestroy]) output = realm.run([klist], expected_code=1) if 'No credentials cache found' not in output: fail('Expected error message not seen in klist output') -# Make a directory collection and use it for client commands. -ccname = 'DIR:' + os.path.join(realm.testdir, 'cc') -realm.env['KRB5CCNAME'] = ccname - realm.addprinc('alice', password('alice')) realm.addprinc('bob', password('bob')) realm.addprinc('carol', password('carol')) -realm.kinit('alice', password('alice')) -output = realm.run([klist]) -if 'Default principal: alice@' not in output: - fail('Initial kinit failed to get credentials for alice.') -realm.run([kdestroy]) -output = realm.run([klist], expected_code=1) -if 'No credentials cache found' not in output: - fail('Initial kdestroy failed to destroy primary cache.') -output = realm.run([klist, '-l'], expected_code=1) -if not output.endswith('---\n') or output.count('\n') != 2: - fail('Initial kdestroy failed to empty cache collection.') +def collection_test(realm, ccname): + realm.env['KRB5CCNAME'] = ccname -realm.kinit('alice', password('alice')) -realm.kinit('carol', password('carol')) -output = realm.run([klist, '-l']) -if '---\ncarol@' not in output or '\nalice@' not in output: - fail('klist -l did not show expected output after two kinits.') -realm.kinit('alice', password('alice')) -output = realm.run([klist, '-l']) -if '---\nalice@' not in output or output.count('\n') != 4: - fail('klist -l did not show expected output after re-kinit for alice.') -realm.kinit('bob', password('bob')) -output = realm.run([klist, '-A']) -if 'bob@' not in output.splitlines()[1] or 'alice@' not in output or \ - 'carol' not in output or output.count('Default principal:') != 3: - fail('klist -A did not show expected output after kinit for bob.') -realm.run([kswitch, '-p', 'carol']) -output = realm.run([klist, '-l']) -if '---\ncarol@' not in output or output.count('\n') != 5: - fail('klist -l did not show expected output after kswitch to carol.') -realm.run([kdestroy]) -output = realm.run([klist, '-l']) -if 'carol@' in output or 'bob@' not in output or output.count('\n') != 4: - fail('kdestroy failed to remove only primary ccache.') -realm.run([kdestroy, '-A']) -output = realm.run([klist, '-l'], expected_code=1) -if not output.endswith('---\n') or output.count('\n') != 2: - fail('kdestroy -a failed to empty cache collection.') + realm.kinit('alice', password('alice')) + output = realm.run([klist]) + if 'Default principal: alice@' not in output: + fail('Initial kinit failed to get credentials for alice.') + realm.run([kdestroy]) + output = realm.run([klist], expected_code=1) + if 'No credentials cache found' not in output: + fail('Initial kdestroy failed to destroy primary cache.') + output = realm.run([klist, '-l'], expected_code=1) + if not output.endswith('---\n') or output.count('\n') != 2: + fail('Initial kdestroy failed to empty cache collection.') + + realm.kinit('alice', password('alice')) + realm.kinit('carol', password('carol')) + output = realm.run([klist, '-l']) + if '---\ncarol@' not in output or '\nalice@' not in output: + fail('klist -l did not show expected output after two kinits.') + realm.kinit('alice', password('alice')) + output = realm.run([klist, '-l']) + if '---\nalice@' not in output or output.count('\n') != 4: + fail('klist -l did not show expected output after re-kinit for alice.') + realm.kinit('bob', password('bob')) + output = realm.run([klist, '-A']) + if 'bob@' not in output.splitlines()[1] or 'alice@' not in output or \ + 'carol' not in output or output.count('Default principal:') != 3: + fail('klist -A did not show expected output after kinit for bob.') + realm.run([kswitch, '-p', 'carol']) + output = realm.run([klist, '-l']) + if '---\ncarol@' not in output or output.count('\n') != 5: + fail('klist -l did not show expected output after kswitch to carol.') + realm.run([kdestroy]) + output = realm.run([klist, '-l']) + if 'carol@' in output or 'bob@' not in output or output.count('\n') != 4: + fail('kdestroy failed to remove only primary ccache.') + realm.run([kdestroy, '-A']) + output = realm.run([klist, '-l'], expected_code=1) + if not output.endswith('---\n') or output.count('\n') != 2: + fail('kdestroy -a failed to empty cache collection.') + + +collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc')) +if test_keyring: + # Use realm.testdir as the collection name to avoid conflicts with + # other build trees. + cname = realm.testdir + + realm.run([keyctl, 'purge', 'keyring', '_krb_' + cname]) + collection_test(realm, 'KEYRING:session:' + cname) + realm.run([keyctl, 'purge', 'keyring', '_krb_' + cname]) + + # Test legacy keyring cache linkage. + realm.env['KRB5CCNAME'] = 'KEYRING:' + cname + realm.run([kdestroy, '-A']) + realm.kinit(realm.user_princ, password('user')) + out = realm.run([klist, '-l']) + if 'KEYRING:legacy:' + cname + ':' + cname not in out: + fail('Wrong initial primary name in keyring legacy collection') + # Make sure this cache is linked to the session keyring. + id = realm.run([keyctl, 'search', '@s', 'keyring', cname]) + out = realm.run([keyctl, 'list', id.strip()]) + if 'user: __krb5_princ__' not in out: + fail('Legacy cache not linked into session keyring') + # Remove the collection keyring. When the collection is + # reinitialized, the legacy cache should reappear inside it + # automatically as the primary cache. + out = realm.run([keyctl, 'purge', 'keyring', '_krb_' + cname]) + if 'purged 1 keys' not in out: + fail('Could not purge collection keyring') + out = realm.run([klist]) + if realm.user_princ not in out: + fail('Cannot see legacy cache after purging collection') + coll_id = realm.run([keyctl, 'search', '@s', 'keyring', '_krb_' + cname]) + out = realm.run([keyctl, 'list', coll_id.strip()]) + if (id.strip() + ':') not in out: + fail('Legacy cache did not reappear in collection after klist') + # Destroy the cache and check that it is unlinked from the session keyring. + realm.run([kdestroy]) + realm.run([keyctl, 'search', '@s', 'keyring', cname], expected_code=1) + # Clean up the collection key. + realm.run([keyctl, 'purge', 'keyring', '_krb_' + cname]) # Test parameter expansion in default_ccache_name realm.stop() -- cgit