diff options
Diffstat (limited to 'src/tests/dejagnu/config/default.exp')
| -rw-r--r-- | src/tests/dejagnu/config/default.exp | 2317 |
1 files changed, 2317 insertions, 0 deletions
diff --git a/src/tests/dejagnu/config/default.exp b/src/tests/dejagnu/config/default.exp new file mode 100644 index 000000000000..2d1686c56c00 --- /dev/null +++ b/src/tests/dejagnu/config/default.exp @@ -0,0 +1,2317 @@ +# Basic expect script for Kerberos tests. +# This is a DejaGnu test script. +# Written by Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>. +# This script is automatically run by DejaGnu before running any of +# the Kerberos test scripts. + +# This file provides several functions which deal with a local +# Kerberos database. We have to do this such that we don't interfere +# with any existing Kerberos database. We will create all the files +# in the directory $tmppwd, which will have been created by the +# testsuite default script. We will use $REALMNAME as our Kerberos +# realm name, defaulting to KRBTEST.COM. + +set timeout 100 +set stty_init {erase \^h kill \^u} +set env(TERM) dumb + +set des3_krbtgt 0 +set tgt_support_desmd5 0 + +# The names of the individual passes must be unique; lots of things +# depend on it. The PASSES variable may not contain comments; only +# small pieces get evaluated, so comments will do strange things. + +# Most of the purpose of using multiple passes is to exercise the +# dependency of various bugs on configuration file settings, +# particularly with regards to encryption types. + +# The des.no-kdc-md5 pass will fail if the KDC does not constrain +# session key enctypes to those in its permitted_enctypes list. It +# works by assuming enctype similarity, thus allowing the client to +# request a des-cbc-md4 session key. Since only des-cbc-crc is in the +# KDC's permitted_enctypes list, the TGT will be unusable. + +# KLUDGE for tracking down leaking ptys +if 0 { + rename spawn oldspawn + rename wait oldwait + proc spawn { args } { + upvar 1 spawn_id spawn_id + verbose "spawn: args=$args" + set pid [eval oldspawn $args] + verbose "spawn: pid=$pid spawn_id=$spawn_id" + return $pid + } + proc wait { args } { + upvar 1 spawn_id spawn_id + verbose "wait: args=$args" + set ret [eval oldwait $args] + verbose "wait: $ret" + return $ret + } +} + +if { [string length $VALGRIND] } { + rename spawn valgrind_aux_spawn + proc spawn { args } { + global VALGRIND + upvar 1 spawn_id spawn_id + set newargs {} + set inflags 1 + set eatnext 0 + foreach arg $args { + if { $arg == "-ignore" \ + || $arg == "-open" \ + || $arg == "-leaveopen" } { + lappend newargs $arg + set eatnext 1 + continue + } + if [string match "-*" $arg] { + lappend newargs $arg + continue + } + if { $eatnext } { + set eatnext 0 + lappend newargs $arg + continue + } + if { $inflags } { + set inflags 0 + # Only run valgrind for local programs, not + # system ones. +#&&![string match "/bin/sh" $arg] sh is used to start kadmind! + if [string match "/" [string index $arg 0]]&&![string match "/bin/ls" $arg]&&![regexp {/kshd$} $arg] { + set newargs [concat $newargs $VALGRIND] + } + } + lappend newargs $arg + } + set pid [eval valgrind_aux_spawn $newargs] + return $pid + } +} + +# Hack around Solaris 9 kernel race condition that causes last output +# from a pty to get dropped. +if { $PRIOCNTL_HACK } { + catch {exec priocntl -s -c FX -m 30 -p 30 -i pid [getpid]} + rename spawn oldspawn + proc spawn { args } { + upvar 1 spawn_id spawn_id + set newargs {} + set inflags 1 + set eatnext 0 + foreach arg $args { + if { $arg == "-ignore" \ + || $arg == "-open" \ + || $arg == "-leaveopen" } { + lappend newargs $arg + set eatnext 1 + continue + } + if [string match "-*" $arg] { + lappend newargs $arg + continue + } + if { $eatnext } { + set eatnext 0 + lappend newargs $arg + continue + } + if { $inflags } { + set inflags 0 + set newargs [concat $newargs {priocntl -e -c FX -p 0}] + } + lappend newargs $arg + } + set pid [eval oldspawn $newargs] + return $pid + } +} + +# The des.des3-tgt.no-kdc-des3 pass will fail if the KDC doesn't +# constrain ticket key enctypes to those in permitted_enctypes. It +# does this by not putting des3 in the permitted_enctypes, while +# creating a TGT princpal that has a des3 key as well as a des key. + +# XXX -- master_key_type is fragile w.r.t. permitted_enctypes; it is +# possible to configure things such that you have a master_key_type +# that is not permitted, and the error message used to be cryptic. + +set passes { + { + des + mode=udp + des3_krbtgt=0 + {supported_enctypes=des-cbc-crc:normal} + {dummy=[verbose -log "DES TGT, DES enctype"]} + } + { + des.des3tgt + mode=udp + des3_krbtgt=1 + {supported_enctypes=des-cbc-crc:normal} + {dummy=[verbose -log "DES3 TGT, DES enctype"]} + } + { + des3 + mode=udp + des3_krbtgt=1 + {supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal} + {dummy=[verbose -log "DES3 TGT, DES3 + DES enctypes"]} + } + { + aes-des + mode=udp + des3_krbtgt=0 + {supported_enctypes=aes256-cts-hmac-sha1-96:normal des-cbc-crc:normal} + {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des-cbc-crc} + {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des-cbc-crc} + {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des-cbc-crc} + {master_key_type=aes256-cts-hmac-sha1-96} + {dummy=[verbose -log "AES + DES enctypes"]} + } + { + aes-only + mode=udp + des3_krbtgt=0 + {supported_enctypes=aes256-cts-hmac-sha1-96:normal} + {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96} + {permitted_enctypes(client)=aes256-cts-hmac-sha1-96} + {permitted_enctypes(server)=aes256-cts-hmac-sha1-96} + {allow_weak_crypto(kdc)=false} + {allow_weak_crypto(slave)=false} + {allow_weak_crypto(client)=false} + {allow_weak_crypto(server)=false} + {master_key_type=aes256-cts-hmac-sha1-96} + {dummy=[verbose -log "AES enctypes"]} + } + { + aes-sha2-only + mode=udp + des3_krbtgt=0 + {supported_enctypes=aes256-sha2:normal} + {permitted_enctypes(kdc)=aes256-sha2} + {permitted_enctypes(slave)=aes256-sha2} + {permitted_enctypes(client)=aes256-sha2} + {permitted_enctypes(server)=aes256-sha2} + {default_tgs_enctypes(kdc)=aes256-sha2} + {default_tgs_enctypes(slave)=aes256-sha2} + {default_tgs_enctypes(client)=aes256-sha2} + {default_tgs_enctypes(server)=aes256-sha2} + {default_tkt_enctypes(kdc)=aes256-sha2} + {default_tkt_enctypes(slave)=aes256-sha2} + {default_tkt_enctypes(client)=aes256-sha2} + {default_tkt_enctypes(server)=aes256-sha2} + {allow_weak_crypto(kdc)=false} + {allow_weak_crypto(slave)=false} + {allow_weak_crypto(client)=false} + {allow_weak_crypto(server)=false} + {master_key_type=aes256-sha2} + {dummy=[verbose -log "aes256-sha2 enctype"]} + } + { + camellia-only + mode=udp + des3_krbtgt=0 + {supported_enctypes=camellia256-cts:normal} + {permitted_enctypes(kdc)=camellia256-cts} + {permitted_enctypes(slave)=camellia256-cts} + {permitted_enctypes(client)=camellia256-cts} + {permitted_enctypes(server)=camellia256-cts} + {default_tgs_enctypes(kdc)=camellia256-cts} + {default_tgs_enctypes(slave)=camellia256-cts} + {default_tgs_enctypes(client)=camellia256-cts} + {default_tgs_enctypes(server)=camellia256-cts} + {default_tkt_enctypes(kdc)=camellia256-cts} + {default_tkt_enctypes(slave)=camellia256-cts} + {default_tkt_enctypes(client)=camellia256-cts} + {default_tkt_enctypes(server)=camellia256-cts} + {allow_weak_crypto(kdc)=false} + {allow_weak_crypto(slave)=false} + {allow_weak_crypto(client)=false} + {allow_weak_crypto(server)=false} + {master_key_type=camellia256-cts} + {dummy=[verbose -log "Camellia-256 enctype"]} + } + { + aes-des3 + mode=udp + des3_krbtgt=0 + {supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal} + {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {master_key_type=aes256-cts-hmac-sha1-96} + {dummy=[verbose -log "AES + DES3 + DES enctypes"]} + } + { + aes-des3tgt + mode=udp + des3_krbtgt=1 + {supported_enctypes=aes256-cts-hmac-sha1-96:normal des3-cbc-sha1:normal des-cbc-crc:normal} + {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {permitted_enctypes(client)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {permitted_enctypes(server)=aes256-cts-hmac-sha1-96 des3-cbc-sha1 des-cbc-crc} + {master_key_type=aes256-cts-hmac-sha1-96} + {dummy=[verbose -log "AES + DES enctypes, DES3 TGT"]} + } + { + des-v4 + mode=udp + des3_krbtgt=0 + {supported_enctypes=des-cbc-crc:v4} + {default_tkt_enctypes(client)=des-cbc-crc} + {dummy=[verbose -log "DES TGT, DES-CRC enctype, V4 salt"]} + } + { + des-md5-v4 + mode=udp + des3_krbtgt=0 + {supported_enctypes=des-cbc-md5:v4 des-cbc-crc:v4} + {default_tkt_enctypes(client)=des-cbc-md5 des-cbc-crc} + {dummy=[verbose -log "DES TGT, DES-MD5 and -CRC enctypes, V4 salt"]} + } + { + all-enctypes + mode=udp + des3_krbtgt=0 + {allow_weak_crypto(kdc)=false} + {allow_weak_crypto(slave)=false} + {allow_weak_crypto(client)=false} + {allow_weak_crypto(server)=false} + {dummy=[verbose -log "all default enctypes"]} + } + { + des.no-kdc-md5 + mode=udp + des3_krbtgt=0 + tgt_support_desmd5=0 + {permitted_enctypes(kdc)=des-cbc-crc} + {default_tgs_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc} + {default_tkt_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc} + {supported_enctypes=des-cbc-crc:normal} + {master_key_type=des-cbc-crc} + {dummy=[verbose -log \ + "DES TGT, KDC permitting only des-cbc-crc"]} + } + { + des.des3-tgt.no-kdc-des3 + mode=udp + tgt_support_desmd5=0 + {permitted_enctypes(kdc)=des-cbc-crc} + {default_tgs_enctypes(client)=des-cbc-crc} + {default_tkt_enctypes(client)=des-cbc-crc} + {supported_enctypes=des3-cbc-sha1:normal des-cbc-crc:normal} + {master_key_type=des-cbc-crc} + {dummy=[verbose -log \ + "DES3 TGT, KDC permitting only des-cbc-crc"]} + } +} + +# des.md5-tgt is set as unused, since it won't trigger the error case +# if SUPPORT_DESMD5 isn't honored. + +# The des.md5-tgt pass will fail if enctype similarity is inconsisent; +# between 1.0.x and 1.1, the decrypt functions became more strict +# about matching enctypes, while the KDB retrieval functions didn't +# coerce the enctype to match what was requested. It works by setting +# SUPPORT_DESMD5 on the TGT principal, forcing an enctype of +# des-cbc-md5 on the TGT key. Since the database only contains a +# des-cbc-crc key, the decrypt will fail if enctypes are not coerced. + +# des.no-kdc-md5.client-md4-skey is retained in unsed_passes, even +# though des.no-kdc-md5 is roughly equivalent, since the associated +# comment needs additional investigation at some point re the kadmin +# client. + +# The des.no-kdc-md5.client-md4-skey will fail on TGS requests due to +# the KDC issuing session keys that it won't accept. It will also +# fail for a kadmin client, but for different reasons, since the kadm5 +# library does some curious filtering of enctypes, and also uses +# get_in_tkt() rather than get_init_creds(); the former does an +# intersection of the enctypes provided by the caller and those listed +# in the config file! + +set unused_passes { + { + des.md5-tgt + des3_krbtgt=0 + tgt_support_desmd5=1 + supported_enctypes=des-cbc-crc:normal + {permitted_enctypes(kdc)=des-cbc-md5 des-cbc-md4 des-cbc-crc} + {permitted_enctypes(client)=des-cbc-md5 des-cbc-md4 des-cbc-crc} + {dummy=[verbose -log "DES TGT, SUPPORTS_DESMD5"]} + } + { + des.md5-tgt.no-kdc-md5 + des3_krbtgt=0 + tgt_support_desmd5=1 + {permitted_enctypes(kdc)=des-cbc-crc} + {default_tgs_enctypes(client)=des-cbc-crc} + {default_tkt_enctypes(client)=des-cbc-crc} + {supported_enctypes=des-cbc-crc:normal} + {master_key_type=des-cbc-crc} + {dummy=[verbose -log \ + "DES TGT, SUPPORTS_DESMD5, KDC permitting only des-cbc-crc"]} + } + { + des.no-kdc-md5.client-md4-skey + des3_krbtgt=0 + {permitted_enctypes(kdc)=des-cbc-crc} + {permitted_enctypes(client)=des-cbc-crc des-cbc-md4} + {default_tgs_enctypes(client)=des-cbc-crc des-cbc-md4} + {default_tkt_enctypes(client)=des-cbc-md4} + {supported_enctypes=des-cbc-crc:normal} + {dummy=[verbose -log \ + "DES TGT, DES enctype, KDC permitting only des-cbc-crc, client requests des-cbc-md4 session key"]} + } + { + all-enctypes + des3_krbtgt=1 + {supported_enctypes=\ + aes256-cts-hmac-sha1-96:normal aes256-cts-hmac-sha1-96:norealm \ + aes128-cts-hmac-sha1-96:normal aes128-cts-hmac-sha1-96:norealm \ + des3-cbc-sha1:normal des3-cbc-sha1:none \ + des-cbc-md5:normal des-cbc-md4:normal des-cbc-crc:normal \ + des-cbc-md5:v4 des-cbc-md4:v4 des-cbc-crc:v4 \ + } + {dummy=[verbose -log "DES3 TGT, default enctypes"]} + } + { + aes-tcp + mode=tcp + des3_krbtgt=0 + {supported_enctypes=aes256-cts-hmac-sha1-96:normal} + {permitted_enctypes(kdc)=aes256-cts-hmac-sha1-96} + {permitted_enctypes(client)=aes256-cts-hmac-sha1-96} + {permitted_enctypes(server)=aes256-cts-hmac-sha1-96} + {master_key_type=aes256-cts-hmac-sha1-96} + {dummy=[verbose -log "AES via TCP"]} + } +} +# {supported_enctypes=des-cbc-md5:normal des-cbc-crc:normal twofish256-hmac-sha1:normal } + +# This shouldn't be necessary on dejagnu-1.4 and later, but 1.3 seems +# to need it because its runtest.exp doesn't deal with PASS at all. +if [info exists PASS] { + foreach pass $passes { + if { [lsearch -exact $PASS [lindex $pass 0]] >= 0 } { + lappend MULTIPASS $pass + } + } +} else { + set MULTIPASS $passes +} + +set last_passname_conf "" +set last_passname_db "" + +# We do everything in a temporary directory. +if ![info exists TMPDIR] { + set tmppwd "[pwd]/tmpdir" + if ![file isdirectory $tmppwd] { + catch "exec mkdir $tmppwd" status + } +} else { + set tmppwd $TMPDIR +} +verbose "tmppwd=$tmppwd" + +# On Ultrix, use /bin/sh5 in preference to /bin/sh. +if ![info exists BINSH] { + if [file exists /bin/sh5] { + set BINSH /bin/sh5 + } else { + set BINSH /bin/sh + } +} + +# For security, we must not use generally known passwords. This is +# because some of the tests may be run as root. If the passwords were +# generally know, then somebody could work out the appropriate +# Kerberos ticket to use, and come in when, say, the telnetd daemon +# was being tested by root. The window for doing this is very very +# small, so the password does not have to be perfect, it just can't be +# constant. +if ![info exists KEY] { + catch {exec $BINSH -c "echo $$"} KEY + verbose "KEY is $KEY" + set keyfile [open $tmppwd/KEY w] + puts $keyfile "$KEY" + close $keyfile +} + +# Clear away any files left over from a previous run. +# We can't use them now because we don't know the right KEY. +# krb5.conf might change if running tests on another host +file delete $tmppwd/krb5.conf $tmppwd/kdc.conf $tmppwd/slave.conf \ + $tmppwd/krb5.client.conf $tmppwd/krb5.server.conf \ + $tmppwd/krb5.kdc.conf $tmppwd/krb5.slave.conf + +proc delete_db {} { + global tmppwd + # Master and slave db files + file delete $tmppwd/kdc-db $tmppwd/kdc-db.ok $tmppwd/kdc-db.kadm5 \ + $tmppwd/kdc-db.kadm5.lock \ + $tmppwd/kdc-db.ulog \ + $tmppwd/slave-db $tmppwd/slave-db.ok $tmppwd/slave-db.kadm5 $tmppwd/slave-db.kadm5.lock \ + $tmppwd/slave-db~ $tmppwd/slave-db~.ok $tmppwd/slave-db~.kadm5 $tmppwd/slave-db~.kadm5.lock + # Creating a new database means we need a new srvtab. + file delete $tmppwd/srvtab $tmppwd/cpw_srvtab +} + +delete_db + +# Put the installed kerberos directories on PATH. +# This needs to be fixed for V5. +# set env(PATH) $env(PATH):/usr/kerberos/bin:/usr/kerberos/etc +# verbose "PATH=$env(PATH)" + +# Some of the tests expect $env(USER) to be set. +if ![info exists env(USER)] { + if [info exists env(LOGNAME)] { + set env(USER) $env(LOGNAME) + } else { + if [info exists logname] { + set env(USER) $logname + } else { + catch "exec whoami" env(USER) + } + } +} + +# set the realm. The user can override this on the runtest line. +if ![info exists REALMNAME] { + set REALMNAME "KRBTEST.COM" +} +verbose "Test realm is $REALMNAME" + +# Find some programs we need. We use the binaries from the build tree +# if they exist. If they do not, then they must be in PATH. We +# expect $objdir to be ...tests/dejagnu. + +foreach i { + {KDB5_UTIL $objdir/../../kadmin/dbutil/kdb5_util} + {KRB5KDC $objdir/../../kdc/krb5kdc} + {KADMIND $objdir/../../kadmin/server/kadmind} + {KADMIN $objdir/../../kadmin/cli/kadmin} + {KADMIN_LOCAL $objdir/../../kadmin/cli/kadmin.local} + {KINIT $objdir/../../clients/kinit/kinit} + {KTUTIL $objdir/../../kadmin/ktutil/ktutil} + {KLIST $objdir/../../clients/klist/klist} + {KDESTROY $objdir/../../clients/kdestroy/kdestroy} + {RESOLVE $objdir/../resolve/resolve} + {T_INETD $objdir/t_inetd} + {KPROPLOG $objdir/../../slave/kproplog} + {KPASSWD $objdir/../../clients/kpasswd/kpasswd} + {KPROPD $objdir/../../slave/kpropd} + {KPROP $objdir/../../slave/kprop} +} { + set varname [lindex $i 0] + if ![info exists $varname] { + eval set varval [lindex $i 1] + set varval [findfile $varval] + set $varname $varval + verbose "$varname=$varval" + } { + eval set varval \$$varname + verbose "$varname already set to $varval" + } +} + +verbose "setting up onexit handler (old handler=[exit -onexit])" +exit -onexit [concat { + verbose "calling stop_kerberos_daemons (onexit handler)" + stop_kerberos_daemons; +} [exit -onexit]] + +# run_once + +# Many tests are independent of the actual enctypes used, which is +# what our passes are (currently) all about. Use this to prevent +# multiple invocations. If a test depends on, say, the master key +# type but nothing else, you could also use the master key type in the +# tag name, and avoid redundant tests in additional passes using the +# same master key type. + +proc run_once { tag body } { + global run_once_tags + if ![info exists run_once_tags($tag)] { + set run_once_tags($tag) 1 + uplevel 1 $body + } +} + +# check_exit_status +# Check the exit status of a spawned program (using the caller's value +# of spawn_id). Returns 1 if the program succeeded, 0 if it failed. + +proc check_exit_status { testname } { + upvar 1 spawn_id spawn_id + + verbose "about to wait ($testname)" + set status_list [wait -i $spawn_id] + verbose "wait -i $spawn_id returned $status_list ($testname)" + catch "close -i $spawn_id" + if { [lindex $status_list 2] != 0 || [lindex $status_list 3] != 0 } { + verbose -log "exit status: $status_list" + fail "$testname" + return 0 + } else { + return 1 + } +} + +# +# ENVSTACK +# + +# These procedures implement an environment variable stack. They use +# the global variable $envvars_tosave for the purpose of identifying +# which environment variables to save. They also track which ones are +# unset at any particular point. The stack pointer is $envstackp, +# which is an integer. The arrays $envstack$envstackp and +# $unenvstack$envstackp store respectively the set of old environment +# variables/values pushed onto the stack and the set of old unset +# environment variables for a given value of $envstackp. + +# Changing the value of $envvars_tosave after performing the first +# push operation may result in strangeness. + +# +# envstack_push +# +# Push set of current environment variables. +# +proc envstack_push { } { + global env + global envvars_tosave + global envstackp + global envstack$envstackp + global unenvstack$envstackp + + verbose "envstack_push: starting, sp=$envstackp" + foreach i $envvars_tosave { + if [info exists env($i)] { + verbose "envstack_push: saving $i=$env($i)" + set envstack${envstackp}($i) $env($i) + } { + verbose "envstack_push: marking $i as unset" + set unenvstack${envstackp}($i) unset + } + } + incr envstackp + verbose "envstack_push: exiting, sp=$envstackp" +} + +# +# envstack_pop +# +# Pop set of current environment variables. +# +proc envstack_pop { } { + global env + global envstackp + + verbose "envstack_pop: starting, sp=$envstackp" + incr envstackp -1 + global envstack$envstackp # YUCK!!! no obvious better way though... + global unenvstack$envstackp + if {$envstackp < 0} { + perror "envstack_pop: stack underflow!" + return + } + if [info exists envstack$envstackp] { + foreach i [array names envstack$envstackp] { + if [info exists env($i)] { + verbose "envstack_pop: $i was $env($i)" + } + eval set env($i) \$envstack${envstackp}($i) + verbose "envstack_pop: restored $i to $env($i)" + } + unset envstack$envstackp + } + if [info exists unenvstack$envstackp] { + foreach i [array names unenvstack$envstackp] { + if [info exists env($i)] { + verbose "envstack_pop: $i was $env($i)" + unset env($i) + verbose "envstack_pop: $i unset" + } { + verbose "envstack_pop: ignoring already unset $i" + } + } + unset unenvstack$envstackp + } + verbose "envstack_pop: exiting, sp=$envstackp" +} + +# +# Initialize the envstack +# +set envvars_tosave { + KRB5_CONFIG KRB5CCNAME KRB5_CLIENT_KTNAME KRB5RCACHEDIR KRB5_KDC_PROFILE +} +set krb5_init_vars [list ] +# XXX -- fix me later! +foreach i $runvarlist { + verbose "processing $i" + if {[regexp "^(\[^=\]*)=(.*)" $i foo evar evalue]} { + verbose "adding $evar to savelist" + lappend envvars_tosave $evar + verbose "savelist $envvars_tosave" + lappend krb5_init_vars $i + } + # Make sure we don't get confused by translated messages + # or localized times. + lappend envvars_tosave "LC_ALL" + lappend krb5_init_vars "LC_ALL=C" +} +set envstackp 0 +envstack_push + +# setup_runtime_flags +# Sets the proper flags for shared libraries. +# Configuration is through a site.exp and the runvarlist variable +# Returns 1 if variables were already set, otherwise 0 +proc setup_runtime_env { } { + global env + global krb5_init_vars + + # Set the variables + foreach i $krb5_init_vars { + regexp "^(\[^=\]*)=(.*)" $i foo evar evalue + set env($evar) "$evalue" + verbose "$evar=$evalue" + } + return 0 +} + +# get_hostname +# This procedure sets the global variale hostname to the local +# hostname as seen by krb5_sname_to_principal. Returns 1 on success, +# 0 on failure. + +proc get_hostname { } { + global RESOLVE + global hostname + global tmppwd + + if {[info exists hostname]} { + return 1 + } + + envstack_push + setup_runtime_env + catch "exec $RESOLVE -q >$tmppwd/hostname" exec_output + envstack_pop + if ![string match "" $exec_output] { + verbose -log $exec_output + perror "can't get hostname" + return 0 + } + set file [open $tmppwd/hostname r] + if { [ gets $file hostname ] == -1 } { + perror "no output from hostname" + return 0 + } + close $file + file delete $tmppwd/hostname + + set hostname [string tolower $hostname] + + return 1 +} + +# modify_principal name options... + +proc modify_principal { name args } { + global KADMIN_LOCAL + global REALMNAME + + envstack_push + setup_kerberos_env kdc + spawn $KADMIN_LOCAL -r $REALMNAME + envstack_pop + expect_after { + eof { + fail "modprinc (kadmin.local)" + return 0 + } + timeout { + fail "modprinc (kadmin.local)" + return 0 + } + } + expect "kadmin.local: " + send "modprinc $args $name\r" + expect -re "modprinc \[^\n\r\]* $name" + expect -re "Principal .* modified." + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status "kadmin.local modprinc"] { + perror "kadmin.local modprinc exited abnormally" + } + return 1 +} + +# kdc listens on +0..+3, depending whether we're testing reachable or not +# client tries +1 and +6 +# kadmind +4 +# kpasswd +5 +# (nothing) +6 +# application servers (krlogind, telnetd, krshd, ftpd, etc) +8 +# iprop +9 (if enabled) +# kpropd +10 +if [info exists PORTBASE] { + set portbase $PORTBASE +} else { + set portbase 3085 +} + +set ulog 0 + +# setup_kerberos_files +# This procedure will create some Kerberos files which must be created +# manually before trying to run any Kerberos programs. Returns 1 on +# success, 0 on failure. + +proc setup_kerberos_files { } { + global REALMNAME + global hostname + global tmppwd + global supported_enctypes + global last_passname_conf + global multipass_name + global master_key_type + global mode + global portbase + global ulog + + if ![get_hostname] { + return 0 + } + + setup_krb5_conf client + setup_krb5_conf server + setup_krb5_conf kdc + setup_krb5_conf slave + + # Create a kdc.conf file. + if { ![file exists $tmppwd/kdc.conf] \ + || $last_passname_conf != $multipass_name } { + set conffile [open $tmppwd/kdc.conf w] + puts $conffile "\[kdcdefaults\]" + puts $conffile " kdc_listen = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]" + puts $conffile " kdc_tcp_listen = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]" + puts $conffile "" + puts $conffile "\[realms\]" + puts $conffile " $REALMNAME = \{" + # Testing with a colon in the name exercises default handling + # for pathnames. + puts $conffile " key_stash_file = $tmppwd/stash:foo" + puts $conffile " acl_file = $tmppwd/acl" + puts $conffile " kadmind_port = [expr 4 + $portbase]" + puts $conffile " kpasswd_port = [expr 5 + $portbase]" + puts $conffile " max_life = 1:00:00" + puts $conffile " max_renewable_life = 3:00:00" + if [info exists master_key_type] { + puts $conffile " master_key_type = $master_key_type" + } + puts $conffile " master_key_name = master/key" + if [info exists supported_enctypes] { + puts $conffile " supported_enctypes = $supported_enctypes" + } + if { $mode == "tcp" } { + puts $conffile " kdc_listen = [expr 3 + $portbase]" + puts $conffile " kdc_tcp_listen = [expr 1 + $portbase],[expr 3 + $portbase]" + } else { + puts $conffile " kdc_listen = [expr 1 + $portbase]" + puts $conffile " kdc_tcp_listen = [expr 3 + $portbase]" + } + puts $conffile " default_principal_expiration = 2037.12.31.23.59.59" + puts $conffile " default_principal_flags = -postdateable forwardable" + puts $conffile " dict_file = $tmppwd/dictfile" + if { $ulog != 0 } { + puts $conffile " iprop_enable = true" + puts $conffile " iprop_port = [expr 9 + $portbase]" + puts $conffile " iprop_logfile = $tmppwd/db.ulog" + } else { + puts $conffile "# no ulog" + } + puts $conffile " \}" + puts $conffile "" + close $conffile + } + + # Create a config file for the slave KDC (kpropd only, no normal + # KDC processes). + if { ![file exists $tmppwd/slave.conf] \ + || $last_passname_conf != $multipass_name } { + set conffile [open $tmppwd/slave.conf w] + puts $conffile "\[kdcdefaults\]" + puts $conffile " kdc_listen = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]" + puts $conffile " kdc_tcp_listen = $portbase,[expr 1 + $portbase],[expr 2 + $portbase]" + puts $conffile "" + puts $conffile "\[realms\]" + puts $conffile " $REALMNAME = \{" + # Testing with a colon in the name exercises default handling + # for pathnames. + puts $conffile " key_stash_file = $tmppwd/slave-stash" + puts $conffile " acl_file = $tmppwd/slave-acl" + puts $conffile " kadmind_port = [expr 4 + $portbase]" + puts $conffile " kpasswd_port = [expr 5 + $portbase]" + puts $conffile " max_life = 1:00:00" + puts $conffile " max_renewable_life = 3:00:00" + if [info exists master_key_type] { + puts $conffile " master_key_type = $master_key_type" + } + puts $conffile " master_key_name = master/key" + if [info exists supported_enctypes] { + puts $conffile " supported_enctypes = $supported_enctypes" + } + if { $mode == "tcp" } { + puts $conffile " kdc_listen = [expr 3 + $portbase]" + puts $conffile " kdc_tcp_listen = [expr 1 + $portbase],[expr 3 + $portbase]" + } else { + puts $conffile " kdc_listen = [expr 1 + $portbase]" + puts $conffile " kdc_tcp_listen = [expr 3 + $portbase]" + } + puts $conffile " default_principal_expiration = 2037.12.31.23.59.59" + puts $conffile " default_principal_flags = -postdateable forwardable" + puts $conffile " dict_file = $tmppwd/dictfile" + if { $ulog != 0 } { + puts $conffile " iprop_enable = true" + puts $conffile " iprop_port = [expr 9 + $portbase]" + puts $conffile " iprop_logfile = $tmppwd/slave-db.ulog" + } else { + puts $conffile "# no ulog" + } + puts $conffile " \}" + puts $conffile "" + close $conffile + } + + # Create ACL file. + set aclfile [open $tmppwd/acl w] + puts $aclfile "krbtest/admin@$REALMNAME *" + puts $aclfile "kiprop/$hostname@$REALMNAME p" + close $aclfile + + # Create dictfile file. + if ![file exists $tmppwd/dictfile] { + set dictfile [open $tmppwd/dictfile w] + puts $dictfile "weak_password" + close $dictfile + } + + set last_passname_conf $multipass_name + return 1 +} + +proc reset_kerberos_files { } { + global tmppwd + file delete $tmppwd/kdc.conf $tmppwd/slave.conf $tmppwd/krb5.client.conf \ + $tmppwd/krb5.server.conf $tmppwd/krb5.kdc.conf + setup_kerberos_files +} + +proc setup_krb5_conf { {type client} } { + global tmppwd + global hostname + global REALMNAME + global last_passname_conf + global multipass_name + global default_tgs_enctypes + global default_tkt_enctypes + global permitted_enctypes + global allow_weak_crypto + global mode + global portbase + global srcdir + + set pkinit_certs [findfile "[pwd]/$srcdir/pkinit-certs" "[pwd]/$srcdir/pkinit-certs" "$srcdir/pkinit-certs"] + # Create a krb5.conf file. + if { ![file exists $tmppwd/krb5.$type.conf] \ + || $last_passname_conf != $multipass_name } { + set conffile [open $tmppwd/krb5.$type.conf w] + puts $conffile "\[libdefaults\]" + puts $conffile " default_realm = $REALMNAME" + puts $conffile " dns_lookup_kdc = false" + if [info exists allow_weak_crypto($type)] { + puts $conffile " allow_weak_crypto = $allow_weak_crypto($type)" + } else { + puts $conffile " allow_weak_crypto = true" + } + puts $conffile " pkinit_anchors = FILE:$pkinit_certs/ca.pem" + if [info exists default_tgs_enctypes($type)] { + puts $conffile \ + " default_tgs_enctypes = $default_tgs_enctypes($type)" + } + if [info exists default_tkt_enctypes($type)] { + puts $conffile \ + " default_tkt_enctypes = $default_tkt_enctypes($type)" + } + if [info exists permitted_enctypes($type)] { + puts $conffile \ + " permitted_enctypes = $permitted_enctypes($type)" + } + if { $mode == "tcp" } { + puts $conffile " udp_preference_limit = 1" + } + puts $conffile " plugin_base_dir = $tmppwd/../../../plugins" + puts $conffile "" + puts $conffile "\[realms\]" + puts $conffile " $REALMNAME = \{" + # There's probably nothing listening here. It would be a good + # test for the handling of a non-responsive KDC address. However, + # on some systems, like Tru64, we often wind up with the client's + # socket bound to this address, causing our request to appear in + # our incoming queue as if it were a response, which causes test + # failures. If we were running the client and KDC on different + # hosts, this would be okay.... + #puts $conffile " kdc = $hostname:[expr 6 + $portbase]" + puts $conffile " pkinit_identity = FILE:$pkinit_certs/kdc.pem,$pkinit_certs/privkey.pem" + puts $conffile " pkinit_anchors = FILE:$pkinit_certs/ca.pem" + puts $conffile " kdc = $hostname:[expr 1 + $portbase]" + puts $conffile " admin_server = $hostname:[expr 4 + $portbase]" + puts $conffile " kpasswd_server = $hostname:[expr 5 + $portbase]" + puts $conffile " database_module = foo_db2" + puts $conffile " \}" + puts $conffile "" + puts $conffile "\[domain_realm\]" + puts $conffile " $hostname = $REALMNAME" + puts $conffile "" + puts $conffile "\[logging\]" + puts $conffile " admin_server = FILE:$tmppwd/kadmind5.log" + puts $conffile " kdc = FILE:$tmppwd/kdc.log" + puts $conffile " default = FILE:$tmppwd/others.log" + puts $conffile "" + puts $conffile "\[dbmodules\]" + puts $conffile " db_module_dir = $tmppwd/../../../plugins/kdb" + puts $conffile " foo_db2 = {" + puts $conffile " db_library = db2" + puts $conffile " database_name = $tmppwd/$type-db" + puts $conffile " }" + close $conffile + } +} + +# Save the original values of the environment variables we are going +# to muck with. + +# XXX deal with envstack later. + +if [info exists env(KRB5_CONFIG)] { + set orig_krb5_conf $env(KRB5_CONFIG) +} else { + catch "unset orig_krb5_config" +} + +if [info exists env(KRB5CCNAME)] { + set orig_krb5ccname $env(KRB5CCNAME) +} else { + catch "unset orig_krb5ccname" +} + +if [info exists env(KRB5_CLIENT_KTNAME)] { + set orig_krb5clientktname $env(KRB5_CLIENT_KTNAME) +} else { + catch "unset orig_krb5clientktname" +} + +if [ info exists env(KRB5RCACHEDIR)] { + set orig_krb5rcachedir $env(KRB5RCACHEDIR) +} else { + catch "unset orig_krb5rcachedir" +} + +# setup_kerberos_env +# Set the environment variables needed to run Kerberos programs. + +proc setup_kerberos_env { {type client} } { + global REALMNAME + global env + global tmppwd + global hostname + global krb5_init_vars + global portbase + + # Set the environment variable KRB5_CONFIG to point to our krb5.conf file. + # All the Kerberos tools check KRB5_CONFIG. + # Actually, V5 doesn't currently use this. + set env(KRB5_CONFIG) $tmppwd/krb5.$type.conf + verbose "KRB5_CONFIG=$env(KRB5_CONFIG)" + + # Direct the Kerberos programs at a local ticket file. + set env(KRB5CCNAME) $tmppwd/tkt + verbose "KRB5CCNAME=$env(KRB5CCNAME)" + + # Direct the Kerberos programs at a local client keytab. + set env(KRB5_CLIENT_KTNAME) $tmppwd/client_keytab + verbose "KRB5_CLIENT_KTNAME=$env(KRB5_CLIENT_KTNAME)" + + # Direct the Kerberos server at a cache file stored in the + # temporary directory. + set env(KRB5RCACHEDIR) $tmppwd + verbose "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)" + + # Get the run time environment variables... (including LD_LIBRARY_PATH) + setup_runtime_env + + # Set our kdc config file, if needed. + switch $type { + client - + server { catch {unset env(KRB5_KDC_PROFILE)} } + kdc { set env(KRB5_KDC_PROFILE) $tmppwd/kdc.conf } + slave { set env(KRB5_KDC_PROFILE) $tmppwd/slave.conf } + default { error "unknown config file type $type" } + } + if [info exists env(KRB5_KDC_PROFILE)] { + verbose "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)" + } + + # Create an environment setup script. (For convenience) + if ![file exists $tmppwd/$type-env.sh] { + set envfile [open $tmppwd/$type-env.sh w] + puts $envfile "KRB5_CONFIG=$env(KRB5_CONFIG)" + puts $envfile "KRB5CCNAME=$env(KRB5CCNAME)" + puts $envfile "KRB5_CLIENT_KTNAME=$env(KRB5_CLIENT_KTNAME)" + puts $envfile "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)" + if [info exists env(KRB5_KDC_PROFILE)] { + puts $envfile "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)" + } else { + puts $envfile "unset KRB5_KDC_PROFILE" + } + puts $envfile "export KRB5_CONFIG KRB5CCNAME KRB5RCACHEDIR" + puts $envfile "export KRB5_KDC_PROFILE KRB5_CLIENT_KTNAME" + foreach i $krb5_init_vars { + regexp "^(\[^=\]*)=(.*)" $i foo evar evalue + puts $envfile "$evar=$env($evar)" + puts $envfile "export $evar" + } + close $envfile + } + if ![file exists $tmppwd/$type-env.csh] { + set envfile [open $tmppwd/$type-env.csh w] + puts $envfile "setenv KRB5_CONFIG $env(KRB5_CONFIG)" + puts $envfile "setenv KRB5CCNAME $env(KRB5CCNAME)" + puts $envfile "setenv KRB5_CLIENT_KTNAME $env(KRB5_CLIENT_KTNAME)" + puts $envfile "setenv KRB5RCACHEDIR $env(KRB5RCACHEDIR)" + if [info exists env(KRB5_KDC_PROFILE)] { + puts $envfile "setenv KRB5_KDC_PROFILE $env(KRB5_KDC_PROFILE)" + } else { + puts $envfile "unsetenv KRB5_KDC_PROFILE" + } + foreach i $krb5_init_vars { + regexp "^(\[^=\]*)=(.*)" $i foo evar evalue + puts $envfile "setenv $evar $env($evar)" + } + close $envfile + } + return 1 +} + +# setup_kerberos_db +# Initialize the Kerberos database. If the argument is non-zero, call +# pass at relevant points. Returns 1 on success, 0 on failure. + +proc setup_kerberos_db { standalone } { + global REALMNAME KDB5_UTIL KADMIN_LOCAL KEY + global tmppwd hostname + global spawn_id + global des3_krbtgt tgt_support_desmd5 + global multipass_name last_passname_db + + set failall 0 + + if {!$standalone && [file exists $tmppwd/kdc-db.ok] \ + && $last_passname_db == $multipass_name} { + return 1 + } + + delete_db + + envstack_push + if { ![setup_kerberos_files] || ![setup_kerberos_env kdc] } { + set failall 1 + } + + # Set up a common expect_after for use in multiple places. + set def_exp_after { + timeout { + set test "$test (timeout)" + break + } + eof { + set test "$test (eof)" + break + } + } + + set test "kdb5_util create" + set body { + if $failall { + break + } + #exec xterm + verbose "starting $test" + spawn $KDB5_UTIL -r $REALMNAME create -W + expect_after $def_exp_after + + expect "Enter KDC database master key:" + + set test "kdb5_util create (verify)" + send "masterkey$KEY\r" + expect "Re-enter KDC database master key to verify:" + + set test "kdb5_util create" + send "masterkey$KEY\r" + expect { + -re "\[Cc\]ouldn't" { + expect eof + break + } + "Cannot find/read stored" exp_continue + "Warning: proceeding without master key" exp_continue + eof { } + } + catch expect_after + if ![check_exit_status kdb5_util] { + break + } + } + set ret [catch $body] + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } + } else { + if $standalone { + pass $test + } + } + + # Stash the master key in a file. + set test "kdb5_util stash" + set body { + if $failall { + break + } + spawn $KDB5_UTIL -r $REALMNAME stash + verbose "starting $test" + expect_after $def_exp_after + expect "Enter KDC database master key:" + send "masterkey$KEY\r" + expect eof + catch expect_after + if ![check_exit_status kdb5_util] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } else { + delete_db + } + } else { + if $standalone { + pass $test + } + } + + # Add an admin user. + set test "kadmin.local ank krbtest/admin" + set body { + if $failall { + break + } + spawn $KADMIN_LOCAL -r $REALMNAME + verbose "starting $test" + expect_after $def_exp_after + + expect "kadmin.local: " + send "ank krbtest/admin@$REALMNAME\r" + # It echos... + expect "ank krbtest/admin@$REALMNAME\r" + expect "Enter password for principal \"krbtest/admin@$REALMNAME\":" + send "adminpass$KEY\r" + expect "Re-enter password for principal \"krbtest/admin@$REALMNAME\":" + send "adminpass$KEY\r" + expect { + "Principal \"krbtest/admin@$REALMNAME\" created" { } + "Principal or policy already exists while creating*" { } + } + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status kadmin_local] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } else { + delete_db + } + } else { + if $standalone { + pass $test + } + } + + # Add an incremental-propagation service. + set test "kadmin.local ank krbtest/fast" + set body { + if $failall { + break + } + spawn $KADMIN_LOCAL -r $REALMNAME + verbose "starting $test" + expect_after $def_exp_after + + expect "kadmin.local: " + send "ank +requires_preauth krbtest/fast@$REALMNAME\r" + expect "Enter password for principal \"krbtest/fast@$REALMNAME\":" + send "adminpass$KEY\r" + expect "Re-enter password for principal \"krbtest/fast@$REALMNAME\":" + send "adminpass$KEY\r" + expect { + "Principal \"krbtest/fast@$REALMNAME\" created" { } + "Principal or policy already exists while creating*" { } + } + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status kadmin_local] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } else { + delete_db + } + } else { + if $standalone { + pass $test + } + } + + if $des3_krbtgt { + # Set the TGT key to DES3. + set test "kadmin.local TGT to DES3" + set body { + if $failall { + break + } + spawn $KADMIN_LOCAL -r $REALMNAME -e des3-cbc-sha1:normal + verbose "starting $test" + expect_after $def_exp_after + + expect "kadmin.local: " + send "cpw -randkey krbtgt/$REALMNAME@$REALMNAME\r" + # It echos... + expect "cpw -randkey krbtgt/$REALMNAME@$REALMNAME\r" + expect { + "Key for \"krbtgt/$REALMNAME@$REALMNAME\" randomized." { } + } + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status kadmin_local] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } else { + delete_db + } + } else { + if $standalone { + pass $test + } + } + } + if $tgt_support_desmd5 { + # Make TGT support des-cbc-md5 + set test "kadmin.local TGT to SUPPORT_DESMD5" + set body { + if $failall { + break + } + spawn $KADMIN_LOCAL -r $REALMNAME + verbose "starting $test" + expect_after $def_exp_after + + expect "kadmin.local: " + send "modprinc +support_desmd5 krbtgt/$REALMNAME@$REALMNAME\r" + # It echos... + expect "modprinc +support_desmd5 krbtgt/$REALMNAME@$REALMNAME\r" + expect { + "Principal \"krbtgt/$REALMNAME@$REALMNAME\" modified.\r\n" { } + } + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status kadmin_local] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + if $standalone { + fail $test + } else { + delete_db + } + } else { + if $standalone { + pass $test + } + } + } + envstack_pop + + # create the admin database lock file + catch "exec touch $tmppwd/adb.lock" + + set last_passname_db $multipass_name + return 1 +} + +# setup_slave_db +# Initialize the slave Kerberos database. Returns 1 on success, 0 on +# failure. + +proc setup_slave_db { } { + global REALMNAME + global KDB5_UTIL + global KADMIN_LOCAL + global KEY + global tmppwd + global spawn_id + + set failall 0 + + envstack_push + if { ![setup_kerberos_files] || ![setup_kerberos_env slave] } { + set failall 1 + } + + # Set up a common expect_after for use in multiple places. + set def_exp_after { + timeout { + set test "$test (timeout)" + break + } + eof { + set test "$test (eof)" + break + } + } + + set test "slave kdb5_util create " + set body { + if $failall { + break + } + #exec xterm + verbose "starting $test" + spawn $KDB5_UTIL -r $REALMNAME create -W + expect_after $def_exp_after + + expect "Enter KDC database master key:" + + set test "slave kdb5_util create (verify)" + send "masterkey$KEY\r" + expect "Re-enter KDC database master key to verify:" + + set test "slave kdb5_util create" + send "masterkey$KEY\r" + expect { + -re "\[Cc\]ouldn't" { + expect eof + break + } + "Cannot find/read stored" exp_continue + "Warning: proceeding without master key" exp_continue + eof { } + } + catch expect_after + if ![check_exit_status kdb5_util] { + break + } + } + set ret [catch $body] + catch expect_after + if $ret { + set failall 1 + } + + # Stash the master key in a file. + set test "slave kdb5_util stash" + set body { + if $failall { + break + } + spawn $KDB5_UTIL -r $REALMNAME stash + verbose "starting $test" + expect_after $def_exp_after + expect "Enter KDC database master key:" + send "masterkey$KEY\r" + expect eof + catch expect_after + if ![check_exit_status kdb5_util] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + set failall 1 + delete_db + } + + if !$failall { + # create the admin database lock file + catch "exec touch $tmppwd/slave-adb.lock" + } + + return [expr !$failall] +} + +proc start_kpropd {} { + global kpropd_pid kpropd_spawn_id KPROPD T_INETD KDB5_UTIL portbase tmppwd + global spawn_id + + envstack_push + setup_kerberos_env slave + spawn $KPROPD -S -d -t -P [expr 10 + $portbase] -s $tmppwd/srvtab -f $tmppwd/incoming-slave-datatrans -p $KDB5_UTIL -a $tmppwd/kpropd-acl + set kpropd_pid [exp_pid] + set kpropd_spawn_id $spawn_id +# send_user [list $KPROPD -S -d -P [expr 10 + $portbase] -s $tmppwd/srvtab -f $tmppwd/incoming-slave-datatrans -p $KDB5_UTIL -a $tmppwd/kpropd-acl]\n +# spawn_shell + envstack_pop +} + +# start_kerberos_daemons +# A procedure to build a Kerberos database and start up the kerberos +# and kadmind daemons. This sets the global variables kdc_pid, +# kdc_spawn_id, kadmind_pid, and kadmind_spawn_id. The procedure +# stop_kerberos_daemons should be used to stop the daemons. If the +# argument is non-zero, call pass at relevant points. Returns 1 on +# success, 0 on failure. + +proc start_kerberos_daemons { standalone } { + global BINSH + global REALMNAME + global KRB5KDC + global KADMIND + global KEY + global kdc_pid + global kdc_spawn_id + global kadmind_pid + global kadmind_spawn_id + global tmppwd + global env + global timeout + + if ![setup_kerberos_db 0] { + return 0 + } + + if {$standalone} { + file delete $tmppwd/krb.log $tmppwd/kadmind.log $tmppwd/krb5kdc_rcache + } + + # Start up the kerberos daemon + # Why are we doing all this with the log file you may ask. + # We need a handle on when the server starts. If we log the output + # of the server to say stderr, then if we stop looking for output, + # buffers will fill and the server will stop working.... + # So, we look to see when a line is added to the log file and then + # check it.. + # The same thing is done a little later for the kadmind + set kdc_lfile $tmppwd/kdc.log + set kadmind_lfile $tmppwd/kadmind5.log + set kdc_pidfile $tmppwd/kdc.pid + set kadmind_pidfile $tmppwd/kadmind.pid + + envstack_push + setup_kerberos_env kdc + # Nuke pid file - to test if setup + file delete $kdc_pidfile + spawn $KRB5KDC -r $REALMNAME -n -P $kdc_pidfile + envstack_pop + set kdc_pid [exp_pid] + set kdc_spawn_id $spawn_id + + expect { + "starting" { } + eof { + if {$standalone} { + verbose -log "krb5kdc failed to start" + fail "krb5kdc" + } else { + perror "krb5kdc failed to start" + } + stop_kerberos_daemons + return 0 + } + } + + if (![file exists $kdc_pidfile]) { + fail "krb5kdc pidfile" + stop_kerberos_daemons + return 0 + } + set f [open $kdc_pidfile "r"] + if {[gets $f foundpid] < 0 || ![string equal $kdc_pid $foundpid]} { + fail "krb5kdc pid file contents" + close $f + stop_kerberos_daemons + return 0 + } + close $f + + if {$standalone} { + pass "krb5kdc" + } + + # Give the kerberos daemon a few seconds to get set up. +# sleep 2 + + # + # Save setting of KRB5_KTNAME. We do not want to override kdc.conf + # file during kadmind startup. (this is in case user has KRB5_KTNAME + # set before starting make check) + # + if [info exists env(KRB5_KTNAME)] { + set start_save_ktname $env(KRB5_KTNAME) + } + catch "unset env(KRB5_KTNAME)" + + # Start up the kadmind daemon + envstack_push + setup_kerberos_env kdc + file delete $kadmind_pidfile + spawn $BINSH -c "exec $KADMIND -r $REALMNAME -W -nofork -P $kadmind_pidfile" + envstack_pop + set kadmind_pid [exp_pid] + set kadmind_spawn_id $spawn_id + + # Restore KRB5_KTNAME + if [info exists start_save_ktname] { + set env(KRB5_KTNAME) $start_save_ktname + unset start_save_ktname + } + + expect { + "Seeding random number" exp_continue + "No principal in keytab matches desired name" { + dump_db + exp_continue + } + "starting" { } + eof { + verbose -log "kadmind failed to start" + if {$standalone} { + fail "kadmind" + } else { + perror "kadmind failed to start" + } + stop_kerberos_daemons + return 0 + } + } + + if (![file exists $kadmind_pidfile]) { + fail "kadmind pidfile" + stop_kerberos_daemons + return 0 + } + set f [open $kadmind_pidfile "r"] + if {[gets $f foundpid] < 0 || ![string equal $kadmind_pid $foundpid]} { + fail "kadmind pid file contents" + close $f + stop_kerberos_daemons + return 0 + } + close $f + + if {$standalone} { + pass "kadmind" + } + + # Give the kadmind daemon a few seconds to get set up. +# sleep 2 + + return 1 +} + +# stop_kerberos_daemons +# Stop the kerberos daemons. Returns 1 on success, 0 on failure. + +proc stop_kerberos_daemons { } { + global kdc_pid + global kdc_spawn_id + global kadmind_pid + global kadmind_spawn_id + + verbose "entered stop_kerberos_daemons" + + if [info exists kdc_pid] { + if [catch "exec kill $kdc_pid" msg] { + verbose "kill kdc: $msg" + } + if [catch "expect -i $kdc_spawn_id eof" msg] { + verbose "expect kdc eof: $msg" + } + set kdc_list [wait -i $kdc_spawn_id] + verbose "wait -i $kdc_spawn_id returned $kdc_list (kdc)" + unset kdc_pid + unset kdc_list + } + + if [info exists kadmind_pid] { + if [catch "exec kill $kadmind_pid" msg] { + verbose "kill kadmind: $msg" + } + if [catch "expect -i $kadmind_spawn_id eof" msg] { + verbose "expect kadmind eof: $msg" + } + set kadmind_list [wait -i $kadmind_spawn_id] + verbose "wait -i $kadmind_spawn_id returned $kadmind_list (kadmind5)" + unset kadmind_pid + unset kadmind_list + } + + verbose "exiting stop_kerberos_daemons" + + return 1 +} + +# add_kerberos_key +# Add an key to the Kerberos database. start_kerberos_daemons must be +# called before this procedure. If the standalone argument is +# non-zero, call pass at relevant points. Returns 1 on success, 0 on +# failure. + +proc add_kerberos_key { kkey standalone } { + global REALMNAME + global KADMIN + global KEY + global spawn_id + + # Use kadmin to add an key. + set test "kadmin ank $kkey" + set body { + envstack_push + setup_kerberos_env client + spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank $kkey@$REALMNAME" + envstack_pop + verbose "starting $test" + expect_after { + "Cannot contact any KDC" { + set test "$test (lost KDC)" + break + } + timeout { + set test "$test (timeout)" + break + } + eof { + set test "$test (eof)" + break + } + } + expect -re "assword\[^\r\n\]*: *" + send "adminpass$KEY\r" + expect "Enter password for principal \"$kkey@$REALMNAME\":" + send "$kkey" + send "$KEY\r" + expect "Re-enter password for principal \"$kkey@$REALMNAME\":" + send "$kkey" + send "$KEY\r" + expect { + "Principal \"$kkey@$REALMNAME\" created" { } + "Principal or policy already exists while creating*" { } + } + expect eof + if ![check_exit_status kadmin] { + break + } + } + set ret [catch $body] + catch "expect eof" + catch expect_after + if $ret { + if $standalone { + fail $test + } + return 0 + } else { + if $standalone { + pass $test + } + return 1 + } +} + +# dump_db +proc dump_db { } { + global KADMIN_LOCAL + global REALMNAME + + spawn $KADMIN_LOCAL -r $REALMNAME + expect_after { + eof { + perror "failed to get debugging dump of database (eof)" + } + timeout { + perror "failed to get debugging dump of database (timeout)" + } + } + expect "kadmin.local: " + send "getprincs\r" + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after +} + +# add_random_key +# Add a key with a random password to the Kerberos database. +# start_kerberos_daemons must be called before this procedure. If the +# standalone argument is non-zero, call pass at relevant points. +# Returns 1 on success, 0 on failure. + +proc add_random_key { kkey standalone } { + global REALMNAME + global KADMIN + global KEY + global spawn_id + + # Use kadmin to add an key. + set test "kadmin ark $kkey" + set body { + envstack_push + setup_kerberos_env client + spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank -randkey $kkey@$REALMNAME" + envstack_pop + expect_after { + timeout { + set test "$test (timeout)" + break + } + eof { + set test "$test (eof)" + break + } + } + expect -re "assword\[^\r\n\]*: *" + send "adminpass$KEY\r" + expect { + "Principal \"$kkey@$REALMNAME\" created" { } + "Principal or policy already exists while creating*" { } + } + expect eof + if ![check_exit_status kadmin] { + break + } + } + if [catch $body] { + catch expect_after + if $standalone { + fail $test + } + return 0 + } else { + catch expect_after + if $standalone { + pass $test + } + return 1 + } +} + +# setup_srvtab +# Set up a srvtab file. start_kerberos_daemons and add_random_key +# $id/$hostname must be called before this procedure. If the +# argument is non-zero, call pass at relevant points. Returns 1 on +# success, 0 on failure. If the id field is not provided, host is used. + +proc setup_srvtab { standalone {id host} } { + global REALMNAME + global KADMIN_LOCAL + global KEY + global tmppwd + global hostname + global spawn_id + global last_service + + if {!$standalone && [file exists $tmppwd/srvtab] && $last_service == $id} { + return 1 + } + + file delete $tmppwd/srvtab $tmppwd/srvtab.old + + if ![get_hostname] { + return 0 + } + + file delete $hostname-new-srvtab + + envstack_push + setup_kerberos_env kdc + spawn $KADMIN_LOCAL -r $REALMNAME + envstack_pop + expect_after { + -re "(.*)\r\nkadmin.local: " { + fail "kadmin.local srvtab (unmatched output: $expect_out(1,string))" + if {!$standalone} { + file delete $tmppwd/srvtab + } + catch "expect_after" + return 0 + } + timeout { + fail "kadmin.local srvtab" + if {!$standalone} { + file delete $tmppwd/srvtab + } + catch "expect_after" + return 0 + } + eof { + fail "kadmin.local srvtab" + if {!$standalone} { + file delete $tmppwd/srvtab + } + catch "expect_after" + return 0 + } + } + expect "kadmin.local: " + send "xst -k $hostname-new-srvtab $id/$hostname kiprop/$hostname\r" + expect "xst -k $hostname-new-srvtab $id/$hostname kiprop/$hostname\r\n" + expect { + -re ".*Entry for principal $id/$hostname.* added to keytab WRFILE:$hostname-new-srvtab." { } + -re "\r\nkadmin.local: " { + if {$standalone} { + fail "kadmin.local srvtab" + } else { + file delete $tmppwd/srvtab + } + catch expect_after + return 0 + } + } + expect "kadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status "kadmin.local srvtab"] { + if {!$standalone} { + file delete $tmppwd/srvtab + } + return 0 + } + + catch "exec mv -f $hostname-new-srvtab $tmppwd/srvtab" exec_output + if ![string match "" $exec_output] { + verbose -log "$exec_output" + perror "can't mv new srvtab" + return 0 + } + + if {$standalone} { + pass "kadmin.local srvtab" + } + + # Make the srvtab file globally readable in case we are using a + # root shell and the srvtab is NFS mounted. + catch "exec chmod a+r $tmppwd/srvtab" + + # Remember what we just extracted + set last_service $id + + return 1 +} + +# kinit +# Use kinit to get a ticket. If the argument is non-zero, call pass +# at relevant points. Returns 1 on success, 0 on failure. + +proc kinit { name pass standalone } { + global REALMNAME + global KINIT + global spawn_id + + # Use kinit to get a ticket. + # + # For now always get forwardable tickets. Later when we need to make + # tests that distiguish between forwardable tickets and otherwise + # we should but another option to this proc. --proven + # + spawn $KINIT -5 -f $name@$REALMNAME + expect { + "Password for $name@$REALMNAME:" { + verbose "kinit started" + } + timeout { + fail "kinit" + return 0 + } + eof { + fail "kinit" + return 0 + } + } + send "$pass\r" + expect eof + if ![check_exit_status kinit] { + return 0 + } + + if {$standalone} { + pass "kinit" + } + + return 1 +} + +proc kinit_renew { name pass standalone } { + global REALMNAME + global KINIT + global spawn_id + + spawn $KINIT -5 -f $name@$REALMNAME + expect { + "Password for $name@$REALMNAME:" { + verbose "kinit started" + } + timeout { + fail "kinit" + return 0 + } + eof { + fail "kinit" + return 0 + } + } + send "$pass\r" + expect eof + if ![check_exit_status kinit] { + return 0 + } + + spawn $KINIT -R + expect eof + if ![check_exit_status "kinit_renew"] { + return 0 + } + + return 1 +} + +# Retrieve a ticket using FAST armor +proc kinit_fast { name pass standalone } { + global REALMNAME + global KINIT + global spawn_id + global env + + # Use kinit to get a ticket. + # + spawn $KINIT -5 -f -T $env(KRB5CCNAME) $name@$REALMNAME + expect { + "Password for $name@$REALMNAME:" { + verbose "kinit started" + } + timeout { + fail "kinit_fast" + return 0 + } + eof { + fail "kinit_fast" + return 0 + } + } + send "$pass\r" + expect eof + if ![check_exit_status kinit] { + return 0 + } + + if {$standalone} { + pass "kinit_fast" + } + + return 1 +} + +proc kinit_anonymous { name } { + global REALMNAME + global KINIT + global spawn_id + + # Use kinit to get a ticket. + # + spawn $KINIT -5 -f -n $name@$REALMNAME + expect { + "Password for $name@$REALMNAME:" { + fail "kinit_anonymous (password requested)" + return 0 + } + timeout { + fail "kinit_anonymous (timeout)" + return 0 + } + eof { } + } + if ![check_exit_status kinit] { + fail "kinit anonymous" + } + + pass "kinit anonymous" + return 1 +} + +proc kinit_kt { name keytab standalone testname } { + global REALMNAME + global KINIT + global spawn_id + + # Use kinit to get a ticket. + # + # For now always get forwardable tickets. Later when we need to make + # tests that distiguish between forwardable tickets and otherwise + # we should but another option to this proc. --proven + # + spawn $KINIT -5 -f -k -t $keytab $name@$REALMNAME + expect { + timeout { + fail "kinit $testname" + return 0 + } + eof { } + } + if ![check_exit_status "kinit $testname"] { + return 0 + } + + if {$standalone} { + pass "kinit $testname" + } + + return 1 +} + +# List tickets. Requires client and server names, and test name. +# Checks that klist exist status is zero. +# Records pass or fail, and returns 1 or 0. +proc do_klist { myname servname testname } { + global KLIST + global tmppwd + + spawn $KLIST -5 -e + expect { + -re "Ticket cache:\[ \]*(.+:)?$tmppwd/tkt.*Default principal:\[ \]*$myname.*$servname\r\n" { + verbose "klist started" + } + timeout { + fail $testname + return 0 + } + eof { + fail $testname + return 0 + } + } + + expect eof + + if ![check_exit_status $testname] { + return 0 + } + pass $testname + return 1 +} + +proc do_klist_kt { keytab testname } { + global KLIST + global tmppwd + + spawn $KLIST -5 -e -k $keytab + expect { + -re "Keytab name:\[ \]*(.+:)?.*KVNO Principal\r\n---- -*\r\n" { + verbose "klist started" + } + timeout { + fail $testname + return 0 + } + eof { + fail $testname + return 0 + } + } + set more 1 + while {$more} { + expect { + -re { *[0-9][0-9]* *[a-zA-Z/@.-]* \([/a-zA-Z 0-9-]*\) *\r\n} { + verbose -log "key: $expect_out(buffer)" + } + eof { set more 0 } + } + } + + if ![check_exit_status $testname] { + return 0 + } + pass $testname + return 1 +} + +proc do_klist_err { testname } { + global KLIST + global spawn_id + + spawn $KLIST -5 + # Might say "credentials cache" or "credentials cache file". + expect { + -re "klist: No credentials cache found.*\r\n" { + verbose "klist started" + } + timeout { + fail $testname + return 0 + } + eof { + fail $testname + return 0 + } + } + # We can't use check_exit_status, because we expect an exit status + # of 1. + catch "expect eof" + set status_list [wait -i $spawn_id] + verbose "wait -i $spawn_id returned $status_list ($testname)" + if { [lindex $status_list 2] != 0 } { + fail "$testname (bad exit status) $status_list" + return 0 + } else { if { [lindex $status_list 3] != 1 } { + fail "$testname (bad exit status) $status_list" + return 0 + } else { + pass $testname + } } + return 1 +} + +proc do_kdestroy { testname } { + global KDESTROY + global spawn_id + + spawn $KDESTROY -5 + if ![check_exit_status $testname] { + fail $testname + return 0 + } + pass $testname + return 1 +} + +proc xst { keytab name } { + global KADMIN_LOCAL + global REALMNAME + + envstack_push + setup_kerberos_env kdc + spawn $KADMIN_LOCAL -r $REALMNAME + envstack_pop + catch expect_after + expect_after { + -re "(.*)\r\nkadmin.local: " { + fail "kadmin.local xst $keytab (unmatched output: $expect_out(1,string)" + catch "expect_after" + return 0 + } + timeout { + fail "kadmin.local xst $keytab (timeout)" + catch "expect_after" + return 0 + } + eof { + fail "kadmin.local xst $keytab (eof)" + catch "expect_after" + return 0 + } + } + expect "kadmin.local: " + send "xst -k $keytab $name\r" + expect -re "xst -k \[^\r\n\]*\r\n.*Entry for principal .* added to keytab WRFILE:.*\r\nkadmin.local: " + send "quit\r" + expect eof + catch expect_after + if ![check_exit_status "kadmin.local $keytab"] { + perror "kadmin.local xst $keytab exited abnormally" + return 0 + } + return 1 +} + +# helpful sometimes for debugging the test suite +proc export_debug_envvars { } { + global env + foreach i {KDB5_UTIL KRB5KDC KADMIND KADMIN KADMIN_LOCAL KINIT KTUTIL KLIST KPASSWD REALMNAME GSSCLIENT KPROPLOG} { + global $i + if [info exists $i] { set env($i) [set $i] } + } +} +proc spawn_xterm { } { + export_debug_envvars + exec "xterm" +} +proc spawn_shell { } { + export_debug_envvars + spawn "sh" + exp_interact +} |
