7f964a3415295713beae5eedcaee443cf0d09da0
[krb5.git] / src / tests / dejagnu / config / default.exp
1 # Basic expect script for Kerberos tests.
2 # This is a DejaGnu test script.
3 # Written by Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>.
4 # This script is automatically run by DejaGnu before running any of
5 # the Kerberos test scripts.
6
7 # This file provides several functions which deal with a local
8 # Kerberos database.  We have to do this such that we don't interfere
9 # with any existing Kerberos database.  We will create all the files
10 # in the directory $tmppwd, which will have been created by the
11 # testsuite default script.  We will use $REALMNAME as our Kerberos
12 # realm name, defaulting to KRBTEST.COM.
13
14 set timeout 100
15 set stty_init {erase \^h kill \^u}
16 set env(TERM) dumb
17
18 set des3_krbtgt 0
19 set tgt_support_desmd5 0
20 set supported_enctypes "des-cbc-crc:normal"
21 set kdc_supported_enctypes "des-cbc-crc:normal"
22
23 # The names of the individual passes must be unique; lots of things
24 # depend on it.  The PASSES variable may not contain comments; only
25 # small pieces get evaluated, so comments will do strange things.
26
27 # Most of the purpose of using multiple passes is to exercise the
28 # dependency of various bugs on configuration file settings,
29 # particularly with regards to encryption types.
30
31 # The des.no-kdc-md5 pass will fail if the KDC does not constrain
32 # session key enctypes to those in its permitted_enctypes list.  It
33 # works by assuming enctype similarity, thus allowing the client to
34 # request a des-cbc-md4 session key.  Since only des-cbc-crc is in the
35 # KDC's permitted_enctypes list, the TGT will be unusable.
36
37 # KLUDGE for tracking down leaking ptys
38 if 0 {
39     rename spawn oldspawn
40     rename wait oldwait
41     proc spawn { args } {
42         upvar 1 spawn_id spawn_id
43         verbose "spawn: args=$args"
44         set pid [eval oldspawn $args]
45         verbose "spawn: pid=$pid spawn_id=$spawn_id"
46         return $pid
47     }
48     proc wait { args } {
49         upvar 1 spawn_id spawn_id
50         verbose "wait: args=$args"
51         set ret [eval oldwait $args]
52         verbose "wait: $ret"
53         return $ret
54     }
55 }
56
57 # Hack around Solaris 9 kernel race condition that causes last output
58 # from a pty to get dropped.
59 if { $PRIOCNTL_HACK } {
60     catch {exec priocntl -s -c FX -m 30 -p 30 -i pid [getpid]}
61     rename spawn oldspawn
62     proc spawn { args } {
63         upvar 1 spawn_id spawn_id
64         set newargs {}
65         set inflags 1
66         set eatnext 0
67         foreach arg $args {
68             if { $arg == "-ignore" \
69                      || $arg == "-open" \
70                      || $arg == "-leaveopen" } {
71                 lappend newargs $arg
72                 set eatnext 1
73                 continue
74             }
75             if [string match "-*" $arg] {
76                 lappend newargs $arg
77                 continue
78             }
79             if { $eatnext } {
80                 set eatnext 0
81                 lappend newargs $arg
82                 continue
83             }
84             if { $inflags } {
85                 set inflags 0
86                 set newargs [concat $newargs {priocntl -e -c FX -p 0}]
87             }
88             lappend newargs $arg
89         }
90         set pid [eval oldspawn $newargs]
91         return $pid
92     }
93 }
94
95 # The des.des3-tgt.no-kdc-des3 pass will fail if the KDC doesn't
96 # constrain ticket key enctypes to those in permitted_enctypes.  It
97 # does this by not putting des3 in the permitted_enctypes, while
98 # creating a TGT princpal that has a des3 key as well as a des key.
99
100 # XXX -- master_key_type is fragile w.r.t. permitted_enctypes; it is
101 # possible to configure things such that you have a master_key_type
102 # that is not permitted, and the error message used to be cryptic.
103
104 set passes {
105     {
106         des
107         mode=udp
108         des3_krbtgt=0
109         {supported_enctypes=des-cbc-crc:normal}
110         {kdc_supported_enctypes=des-cbc-crc:normal}
111         {dummy=[verbose -log "DES TGT, DES enctype"]}
112     }
113     {
114         des.des3tgt
115         mode=udp
116         des3_krbtgt=1
117         {supported_enctypes=des-cbc-crc:normal}
118         {kdc_supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal}
119         {dummy=[verbose -log "DES3 TGT, DES enctype"]}
120     }
121     {
122         des3
123         mode=udp
124         des3_krbtgt=1
125         {supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal}
126         {kdc_supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal}
127         {dummy=[verbose -log "DES3 TGT, DES3 + DES enctypes"]}
128     }
129     {
130         aes
131         mode=udp
132         des3_krbtgt=0
133         {supported_enctypes=aes256-cts-hmac-sha1-96:normal des-cbc-crc:normal}
134         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal des-cbc-crc:normal}
135         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des-cbc-crc}
136         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des-cbc-crc}
137         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des-cbc-crc}
138         {master_key_type=aes256-cts-hmac-sha1-96}
139         {dummy=[verbose -log "AES + DES enctypes"]}
140     }
141     {
142         aesonly
143         mode=udp
144         des3_krbtgt=0
145         {supported_enctypes=aes256-cts-hmac-sha1-96:normal}
146         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal}
147         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96}
148         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96}
149         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96}
150         {master_key_type=aes256-cts-hmac-sha1-96}
151         {dummy=[verbose -log "AES enctypes"]}
152     }
153     {
154         aes-tcp
155         mode=tcp
156         des3_krbtgt=0
157         {supported_enctypes=aes256-cts-hmac-sha1-96:normal}
158         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal}
159         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96}
160         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96}
161         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96}
162         {master_key_type=aes256-cts-hmac-sha1-96}
163         {dummy=[verbose -log "AES via TCP"]}
164     }
165     {
166         aes-des3
167         mode=udp
168         des3_krbtgt=0
169         {supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal}
170         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal}
171         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
172         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
173         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
174         {master_key_type=aes256-cts-hmac-sha1-96}
175         {dummy=[verbose -log "AES + DES enctypes"]}
176     }
177     {
178         des3-aes
179         mode=udp
180         des3_krbtgt=1
181         {supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal}
182         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal}
183         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
184         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
185         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc}
186         {master_key_type=aes256-cts-hmac-sha1-96}
187         {dummy=[verbose -log "AES + DES enctypes, DES3 TGT"]}
188     }
189     {
190         des-v4
191         mode=udp
192         des3_krbtgt=0
193         {supported_enctypes=des-cbc-crc:v4}
194         {kdc_supported_enctypes=des-cbc-crc:v4}
195         {default_tkt_enctypes(client)=des-cbc-crc}
196         {dummy=[verbose -log "DES TGT, DES-CRC enctype, V4 salt"]}
197     }
198     {
199         des-md5-v4
200         mode=udp
201         des3_krbtgt=0
202         {supported_enctypes=des-cbc-md5:v4 des-cbc-crc:v4}
203         {kdc_supported_enctypes=des-cbc-md5:v4 des-cbc-crc:v4}
204         {default_tkt_enctypes(client)=des-cbc-md5 des-cbc-crc}
205         {dummy=[verbose -log "DES TGT, DES-MD5 and -CRC enctypes, V4 salt"]}
206     }
207     {
208         all-des-des3-enctypes
209         mode=udp
210         des3_krbtgt=1
211         {supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal \
212                 des-cbc-md5:normal des-cbc-crc:v4 des-cbc-md5:norealm \
213                 des-cbc-md4:normal}
214         {kdc_supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal \
215                 des-cbc-md5:normal des-cbc-crc:v4 des-cbc-md5:norealm \
216                 des-cbc-md4:normal}
217         {dummy=[verbose -log "DES3 TGT, many DES3 + DES enctypes"]}
218     }
219     {
220         des.no-kdc-md5
221         mode=udp
222         des3_krbtgt=0
223         tgt_support_desmd5=0
224         {permitted_enctypes(kdc)=des-cbc-crc}
225         {default_tgs_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc}
226         {default_tkt_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc}
227         {supported_enctypes=des-cbc-crc:normal}
228         {kdc_supported_enctypes=des-cbc-crc:normal}
229         {master_key_type=des-cbc-crc}
230         {dummy=[verbose -log \
231                 "DES TGT, KDC permitting only des-cbc-crc"]}
232     }
233     {
234         des.des3-tgt.no-kdc-des3
235         mode=udp
236         tgt_support_desmd5=0
237         {permitted_enctypes(kdc)=des-cbc-crc}
238         {default_tgs_enctypes(client)=des-cbc-crc}
239         {default_tkt_enctypes(client)=des-cbc-crc}
240         {supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal}
241         {kdc_supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal}
242         {master_key_type=des-cbc-crc}
243         {dummy=[verbose -log \
244                 "DES3 TGT, KDC permitting only des-cbc-crc"]}
245     }
246 }
247
248 # des.md5-tgt is set as unused, since it won't trigger the error case
249 # if SUPPORT_DESMD5 isn't honored.
250
251 # The des.md5-tgt pass will fail if enctype similarity is inconsisent;
252 # between 1.0.x and 1.1, the decrypt functions became more strict
253 # about matching enctypes, while the KDB retrieval functions didn't
254 # coerce the enctype to match what was requested.  It works by setting
255 # SUPPORT_DESMD5 on the TGT principal, forcing an enctype of
256 # des-cbc-md5 on the TGT key.  Since the database only contains a
257 # des-cbc-crc key, the decrypt will fail if enctypes are not coerced.
258
259 # des.no-kdc-md5.client-md4-skey is retained in unsed_passes, even
260 # though des.no-kdc-md5 is roughly equivalent, since the associated
261 # comment needs additional investigation at some point re the kadmin
262 # client.
263
264 # The des.no-kdc-md5.client-md4-skey will fail on TGS requests due to
265 # the KDC issuing session keys that it won't accept.  It will also
266 # fail for a kadmin client, but for different reasons, since the kadm5
267 # library does some curious filtering of enctypes, and also uses
268 # get_in_tkt() rather than get_init_creds(); the former does an
269 # intersection of the enctypes provided by the caller and those listed
270 # in the config file!
271
272 set unused_passes {
273     {
274         des.md5-tgt
275         des3_krbtgt=0
276         tgt_support_desmd5=1
277         supported_enctypes=des-cbc-crc:normal
278         kdc_supported_enctypes=des-cbc-crc:normal
279         {permitted_enctypes(kdc)=des-cbc-md5 des-cbc-md4 des-cbc-crc}
280         {permitted_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc}
281         {dummy=[verbose -log "DES TGT, SUPPORTS_DESMD5"]}
282     }
283     {
284         des.md5-tgt.no-kdc-md5
285         des3_krbtgt=0
286         tgt_support_desmd5=1
287         {permitted_enctypes(kdc)=des-cbc-crc}
288         {default_tgs_enctypes(client)=des-cbc-crc}
289         {default_tkt_enctypes(client)=des-cbc-crc}
290         {supported_enctypes=des-cbc-crc:normal}
291         {kdc_supported_enctypes=des-cbc-crc:normal}
292         {master_key_type=des-cbc-crc}
293         {dummy=[verbose -log \
294                 "DES TGT, SUPPORTS_DESMD5, KDC permitting only des-cbc-crc"]}
295     }
296     {
297         des.no-kdc-md5.client-md4-skey
298         des3_krbtgt=0
299         {permitted_enctypes(kdc)=des-cbc-crc}
300         {permitted_enctypes(client)=des-cbc-crc des-cbc-md4}
301         {default_tgs_enctypes(client)=des-cbc-crc des-cbc-md4}
302         {default_tkt_enctypes(client)=des-cbc-md4}
303         {supported_enctypes=des-cbc-crc:normal}
304         {kdc_supported_enctypes=des-cbc-crc:normal}
305         {dummy=[verbose -log \
306                 "DES TGT, DES enctype, KDC permitting only des-cbc-crc, client requests des-cbc-md4 session key"]}
307     }
308     {
309         all-enctypes
310         des3_krbtgt=1
311         {supported_enctypes=\
312         aes256-cts-hmac-sha1-96:normal aes256-cts-hmac-sha1-96:norealm \
313         aes128-cts-hmac-sha1-96:normal aes128-cts-hmac-sha1-96:norealm \
314         des3-cbc-sha1:normal des3-cbc-sha1:none \
315         des-cbc-md5:normal des-cbc-md4:normal des-cbc-crc:normal \
316         des-cbc-md5:v4 des-cbc-md4:v4 des-cbc-crc:v4 \
317         }
318         {kdc_supported_enctypes=\
319         des3-cbc-sha1:normal des3-cbc-sha1:none \
320         des-cbc-md5:normal des-cbc-md4:normal des-cbc-crc:normal \
321         des-cbc-md5:v4 des-cbc-md4:v4 des-cbc-crc:v4 \
322         }
323         {dummy=[verbose -log "DES3 TGT, default enctypes"]}
324     }
325     # This won't work for anything using GSSAPI until it gets AES support.
326     {
327         aes-only
328         des3_krbtgt=0
329         {supported_enctypes=aes256-cts-hmac-sha1-96:normal}
330         {kdc_supported_enctypes=aes256-cts-hmac-sha1-96:normal}
331         {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96}
332         {permitted_enctypes(client)=aes256-cts-hmac-sha1-96}
333         {permitted_enctypes(server)=aes256-cts-hmac-sha1-96}
334         {master_key_type=aes256-cts-hmac-sha1-96}
335         {dummy=[verbose -log "AES only, no DES or DES3 support"]}
336     }
337 }
338 #       {supported_enctypes=des-cbc-md5:normal des-cbc-crc:normal twofish256-hmac-sha1:normal }
339 #       {kdc_supported_enctypes= des-cbc-md5:normal des-cbc-crc:normal twofish256-hmac-sha1:normal}
340
341 # This shouldn't be necessary on dejagnu-1.4 and later, but 1.3 seems
342 # to need it because its runtest.exp doesn't deal with PASS at all.
343 if [info exists PASS] {
344     foreach pass $passes {
345         if { [lsearch -exact $PASS [lindex $pass 0]] >= 0 } {
346             lappend MULTIPASS $pass
347         }
348     }
349 } else {
350     set MULTIPASS $passes
351 }
352
353 set last_passname_conf ""
354 set last_passname_db ""
355
356 # We do everything in a temporary directory.
357 if ![info exists TMPDIR] {
358     set tmppwd "[pwd]/tmpdir"
359     if ![file isdirectory $tmppwd] {
360         catch "exec mkdir $tmppwd" status
361     }
362 } else {
363     set tmppwd $TMPDIR
364 }
365 verbose "tmppwd=$tmppwd"
366
367 # On Ultrix, use /bin/sh5 in preference to /bin/sh.
368 if ![info exists BINSH] {
369     if [file exists /bin/sh5] {
370         set BINSH /bin/sh5
371     } else {
372         set BINSH /bin/sh
373     }
374 }
375
376 # For security, we must not use generally known passwords.  This is
377 # because some of the tests may be run as root.  If the passwords were
378 # generally know, then somebody could work out the appropriate
379 # Kerberos ticket to use, and come in when, say, the telnetd daemon
380 # was being tested by root.  The window for doing this is very very
381 # small, so the password does not have to be perfect, it just can't be
382 # constant.
383 if ![info exists KEY] {
384     catch {exec $BINSH -c "echo $$"} KEY
385     verbose "KEY is $KEY"
386     set keyfile [open $tmppwd/KEY w]
387     puts $keyfile "$KEY"
388     close $keyfile
389 }
390
391 # Clear away any files left over from a previous run.
392 # We can't use them now because we don't know the right KEY.
393 # krb5.conf might change if running tests on another host
394 catch "exec rm -f $tmppwd/db.ok $tmppwd/srvtab $tmppwd/krb5.conf $tmppwd/kdc.conf $tmppwd/cpw_srvtab $tmppwd/krb.realms $tmppwd/krb.conf"
395
396 # Put the installed kerberos directories on PATH.
397 # This needs to be fixed for V5.
398 # set env(PATH) $env(PATH):/usr/kerberos/bin:/usr/kerberos/etc
399 # verbose "PATH=$env(PATH)"
400
401 # Some of the tests expect $env(USER) to be set.
402 if ![info exists env(USER)] {
403     if [info exists env(LOGNAME)] {
404         set env(USER) $env(LOGNAME)
405     } else {
406         if [info exists logname] {
407             set env(USER) $logname
408         } else {
409             catch "exec whoami" env(USER)
410         }
411     }
412 }
413
414 # set the realm. The user can override this on the runtest line.
415 if ![info exists REALMNAME] {
416     set REALMNAME "KRBTEST.COM"
417 }
418 verbose "Test realm is $REALMNAME"
419
420 # Find some programs we need.  We use the binaries from the build tree
421 # if they exist.  If they do not, then they must be in PATH.  We
422 # expect $objdir to be ...tests/dejagnu.
423
424 foreach i {
425     {KDB5_UTIL $objdir/../../kadmin/dbutil/kdb5_util}
426     {KRB5KDC $objdir/../../kdc/krb5kdc}
427     {KADMIND $objdir/../../kadmin/server/kadmind}
428     {KADMIN $objdir/../../kadmin/cli/kadmin}
429     {KADMIN_LOCAL $objdir/../../kadmin/cli/kadmin.local}
430     {KINIT $objdir/../../clients/kinit/kinit}
431     {KTUTIL $objdir/../../kadmin/ktutil/ktutil}
432     {KLIST $objdir/../../clients/klist/klist}
433     {KDESTROY $objdir/../../clients/kdestroy/kdestroy}
434     {RESOLVE $objdir/../resolve/resolve}
435     {T_INETD $objdir/t_inetd}
436 } {
437     set varname [lindex $i 0]
438     if ![info exists $varname] {
439         eval set varval [lindex $i 1]
440         set varval [findfile $varval]
441         set $varname $varval
442         verbose "$varname=$varval"
443     } {
444         eval set varval \$$varname
445         verbose "$varname already set to $varval"
446     }
447 }
448
449 if ![info exists RLOGIN] {
450     set RLOGIN rlogin
451 }
452
453 if ![info exists RLOGIN_FLAGS] {
454     set RLOGIN_FLAGS "-x"
455 }
456
457 # We use a couple of variables to hold shell prompts which may be
458 # overridden by the user.
459
460 if ![info exists ROOT_PROMPT] {
461     set ROOT_PROMPT "(%|#|>|\\$) $"
462 }
463
464 if ![info exists SHELL_PROMPT] {
465     set SHELL_PROMPT "(%|#|>|\\$) $"
466 }
467
468 verbose "setting up onexit handler (old handler=[exit -onexit])"
469 exit -onexit [concat {
470     verbose "calling stop_kerberos_daemons (onexit handler)"
471     stop_kerberos_daemons;
472 } [exit -onexit]]
473
474 # check_k5login
475
476 # Most of the tests won't work if the user has a .k5login file, unless
477 # the user's name appears with $REALMNAME in .k5login
478
479 # This procedure returns 1 if the .k5login file appears to be OK, 0
480 # otherwise.  This check is not foolproof.
481
482 # Note that this previously checked for a username with no realm; this
483 # works for krb4's kuserok() but not for krb5_kuserok(), due to some
484 # implementation details.  *sigh*
485
486 proc check_k5login { testname } {
487     global env
488     global REALMNAME
489
490     if {![file exists ~/.k5login]} {
491         if {$env(USER) == "root"} {
492             return 0
493         } else {
494             return 1
495         }
496     }
497
498     verbose "looking for $env(USER)@$REALMNAME in ~/.k5login" 2
499     set file [open ~/.k5login r]
500     while { [gets $file principal] != -1 } {
501         verbose " found $principal" 2
502         if { $principal == "$env(USER)@$REALMNAME" } {
503             close $file
504             return 1
505         }
506     }
507     close $file
508
509     note "$testname test requires that your name appear in your ~/.k5login"
510     note "file in the form $env(USER)@$REALMNAME"
511     unsupported "$testname"
512
513     return 0
514 }
515
516 proc check_klogin { testname } {
517     global env
518     global REALMNAME
519
520     if {![file exists ~/.klogin]} {
521         if {$env(USER) == "root"} {
522             return 0
523         } else {
524             return 1
525         }
526     }
527
528     verbose "looking for $env(USER) in ~/.klogin" 2
529     set file [open ~/.klogin r]
530     while { [gets $file principal] != -1 } {
531         verbose " found $principal" 2
532         if { $principal == "$env(USER)" \
533                 || $principal == "$env(USER)@$REALMNAME" } {
534             close $file
535             return 1
536         }
537     }
538     close $file
539
540     note "$testname test requires that your name appear in your ~/.klogin"
541     note "file without a realm."
542     unsupported "$testname"
543
544     return 0
545 }
546
547 # check_exit_status
548 # Check the exit status of a spawned program.  Returns 1 if the
549 # program succeeded, 0 if it failed.
550
551 proc check_exit_status { testname } {
552     global spawn_id
553
554     verbose "about to wait ($testname)"
555     set status_list [wait -i $spawn_id]
556     verbose "wait -i $spawn_id returned $status_list ($testname)"
557     catch "close -i $spawn_id"
558     if { [lindex $status_list 2] != 0 || [lindex $status_list 3] != 0 } {
559         verbose -log "exit status: $status_list"
560         fail "$testname"
561         return 0
562     } else {
563         return 1
564     }
565 }
566
567 #
568 # ENVSTACK
569 #
570
571 # These procedures implement an environment variable stack.  They use
572 # the global variable $envvars_tosave for the purpose of identifying
573 # which environment variables to save.  They also track which ones are
574 # unset at any particular point.  The stack pointer is $envstackp,
575 # which is an integer.  The arrays $envstack$envstackp and
576 # $unenvstack$envstackp store respectively the set of old environment
577 # variables/values pushed onto the stack and the set of old unset
578 # environment variables for a given value of $envstackp.
579
580 # Changing the value of $envvars_tosave after performing the first
581 # push operation may result in strangeness.
582
583 #
584 # envstack_push
585 #
586 # Push set of current environment variables.
587 #
588 proc envstack_push { } {
589     global env
590     global envvars_tosave
591     global envstackp
592     global envstack$envstackp
593     global unenvstack$envstackp
594
595     verbose "envstack_push: starting, sp=$envstackp"
596     foreach i $envvars_tosave {
597         if [info exists env($i)] {
598             verbose "envstack_push: saving $i=$env($i)"
599             set envstack${envstackp}($i) $env($i)
600         } {
601             verbose "envstack_push: marking $i as unset"
602             set unenvstack${envstackp}($i) unset
603         }
604     }
605     incr envstackp
606     verbose "envstack_push: exiting, sp=$envstackp"
607 }
608
609 #
610 # envstack_pop
611 #
612 # Pop set of current environment variables.
613 #
614 proc envstack_pop { } {
615     global env
616     global envstackp
617
618     verbose "envstack_pop: starting, sp=$envstackp"
619     incr envstackp -1
620     global envstack$envstackp   # YUCK!!! no obvious better way though...
621     global unenvstack$envstackp
622     if {$envstackp < 0} {
623         perror "envstack_pop: stack underflow!"
624         return
625     }
626     if [info exists envstack$envstackp] {
627         foreach i [array names envstack$envstackp] {
628             if [info exists env($i)] {
629                 verbose "envstack_pop: $i was $env($i)"
630             }
631             eval set env($i) \$envstack${envstackp}($i)
632             verbose "envstack_pop: restored $i to $env($i)"
633         }
634         unset envstack$envstackp
635     }
636     if [info exists unenvstack$envstackp] {
637         foreach i [array names unenvstack$envstackp] {
638             if [info exists env($i)] {
639                 verbose "envstack_pop: $i was $env($i)"
640                 unset env($i)
641                 verbose "envstack_pop: $i unset"
642             } {
643                 verbose "envstack_pop: ignoring already unset $i"
644             }
645         }
646         unset unenvstack$envstackp
647     }
648     verbose "envstack_pop: exiting, sp=$envstackp"
649 }
650
651 #
652 # Initialize the envstack
653 #
654 set envvars_tosave {
655     KRB5_CONFIG KRB5CCNAME KRBTKFILE KRB5RCACHEDIR
656     KERBEROS_SERVER KRB5_KDC_PROFILE
657 }
658 set krb5_init_vars [list ]
659 # XXX -- fix me later!
660 foreach i $runvarlist {
661     verbose "processing $i"
662     if {[regexp "^(\[^=\]*)=(.*)" $i foo evar evalue]} {
663         verbose "adding $evar to savelist"
664         lappend envvars_tosave $evar
665         verbose "savelist $envvars_tosave"
666         lappend krb5_init_vars $i
667     }
668 }
669 set envstackp 0
670 envstack_push
671
672 # setup_runtime_flags
673 # Sets the proper flags for shared libraries. 
674 # Configuration is through a site.exp and the runvarlist variable
675 # Returns 1 if variables were already set, otherwise 0
676 proc setup_runtime_env { } {
677     global env
678     global krb5_init_vars
679
680     # Set the variables
681     foreach i $krb5_init_vars {
682         regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
683         set env($evar) "$evalue"
684         verbose "$evar=$evalue"
685     }
686     return 0
687 }
688
689 # get_hostname
690 # This procedure will get the local hostname.  It sets the global
691 # variables hostname (the full name) and localhostname (the first part
692 # of the name).  Returns 1 on success, 0 on failure.
693
694 proc get_hostname { } {
695     global RESOLVE
696     global hostname
697     global localhostname
698     global domain
699     global tmppwd
700
701     if {[info exists hostname] && [info exists localhostname]} {
702         return 1
703     }
704
705     envstack_push
706     setup_runtime_env
707     catch "exec $RESOLVE -q >$tmppwd/hostname" exec_output
708     envstack_pop
709     if ![string match "" $exec_output] {
710         verbose -log $exec_output
711         perror "can't get hostname"
712         return 0
713     }
714     set file [open $tmppwd/hostname r]
715     if { [ gets $file hostname ] == -1 } {
716         perror "no output from hostname"
717         return 0
718     }
719     close $file
720     catch "exec rm -f $tmppwd/hostname" exec_output
721     regexp "^(\[^.\]*)\\.(.*)$" $hostname foo localhostname domain
722
723     set hostname [string tolower $hostname]
724     set localhostname [string tolower $localhostname]
725     set domain [string tolower $domain]
726     verbose "hostname: $hostname; localhostname: $localhostname; domain $domain"
727
728     return 1
729 }
730
731 # modify_principal name options...
732
733 proc modify_principal { name args } {
734     global KADMIN_LOCAL
735     global REALMNAME
736
737     spawn $KADMIN_LOCAL -r $REALMNAME
738     expect_after {
739         eof {
740             fail "modprinc (kadmin.local)"
741             return 0
742         }
743         timeout {
744             fail "modprinc (kadmin.local)"
745             return 0
746         }
747     }
748     expect "kadmin.local: "
749     send "modprinc $args $name\r"
750     expect -re "modprinc \[^\n\r\]* $name"
751     expect -re "Principal .* modified."
752     send "quit\r"
753     expect eof
754     catch expect_after
755     if ![check_exit_status "kadmin.local modprinc"] {
756         perror "kadmin.local modprinc exited abnormally"
757     }
758     return 1
759 }
760
761 # kdc listens on +0..+3, depending whether we're testing reachable or not
762 # client tries +1 and +6
763 # kadmind +4
764 # kpasswd +5
765 # krb524 +7
766 # application servers (krlogind, telnetd, krshd, ftpd, etc) +8
767 if [info exists PORTBASE] {
768     set portbase $PORTBASE
769 } else {
770     set portbase 3085
771 }
772
773 # setup_kerberos_files
774 # This procedure will create some Kerberos files which must be created
775 # manually before trying to run any Kerberos programs.  Returns 1 on
776 # success, 0 on failure.
777
778 proc setup_kerberos_files { } {
779     global REALMNAME
780     global hostname
781     global domain
782     global tmppwd
783     global supported_enctypes
784     global kdc_supported_enctypes
785     global last_passname_conf
786     global multipass_name
787     global master_key_type
788     global mode
789     global portbase
790
791     if ![get_hostname] { 
792         return 0
793     }
794
795     setup_krb5_conf client
796     setup_krb5_conf server
797     setup_krb5_conf kdc
798
799     # Create a kdc.conf file.
800     if { ![file exists $tmppwd/kdc.conf] \
801             || $last_passname_conf != $multipass_name } {
802         if ![info exists master_key_type] {
803             set master_key_type des-cbc-md5
804         }
805         set conffile [open $tmppwd/kdc.conf w]
806         puts $conffile "\[kdcdefaults\]"
807         puts $conffile "        kdc_ports = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]"
808         puts $conffile "        kdc_tcp_ports = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]"
809         puts $conffile ""
810         puts $conffile "\[realms\]"
811         puts $conffile "        $REALMNAME = \{"
812 #       puts $conffile "                database_name = $tmppwd/db"
813         puts $conffile "                admin_database_name = $tmppwd/adb"
814         puts $conffile "                admin_database_lockfile = $tmppwd/adb.lock"
815         puts $conffile "                key_stash_file = $tmppwd/stash"
816         puts $conffile "                acl_file = $tmppwd/acl"
817         puts $conffile "                kadmind_port = [expr 4 + $portbase]"
818         puts $conffile "                kpasswd_port = [expr 5 + $portbase]"
819         puts $conffile "                max_life = 1:00:00"
820         puts $conffile "                max_renewable_life = 3:00:00"
821         puts $conffile "                master_key_type = $master_key_type"
822         puts $conffile "                master_key_name = master/key"
823         puts $conffile "                supported_enctypes = $supported_enctypes"
824         puts $conffile "                kdc_supported_enctypes = $kdc_supported_enctypes"
825         if { $mode == "tcp" } {
826             puts $conffile "            kdc_ports = [expr 3 + $portbase]"
827             puts $conffile "            kdc_tcp_ports = [expr 1 + $portbase],[expr 3 + $portbase]"
828         } else {
829             puts $conffile "            kdc_ports = [expr 1 + $portbase]"
830             puts $conffile "            kdc_tcp_ports = [expr 3 + $portbase]"
831         }
832         puts $conffile "                default_principal_expiration = 2037.12.31.23.59.59"
833         puts $conffile "                default_principal_flags = -postdateable forwardable"
834         puts $conffile "                dict_file = $tmppwd/dictfile"
835         puts $conffile "        \}"
836         puts $conffile ""
837         close $conffile
838     }
839
840     # Create ACL file.
841     if ![file exists $tmppwd/acl] {
842         set aclfile [open $tmppwd/acl w]
843         puts $aclfile "krbtest/admin@$REALMNAME *"
844         close $aclfile
845     }
846
847     # Create krb.conf file
848     if ![file exists $tmppwd/krb.conf] {
849         set conffile [open $tmppwd/krb.conf w]
850         puts $conffile "$REALMNAME"
851         puts $conffile "$REALMNAME $hostname:[expr 1 + $portbase] admin server"
852         close $conffile
853     }
854
855     # Create krb.realms file
856     if ![file exists $tmppwd/krb.realms] {
857         set conffile [open $tmppwd/krb.realms w]
858         puts $conffile ".[string toupper $domain] $REALMNAME"
859         puts $conffile "[string toupper $domain]. $REALMNAME"
860         close $conffile
861     }
862
863     # Create dictfile file.
864     if ![file exists $tmppwd/dictfile] {
865         set dictfile [open $tmppwd/dictfile w]
866         puts $dictfile "weak_password"
867         close $dictfile
868     }
869
870     set last_passname_conf $multipass_name
871     return 1
872 }
873
874 proc setup_krb5_conf { {type client} } {
875     global tmppwd
876     global hostname
877     global domain
878     global REALMNAME
879     global last_passname_conf
880     global multipass_name
881     global default_tgs_enctypes
882     global default_tkt_enctypes
883     global permitted_enctypes
884     global mode
885     global portbase
886     global KRB5_DB_MODULE_DIR
887
888     # Create a krb5.conf file.
889     if { ![file exists $tmppwd/krb5.$type.conf] \
890             || $last_passname_conf != $multipass_name } {
891         set conffile [open $tmppwd/krb5.$type.conf w]
892         puts $conffile "\[libdefaults\]"
893         puts $conffile "        default_realm = $REALMNAME"
894         puts $conffile "        dns_lookup_kdc = false"
895         if [info exists default_tgs_enctypes($type)] {
896             puts $conffile \
897                     "   default_tgs_enctypes = $default_tgs_enctypes($type)"
898         }
899         if [info exists default_tkt_enctypes($type)] {
900             puts $conffile \
901                     "   default_tkt_enctypes = $default_tkt_enctypes($type)"
902         }
903         if [info exists permitted_enctypes($type)] {
904             puts $conffile \
905                     "   permitted_enctypes = $permitted_enctypes($type)"
906         }
907         puts $conffile "        krb4_config = $tmppwd/krb.conf"
908         puts $conffile "        krb4_realms = $tmppwd/krb.realms"
909         puts $conffile "        krb4_srvtab = $tmppwd/v4srvtab"
910         if { $mode == "tcp" } {
911             puts $conffile "    udp_preference_limit = 1"
912         }
913         puts $conffile ""
914         puts $conffile "\[realms\]"
915         puts $conffile "        $REALMNAME = \{"
916         # There's probably nothing listening here.  It would be a good
917         # test for the handling of a non-responsive KDC address.  However,
918         # on some systems, like Tru64, we often wind up with the client's
919         # socket bound to this address, causing our request to appear in
920         # our incoming queue as if it were a response, which causes test
921         # failures.  If we were running the client and KDC on different
922         # hosts, this would be okay....
923         #puts $conffile "               kdc = $hostname:[expr 6 + $portbase]"
924         puts $conffile "                kdc = $hostname:[expr 1 + $portbase]"
925         puts $conffile "                admin_server = $hostname:[expr 4 + $portbase]"
926         puts $conffile "                kpasswd_server = $hostname:[expr 5 + $portbase]"
927         puts $conffile "                default_domain = $domain"
928         puts $conffile "                krb524_server = $hostname:[expr 7 + $portbase]"
929         puts $conffile "                database_module = foo_db2"
930         puts $conffile "        \}"
931         puts $conffile ""
932         puts $conffile "\[domain_realm\]"
933         puts $conffile "        .$domain = $REALMNAME"
934         puts $conffile "        $domain = $REALMNAME"
935         puts $conffile ""
936         puts $conffile "\[logging\]"
937         puts $conffile "        admin_server = FILE:$tmppwd/kadmind5.log"
938         puts $conffile "        kdc = FILE:$tmppwd/kdc.log"
939         puts $conffile "        default = FILE:$tmppwd/others.log"
940         puts $conffile ""
941         puts $conffile "\[dbmodules\]"
942         puts $conffile "        db_module_dir = $tmppwd/../../../util/fakedest$KRB5_DB_MODULE_DIR"
943         puts $conffile "        foo_db2 = {"
944         puts $conffile "                db_library = db2"
945         puts $conffile "                database_name = $tmppwd/db"
946         puts $conffile "        }"
947         close $conffile
948     }
949 }
950
951 # Save the original values of the environment variables we are going
952 # to muck with.
953
954 # XXX deal with envstack later.
955
956 if [info exists env(KRB5_CONFIG)] {
957     set orig_krb5_conf $env(KRB5_CONFIG)
958 } else {
959     catch "unset orig_krb5_config"
960 }
961
962 if [info exists env(KRB5CCNAME)] {
963     set orig_krb5ccname $env(KRB5CCNAME)
964 } else {
965     catch "unset orig_krb5ccname"
966 }
967
968 if [ info exists env(KRB5RCACHEDIR)] {
969     set orig_krb5rcachedir $env(KRB5RCACHEDIR)
970 } else {
971     catch "unset orig_krb5rcachedir"
972 }
973
974 if [ info exists env(KERBEROS_SERVER)] {
975     set orig_kerberos_server $env(KERBEROS_SERVER)
976 } else {
977     catch "unset orig_kerberos_server"
978 }
979
980 # setup_kerberos_env
981 # Set the environment variables needed to run Kerberos programs.
982
983 proc setup_kerberos_env { {type client} } {
984     global REALMNAME
985     global env
986     global tmppwd
987     global hostname
988     global krb5_init_vars
989     global portbase
990
991     # Set the environment variable KRB5_CONFIG to point to our krb5.conf file.
992     # All the Kerberos tools check KRB5_CONFIG.
993     # Actually, V5 doesn't currently use this.
994     set env(KRB5_CONFIG) $tmppwd/krb5.$type.conf
995     verbose "KRB5_CONFIG=$env(KRB5_CONFIG)"
996
997     # Direct the Kerberos programs at a local ticket file.
998     set env(KRB5CCNAME) $tmppwd/tkt
999     verbose "KRB5CCNAME=$env(KRB5CCNAME)"
1000
1001     # Direct the Kerberos programs at a local ticket file.
1002     set env(KRBTKFILE) $tmppwd/tktv4
1003     verbose "KRBTKFILE=$env(KRBTKFILE)"
1004
1005     # Direct the Kerberos server at a cache file stored in the
1006     # temporary directory.
1007     set env(KRB5RCACHEDIR) $tmppwd
1008     verbose "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)"
1009
1010     # Tell the Kerberos tools how to contact the $REALMNAME server.
1011     set env(KERBEROS_SERVER) "$REALMNAME:$hostname:[expr 1 + $portbase]"
1012     verbose "KERBEROS_SERVER=$env(KERBEROS_SERVER)"
1013
1014     # Get the run time environment variables... (including LD_LIBRARY_PATH)
1015     setup_runtime_env
1016
1017     # Set our kdc config file.
1018     set env(KRB5_KDC_PROFILE) $tmppwd/kdc.conf
1019     verbose "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)"
1020
1021     # Create an environment setup script.  (For convenience)
1022     if ![file exists $tmppwd/env.sh] {
1023         set envfile [open $tmppwd/env.sh w]
1024         puts $envfile "KRB5_CONFIG=$env(KRB5_CONFIG)"
1025         puts $envfile "KRB5CCNAME=$env(KRB5CCNAME)"
1026         puts $envfile "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)"
1027         puts $envfile "KERBEROS_SERVER=$env(KERBEROS_SERVER)"
1028         puts $envfile "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)"
1029         puts $envfile "export KRB5_CONFIG KRB5CCNAME KRB5RCACHEDIR"
1030         puts $envfile "export KERBEROS_SERVER KRB5_KDC_PROFILE"
1031         foreach i $krb5_init_vars {
1032                 regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
1033                 puts $envfile "$evar=$env($evar)"
1034                 puts $envfile "export $evar"
1035         }
1036         close $envfile
1037     }
1038     if ![file exists $tmppwd/env.csh] {
1039         set envfile [open $tmppwd/env.csh w]
1040         puts $envfile "setenv KRB5_CONFIG $env(KRB5_CONFIG)"
1041         puts $envfile "setenv KRB5CCNAME $env(KRB5CCNAME)"
1042         puts $envfile "setenv KRB5RCACHEDIR $env(KRB5RCACHEDIR)"
1043         puts $envfile "setenv KERBEROS_SERVER $env(KERBEROS_SERVER)"
1044         puts $envfile "setenv KRB5_KDC_PROFILE $env(KRB5_KDC_PROFILE)"
1045         foreach i $krb5_init_vars {
1046                 regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
1047                 puts $envfile "setenv $evar $env($evar)"
1048         }
1049         close $envfile
1050     }
1051     return 1
1052 }
1053
1054 # Restore the Kerberos environment, in case setup_kerberos_env was
1055 # already called by an earlier test.
1056
1057 proc restore_kerberos_env { } {
1058     global env
1059     global orig_krb5_config
1060     global orig_krb5ccname
1061     global orig_krb5rcachedir
1062     global orig_kerberos_server
1063
1064     if [info exists orig_krb5_config] {
1065     set env(KRB5_CONFIG) $orig_krb5_config
1066     } else {
1067     catch "unset env(KRB5_CONFIG)"
1068     }
1069
1070     if [info exists orig_krb5ccname] {
1071         set env(KRB5CCNAME) $orig_krb5ccname
1072     } else {
1073         catch "unset env(KRB5CCNAME)"
1074     }
1075
1076     if [info exists orig_krb5rcachedir] {
1077         set env(KRB5RCACHEDIR) $orig_krb5rcachedir
1078     } else {
1079         catch "unset env(KRB5RCACHEDIR)"
1080     }
1081
1082     if [info exists orig_kerberos_server] {
1083         set env(KERBEROS_SERVER) $orig_kerberos_server
1084     } else {
1085         catch "unset env(KERBEROS_SERVER)"
1086     }
1087
1088 }
1089
1090 # setup_kerberos_db
1091 # Initialize the Kerberos database.  If the argument is non-zero, call
1092 # pass at relevant points.  Returns 1 on success, 0 on failure.
1093
1094 proc setup_kerberos_db { standalone } {
1095     global REALMNAME
1096     global KDB5_UTIL
1097     global KADMIN_LOCAL
1098     global KEY
1099     global tmppwd
1100     global spawn_id
1101     global des3_krbtgt
1102     global tgt_support_desmd5
1103     global multipass_name
1104     global last_passname_db
1105
1106     set failall 0
1107
1108     if {!$standalone && [file exists $tmppwd/db.ok] \
1109         && $last_passname_db == $multipass_name} {
1110         return 1
1111     }
1112
1113     catch "exec rm -f [glob -nocomplain $tmppwd/db* $tmppwd/adb*]"
1114
1115     # Creating a new database means we need a new srvtab.
1116     catch "exec rm -f $tmppwd/srvtab"
1117
1118     envstack_push
1119     if { ![setup_kerberos_files] || ![setup_kerberos_env kdc] } {
1120         set failall 1
1121     }
1122
1123     # Set up a common expect_after for use in multiple places.
1124     set def_exp_after {
1125         timeout {
1126             set test "$test (timeout)"
1127             break
1128         }
1129         eof {
1130             set test "$test (eof)"
1131             break
1132         }
1133     }
1134
1135     set test "kdb5_util create"
1136     set body {
1137         if $failall {
1138             break
1139         }
1140         #exec xterm
1141         verbose "starting $test"
1142         spawn $KDB5_UTIL -r $REALMNAME create
1143         expect_after $def_exp_after
1144
1145         expect "Enter KDC database master key:"
1146
1147         set test "kdb5_util create (verify)"
1148         send "masterkey$KEY\r"
1149         expect "Re-enter KDC database master key to verify:"
1150
1151         set test "kdb5_util create"
1152         send "masterkey$KEY\r"
1153         expect {
1154             -re "\[Cc\]ouldn't" {
1155                 expect eof
1156                 break
1157             }
1158             "Cannot find/read stored" exp_continue
1159             "Warning: proceeding without master key" exp_continue
1160             eof { }
1161         }
1162         catch expect_after
1163         if ![check_exit_status kdb5_util] {
1164             break
1165         }
1166     }
1167     set ret [catch $body]
1168     catch expect_after
1169     if $ret {
1170         set failall 1
1171         if $standalone {
1172             fail $test
1173         }
1174     } else {
1175         if $standalone {
1176             pass $test
1177         }
1178     }
1179
1180     # Stash the master key in a file.
1181     set test "kdb5_util stash"
1182     set body {
1183         if $failall {
1184             break
1185         }
1186         spawn $KDB5_UTIL  -r $REALMNAME stash
1187         verbose "starting $test"
1188         expect_after $def_exp_after
1189         expect "Enter KDC database master key:"
1190         send "masterkey$KEY\r"
1191         expect eof
1192         catch expect_after
1193         if ![check_exit_status kdb5_util] {
1194             break
1195         }
1196     }
1197     set ret [catch $body]
1198     catch "expect eof"
1199     catch expect_after
1200     if $ret {
1201         set failall 1
1202         if $standalone {
1203             fail $test
1204         } else {
1205             catch "exec rm -f $tmppwd/db.ok $tmppwd/adb.db"
1206         }
1207     } else {
1208         if $standalone {
1209             pass $test
1210         }
1211     }
1212
1213     # Add an admin user.
1214 #send_user "will run: $KADMIN_LOCAL -r $REALMNAME\n"
1215 #exec xterm
1216     set test "kadmin.local ank krbtest/admin"
1217     set body {
1218         if $failall {
1219             break
1220         }
1221         spawn $KADMIN_LOCAL -r $REALMNAME
1222         verbose "starting $test"
1223         expect_after $def_exp_after
1224
1225         expect "kadmin.local: "
1226         send "ank krbtest/admin@$REALMNAME\r"
1227         # It echos...
1228         expect "ank krbtest/admin@$REALMNAME\r"
1229         expect "Enter password for principal \"krbtest/admin@$REALMNAME\":"
1230         send "adminpass$KEY\r"
1231         expect "Re-enter password for principal \"krbtest/admin@$REALMNAME\":"
1232         send "adminpass$KEY\r"
1233         expect {
1234             "Principal \"krbtest/admin@$REALMNAME\" created" { }
1235             "Principal or policy already exists while creating*" { }
1236         }
1237         expect "kadmin.local: "
1238         send "quit\r"
1239         expect eof
1240         catch expect_after
1241         if ![check_exit_status kadmin_local] {
1242             break
1243         }
1244     }
1245     set ret [catch $body]
1246     catch "expect eof"
1247     catch expect_after
1248     if $ret {
1249         set failall 1
1250         if $standalone {
1251             fail $test
1252         } else {
1253             catch "exec rm -f $tmppwd/db.ok $tmppwd/adb.db"
1254         }
1255     } else {
1256         if $standalone {
1257             pass $test
1258         }
1259     }
1260
1261     if $des3_krbtgt {
1262         # Set the TGT key to DES3.
1263         set test "kadmin.local TGT to DES3"
1264         set body {
1265             if $failall {
1266                 break
1267             }
1268             spawn $KADMIN_LOCAL -r $REALMNAME -e des3-cbc-sha1:normal
1269             verbose "starting $test"
1270             expect_after $def_exp_after
1271
1272             expect "kadmin.local: "
1273             send "cpw -randkey krbtgt/$REALMNAME@$REALMNAME\r"
1274             # It echos...
1275             expect "cpw -randkey krbtgt/$REALMNAME@$REALMNAME\r"
1276             expect {
1277                 "Key for \"krbtgt/$REALMNAME@$REALMNAME\" randomized." { }
1278             }
1279             expect "kadmin.local: "
1280             send "quit\r"
1281             expect eof
1282             catch expect_after
1283             if ![check_exit_status kadmin_local] {
1284                 break
1285             }
1286         }
1287         set ret [catch $body]
1288         catch "expect eof"
1289         catch expect_after
1290         if $ret {
1291             set failall 1
1292             if $standalone {
1293                 fail $test
1294             } else {
1295                 catch "exec rm -f $tmppwd/db.ok $tmppwd/adb.db"
1296             }
1297         } else {
1298             if $standalone {
1299                 pass $test
1300             }
1301         }
1302     }
1303     if $tgt_support_desmd5 {
1304         # Make TGT support des-cbc-md5
1305         set test "kadmin.local TGT to SUPPORT_DESMD5"
1306         set body {
1307             if $failall {
1308                 break
1309             }
1310             spawn $KADMIN_LOCAL -r $REALMNAME
1311             verbose "starting $test"
1312             expect_after $def_exp_after
1313
1314             expect "kadmin.local: "
1315             send "modprinc +support_desmd5 krbtgt/$REALMNAME@$REALMNAME\r"
1316             # It echos...
1317             expect "modprinc +support_desmd5 krbtgt/$REALMNAME@$REALMNAME\r"
1318             expect {
1319                 "Principal \"krbtgt/$REALMNAME@$REALMNAME\" modified.\r\n" { }
1320             }
1321             expect "kadmin.local: "
1322             send "quit\r"
1323             expect eof
1324             catch expect_after
1325             if ![check_exit_status kadmin_local] {
1326                 break
1327             }
1328         }
1329         set ret [catch $body]
1330         catch "expect eof"
1331         catch expect_after
1332         if $ret {
1333             set failall 1
1334             if $standalone {
1335                 fail $test
1336             } else {
1337                 catch "exec rm -f $tmppwd/db.ok $tmppwd/adb.db"
1338             }
1339         } else {
1340             if $standalone {
1341                 pass $test
1342             }
1343         }
1344     }
1345     envstack_pop
1346
1347     # create the admin database lock file
1348     catch "exec touch $tmppwd/adb.lock"
1349
1350     set last_passname_db $multipass_name
1351     return 1
1352 }
1353
1354 proc start_tail { fname spawnid_var pid_var which standalone } {
1355     upvar $spawnid_var spawnid
1356     upvar $pid_var pid
1357     global timeout
1358
1359     set f [open $fname a]
1360
1361     spawn tail -f $fname
1362     set spawnid $spawn_id
1363     set pid [exp_pid]
1364
1365     set markstr "===MARK $pid [clock format [clock seconds]] ==="
1366     puts $f $markstr
1367     flush $f
1368
1369     set p 0
1370     set otimeout $timeout
1371     set timeout 1
1372     set ok 0
1373     while { $ok == 0 && $p < 3 } {
1374         expect {
1375             -i $spawn_id
1376             -ex "$markstr\r\n" { set ok 1 }
1377             -re "\[^\r\n\]*\r\n" { exp_continue }
1378             timeout {
1379                 # Some versions of GNU tail had a race condition where
1380                 # the first batch of data would be read from the end
1381                 # of the file, and then there was a brief window
1382                 # before calling stat and recording the size of the
1383                 # file.  If the marker is written during that window,
1384                 # then yet another file modification is needed to get
1385                 # the first one noticed.
1386                 if { $p < 3 } {
1387                     verbose -log "no tail output yet, prodding with a blank line"
1388                     incr p
1389                     puts $f ""
1390                     flush $f
1391                     exp_continue
1392                 } else {
1393                     close $f
1394                     verbose -log "tail $fname output:"
1395                     verbose -log [exec tail $fname]
1396                     if {$standalone} {
1397                         verbose -log "tail -f timed out ($timeout sec) looking for mark in $which log"
1398                         fail "$which"
1399                     } else {
1400                         perror "$which tail -f timed out ($timeout sec) looking for mark in $which log"
1401                     }
1402                     stop_kerberos_daemons
1403                     exec kill $pid
1404                     expect -i $spawn_id eof
1405                     wait -i $spawn_id
1406                     set timeout $otimeout
1407                     return 0
1408                 }
1409             }
1410         }
1411     }
1412     close $f
1413     set timeout $otimeout
1414     return 1
1415 }
1416
1417 # start_kerberos_daemons
1418 # A procedure to build a Kerberos database and start up the kerberos
1419 # and kadmind daemons.  This sets the global variables kdc_pid,
1420 # kdc_spawn_id, kadmind_pid, and kadmind_spawn_id.  The procedure
1421 # stop_kerberos_daemons should be used to stop the daemons.  If the
1422 # argument is non-zero, call pass at relevant points.  Returns 1 on
1423 # success, 0 on failure.
1424
1425 proc start_kerberos_daemons { standalone } {
1426     global BINSH
1427     global REALMNAME
1428     global KRB5KDC
1429     global KADMIND
1430     global KEY
1431     global kdc_pid
1432     global kdc_spawn_id
1433     global kadmind_pid
1434     global kadmind_spawn_id
1435     global tmppwd
1436     global env
1437     global timeout
1438
1439     if ![setup_kerberos_db 0] {
1440         return 0
1441     }
1442
1443     if {$standalone} {
1444         catch "exec rm -f $tmppwd/krb.log"
1445         catch "exec rm -f $tmppwd/kadmind.log"
1446         catch "exec rm -f $tmppwd/krb5kdc_rcache"
1447     }
1448
1449     # Start up the kerberos daemon
1450     # Why are we doing all this with the log file you may ask.
1451     #   We need a handle on when the server starts. If we log the output
1452     #   of the server to say stderr, then if we stop looking for output,
1453     #   buffers will fill and the server will stop working....
1454     #   So, we look to see when a line is added to the log file and then
1455     #   check it..
1456     # The same thing is done a little later for the kadmind
1457     set kdc_lfile $tmppwd/kdc.log
1458     set kadmind_lfile $tmppwd/kadmind5.log
1459
1460     if ![start_tail $kdc_lfile tailf_spawn_id tailf_pid krb5kdc $standalone] {
1461         return 0
1462     }
1463
1464     envstack_push
1465     setup_kerberos_env kdc
1466     spawn $KRB5KDC -r $REALMNAME -n -4 full
1467     envstack_pop
1468     set kdc_pid [exp_pid]
1469     set kdc_spawn_id $spawn_id
1470
1471     expect {
1472         -i $tailf_spawn_id
1473         -re "commencing operation\r\n" { }
1474         -re "krb5kdc: \[a-zA-Z\]* - Cannot bind server socket to \[ 0-9a-fA-F:.\]*\r\n" {
1475             verbose -log "warning: $expect_out(0,string)"
1476             exp_continue
1477         }
1478         "no sockets set up?" {
1479             if {$standalone} {
1480                 verbose -log "krb5kdc startup failed to bind listening sockets"
1481                 fail "krb5kdc"
1482             } else {
1483                 perror "krb5kdc startup failed to bind listening sockets"
1484             }
1485             stop_kerberos_daemons
1486             exec kill $tailf_pid
1487             expect -i $tailf_spawn_id eof
1488             wait -i $tailf_spawn_id
1489             return 0
1490         }
1491         timeout {
1492             if {$standalone} {
1493                 verbose -log "krb5kdc startup timed out"
1494                 fail "krb5kdc"
1495             } else {
1496                 perror "krb5kdc startup timed out"
1497             }
1498             stop_kerberos_daemons
1499             exec kill $tailf_pid
1500             expect -i $tailf_spawn_id eof
1501             wait -i $tailf_spawn_id
1502             return 0
1503         }
1504     }
1505     exec kill $tailf_pid
1506     expect -i $tailf_spawn_id eof
1507     wait -i $tailf_spawn_id
1508
1509     if {$standalone} {
1510         pass "krb5kdc"
1511     }
1512
1513     # Give the kerberos daemon a few seconds to get set up.
1514 #    sleep 2
1515
1516     #
1517     # Save setting of KRB5_KTNAME. We do not want to override kdc.conf
1518     # file during kadmind startup. (this is in case user has KRB5_KTNAME
1519     # set before starting make check)
1520     #
1521     if [info exists env(KRB5_KTNAME)] {
1522         set start_save_ktname $env(KRB5_KTNAME)
1523     }
1524     catch "unset env(KRB5_KTNAME)"
1525
1526     if ![start_tail $kadmind_lfile tailf_spawn_id tailf_pid kadmind $standalone] {
1527         return 0
1528     }
1529
1530     # Start up the kadmind daemon
1531     # XXXX kadmind uses stderr a lot.  the sh -c and redirect can be
1532     # removed when this is fixed
1533     envstack_push
1534     setup_kerberos_env kdc
1535     spawn $BINSH -c "exec $KADMIND -r $REALMNAME -nofork 2>>$kadmind_lfile"
1536     envstack_pop
1537     set kadmind_pid [exp_pid]
1538     set kadmind_spawn_id $spawn_id
1539
1540     # Restore KRB5_KTNAME
1541     if [info exists start_save_ktname] {
1542         set env(KRB5_KTNAME) $start_save_ktname
1543         unset start_save_ktname
1544     }
1545
1546     expect {
1547         -i $tailf_spawn_id
1548         "Seeding random number" exp_continue
1549         "cannot initialize network" {
1550             if {$standalone} {
1551                 verbose -log "kadmind failed network init"
1552                 fail "kadmind"
1553             } else {
1554                 perror "kadmind failed network init"
1555             }
1556             stop_kerberos_daemons
1557             exec kill $tailf_pid
1558             expect -i $tailf_spawn_id eof
1559             wait -i $tailf_spawn_id
1560             return 0
1561         }
1562         "cannot bind to network address" {
1563             if {$standalone} {
1564                 verbose -log "kadmind failed to bind socket"
1565                 fail "kadmind"
1566             } else {
1567                 perror "kadmind failed to bind socket"
1568             }
1569             stop_kerberos_daemons
1570             exec kill $tailf_pid
1571             expect -i $tailf_spawn_id eof
1572             wait -i $tailf_spawn_id
1573             return 0
1574         }
1575         "No principal in keytab matches desired name" {
1576             dump_db
1577             exp_continue
1578         }
1579         "starting" { }
1580         timeout {
1581             if {$standalone} {
1582                 verbose -log "kadmind failed to start"
1583                 fail "kadmind"
1584             } else {
1585                 verbose -log "kadmind failed to start"
1586                 perror "kadmind failed to start"
1587             }
1588 #sleep 10
1589             stop_kerberos_daemons
1590             exec kill $tailf_pid
1591             expect -i $tailf_spawn_id eof
1592             wait -i $tailf_spawn_id
1593             return 0
1594         }
1595     }
1596     exec kill $tailf_pid
1597     expect -i $tailf_spawn_id eof
1598     wait -i $tailf_spawn_id
1599
1600     if {$standalone} {
1601         pass "kadmind"
1602     }
1603
1604     # Give the kadmind daemon a few seconds to get set up.
1605 #    sleep 2
1606
1607     return 1
1608 }
1609
1610 # stop_kerberos_daemons
1611 # Stop the kerberos daemons.  Returns 1 on success, 0 on failure.
1612
1613 proc stop_kerberos_daemons { } {
1614     global kdc_pid
1615     global kdc_spawn_id
1616     global kadmind_pid
1617     global kadmind_spawn_id
1618
1619     verbose "entered stop_kerberos_daemons"
1620
1621     if [info exists kdc_pid] {
1622         if [catch "exec kill $kdc_pid" msg] {
1623             verbose "kill kdc: $msg"
1624         }
1625         if [catch "expect -i $kdc_spawn_id eof" msg] {
1626             verbose "expect kdc eof: $msg"
1627         }
1628         set kdc_list [wait -i $kdc_spawn_id]
1629         verbose "wait -i $kdc_spawn_id returned $kdc_list (kdc)"
1630         unset kdc_pid
1631         unset kdc_list
1632     }
1633
1634     if [info exists kadmind_pid] {
1635         if [catch "exec kill $kadmind_pid" msg] {
1636             verbose "kill kadmind: $msg"
1637         }
1638         if [catch "expect -i $kadmind_spawn_id eof" msg] {
1639             verbose "expect kadmind eof: $msg"
1640         }
1641         set kadmind_list [wait -i $kadmind_spawn_id]
1642         verbose "wait -i $kadmind_spawn_id returned $kadmind_list (kadmind5)"
1643         unset kadmind_pid
1644         unset kadmind_list
1645     }
1646
1647     verbose "exiting stop_kerberos_daemons"
1648
1649     return 1
1650 }
1651
1652 # add_kerberos_key
1653 # Add an key to the Kerberos database.  start_kerberos_daemons must be
1654 # called before this procedure.  If the standalone argument is
1655 # non-zero, call pass at relevant points.  Returns 1 on success, 0 on
1656 # failure.
1657
1658 proc add_kerberos_key { kkey standalone } {
1659     global REALMNAME
1660     global KADMIN
1661     global KEY
1662     global spawn_id
1663
1664     # Use kadmin to add an key.
1665     set test "kadmin ank $kkey"
1666     set body {
1667         envstack_push
1668         setup_kerberos_env client
1669         spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank $kkey@$REALMNAME"
1670         envstack_pop
1671         verbose "starting $test"
1672         expect_after {
1673             "Cannot contact any KDC" {
1674                 set test "$test (lost KDC)"
1675                 break
1676             }
1677             timeout {
1678                 set test "$test (timeout)"
1679                 break
1680             }
1681             eof {
1682                 set test "$test (eof)"
1683                 break
1684             }
1685         }
1686         expect -re "assword\[^\r\n\]*: *"
1687         send "adminpass$KEY\r"
1688         expect "Enter password for principal \"$kkey@$REALMNAME\":"
1689         send "$kkey"
1690         send "$KEY\r"
1691         expect "Re-enter password for principal \"$kkey@$REALMNAME\":"
1692         send "$kkey"
1693         send "$KEY\r"
1694         expect {
1695             "Principal \"$kkey@$REALMNAME\" created" { }
1696             "Principal or policy already exists while creating*" { }
1697         }
1698         expect eof
1699         if ![check_exit_status kadmin] {
1700             break
1701         }
1702     }
1703     set ret [catch $body]
1704     catch "expect eof"
1705     catch expect_after
1706     if $ret {
1707         if $standalone {
1708             fail $test
1709         }
1710         return 0
1711     } else {
1712         if $standalone {
1713             pass $test
1714         }
1715         return 1
1716     }
1717 }
1718
1719 # dump_db
1720 proc dump_db { } {
1721     global KADMIN_LOCAL
1722     global REALMNAME
1723
1724     spawn $KADMIN_LOCAL -r $REALMNAME
1725     expect_after {
1726         eof {
1727             perror "failed to get debugging dump of database (eof)"
1728         }
1729         timeout {
1730             perror "failed to get debugging dump of database (timeout)"
1731         }
1732     }
1733     expect "kadmin.local: "
1734     send "getprincs\r"
1735     expect "kadmin.local: "
1736     send "quit\r"
1737     expect eof
1738     catch expect_after
1739 }
1740
1741 # add_random_key
1742 # Add a key with a random password to the Kerberos database.
1743 # start_kerberos_daemons must be called before this procedure.  If the
1744 # standalone argument is non-zero, call pass at relevant points.
1745 # Returns 1 on success, 0 on failure.
1746
1747 proc add_random_key { kkey standalone } {
1748     global REALMNAME
1749     global KADMIN
1750     global KEY
1751     global spawn_id
1752
1753     # Use kadmin to add an key.
1754     set test "kadmin ark $kkey"
1755     set body {
1756         envstack_push
1757         setup_kerberos_env client
1758         spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank -randkey $kkey@$REALMNAME"
1759         envstack_pop
1760         expect_after {
1761             timeout {
1762                 set test "$test (timeout)"
1763                 break
1764             }
1765             eof {
1766                 set test "$test (eof)"
1767                 break
1768             }
1769         }
1770         expect -re "assword\[^\r\n\]*: *"
1771         send "adminpass$KEY\r"
1772         expect {
1773             "Principal \"$kkey@$REALMNAME\" created" { }
1774             "Principal or policy already exists while creating*" { }
1775         }
1776         expect eof
1777         if ![check_exit_status kadmin] {
1778             break
1779         }
1780     }
1781     if [catch $body] {
1782         catch expect_after
1783         if $standalone {
1784             fail $test
1785         }
1786         return 0
1787     } else {
1788         catch expect_after
1789         if $standalone {
1790             pass $test
1791         }
1792         return 1
1793     }
1794 }
1795
1796 # setup_srvtab
1797 # Set up a srvtab file.  start_kerberos_daemons and add_random_key
1798 # $id/$hostname must be called before this procedure.  If the
1799 # argument is non-zero, call pass at relevant points.  Returns 1 on
1800 # success, 0 on failure. If the id field is not provided, host is used.
1801
1802 proc setup_srvtab { standalone {id host} } {
1803     global REALMNAME
1804     global KADMIN_LOCAL
1805     global KEY
1806     global tmppwd
1807     global hostname
1808     global spawn_id
1809     global last_service
1810
1811     if {!$standalone && [file exists $tmppwd/srvtab] && $last_service == $id} {
1812         return 1
1813     }
1814
1815     catch "exec rm -f $tmppwd/srvtab $tmppwd/srvtab.old"
1816
1817     if ![get_hostname] {
1818         return 0
1819     }
1820
1821     catch "exec rm -f $hostname-new-srvtab"
1822
1823     envstack_push
1824     setup_kerberos_env kdc
1825     spawn $KADMIN_LOCAL -r $REALMNAME
1826     envstack_pop
1827     expect_after {
1828         -re "(.*)\r\nkadmin.local:  " {
1829             fail "kadmin.local srvtab (unmatched output: $expect_out(1,string))"
1830             if {!$standalone} {
1831                 catch "exec rm -f $tmppwd/srvtab"
1832             }
1833             catch "expect_after"
1834             return 0
1835         }
1836         timeout {
1837             fail "kadmin.local srvtab"
1838             if {!$standalone} {
1839                 catch "exec rm -f $tmppwd/srvtab"
1840             }
1841             catch "expect_after"
1842             return 0
1843         }
1844         eof {
1845             fail "kadmin.local srvtab"
1846             if {!$standalone} {
1847                 catch "exec rm -f $tmppwd/srvtab"
1848             }
1849             catch "expect_after"
1850             return 0
1851         }
1852     }
1853     expect "kadmin.local:  "
1854     send "xst -k $hostname-new-srvtab $id/$hostname\r"
1855     expect "xst -k $hostname-new-srvtab $id/$hostname\r\n"
1856     expect {
1857         -re ".*Entry for principal $id/$hostname.* added to keytab WRFILE:$hostname-new-srvtab." { }
1858         -re "\r\nkadmin.local:  " {
1859             if {$standalone} {
1860                 fail "kadmin.local srvtab"
1861             } else {
1862                 catch "exec rm -f $tmppwd/srvtab"
1863             }
1864             catch expect_after
1865             return 0
1866         }
1867     }
1868     expect "kadmin.local:  "
1869     send "quit\r"
1870     expect eof
1871     catch expect_after
1872     if ![check_exit_status "kadmin.local srvtab"] {
1873         if {!$standalone} {
1874             catch "exec rm -f $tmppwd/srvtab"
1875         }
1876         return 0
1877     }
1878
1879     catch "exec mv -f $hostname-new-srvtab $tmppwd/srvtab" exec_output
1880     if ![string match "" $exec_output] {
1881         verbose -log "$exec_output"
1882         perror "can't mv new srvtab"
1883         return 0
1884     }
1885
1886     if {$standalone} {
1887         pass "kadmin.local srvtab"
1888     }
1889
1890     # Make the srvtab file globally readable in case we are using a
1891     # root shell and the srvtab is NFS mounted.
1892     catch "exec chmod a+r $tmppwd/srvtab"
1893
1894     # Remember what we just extracted
1895     set last_service $id
1896
1897     return 1
1898 }
1899
1900 # kinit
1901 # Use kinit to get a ticket.  If the argument is non-zero, call pass
1902 # at relevant points.  Returns 1 on success, 0 on failure.
1903
1904 proc kinit { name pass standalone } {
1905     global REALMNAME
1906     global KINIT
1907     global spawn_id
1908
1909     # Use kinit to get a ticket.
1910         #
1911         # For now always get forwardable tickets. Later when we need to make
1912         # tests that distiguish between forwardable tickets and otherwise
1913         # we should but another option to this proc. --proven
1914         #
1915     spawn $KINIT -5 -f $name@$REALMNAME
1916     expect {
1917         "Password for $name@$REALMNAME:" {
1918             verbose "kinit started"
1919         }
1920         timeout {
1921             fail "kinit"
1922             return 0
1923         }
1924         eof {
1925             fail "kinit"
1926             return 0
1927         }
1928     }
1929     send "$pass\r"
1930     expect eof
1931     if ![check_exit_status kinit] {
1932         return 0
1933     }
1934
1935     if {$standalone} {
1936         pass "kinit"
1937     }
1938
1939     return 1
1940 }
1941
1942 proc kinit_kt { name keytab standalone testname } {
1943     global REALMNAME
1944     global KINIT
1945     global spawn_id
1946
1947     # Use kinit to get a ticket.
1948         #
1949         # For now always get forwardable tickets. Later when we need to make
1950         # tests that distiguish between forwardable tickets and otherwise
1951         # we should but another option to this proc. --proven
1952         #
1953     spawn $KINIT -5 -f -k -t $keytab $name@$REALMNAME
1954     expect {
1955         timeout {
1956             fail "kinit $testname"
1957             return 0
1958         }
1959         eof { }
1960     }
1961     if ![check_exit_status "kinit $testname"] {
1962         return 0
1963     }
1964
1965     if {$standalone} {
1966         pass "kinit $testname"
1967     }
1968
1969     return 1
1970 }
1971
1972 # List tickets.  Requires client and server names, and test name.
1973 # Checks that klist exist status is zero.
1974 # Records pass or fail, and returns 1 or 0.
1975 proc do_klist { myname servname testname } {
1976     global KLIST
1977     global tmppwd
1978
1979     spawn $KLIST -5 -e
1980     expect {
1981         -re "Ticket cache:\[    \]*(.+:)?$tmppwd/tkt.*Default principal:\[      \]*$myname.*$servname\r\n" {
1982             verbose "klist started"
1983         }
1984         timeout {
1985             fail $testname
1986             return 0
1987         }
1988         eof {
1989             fail $testname
1990             return 0
1991         }
1992     }
1993
1994     expect eof
1995
1996     if ![check_exit_status $testname] {
1997         return 0
1998     }
1999     pass $testname
2000     return 1
2001 }
2002
2003 proc do_klist_kt { keytab testname } {
2004     global KLIST
2005     global tmppwd
2006
2007     spawn $KLIST -5 -e -k $keytab
2008     expect {
2009         -re "Keytab name:\[     \]*(.+:)?.*KVNO Principal\r\n---- -*\r\n" {
2010             verbose "klist started"
2011         }
2012         timeout {
2013             fail $testname
2014             return 0
2015         }
2016         eof {
2017             fail $testname
2018             return 0
2019         }
2020     }
2021     set more 1
2022     while {$more} {
2023         expect {
2024             -re { *[0-9][0-9]* *[a-zA-Z/@.-]* \([/a-zA-Z 0-9-]*\) *\r\n} {
2025                 verbose -log "key: $expect_out(buffer)"
2026             }
2027             eof { set more 0 }
2028         }
2029     }
2030
2031     if ![check_exit_status $testname] {
2032         return 0
2033     }
2034     pass $testname
2035     return 1
2036 }
2037
2038 proc do_klist_err { testname } {
2039     global KLIST
2040     global spawn_id
2041
2042     spawn $KLIST -5
2043     # Might say "credentials cache" or "credentials cache file".
2044     expect {
2045         -re "klist: No credentials cache.*found.*\r\n" {
2046             verbose "klist started"
2047         }
2048         timeout {
2049             fail $testname
2050             return 0
2051         }
2052         eof {
2053             fail $testname
2054             return 0
2055         }
2056     }
2057     # We can't use check_exit_status, because we expect an exit status
2058     # of 1.
2059     catch "expect eof"
2060     set status_list [wait -i $spawn_id]
2061     verbose "wait -i $spawn_id returned $status_list ($testname)"
2062     if { [lindex $status_list 2] != 0 } {
2063         fail "$testname (bad exit status) $status_list"
2064         return 0
2065     } else { if { [lindex $status_list 3] != 1 } {
2066         fail "$testname (bad exit status) $status_list"
2067         return 0
2068     } else {
2069         pass $testname
2070     } }
2071     return 1
2072 }
2073
2074 proc do_kdestroy { testname } {
2075     global KDESTROY
2076     spawn $KDESTROY -5
2077     if ![check_exit_status $testname] {
2078         fail $testname
2079         return 0
2080     }
2081     pass $testname
2082     return 1
2083 }
2084
2085 proc xst { keytab name } {
2086     global KADMIN_LOCAL
2087     global REALMNAME
2088
2089     envstack_push
2090     setup_kerberos_env kdc
2091     spawn $KADMIN_LOCAL -r $REALMNAME
2092     envstack_pop
2093     catch expect_after
2094     expect_after {
2095         -re "(.*)\r\nkadmin.local:  " {
2096             fail "kadmin.local xst $keytab (unmatched output: $expect_out(1,string)"
2097             catch "expect_after"
2098             return 0
2099         }
2100         timeout {
2101             fail "kadmin.local xst $keytab (timeout)"
2102             catch "expect_after"
2103             return 0
2104         }
2105         eof {
2106             fail "kadmin.local xst $keytab (eof)"
2107             catch "expect_after"
2108             return 0
2109         }
2110     }
2111     expect "kadmin.local:  "
2112     send "xst -k $keytab $name\r"
2113     expect -re "xst -k \[^\r\n\]*\r\n.*Entry for principal .* added to keytab WRFILE:.*\r\nkadmin.local:  "
2114     send "quit\r"
2115     expect eof
2116     catch expect_after
2117     if ![check_exit_status "kadmin.local $keytab"] {
2118         perror "kadmin.local xst $keytab exited abnormally"
2119         return 0
2120     }
2121     return 1
2122 }
2123
2124 # v4_compatible_enctype
2125 # Returns 1 if v4 testing is enabled this passes encryption types are compatable with kerberos 4 work
2126 proc v4_compatible_enctype {} {
2127     global supported_enctypes
2128     global KRBIV
2129
2130     if ![info exists KRBIV] {
2131         return 0;
2132     }
2133
2134     if { $KRBIV && [string first des-cbc-crc:v4 "$supported_enctypes"] >= 0} {
2135         return 1
2136     } else {
2137         return 0
2138     }
2139 }
2140
2141 # kinit
2142 # Use kinit to get a ticket.  If the argument is non-zero, call pass
2143 # at relevant points.  Returns 1 on success, 0 on failure.
2144
2145 proc v4kinit { name pass standalone } {
2146     global REALMNAME
2147     global KINIT
2148     global spawn_id
2149     global des3_krbtgt
2150
2151     # Use kinit to get a ticket.
2152         #
2153         # For now always get forwardable tickets. Later when we need to make
2154         # tests that distiguish between forwardable tickets and otherwise
2155         # we should but another option to this proc. --proven
2156         #
2157     spawn $KINIT -4 $name@$REALMNAME
2158     expect {
2159         "Password for $name@$REALMNAME:" {
2160             verbose "v4kinit started"
2161         }
2162         timeout {
2163             fail "v4kinit"
2164             return 0
2165         }
2166         eof {
2167             fail "v4kinit"
2168             return 0
2169         }
2170     }
2171     send "$pass\r"
2172     expect eof
2173     if {$des3_krbtgt == 0} {
2174         if ![check_exit_status v4kinit] {
2175             return 0
2176         }
2177     } else {
2178         # Fail if kinit is successful with a des3 TGT.
2179         set status_list [wait -i $spawn_id]
2180         set testname v4kinit
2181         verbose "wait -i $spawn_id returned $status_list ($testname)"
2182         if { [lindex $status_list 2] != 0 || [lindex $status_list 3] != 1 } {
2183             verbose -log "exit status: $status_list"
2184             fail "$testname (exit status)"
2185         }
2186     }
2187     if {$standalone} {
2188         pass "v4kinit"
2189     }
2190
2191     return 1
2192 }
2193
2194 proc v4kinit_kt { name keytab standalone } {
2195     global REALMNAME
2196     global KINIT
2197     global spawn_id
2198
2199     # Use kinit to get a ticket.
2200         #
2201         # For now always get forwardable tickets. Later when we need to make
2202         # tests that distiguish between forwardable tickets and otherwise
2203         # we should but another option to this proc. --proven
2204         #
2205     spawn $KINIT -4 -k -t $keytab $name@$REALMNAME
2206     expect {
2207         timeout {
2208             fail "v4kinit"
2209             return 0
2210         }
2211         eof { }
2212     }
2213     if ![check_exit_status kinit] {
2214         return 0
2215     }
2216
2217     if {$standalone} {
2218         pass "v4kinit"
2219     }
2220
2221     return 1
2222 }
2223
2224 # List v4 tickets.
2225 # Client and server are regular expressions.
2226 proc v4klist { client server testname } {
2227     global KLIST
2228     global tmppwd
2229
2230     spawn $KLIST -4
2231     expect {
2232         -re "Kerberos 4 ticket cache:\[         \]*(.+:)?$tmppwd/tkt.*Principal:\[      \]*$client.*$server\r\n" {
2233             verbose "klist started"
2234         }
2235         timeout {
2236             fail $testname
2237             return 0
2238         }
2239         eof {
2240             fail $testname
2241             return 0
2242         }
2243     }
2244
2245     expect eof
2246
2247     if ![check_exit_status $testname] {
2248         return 0
2249     }
2250     pass $testname
2251     return 1
2252 }
2253
2254 # Destroy tickets.
2255 proc v4kdestroy { testname } {
2256     global KDESTROY
2257     spawn $KDESTROY -4
2258     if ![check_exit_status $testname] {
2259         return 0
2260     }
2261     pass $testname
2262     return 1
2263 }
2264
2265 # Try to list the krb4 tickets -- there shouldn't be any ticket file.
2266 proc v4klist_none { testname } {
2267     global KLIST
2268     global tmppwd
2269
2270     # Double check that the ticket was destroyed.
2271     spawn $KLIST -4
2272     expect {
2273         -re "Kerberos 4 ticket cache:\[         \]*(.+:)?$tmppwd/tkt.*klist: You have no tickets cached.*\r\n" {
2274             verbose "v4klist started"
2275             pass "$testname (output)"
2276         }
2277         timeout {
2278             fail "$testname (output)"
2279             # Skip the 'wait' below, if it's taking too long.
2280             untested "$testname (exit status)"
2281             return 0
2282         }
2283         eof {
2284             fail "$testname (output)"
2285         }
2286     }
2287     # We can't use check_exit_status, because we expect an exit status
2288     # of 1.
2289     expect eof
2290     set status_list [wait -i $spawn_id]
2291     verbose "wait -i $spawn_id returned $status_list (v4klist)"
2292     if { [lindex $status_list 2] != 0 } {
2293         fail "$testname (exit status)"
2294         return 0
2295     } else {
2296         if { [lindex $status_list 3] != 1 } {
2297             fail "$testname (exit status)"
2298             return 0
2299         } else {
2300             pass "$testname (exit status)"
2301         }
2302     }
2303     return 1
2304 }
2305
2306 # Set up a root shell using rlogin $hostname -l root.  This is used
2307 # when testing the daemons that must be run as root, such as telnetd
2308 # or rlogind.  This sets the global variables rlogin_spawn_id and
2309 # rlogin_pid.  Returns 1 on success, 0 on failure.
2310 #
2311 # This procedure will only succeed if the person running the test has
2312 # a valid ticket for a name listed in the /.klogin file.  Naturally,
2313 # Kerberos must already be installed on this machine.  It's a pain,
2314 # but I can't think of a better approach.
2315
2316 if ![info exists can_get_root] { set can_get_root yes }
2317
2318 proc setup_root_shell { testname } {
2319     global BINSH
2320     global ROOT_PROMPT
2321     global KEY
2322     global RLOGIN
2323     global RLOGIN_FLAGS
2324     global hostname
2325     global rlogin_spawn_id
2326     global rlogin_pid
2327     global tmppwd
2328     global env
2329     global krb5_init_vars
2330     global can_get_root
2331
2332     global timeout
2333
2334     if [string match $can_get_root no] {
2335         note "$testname test requires ability to log in as root"
2336         unsupported $testname
2337         return 0
2338     }
2339
2340     # Make sure we are using the original values of the environment
2341     # variables.  This means that the caller must call
2342     # setup_kerberos_env after calling this procedure.
2343
2344     # XXX fixme to deal with envstack
2345     restore_kerberos_env
2346
2347     setup_runtime_env
2348
2349     set me [exec whoami]
2350     if [string match root $me] {
2351         return [setup_root_shell_noremote $testname]
2352     }
2353
2354     if ![get_hostname] {
2355         set can_get_root no
2356         return 0
2357     }
2358
2359     # If you have not installed Kerberos on your system, and you want
2360     # to run these tests, you can do it if you are willing to put your
2361     # root password in this file (this is not a very good idea, but
2362     # it's safe enough if you disconnect from the network and remember
2363     # to remove the password later).  Change the rlogin in the next
2364     # line to be /usr/ucb/rlogin (or whatever is appropriate for your
2365     # system).  Then change the lines after "word:" a few lines
2366     # farther down to be
2367     #    send "rootpassword\r"
2368     #    exp_continue
2369
2370     eval spawn $RLOGIN $hostname -l root $RLOGIN_FLAGS
2371     set rlogin_spawn_id $spawn_id
2372     set rlogin_pid [exp_pid]
2373     set old_timeout $timeout
2374     set timeout 300
2375     set got_refused 0
2376
2377     expect {
2378         -re {connect to address [0-9a-fA-F.:]*: Connection refused} {
2379             note $expect_out(buffer)
2380             set got_refused 1
2381             exp_continue
2382         }
2383         -re "word:|erberos rlogin failed|ection refused|ection reset by peer|not authorized" {
2384             note "$testname test requires ability to rlogin as root"
2385             unsupported "$testname"
2386             set timeout $old_timeout
2387             stop_root_shell
2388             set can_get_root no
2389             return 0
2390         }
2391         "Cannot assign requested address" {
2392             note "$testname: rlogin as root 'cannot assign requested address'"
2393             unsupported "$testname"
2394             set timeout $old_timeout
2395             stop_root_shell
2396             set can_get_root no
2397             return 0
2398         }
2399         -re "usage: rlogin|illegal option -- x|invalid option -- x" {
2400             note "$testname: rlogin doesn't like command-line flags"
2401             unsupported "$testname"
2402             set timeout $old_timeout
2403             stop_root_shell
2404             set can_get_root no
2405             return 0
2406         }
2407         -re "$ROOT_PROMPT" { }
2408         timeout {
2409             perror "timeout from rlogin $hostname -l root"
2410             perror "If you have an unusual root prompt,"
2411             perror "try running with ROOT_PROMPT=\"regexp\""
2412             set timeout $old_timeout
2413             stop_root_shell
2414             set can_get_root no
2415             return 0
2416         }
2417         eof {
2418             if {$got_refused} {
2419                 # reported some errors, continued, and failed
2420                 note "$testname test requires ability to log in as root"
2421                 unsupported $testname
2422             } else {
2423                 # unknown problem?
2424 #               perror "eof from rlogin $hostname -l root"
2425                 note "eof (and unrecognized messages?) from rlogin $hostname -l root"
2426                 note "$testname test requires ability to log in as root"
2427                 unsupported $testname
2428             }
2429             stop_root_shell
2430             set timeout $old_timeout
2431             catch "expect_after"
2432             set can_get_root no
2433             return 0
2434         }
2435     }
2436
2437     expect_after {
2438         timeout {
2439             perror "timeout from rlogin $hostname -l root"
2440             stop_root_shell
2441             set timeout $old_timeout
2442             catch "expect_after"
2443             set can_get_root no
2444             return 0
2445         }
2446         eof {
2447             perror "eof from rlogin $hostname -l root"
2448             stop_root_shell
2449             set timeout $old_timeout
2450             catch "expect_after"
2451             set can_get_root no
2452             return 0
2453         }
2454     }
2455
2456     # Make sure the root shell is using /bin/sh.
2457     send "$BINSH\r"
2458     expect {
2459         -re "$ROOT_PROMPT" { }
2460     }
2461
2462     # Set up a shell variable tmppwd.  The callers use this to keep
2463     # command line lengths down.  The command line length is important
2464     # because we are feeding input to a shell via a pty.  On some
2465     # systems a pty will only accept 255 characters.
2466     send "tmppwd=$tmppwd\r"
2467     expect {
2468         -re "$ROOT_PROMPT" { }
2469     }
2470
2471     # Set up our krb5.conf
2472     send "KRB5_CONFIG=$tmppwd/krb5.server.conf\r"
2473     expect {
2474         -re "$ROOT_PROMPT" { }
2475     }
2476     send "export KRB5_CONFIG\r"
2477     expect {
2478         -re "$ROOT_PROMPT" { }
2479     }
2480
2481     # For all of our runtime environment variables - send them over...
2482     foreach i $krb5_init_vars {
2483         regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
2484         send "$evar=$env($evar)\r"
2485         expect {
2486                 -re "$ROOT_PROMPT" { }
2487         }
2488
2489         send "export $evar\r"
2490         expect {
2491                 -re "$ROOT_PROMPT" { }
2492         }
2493     }
2494
2495     # Move over to the right directory.
2496     set dir [pwd]
2497     send "cd $dir\r"
2498     expect {
2499         -re "$ROOT_PROMPT" { }
2500         "$dir:" {
2501             perror "root shell can not cd to $dir"
2502             set timeout $old_timeout
2503             stop_root_shell
2504             set can_get_root no
2505             return 0
2506         }
2507     }
2508
2509     expect_after
2510     set timeout $old_timeout
2511
2512     return 1
2513 }
2514
2515 proc setup_root_shell_noremote { testname } {
2516     global BINSH
2517     global ROOT_PROMPT
2518     global KEY
2519     global hostname
2520     global rlogin_spawn_id
2521     global rlogin_pid
2522     global tmppwd
2523     global env
2524     global krb5_init_vars
2525
2526     eval spawn $BINSH
2527     set rlogin_spawn_id $spawn_id
2528     set rlogin_pid [exp_pid]
2529
2530     expect_after {
2531         timeout {
2532             perror "timeout from root shell"
2533             stop_root_shell
2534             catch "expect_after"
2535             return 0
2536         }
2537         eof {
2538             perror "eof from root shell"
2539             stop_root_shell
2540             catch "expect_after"
2541             return 0
2542         }
2543     }
2544     expect {
2545         -re "$ROOT_PROMPT" { }
2546     }
2547
2548     # Set up a shell variable tmppwd.  The callers use this to keep
2549     # command line lengths down.  The command line length is important
2550     # because we are feeding input to a shell via a pty.  On some
2551     # systems a pty will only accept 255 characters.
2552     send "tmppwd=$tmppwd\r"
2553     expect {
2554         -re "$ROOT_PROMPT" { }
2555     }
2556
2557     # Set up our krb5.conf
2558     send "KRB5_CONFIG=$tmppwd/krb5.server.conf\r"
2559     expect {
2560         -re "$ROOT_PROMPT" { }
2561     }
2562     send "export KRB5_CONFIG\r"
2563     expect {
2564         -re "$ROOT_PROMPT" { }
2565     }
2566
2567     # For all of our runtime environment variables - send them over...
2568     foreach i $krb5_init_vars {
2569         regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
2570         send "$evar=$env($evar)\r"
2571         expect {
2572                 -re "$ROOT_PROMPT" { }
2573         }
2574
2575         send "export $evar\r"
2576         expect {
2577                 -re "$ROOT_PROMPT" { }
2578         }
2579     }
2580
2581     # Move over to the right directory.
2582     set dir [pwd]
2583     send "cd $dir\r"
2584     expect {
2585         -re "$ROOT_PROMPT" { }
2586         "$dir:" {
2587             perror "root shell can not cd to $dir"
2588             stop_root_shell
2589             return 0
2590         }
2591     }
2592
2593     expect_after
2594
2595     return 1
2596 }
2597
2598 # Kill off a root shell started by setup_root_shell.
2599
2600 proc stop_root_shell { } {
2601     global rlogin_spawn_id
2602     global rlogin_pid
2603
2604     catch "close -i $rlogin_spawn_id"
2605     catch "exec kill $rlogin_pid"
2606     sleep 1
2607     catch "exec kill -9 $rlogin_pid"
2608     catch "wait -i $rlogin_spawn_id"
2609 }
2610
2611 # Check the date.  The string will be the output of date on this
2612 # system, and we must make sure that it is in the same timezone as the
2613 # output of date run a second time.  The first date will be run on an
2614 # rlogin or some such connection to the local system.  This is to test
2615 # to make sure that the TZ environment variable is handled correctly.
2616 # Returns 1 on sucess, 0 on failure.
2617
2618 proc check_date { date } {
2619     catch "exec date" ndate
2620     set atz ""
2621     set ntz ""
2622     scan $date "%s %s %d %d:%d:%d %s %d" adow amon adom ahr amn asc atz ayr
2623     scan $ndate "%s %s %d %d:%d:%d %s %d" ndow nmon ndom nhr nmn nsc ntz nyr
2624     if { $atz != $ntz } {
2625         verbose -log "date check failed: $atz != $ntz"
2626         return 0
2627     }
2628     return 1
2629 }
2630
2631 proc touch { file } {
2632     set f [open $file "a"]
2633     puts $f ""
2634     close $f
2635 }
2636
2637 # Implement this in tcl someday?
2638 proc tail1 { file } {
2639     exec tail -1 $file
2640 }
2641
2642 # setup_wrapper
2643 # Sets up a wraper script to set the runtime shared library environment 
2644 # variables and then executes a specific command. This is used to allow
2645 # a "rsh klist" or telnetd to execute login.krb5. 
2646 proc setup_wrapper { file command } {
2647     global BINSH
2648     global env
2649     global krb5_init_vars
2650
2651     # We will start with a BINSH script
2652     catch "exec rm -f $file"
2653
2654     set f [open $file "w" 0777]
2655     puts $f "#!$BINSH"
2656     puts $f "KRB5_CONFIG=$env(KRB5_CONFIG)"
2657     puts $f "export KRB5_CONFIG"
2658     foreach i $krb5_init_vars {
2659         regexp "^(\[^=\]*)=(.*)" $i foo evar evalue
2660         puts $f "$evar=$env($evar)"
2661         puts $f "export $evar"
2662     }
2663     puts $f "exec $command"
2664     close $f
2665     
2666     return 1
2667 }
2668
2669 proc krb_exit { } {
2670     stop_kerberos_daemons
2671 }
2672
2673 # helpful sometimes for debugging the test suite
2674 proc spawn_xterm { } {
2675     global env
2676     foreach i {KDB5_UTIL KRB5KDC KADMIND KADMIN KADMIN_LOCAL KINIT KTUTIL KLIST RLOGIN RLOGIND FTP FTPD KPASSWD REALMNAME GSSCLIENT} {
2677         global $i
2678         if [info exists $i] { set env($i) [set $i] }
2679     }
2680     exec "xterm"
2681 }