1# Copyright (C) 2011 by the Massachusetts Institute of Technology.
2# All rights reserved.
3
4# Export of this software from the United States of America may
5#   require a specific license from the United States Government.
6#   It is the responsibility of any person or organization contemplating
7#   export to obtain such a license before exporting.
8#
9# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
10# distribute this software and its documentation for any purpose and
11# without fee is hereby granted, provided that the above copyright
12# notice appear in all copies and that both that copyright notice and
13# this permission notice appear in supporting documentation, and that
14# the name of M.I.T. not be used in advertising or publicity pertaining
15# to distribution of the software without specific, written prior
16# permission.  Furthermore if you modify this software you must label
17# your software as modified software and not distribute it in such a
18# fashion that it might be confused with the original M.I.T. software.
19# M.I.T. makes no representations about the suitability of
20# this software for any purpose.  It is provided "as is" without express
21# or implied warranty.
22
23from k5test import *
24
25kcm_socket_path = os.path.join(os.getcwd(), 'testdir', 'kcm')
26conf = {'libdefaults': {'kcm_socket': kcm_socket_path,
27                        'kcm_mach_service': '-'}}
28realm = K5Realm(create_host=False, krb5_conf=conf)
29
30keyctl = which('keyctl')
31out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1)
32test_keyring = (keyctl is not None and
33                'Unknown credential cache type' not in out)
34if not test_keyring:
35    skipped('keyring ccache tests', 'keyring support not built')
36
37# Test kdestroy and klist of a non-existent ccache.
38mark('no ccache')
39realm.run([kdestroy])
40realm.run([klist], expected_code=1, expected_msg='No credentials cache found')
41
42# Test kinit with an inaccessible ccache.
43mark('inaccessible ccache')
44realm.kinit(realm.user_princ, password('user'), flags=['-c', 'testdir/xx/yy'],
45            expected_code=1, expected_msg='Failed to store credentials')
46
47# Test klist -s with a single ccache.
48mark('klist -s single ccache')
49realm.run([klist, '-s'], expected_code=1)
50realm.kinit(realm.user_princ, password('user'))
51realm.run([klist, '-s'])
52realm.kinit(realm.user_princ, password('user'), ['-l', '-1s'])
53realm.run([klist, '-s'], expected_code=1)
54realm.kinit(realm.user_princ, password('user'), ['-S', 'kadmin/admin'])
55realm.run([klist, '-s'])
56realm.run([kdestroy])
57realm.run([klist, '-s'], expected_code=1)
58
59realm.addprinc('alice', password('alice'))
60realm.addprinc('bob', password('bob'))
61realm.addprinc('carol', password('carol'))
62realm.addprinc('doug', password('doug'))
63
64def collection_test(realm, ccname):
65    cctype = ccname.partition(':')[0]
66    oldccname = realm.env['KRB5CCNAME']
67    realm.env['KRB5CCNAME'] = ccname
68
69    mark('%s collection, single cache' % cctype)
70    realm.run([klist, '-A', '-s'], expected_code=1)
71    realm.kinit('alice', password('alice'))
72    realm.run([klist], expected_msg='Default principal: alice@')
73    realm.run([klist, '-A', '-s'])
74    realm.run([kdestroy])
75    output = realm.run([klist], expected_code=1)
76    if 'No credentials cache' not in output and 'not found' not in output:
77        fail('Initial kdestroy failed to destroy primary cache.')
78    output = realm.run([klist, '-l'], expected_code=1)
79    if not output.endswith('---\n') or output.count('\n') != 2:
80        fail('Initial kdestroy failed to empty cache collection.')
81    realm.run([klist, '-A', '-s'], expected_code=1)
82
83    mark('%s collection, multiple caches' % cctype)
84    realm.kinit('alice', password('alice'))
85    realm.kinit('carol', password('carol'))
86    output = realm.run([klist, '-l'])
87    if '---\ncarol@' not in output or '\nalice@' not in output:
88        fail('klist -l did not show expected output after two kinits.')
89    realm.kinit('alice', password('alice'))
90    output = realm.run([klist, '-l'])
91    if '---\nalice@' not in output or output.count('\n') != 4:
92        fail('klist -l did not show expected output after re-kinit for alice.')
93    realm.kinit('doug', password('doug'))
94    realm.kinit('bob', password('bob'))
95    output = realm.run([klist, '-A', ccname])
96    if 'bob@' not in output.splitlines()[1] or 'alice@' not in output or \
97       'carol@' not in output or 'doug@' not in output or \
98       output.count('Default principal:') != 4:
99        fail('klist -A did not show expected output after kinit doug+bob.')
100    realm.run([kswitch, '-p', 'carol'])
101    output = realm.run([klist, '-l'])
102    if '---\ncarol@' not in output or output.count('\n') != 6:
103        fail('klist -l did not show expected output after kswitch to carol.')
104
105    # Switch to specifying the collection name on the command line
106    # (only works with klist/kdestroy for now, not kinit/kswitch).
107    realm.env['KRB5CCNAME'] = oldccname
108
109    mark('%s collection, command-line specifier' % cctype)
110    realm.run([kdestroy, '-c', ccname])
111    output = realm.run([klist, '-l', ccname])
112    if 'carol@' in output or 'bob@' not in output or output.count('\n') != 5:
113        fail('kdestroy failed to remove only primary ccache.')
114    realm.run([klist, '-s', ccname], expected_code=1)
115    realm.run([klist, '-A', '-s', ccname])
116    realm.run([kdestroy, '-p', 'alice', '-c', ccname])
117    output = realm.run([klist, '-l', ccname])
118    if 'alice@' in output or 'bob@' not in output or output.count('\n') != 4:
119        fail('kdestroy -p failed to remove alice')
120    realm.run([kdestroy, '-A', '-c', ccname])
121    output = realm.run([klist, '-l', ccname], expected_code=1)
122    if not output.endswith('---\n') or output.count('\n') != 2:
123        fail('kdestroy -a failed to empty cache collection.')
124    realm.run([klist, '-A', '-s', ccname], expected_code=1)
125
126
127collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc'))
128kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py')
129realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
130                   'starting...')
131collection_test(realm, 'KCM:')
132if test_keyring:
133    def cleanup_keyring(anchor, name):
134        out = realm.run(['keyctl', 'list', anchor])
135        if ('keyring: ' + name + '\n') in out:
136            keyid = realm.run(['keyctl', 'search', anchor, 'keyring', name])
137            realm.run(['keyctl', 'unlink', keyid.strip(), anchor])
138
139    # Use realm.testdir as the collection name to avoid conflicts with
140    # other build trees.
141    cname = realm.testdir
142    col_ringname = '_krb_' + cname
143
144    cleanup_keyring('@s', col_ringname)
145    collection_test(realm, 'KEYRING:session:' + cname)
146    cleanup_keyring('@s', col_ringname)
147
148    # Test legacy keyring cache linkage.
149    mark('legacy keyring cache linkage')
150    realm.env['KRB5CCNAME'] = 'KEYRING:' + cname
151    realm.run([kdestroy, '-A'])
152    realm.kinit(realm.user_princ, password('user'))
153    msg = 'KEYRING:legacy:' + cname + ':' + cname
154    realm.run([klist, '-l'], expected_msg=msg)
155    # Make sure this cache is linked to the session keyring.
156    id = realm.run([keyctl, 'search', '@s', 'keyring', cname])
157    realm.run([keyctl, 'list', id.strip()],
158              expected_msg='user: __krb5_princ__')
159    # Remove the collection keyring.  When the collection is
160    # reinitialized, the legacy cache should reappear inside it
161    # automatically as the primary cache.
162    cleanup_keyring('@s', col_ringname)
163    realm.run([klist], expected_msg=realm.user_princ)
164    coll_id = realm.run([keyctl, 'search', '@s', 'keyring', '_krb_' + cname])
165    msg = id.strip() + ':'
166    realm.run([keyctl, 'list', coll_id.strip()], expected_msg=msg)
167    # Destroy the cache and check that it is unlinked from the session keyring.
168    realm.run([kdestroy])
169    realm.run([keyctl, 'search', '@s', 'keyring', cname], expected_code=1)
170    cleanup_keyring('@s', col_ringname)
171
172# Test parameter expansion in default_ccache_name
173mark('default_ccache_name parameter expansion')
174realm.stop()
175conf = {'libdefaults': {'default_ccache_name': 'testdir/%{null}abc%{uid}'}}
176realm = K5Realm(krb5_conf=conf, create_kdb=False)
177del realm.env['KRB5CCNAME']
178uidstr = str(os.getuid())
179msg = 'testdir/abc%s' % uidstr
180realm.run([klist], expected_code=1, expected_msg=msg)
181
182success('Credential cache tests')
183