Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/clients/ksu/t_ksu.py
34890 views
1
from k5test import *
2
import pwd
3
import stat
4
5
krb5_conf = '/etc/krb5.conf'
6
krb5_conf_save = krb5_conf + '.save-ksutest'
7
krb5_conf_nosave = krb5_conf + '.nosave-ksutest'
8
ksu = './ksu.ksutest'
9
if 'SUDO_UID' not in os.environ or os.geteuid() != 0:
10
fail('this script must be run as root via sudo')
11
caller_uid = int(os.environ['SUDO_UID'])
12
if caller_uid == 0:
13
fail('the user invoking sudo must not be root')
14
caller_username = os.environ['SUDO_USER']
15
os.chown('testlog', caller_uid, -1)
16
17
# Set the real and effective UIDs to the calling user, but preserve
18
# the ability to restore root privileges.
19
def be_caller():
20
os.setresuid(caller_uid, caller_uid, 0)
21
22
23
# Restore root privileges.
24
def be_root():
25
os.setresuid(0, 0, 0)
26
27
28
# Remove the ksutest account.
29
def cleanup_user():
30
# userdel commonly gives a warning about being unable to delete
31
# the mail spool; filter it out.
32
out = subprocess.check_output(['userdel', '-r', 'ksutest'],
33
stderr=subprocess.STDOUT)
34
if out.count(b'\n') > 1 or b'ksutest mail spool' not in out:
35
print(out)
36
37
38
# Restore /etc/krb5.conf to the state it was in previously.
39
def cleanup_krb5_conf():
40
if os.path.exists(krb5_conf_save):
41
os.unlink(krb5_conf)
42
os.rename(krb5_conf_save, krb5_conf)
43
elif os.path.exists(krb5_conf_nosave):
44
os.unlink(krb5_conf)
45
os.unlink(krb5_conf_nosave)
46
47
48
def onexit():
49
if len(sys.argv) >= 2 and sys.argv[1] == 'nocleanup':
50
return
51
be_root()
52
cleanup_user()
53
cleanup_krb5_conf()
54
if os.path.exists(ksu):
55
os.unlink(ksu)
56
57
58
# Create a ksutest account and return its home directory.
59
def setup_user():
60
try:
61
ent = pwd.getpwnam('ksutest')
62
return ent.pw_dir
63
except KeyError:
64
subprocess.check_call(['useradd', '-m', '-r', 'ksutest'])
65
return pwd.getpwnam('ksutest').pw_dir
66
67
68
# Make krb5.conf a copy of realm's krb5.conf file. Save the old
69
# contents in krb5_conf_save, or create krb5_conf_noexist to indicate
70
# that the file didn't previously exist.
71
def setup_krb5_conf(realm):
72
if not os.path.exists(krb5_conf):
73
open(krb5_conf_nosave, 'w').close()
74
elif not os.path.exists(krb5_conf_save):
75
os.rename(krb5_conf, krb5_conf_save)
76
shutil.copyfile(os.path.join(realm.testdir, 'krb5.conf'), krb5_conf)
77
78
79
# Temporarily acting as root, write a file named fname in ksutest's
80
# home directory with the given contents. If wrong_owner is set, make
81
# the file owned by the caller uid in order to trip ksu's owner check.
82
def write_authz_file(fname, contents, wrong_owner=False):
83
be_root()
84
path = os.path.join(ksutest_home, fname)
85
with open(path, 'w') as f:
86
f.write('\n'.join(contents) + '\n')
87
if wrong_owner:
88
os.chown(path, caller_uid, -1)
89
be_caller()
90
91
92
# Temporarily acting as root, remove fname from ksutest's home
93
# directory.
94
def remove_authz_file(fname):
95
be_root()
96
path = os.path.join(ksutest_home, fname)
97
if os.path.exists(path):
98
os.remove(path)
99
be_caller()
100
101
102
be_caller()
103
104
# Set up a realm. Set default_keytab_name since ksu won't respect the
105
# KRB5_KTNAME environment variable.
106
keytab = os.path.join(os.getcwd(), 'testdir', 'keytab')
107
realm = K5Realm(create_user=False,
108
krb5_conf={'libdefaults': {'default_keytab_name': keytab}})
109
realm.addprinc('alice', 'pwalice')
110
realm.addprinc('ksutest', 'pwksutest')
111
realm.addprinc('ksutest/root', 'pwroot')
112
realm.addprinc(caller_username, 'pwcaller')
113
114
# Root setup:
115
# - /etc/krb5.conf is a copy of the test realm krb5.conf
116
# - a newly created user named ksutest exists (with homedir ksutest_home)
117
# - a setuid copy of ksu exists in the build dir
118
# Register an atexit handler to undo these changes.
119
atexit.register(onexit)
120
be_root()
121
ksutest_home = setup_user()
122
setup_krb5_conf(realm)
123
if os.path.exists(ksu):
124
os.unlink(ksu)
125
shutil.copyfile('ksu', ksu)
126
os.chmod(ksu, 0o4755)
127
be_caller()
128
129
mark('no authorization')
130
realm.kinit('alice', 'pwalice')
131
realm.run([ksu, 'ksutest', '-n', 'alice', '-a', '-c', klist], expected_code=1,
132
expected_msg='authorization of [email protected] failed')
133
134
mark('an2ln authorization')
135
realm.kinit('ksutest', 'pwksutest')
136
realm.run([ksu, 'ksutest', '-a', '-c', klist],
137
expected_msg='authorization for [email protected] successful')
138
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
139
expected_msg='account ksutest: authorization failed')
140
141
mark('.k5login wrong owner')
142
write_authz_file('.k5login', ['[email protected]'], wrong_owner=True)
143
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
144
expected_msg='account ksutest: authorization failed')
145
remove_authz_file('.k5login')
146
147
mark('.k5users wrong owner')
148
write_authz_file('.k5users', ['[email protected]'], wrong_owner=True)
149
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
150
expected_msg='account ksutest: authorization failed')
151
remove_authz_file('.k5users')
152
153
mark('.k5login authorization')
154
realm.kinit('alice', 'pwalice')
155
write_authz_file('.k5login', ['[email protected]'])
156
realm.run([ksu, 'ksutest', '-a', '-c', klist],
157
expected_msg='authorization for [email protected] successful')
158
realm.run([ksu, 'ksutest', '-e', klist],
159
expected_msg='authorization for [email protected] for execution of')
160
write_authz_file('.k5login', ['[email protected]'])
161
realm.run([ksu, 'ksutest', '-a', '-c', klist], expected_code=1,
162
expected_msg='account ksutest: authorization failed')
163
remove_authz_file('.k5login')
164
165
mark('.k5users authorization (no second field)')
166
write_authz_file('.k5users', ['[email protected]'])
167
realm.run([ksu, 'ksutest', '-a', '-c', klist],
168
expected_msg='authorization for [email protected] successful')
169
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
170
expected_msg='account ksutest: authorization failed')
171
write_authz_file('.k5users', ['[email protected]'])
172
realm.run([ksu, 'ksutest', '-a', '-c', klist], expected_code=1,
173
expected_msg='account ksutest: authorization failed')
174
175
mark('k5users authorization (wildcard)')
176
write_authz_file('.k5users', ['[email protected] *'])
177
realm.run([ksu, 'ksutest', '-a', '-c', klist],
178
expected_msg='authorization for [email protected] successful')
179
realm.run([ksu, 'ksutest', '-e', klist],
180
expected_msg='authorization for [email protected] for execution of')
181
182
mark('k5users authorization (command list)')
183
write_authz_file('.k5users', ['[email protected] doesnotexist ' + klist])
184
realm.run([ksu, 'ksutest', '-a', '-c', klist], expected_code=1,
185
expected_msg='account ksutest: authorization failed')
186
realm.run([ksu, 'ksutest', '-e', klist],
187
expected_msg='authorization for [email protected] for execution of')
188
realm.run([ksu, 'ksutest', '-e', kvno], expected_code=1,
189
expected_msg='account ksutest: authorization failed')
190
realm.run([ksu, 'ksutest', '-e', 'doesnotexist'], expected_code=1,
191
expected_msg='Error: not found ->')
192
remove_authz_file('.k5users')
193
194
mark('principal heuristic (no authz files)')
195
realm.run([ksu, 'ksutest', '-a', '-c', klist], input='pwksutest\n',
196
expected_msg='Authenticated [email protected]')
197
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
198
expected_msg='account ksutest: authorization failed')
199
200
mark('principal heuristic (empty authz files)')
201
write_authz_file('.k5login', [])
202
write_authz_file('.k5users', [])
203
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
204
expected_msg='account ksutest: authorization failed')
205
remove_authz_file('.k5login')
206
remove_authz_file('.k5users')
207
208
# Untested: if the ccache default principal is not authorized,
209
# get_best_princ_for_target() looks for a TGT or host service ticket
210
# for the target and source users (if authorized) or any other
211
# authorized user. This is not really useful because a ccache usually
212
# only contains tickets for its default client principal (aside from
213
# caches created for S4U2Proxy). If the heuristic is ever changed to
214
# search the cache collection instead of only the primary cache, we
215
# should add tests for that here.
216
217
mark('principal heuristic (.k5login)')
218
write_authz_file('.k5login', ['[email protected]'])
219
realm.run([ksu, 'ksutest', '-a', '-c', klist], input='pwksutest\n',
220
expected_msg='Authenticated [email protected]')
221
realm.run([ksu, 'ksutest', '-e', klist], input='pwksutest\n',
222
expected_msg='Authenticated [email protected]')
223
write_authz_file('.k5login', [caller_username + '@KRBTEST.COM'])
224
realm.run([ksu, 'ksutest', '-e', klist], input='pwcaller\n',
225
expected_msg='Authenticated %[email protected]' % caller_username)
226
remove_authz_file('.k5login')
227
228
mark('principal heuristic (.k5users)')
229
write_authz_file('.k5users', ['[email protected] ' + klist,
230
'[email protected]',
231
caller_username + '@KRBTEST.COM *'])
232
realm.run([ksu, 'ksutest', '-e', klist],
233
expected_msg='Authenticated [email protected]')
234
realm.run([ksu, 'ksutest', '-a', '-c', klist], input='pwksutest\n',
235
expected_msg='Authenticated [email protected]')
236
realm.run([ksu, 'ksutest', '-e', kvno, 'alice'], input='pwcaller\n',
237
expected_msg='Authenticated %[email protected]' % caller_username)
238
write_authz_file('.k5users', ['[email protected] ' + klist,
239
'ksutest/[email protected] ' + kvno])
240
realm.run([ksu, 'ksutest', '-e', kvno, 'alice'], input='pwroot\n',
241
expected_msg='Authenticated ksutest/[email protected]')
242
243
mark('principal heuristic (no authorization)')
244
realm.run([ksu, '.', '-e', klist],
245
expected_msg='Default principal: [email protected]')
246
be_root()
247
realm.run([ksu, 'ksutest', '-e', klist], expected_code=1,
248
expected_msg='No credentials cache found')
249
be_caller()
250
realm.kinit('ksutest', 'pwksutest')
251
be_root()
252
realm.run([ksu, 'ksutest', '-e', klist],
253
expected_msg='Default principal: [email protected]')
254
be_caller()
255
realm.run([kdestroy])
256
realm.run([ksu, '.', '-e', klist], expected_code=1,
257
expected_msg='No credentials cache found')
258
259
mark('authentication without authorization')
260
realm.run([ksu, '.', '-n', 'ksutest', '-e', klist], input='pwksutest\n',
261
expected_msg='Leaving uid as ' + caller_username)
262
263
# It's hard to make this flag do anything detectable, but we can
264
# exercise the code.
265
mark('-z flag')
266
realm.kinit(caller_username, 'pwcaller')
267
realm.run([ksu, '.', '-z', '-e', klist],
268
expected_msg='Default principal: ' + caller_username)
269
270
realm.run([ksu, '.', '-Z', '-e', klist], expected_code=1,
271
expected_msg='No credentials cache found')
272
273
success('ksu tests')
274
275