# Basic expect script for Kerberos tests. # This is a DejaGnu test script. # Written by Ian Lance Taylor, Cygnus Support, . # 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 tmpdir, 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 # We do everything in a temporary directory. if ![file isdirectory tmpdir] {catch "exec mkdir tmpdir" status} set tmppwd "[pwd]/tmpdir" # 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 tmpdir/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 catch "exec rm -f tmpdir/db.ok tmpdir/srvtab tmpdir/krb5.conf tmpdir/kdc.conf tmpdir/cpw_srvtab" # 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. if ![info exists KDB5_UTIL] { set KDB5_UTIL [findfile $objdir/../../kadmin/dbutil/kdb5_util] } if ![info exists KRB5KDC] { set KRB5KDC [findfile $objdir/../../kdc/krb5kdc] } if ![info exists KADMIND] { set KADMIND [findfile $objdir/../../kadmin/server/kadmind] } if ![info exists KADMIN] { set KADMIN [findfile $objdir/../../kadmin/cli/kadmin] } if ![info exists KADMIN_LOCAL] { set KADMIN_LOCAL [findfile $objdir/../../kadmin/cli/kadmin.local] } if ![info exists KINIT] { set KINIT [findfile $objdir/../../clients/kinit/kinit] } if ![info exists KTUTIL] { set KTUTIL [findfile $objdir/../../kadmin/ktutil/ktutil] } if ![info exists RESOLVE] { set RESOLVE [findfile $objdir/../resolve/resolve] } if ![info exists T_INETD] { set T_INETD [findfile $objdir/t_inetd] } # We use a couple of variables to hold shell prompts which may be # overridden by the user. if ![info exists ROOT_PROMPT] { set ROOT_PROMPT "(%|#|>|\\$) $" } if ![info exists SHELL_PROMPT] { set SHELL_PROMPT "(%|#|>|\\$) $" } # check_k5login # Most of the tests won't work if the user has a .k5login file, unless # the user's name appears unadorned in .k5login (in which case kuserok # will assume a null instance and the local realm). This procedure # returns 1 if the .k5login file appears to be OK, 0 otherwise. This # check is not foolproof. proc check_k5login { testname } { global env global REALMNAME if ![file exists ~/.k5login] { return 1 } set file [open ~/.k5login r] while { [gets $file principal] != -1 } { if { $principal == "$env(USER)@$REALMNAME" } { close $file return 1 } } close $file untested "$testname test requires that your name appear in your ~/.k5login" untested "file with no realm or instance." return 0 } # check_exit_status # Check the exit status of a spawned program. Returns 1 if the # program succeeded, 0 if it failed. proc check_exit_status { testname } { global 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 } { send_log "exit status: $status_list\n" verbose "exit status: $status_list" fail "$testname" return 0 } else { return 1 } } # 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 runvarlist global krb5_init_vars global krb5_old_vars global runtime_setup if [info exists runtime_setup] { return 1 } set runtime_setup 1 set krb5_init_vars [list ] set krb5_old_vars [list ] # Only keep the foo=bar and ignore export commands... foreach i $runvarlist { if {[regexp ".*=.*" $i]} { lappend krb5_init_vars $i } } # Set the variables... (and save the old ones) foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue if [info exists env($evar)] { lappend krb5_old_vars $evar=$env($evar) } set env($evar) "$evalue" verbose "$evar=$evalue" } return 0 } # Configuration is through a site.exp and the runvarlist variable proc restore_runtime_env { } { global env global krb5_init_vars global krb5_old_vars global runtime_setup if ![info exists runtime_setup] { return 1 } # restore the variables... foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue set idx [lsearch -regexp $krb5_old_vars "^$evar=" ] if {$idx >= 0} { regexp "^(\[^=\]*)=(.*)" [lindex $krb5_old_vars $idx] foo evar evalue set env($evar) "$evalue" } else { catch "unset env($evar)" } } unset runtime_setup } # get_hostname # This procedure will get the local hostname. It sets the global # variables hostname (the full name) and localhostname (the first part # of the name). Returns 1 on success, 0 on failure. proc get_hostname { } { global RESOLVE global hostname global localhostname global domain if {[info exists hostname] && [info exists localhostname]} { return 1 } set setup [setup_runtime_env] catch "exec $RESOLVE -q >tmpdir/hostname" exec_output if ![string match "" $exec_output] { send_log "$exec_output\n" verbose $exec_output send_error "ERROR: can't get hostname\n" if {$setup == 0} restore_runtime_env return 0 } set file [open tmpdir/hostname r] if { [ gets $file hostname ] == -1 } { send_error "ERROR: no output from hostname\n" if {$setup == 0} restore_runtime_env return 0 } close $file catch "exec rm -f tmpdir/hostname" exec_output regexp "^(\[^.\]*)\.(.*)$" $hostname foo localhostname domain set hostname [string tolower $hostname] set localhostname [string tolower $localhostname] set domain [string tolower $domain] verbose "hostname: $hostname; localhostname: $localhostname; domain $domain" if {$setup == 0} restore_runtime_env return 1 } # 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 domain global tmppwd if ![get_hostname] { return 0 } # Create a krb5.conf file. if ![file exists tmpdir/krb5.conf] { set conffile [open tmpdir/krb5.conf w] puts $conffile "\[libdefaults\]" puts $conffile " default_realm = $REALMNAME" puts $conffile "default_tgs_enctypes = des3-cbc-md5 des-cbc-md5 des-cbc-crc" puts $conffile "" puts $conffile "\[realms\]" puts $conffile " $REALMNAME = \{" puts $conffile " kdc = $hostname:3088" puts $conffile " admin_server = $hostname:3750" puts $conffile " kpasswd_server = $hostname:3751" puts $conffile " default_domain = $domain" puts $conffile " \}" puts $conffile "" puts $conffile "\[domain_realm\]" puts $conffile " .$domain = $REALMNAME" puts $conffile " $domain = $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" close $conffile } # Create a kdc.conf file. if ![file exists tmpdir/kdc.conf] { set conffile [open tmpdir/kdc.conf w] puts $conffile "\[kdcdefaults\]" puts $conffile " kdc_ports = 3085,3086,3087,3088,3089" puts $conffile "" puts $conffile "\[realms\]" puts $conffile " $REALMNAME = \{" puts $conffile " database_name = $tmppwd/db" puts $conffile " admin_database_name = $tmppwd/adb" puts $conffile " admin_database_lockfile = $tmppwd/adb.lock" puts $conffile " admin_keytab = $tmppwd/admin-keytab" puts $conffile " key_stash_file = $tmppwd/stash" puts $conffile " acl_file = $tmppwd/acl" puts $conffile " kadmind_port = 3750" puts $conffile " kpasswd_port = 3751" puts $conffile " max_life = 1:00:00" puts $conffile " max_renewable_life = 3:00:00" puts $conffile " master_key_type = des-cbc-md5" puts $conffile " master_key_name = master/key" puts $conffile " supported_enctypes = des-cbc-crc:normal des-cbc-md5:normal des-cbc-crc:v4 des-cbc-md5:norealm" puts $conffile " kdc_ports = 3088" puts $conffile " default_principal_expiration = 99.12.31.23.59.59" puts $conffile " default_principal_flags = -postdateable forwardable" puts $conffile " \}" puts $conffile "" close $conffile } # Create ACL file. if ![file exists tmpdir/acl] { set aclfile [open tmpdir/acl w] puts $aclfile "krbtest/admin@$REALMNAME *" close $aclfile } return 1 } # Save the original values of the environment variables we are going # to muck with. if [info exists env(KRB5_CONFIG)] { set orig_krb_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(KRB5RCACHEDIR)] { set orig_krb5rcachedir $env(KRB5RCACHEDIR) } else { catch "unset orig_krb5rcachedir" } if [ info exists env(KERBEROS_SERVER)] { set orig_kerberos_server $env(KERBEROS_SERVER) } else { catch "unset orig_kerberos_server" } # setup_kerberos_env # Set the environment variables needed to run Kerberos programs. proc setup_kerberos_env { } { global REALMNAME global env global tmppwd global hostname global krb5_init_vars # 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.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 server at a cache file stored in the # temporary directory. set env(KRB5RCACHEDIR) $tmppwd verbose "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)" # Tell the Kerberos tools how to contact the $REALMNAME server. set env(KERBEROS_SERVER) "$REALMNAME:$hostname:3088" verbose "KERBEROS_SERVER=$env(KERBEROS_SERVER)" # Get the run time environment variables... (including LD_LIBRARY_PATH) setup_runtime_env # Set our kdc config file. set env(KRB5_KDC_PROFILE) $tmppwd/kdc.conf verbose "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)" # Create an environment setup script. (For convenience) if ![file exists tmpdir/env.sh] { set envfile [open tmpdir/env.sh w] puts $envfile "KRB5_CONFIG=$env(KRB5_CONFIG)" puts $envfile "KRB5CCNAME=$env(KRB5CCNAME)" puts $envfile "KRB5RCACHEDIR=$env(KRB5RCACHEDIR)" puts $envfile "KERBEROS_SERVER=$env(KERBEROS_SERVER)" puts $envfile "KRB5_KDC_PROFILE=$env(KRB5_KDC_PROFILE)" puts $envfile "export KRB5_CONFIG KRB5CCNAME KRB5RCACHEDIR" puts $envfile "export KERBEROS_SERVER KRB5_KDC_PROFILE" foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue puts $envfile "$evar=$env($evar)" puts $envfile "export $evar" } close $envfile } if ![file exists tmpdir/env.csh] { set envfile [open tmpdir/env.csh w] puts $envfile "setenv KRB5_CONFIG $env(KRB5_CONFIG)" puts $envfile "setenv KRB5CCNAME $env(KRB5CCNAME)" puts $envfile "setenv KRB5RCACHEDIR $env(KRB5RCACHEDIR)" puts $envfile "setenv KERBEROS_SERVER $env(KERBEROS_SERVER)" puts $envfile "setenv KRB5_KDC_PROFILE $env(KRB5_KDC_PROFILE)" foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue puts $envfile "setenv $evar $env($evar)" } close $envfile } return 1 } # Restore the Kerberos environment, in case setup_kerberos_env was # already called by an earlier test. proc restore_kerberos_env { } { global env global orig_krb5_config global orig_krb5ccname global orig_krb5rcachedir global orig_kerberos_server if [info exists orig_krb5_config] { set env(KRB5_CONFIG) $orig_krb5_config } else { catch "unset env(KRB5_CONFIG)" } if [info exists orig_krb5ccname] { set env(KRB5CCNAME) $orig_krb5ccname } else { catch "unset env(KRB5CCNAME)" } if [info exists orig_krb5rcachedir] { set env(KRB5RCACHEDIR) $orig_krb5rcachedir } else { catch "unset env(KRB5RCACHEDIR)" } if [info exists orig_kerberos_server] { set env(KERBEROS_SERVER) $orig_kerberos_server } else { catch "unset env(KERBEROS_SERVER)" } restore_runtime_env } # setup_kadmind_srvtab # A procedure to build the srvtab for kadmind5 so that kadmin5 and it # may successfully communicate. # Returns 1 on success, 0 on failure. proc setup_kadmind_srvtab { } { global REALMNAME global KADMIN_LOCAL global KEY global tmppwd catch "exec rm -f tmpdir/admin-keytab" spawn $KADMIN_LOCAL -r $REALMNAME expect_after { timeout { fail "kadmin.local admin-keytab (timeout)" catch "exec rm -f tmpdir/admin-keytab" catch "expect_after" return 0 } eof { fail "kadmin.local admin-keytab (eof)" catch "exec rm -f tmpdir/admin-keytab" catch "expect_after" return 0 } } expect "kadmin.local: " send "xst -k admin-new-srvtab kadmin/admin\r" expect -re ".*Entry for principal kadmin/admin.* added to keytab WRFILE:admin-new-srvtab." expect "kadmin.local: " catch "exec mv -f admin-new-srvtab changepw-new-srvtab" exec_output if ![string match "" $exec_output] { send_log "$exec_output\n" verbose $exec_output send_error "ERROR: can't mv admin-new-srvtab\n" return 0 } send "xst -k changepw-new-srvtab kadmin/changepw\r" expect -re ".*Entry for principal kadmin/changepw.* added to keytab WRFILE:changepw-new-srvtab." expect "kadmin.local: " send "quit\r" expect "\r" expect_after if ![check_exit_status "kadmin.local admin-keytab"] { catch "exec rm -f tmpdir/admin-keytab" send_error "ERROR: kadmin.local admin-keytab exited abnormally\n" return 0 } catch "exec mv -f changepw-new-srvtab tmpdir/admin-keytab" exec_output if ![string match "" $exec_output] { send_log "$exec_output\n" verbose $exec_output send_error "ERROR: can't mv new admin-keytab\n" return 0 } # 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 tmpdir/admin-keytab" 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 global KDB5_UTIL global KADMIN_LOCAL global KEY global tmppwd global spawn_id if {!$standalone && [file exists tmpdir/db.ok]} { return 1 } catch "exec rm -f [glob -nocomplain tmpdir/db* tmpdir/adb*]" # Creating a new database means we need a new srvtab. catch "exec rm -f tmpdir/srvtab" if { ![setup_kerberos_files] || ![setup_kerberos_env] } { return 0 } spawn $KDB5_UTIL -r $REALMNAME create expect { "Enter KDC database master key:" { verbose "kdb5_util started" } timeout { fail "kdb5_util - create" return 0 } eof { fail "kdb5_util - create" return 0 } } send "masterkey$KEY\r" set failed 0 expect { "Re-enter KDC database master key to verify:" { } timeout { fail "kdb5_util create - verify" return 0 } eof { fail "kdb5_util create - verify" return 0 } } send "masterkey$KEY\r" expect { -re "\[Cc\]ouldn't" { fail "kdb5_util - create" return 0 } "Cannot find/read stored" { exp_continue } "Warning: proceeding without master key" { exp_continue } timeout { fail "kdb5_util - create" return 0 } eof { } } if ![check_exit_status kdb5_util] { return 0 } if {$standalone} { pass "kdb5_util - create" } # Stash the master key in a file. spawn $KDB5_UTIL -r $REALMNAME stash expect { "Enter KDC database master key:" { verbose "kdb5_util stash started" } timeout { fail "kdb5_util stash" if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } eof { fail "kdb5_util stash" if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } } send "masterkey$KEY\r" expect { eof { } timeout { fail "kdb5_util stash" if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } } if ![check_exit_status kdb5_util] { return 0 } if {$standalone} { pass "kdb5_util stash" } # Add an admin user. spawn $KADMIN_LOCAL -r $REALMNAME expect_after { timeout { catch "expect_after" fail "kadmin.local (timeout)" if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } eof { catch "expect_after" fail "kadmin.local (eof)" if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } } 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 eof } } expect "kadmin.local: " send "quit\r" expect "\r" expect_after if ![check_exit_status kadmin_local] { if {!$standalone} { catch "exec rm -f tmpdir/db.ok tmpdir/adb.db" } return 0 } if ![setup_kadmind_srvtab] { return 0 } # create the admin database lock file catch "exec touch tmpdir/adb.lock" if {$standalone} { pass "kadmin_local" } return 1 } # 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 if ![setup_kerberos_db 0] { return 0 } if {$standalone} { catch "exec rm -f tmpdir/krb.log" catch "exec rm -f tmpdir/kadmind.log" } # 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 retry 30 if ![file exists $kdc_lfile] then { catch [touch $kdc_lfile] } sleep 2 set kdc_start [file mtime $kdc_lfile] spawn $KRB5KDC -r $REALMNAME -n set kdc_pid [exp_pid] set kdc_spawn_id $spawn_id for {set count 0} {$count < $retry} {incr count} { if { [file mtime $kdc_lfile] != $kdc_start } then { break; } sleep 2 } if {$count >= $retry} { fail "krb5kdc" stop_kerberos_daemons return 0 } if ![regexp "commencing operation" [tail1 $kdc_lfile]] { fail "krb5kdc" stop_kerberos_daemons return 0 } 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)" if ![file exists $kadmind_lfile] then { catch [touch $kadmind_lfile] sleep 1 } set kadmind_start [file mtime $kadmind_lfile] # Start up the kadmind daemon # XXXX kadmind uses stderr a lot. the sh -c and redirect can be # removed when this is fixed spawn $BINSH -c "exec $KADMIND -r $REALMNAME -nofork 2>>$kadmind_lfile" set kadmind_pid [exp_pid] set kadmind_spawn_id $spawn_id for {set count 0} {$count < $retry} {incr count} { if { [file mtime $kadmind_lfile] != $kadmind_start } then { break; } sleep 1 } if {$count >= $retry} { fail "kadmin5 (starting)" if [info exists start_save_ktname] { set env(KRB5_KTNAME) $start_save_ktname unset start_save_ktname } stop_kerberos_daemons return 0 } # Restore KRB5_KTNAME if [info exists start_save_ktname] { set env(KRB5_KTNAME) $start_save_ktname unset start_save_ktname } switch -regexp [tail1 $kadmind_lfile] { "cannot initialize network" { fail "kadmind (network init)" stop_kerberos_daemons return 0 } "cannot bind to network address" { fail "kadmind (bind)" stop_kerberos_daemons return 0 } "starting" { } default { fail "kadmind (startup)" stop_kerberos_daemons return 0 } } 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 if [info exists kdc_pid] { catch "close -i $kdc_spawn_id" catch "exec kill $kdc_pid" 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] { catch "close -i $kadmind_spawn_id" catch "exec kill $kadmind_pid" set kadmind_list [wait -i $kadmind_spawn_id] verbose "wait -i $kadmind_spawn_id returned $kadmind_list (kadmind5)" unset kadmind_pid unset kadmind_list } 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. spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank $kkey@$REALMNAME" expect_after { "Cannot contact any KDC" { fail "kadmin interactive add $kkey lost KDC" catch "expect_after" return 0 } timeout { fail "kadmin $kkey" catch "expect_after" return 0 } eof { fail "kadmin $kkey" return 0 } } expect "Enter password:" 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 } } catch "expect_after" if ![check_exit_status kadmin] { return 0 } if {$standalone} { pass "kadmin $kkey" } return 1 } # 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. spawn $KADMIN -p krbtest/admin@$REALMNAME -q "ank -randkey $kkey@$REALMNAME" expect_after { timeout { fail "kadmin $kkey" catch "expect_after" return 0 } eof { fail "kadmin $kkey" catch "expect_after" return 0 } } expect "Enter password:" send "adminpass$KEY\r" expect { "Principal \"$kkey@$REALMNAME\" created" { } "Principal or policy already exists while creating*" { expect eof} } catch "expect_after" if ![check_exit_status kadmin] { return 0 } if {$standalone} { pass "kadmin $kkey" } 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 tmpdir/srvtab] && $last_service == $id} { return 1 } catch "exec rm -f tmpdir/srvtab tmpdir/srvtab.old" if ![get_hostname] { return 0 } catch "exec rm -f $hostname-new-srvtab" spawn $KADMIN_LOCAL -r $REALMNAME expect_after { timeout { fail "kadmin.local srvtab" if {!$standalone} { catch "exec rm -f tmpdir/srvtab" } catch "expect_after" return 0 } eof { fail "kadmin.local srvtab" if {!$standalone} { catch "exec rm -f tmpdir/srvtab" } catch "expect_after" return 0 } } expect "kadmin.local: " send "xst -k $hostname-new-srvtab $id/$hostname\r" expect -re ".*Entry for principal $id/$hostname.* added to keytab WRFILE:$hostname-new-srvtab." expect "kadmin.local: " send "quit\r" expect "\r" expect_after if ![check_exit_status "kadmin.local srvtab"] { if {!$standalone} { catch "exec rm -f tmpdir/srvtab" } return 0 } catch "exec mv -f $hostname-new-srvtab tmpdir/srvtab" exec_output if ![string match "" $exec_output] { send_log "$exec_output\n" verbose $exec_output send_error "ERROR: can't mv new srvtab\n" 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 tmpdir/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 -f $name@$REALMNAME expect { "Password for $name@$REALMNAME:" { verbose "kinit started" } timeout { fail "kinit" return 0 } eof { fail "kinit" return 0 } } send "$pass\r" # This last expect seems useless, but without it the test hangs on # AIX. expect { "\r" { } } expect eof if ![check_exit_status kinit] { return 0 } if {$standalone} { pass "kinit" } return 1 } # Set up a root shell using rlogin $hostname -l root. This is used # when testing the daemons that must be run as root, such as telnetd # or rlogind. This sets the global variables rlogin_spawn_id and # rlogin_pid. Returns 1 on success, 0 on failure. # # This procedure will only succeed if the person running the test has # a valid ticket for a name listed in the /.klogin file. Naturally, # Kerberos must already be installed on this machine. It's a pain, # but I can't think of a better approach. proc setup_root_shell { testname } { global BINSH global ROOT_PROMPT global KEY global hostname global rlogin_spawn_id global rlogin_pid global tmppwd global env global krb5_init_vars # Make sure we are using the original values of the environment # variables. This means that the caller must call # setup_kerberos_env after calling this procedure. restore_kerberos_env setup_runtime_env if ![get_hostname] { return 0 } # If you have not installed Kerberos on your system, and you want # to run these tests, you can do it if you are willing to put your # root password in this file (this is not a very good idea, but # it's safe enough if you disconnect from the network and remember # to remove the password later). Change the rlogin in the next # line to be /usr/ucb/rlogin (or whatever is appropriate for your # system). Then change the lines after "word:" a few lines # farther down to be # send "rootpassword\r" # exp_continue spawn rlogin $hostname -l root set rlogin_spawn_id $spawn_id set rlogin_pid [exp_pid] expect { "word:" { untested "$testname test requires ability to rlogin as root" stop_root_shell return 0 } "Kerberos rlogin failed" { untested "$testname test requires ability to rlogin as root" stop_root_shell return 0 } eof { untested "$testname test requires ability to rlogin as root" stop_root_shell return 0 } -re "$ROOT_PROMPT" { } timeout { send_error "ERROR: timeout from rlogin $hostname -l root\n" send_error "ERROR: If you have an unusual root prompt,\n" send_error "ERROR: try running with ROOT_PROMPT=\"regexp\"\n" stop_root_shell return 0 } } expect_after { timeout { send_error "ERROR: timeout from rlogin $hostname -l root\n" stop_root_shell catch "expect_after" return 0 } eof { send_error "ERROR: eof from rlogin $hostname -l root\n" stop_root_shell catch "expect_after" return 0 } } # Make sure the root shell is using /bin/sh. send "$BINSH\r" expect { -re "$ROOT_PROMPT" { } } # Set up a shell variable tmppwd. The callers use this to keep # command line lengths down. The command line length is important # because we are feeding input to a shell via a pty. On some # systems a pty will only accept 255 characters. send "tmppwd=$tmppwd\r" expect { -re "$ROOT_PROMPT" { } } # Set up our krb5.conf send "KRB5_CONFIG=$tmppwd/krb5.conf\r" expect { -re "$ROOT_PROMPT" { } } send "export KRB5_CONFIG\r" expect { -re "$ROOT_PROMPT" { } } # For all of our runtime environment variables - send them over... foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue send "$evar=$env($evar)\r" expect { -re "$ROOT_PROMPT" { } } send "export $evar\r" expect { -re "$ROOT_PROMPT" { } } } # Move over to the right directory. set dir [pwd] send "cd $dir\r" expect { -re "$ROOT_PROMPT" { } "$dir:" { send_error "ERROR: root shell can not cd to $dir\n" stop_root_shell return 0 } } restore_runtime_env expect_after return 1 } # Kill off a root shell started by setup_root_shell. proc stop_root_shell { } { global rlogin_spawn_id global rlogin_pid catch "close -i $rlogin_spawn_id" catch "exec kill $rlogin_pid" sleep 1 catch "exec kill -9 $rlogin_pid" catch "wait -i $rlogin_spawn_id" } # Check the date. The string will be the output of date on this # system, and we must make sure that it is in the same timezone as the # output of date run a second time. The first date will be run on an # rlogin or some such connection to the local system. This is to test # to make sure that the TZ environment variable is handled correctly. # Returns 1 on sucess, 0 on failure. proc check_date { date } { catch "exec date" ndate set atz "" set ntz "" scan $date "%s %s %d %d:%d:%d %s %d" adow amon adom ahr amn asc atz ayr scan $ndate "%s %s %d %d:%d:%d %s %d" ndow nmon ndom nhr nmn nsc ntz nyr if { $atz != $ntz } { verbose "date check failed: $atz != $ntz" send_log "date check failed: $atz != $ntz\n" return 0 } return 1 } proc touch { file } { set f [open $file "a"] puts $f "" close $f } # Implement this in tcl someday? proc tail1 { file } { exec tail -1 $file } # setup_wrapper # Sets up a wraper script to set the runtime shared library environment # variables and then executes a specific command. This is used to allow # a "rsh klist" or telnetd to execute login.krb5. proc setup_wrapper { file command } { global BINSH global env global krb5_init_vars # We will start with a BINSH script catch "exec rm -f $file" set f [open $file "w" 0777] puts $f "#!$BINSH" puts $f "KRB5_CONFIG=$env(KRB5_CONFIG)" puts $f "export KRB5_CONFIG" foreach i $krb5_init_vars { regexp "^(\[^=\]*)=(.*)" $i foo evar evalue puts $f "$evar=$env($evar)" puts $f "export $evar" } puts $f "exec $command" close $f return 1 }