Showing posts with label expect. Show all posts
Showing posts with label expect. Show all posts

Monday, December 15, 2008

Updating Unix And Linux Passwords Via The Web Browser

Hey there,

QUICK NOTICE FOR REGULAR READERS: We didn't want to lead off the week with this, but tomorrow's post will be an op-ed bit having to do with this blog, its contents and some of the shortcomings of, shall we say, "where we are" (literally). We'll also be putting up a poll to confirm that our opinion is the same as yours (we hope ;) To cut it short, if you find downloading scripts form this site a HUGE PITA, please glance at tomorrow's post and anonymously answer the poll if possible. Thanks :)

This Monday's script is actually a web-page frontend (CGI, to be exact) to make use of another script. That script can be any you want, but, if you like, we published an Expect script some time ago to do network wide updates from a single source that can be modified to restrict it to password changing only.

Now, we'll go on record right away (admittedly, this is the second paragraph, so we're lagging behind already ;) by stating that we do not specifically endorse this method of password changing. It's very convenient (for users both trustworthy and malicious) and does the job, but, use of this script in a secure environment (or any environment that requires protection) is not recommended. Not only does this script open up many potential security holes by allowing access to system commands (albeit via another script that gets called, so it's not "unbelievably" easy to misuse), it will almost guarantee that you won't get your Sarbanes Oxley compliance certificate :) That being said, if you have a small internal net (quarantined from production and other environments) this can be a handy way to do your updates. In the worst case, you can just forego the "web experience" and do your mass password updates using Expect (or any other tool) from the CLI.

Since glitz seems to be the order of the day, the web frontend is presented here and now. We've left out the frontend form where you would enter your name, current password and new password, as creating one of these would probably be specific to your organization and simple enough to knock out. If this is a pain for any of you, just write in (via the "Send Me A Comment" link at upper right corner of every page) and we'll be happy to write one up and post it here.

Hope you enjoy it but, as always, exercise prudence, caution, and all those other qualities that will keep you from getting into a situation that involves immediate termination of employment :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# change_password.cgi
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

&read_parse;

$login = $FORM{'mylogin'};
$password = $FORM{'mypwd'};
$password =~ s/([;?!])/\\$1/;
$new1 = $FORM{'mynewpwd1'};
$new2 = $FORM{'mynewpwd2'};
if ( $new1 ne $new2 ) {
¬_match;
}
$new1 =~ s/([;?!])/\\$1/;
$new2 =~ s/([;?!])/\\$1/;
$nickname = &get_gecos;
$litmus = 0;
$date=`date +%m%d%y`;
open(PASS, "</etc/passwd");
@etcpass = <PASS>;
close(PASS);

foreach $line (@etcpass) {
if ( $line =~ /^$login/ ) {
$litmus = 1;
}
}

if ($litmus == 0) {
print "Content-type: text/html\n\n";
print "<html>\n";
print "<head>\n";
print "<title>Error</title>\n";
print "</head>\n";
print "<body BGCOLOR=\"#000000\" TEXT=\"#00FF00\" LINK=\"#FFFF00\" VLINK=\"#0000FF\" ALINK=\"#BECD8C\">\n";
print "<p>\n";
print "<br><hr><br>\n";
print "<p>\n";
print "<center><FONT COLOR=\"#FF0000\"><H3>Bad User Name</h3></FONT></center>\n";
print "<p>\n";
print "<br>\n";
print "<p>\n";
print "<center>The Name $login doesn't have a login yet!</center>\n";
print "</body>\n";
print "</html>\n";
exit;
}

system("/path/to/your/mass/password/changing/program $login $password $new1");

print "Content-type: text/html\n\n";
print "<html>\n";
print "<head>\n";
print "<title>All Done</title>\n";
print "</head>\n";
print "<h3>\@hummer</h3>\n";
print "<body BGCOLOR=\"#000000\" TEXT=\"#00FF00\" LINK=\"#FFFF00\" VLINK=\"#0000FF\" ALINK=\"#BECD8C\">\n";
print "<p>\n";
print "<br><hr><br>\n";
print "<p>\n";
print "<center><FONT COLOR=\"#FF0000\"><H3>Fine Job, $nickname!</h3></FONT></center>\n";
print "<p>\n";
print "<br>\n";
print "<center><FONT COLOR=\"#FF0000\"><H3>Assuming that your intial password was valid, your Password has been changed!</h3></FONT></center>\n";
print "<center><FONT COLOR=\"#FF0000\"><H3>Remember, also, that all passwords must contain at least one numeric or non-alpha character in order to be accepted.</h3></FONT></center>\n";
print "<p> \n";
print "</body>\n";
print "</html>\n";
exit;

sub not_match {
print "Content-type: text/html\n\n";
print "<html>\n";
print "<head>\n";
print "<title>Error</title>\n";
print "</head>\n";
print "<body BGCOLOR=\"#000000\" TEXT=\"#00FF00\" LINK=\"#FFFF00\" VLINK=\"#0000FF\" ALINK=\"#BECD8C\">\n";
print "<p>\n";
print "<br><hr><br>\n";
print "<p>\n";
print "<center><FONT COLOR=\"#FF0000\"><H3Passwords Don't Match!</h3></FONT></center>\n";
print "<p>\n";
print "<br>\n";
print "<p>\n";
print "<center>The Password $new1 doesn't match $new2</center>\n";
print "</body>\n";
print "</html>\n";
exit;
}

sub read_parse {
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
}

sub get_gecos {
$boner = `grep $login /etc/passwd`;
@stuff = split(/:/, $boner);
return $stuff[4];
}


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Sunday, March 30, 2008

Linux Update Patch For Expect User Removal Script

Hey There,

Today we have the "Linux Additions" patch to follow up on yesterday's post on removing old users network wide.

Note that this patch was created with "diff -c," so I've been sure to specify both the file I want to patch and the patch file in my arguments to "patch" (whereas I didn't need to in our post on easily patching multiple files). The exact command line to create the patch was:

host # diff -c eraser eraser.new > eraser.patch

All you'll need to do to update yesterday's script to include today's modifications is put the patch file (tagged on to the end of this post) in the same directory as the original script (or wherever you want, assuming you know how this all works and my explaining it is trivial ;) and name it something like "eraser.patch" - Then do the following:

host # ls
. .. eraser eraser.patch
host # cp eraser eraser.bak
<--- Just in case something goes wrong, I'm always for making backups :)

As I noted before, you'll want to be specific in your arguments to "patch" when you run this, or be careful of "patch"'s default behaviour and don't choose to do a reverse patching, like this (BTW, even though my new script "eraser.new" is listed in the patch file, you don't actually need to have it for all of this to work):

host # patch -p0 -i eraser.patch
Hmm... Looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** eraser Sat Mar 29 18:57:29 2008
|--- eraser.new Sat Mar 29 18:57:50 2008
--------------------------
Patching file eraser using Plan A...
Reversed (or previously applied) patch detected! Assume -R? [y]
^C


Instead, patch it like this, so you can do it hands off:

host # patch -p0 eraser eraser.patch
Hmm... Looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** eraser Sat Mar 29 18:57:29 2008
|--- eraser.new Sat Mar 29 18:57:50 2008
--------------------------
Patching file eraser using Plan A...
Hunk #1 succeeded at 3.
Hunk #2 succeeded at 36.
Hunk #3 succeeded at 72.
Hunk #4 succeeded at 145.
Hunk #5 succeeded at 235.
Hunk #6 succeeded at 267.
done


And "diff -c" will show that your "eraser" Expect script is now different than the original that you copied off before:

host # diff -c eraser eraser.bak
*** eraser Sat Mar 29 19:02:21 2008
--- eraser.bak Sat Mar 29 18:57:50 2008
***************
...


And, of course, you can just check your "eraser" file and see the changes. If you need to back out the patch, simply do the "reverse patch" operation, like so:

host # patch -R -p0 eraser eraser.patch
Hmm... Looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** eraser Sat Mar 29 18:57:29 2008
|--- eraser.new Sat Mar 29 18:57:50 2008
--------------------------
Patching file eraser using Plan A...
Hunk #1 succeeded at 3.
Hunk #2 succeeded at 36.
Hunk #3 succeeded at 70.
Hunk #4 succeeded at 130.
Hunk #5 succeeded at 186.
Hunk #6 succeeded at 217.
done


And then do a cursory check to make sure the unpatched file matches the original that you copied off before:

host # diff -c eraser eraser.bak <--- You'll get no output if the files are the same. That's good :)

Enjoy and have a restful Sunday :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

*** eraser Sat Mar 29 18:57:29 2008
--- eraser.new Sat Mar 29 18:57:50 2008
***************
*** 3,9 ****
log_user 0

#########################################################################
! # eraser - root out ex-employees across network
# 2008 - Mike Golvach - eggi@comcast.net
# <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License</a>
#
--- 3,9 ----
log_user 0

#########################################################################
! # eraser - root out ex-employees across network - with Linux procs added
# 2008 - Mike Golvach - eggi@comcast.net
# <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/us/">Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License</a>
#
***************
*** 36,41 ****
--- 36,43 ----
puts " sun1 sun2"
puts "HP Hosts:"
puts " hp1 hp2"
+ puts "LINUX Hosts:"
+ puts " linux1 linux2"
puts "#############################################################################"
puts "FLAGS:"
puts " -h: Show this message."
***************
*** 70,75 ****
--- 72,90 ----
expect "word: " {send "$qpass\r"}
}

+ proc linux_login {name tprompt login lpass} {
+ send_user "$name\n"
+
+ set timeout 3
+ set hostname $name
+ set prompt $tprompt
+ set qlogin $login
+ set qpass $lpass
+
+ expect "ogin: " {send "$qlogin\r"}
+ expect "word: " {send "$qpass\r"}
+ }
+
proc hp_login {tprompt login lpass} {

set timeout 12
***************
*** 130,135 ****
--- 145,184 ----
}
return "$hostname $insidepwd $insidealias"
}
+
+ proc linux_q {tprompt login lpass userlogin host} {
+ set timeout 5
+ set log $login
+ set opass $lpass
+ set prompt $tprompt
+ set ulogin $userlogin
+ set hostname $host
+ set insidealias none
+ set insidepwd none
+
+ send_user "Scanning $hostname... "
+
+ expect -re $tprompt {send "grep $ulogin /etc/passwd\r"}
+ expect -re $tprompt
+ send "echo $?\r"
+ expect -re "\r\n(.*)\r\n"
+ set returnval $expect_out(1,string)
+ if { $returnval == 0 } {
+ set insidepwd 1
+ }
+ expect -re $tprompt {send "grep $ulogin /etc/mail/aliases\r"}
+ expect -re $tprompt
+ send "echo $?\r"
+ expect -re "\r\n(.*)\r\n"
+ set returnval $expect_out(1,string)
+ if { $returnval == 0 } {
+ set insidealias 1
+ }
+ expect -re $tprompt {
+ send "exit\r"
+ }
+ return "$hostname $insidepwd $insidealias"
+ }

proc sco_q {tprompt login lpass userlogin host} {
set timeout 5
***************
*** 186,191 ****
--- 235,241 ----
set sun [list sun1 sun2]
set sco [list sco1 sco2]
set hp [list hp1 hp2]
+ set linux [list linux1 linux2]

set userlogin [lindex $argv 0]
set login [lindex $argv 1]
***************
*** 217,222 ****
--- 267,278 ----
spawn telnet $host
hp_login $tprompt $login $lpass
lappend finalists [sunhp_q $tprompt $login $lpass $userlogin $host]
+ }
+
+ foreach host $linux {
+ spawn telnet $host
+ linux_login $tprompt $login $lpass
+ lappend finalists [linux_q $tprompt $login $lpass $userlogin $host]
}

foreach host $sco {


, Mike




Saturday, March 29, 2008

Removing Old Users Network Wide Using Expect

Hey there,

Here comes another Expect monster-sized weekend script for you ;)

Today's script is a twist on a previous script we posted to do network wide updates. The essence is still the same. This should basically save you the hassle of having to log in to a million different servers to find and remove users who you know shouldn't have accounts on your boxes.

Again, you can also delete the "Weed out the undesirables pronto" section if you don't want to minimally secure the script from unauthorized usage by doing a logname check.

We've left out the login functionality for Linux this time, but we'll add it tomorrow as a "diff -c" patch file and add it to this file with "patch" to give the week an appropriate finish :)

There's a section I wrote specifically for an Informix sql database, that you can modify (or remove) if you like, to include an example of how to check for users (or anything, really) in a database. If you don't want or need that functionality, just delete the "$userlogin mail server sql user report:" section. All the sections are highlighted with tons of pound signs (#)

Once you edit this Expect script to suit your needs, you can run it easily, like so (with -h if you want to see the help screen):

Ex:
host # ./eraser


or

host # ./eraser -h

See you tomorrow with the Linux additions. Have a great weekend :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/local/bin/expect

log_user 0

#########################################################################
# eraser - root out ex-employees across network
# 2008 - Mike Golvach - eggi@comcast.net
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# Usage: eraser <-h> [User Login] [Your Login]
#########################################################################

#########################################################################
# Weed out the undesirables pronto
#########################################################################

set gauntlet [exec logname]
if { [string compare $gauntlet "user1"] != 0 && [string compare $gauntlet "user2"] != 0 } {
puts "Sorry, $gauntlet. You may not use eraser."
exit
}

#########################################################################
# Go for the help short-circuit
#########################################################################

foreach member $argv {
if { $member == "-h" } {
puts "#############################################################################"
puts "USAGE:"
puts " $argv0 <-h> \[User Login\] \[Your Login\]"
puts "#############################################################################"
puts "SCO Hosts:"
puts " sco1 sco2"
puts "SUN Hosts:"
puts " sun1 sun2"
puts "HP Hosts:"
puts " hp1 hp2"
puts "#############################################################################"
puts "FLAGS:"
puts " -h: Show this message."
puts " User Login: Theirs."
puts " Your Login: Yours."
puts "#############################################################################"
puts "A FEW VALID COMMAND LINE EXPRESSIONS:"
puts " $argv0 -h"
puts " \(The -h command invalidates all others\)"
puts " $argv0 userlogin yourlogin"
puts " \(The Regular Way\)"
puts "#############################################################################"
exit
} elseif { [string match -* $member] == 1 } {
puts "Usage: $argv0 <-h> \[User Login\] \[Your Login\]"
exit
}
}

#########################################################################
# Generic login procs divided by architecture
#########################################################################

proc sun_login {tprompt login lpass} {

set timeout 3
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word: " {send "$qpass\r"}
}

proc hp_login {tprompt login lpass} {

set timeout 12
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word:" {send "$qpass\r"}
}

proc sco_login {tprompt login lpass} {

set timeout 6
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word:" {send "$qpass\r"}
expect -re $tprompt {send "\r"}
}

#########################################################################
# passwd and mail aliases groper procs by architecture
#########################################################################

proc sunhp_q {tprompt login lpass userlogin host} {
set timeout 5
set log $login
set opass $lpass
set prompt $tprompt
set ulogin $userlogin
set hostname $host
set insidealias none
set insidepwd none

send_user "Scanning $hostname... "

expect -re $tprompt {send "grep $ulogin /etc/passwd\r"}
expect -re $tprompt
send "echo $?\r"
expect -re "\r\n(.*)\r\n"
set returnval $expect_out(1,string)
if { $returnval == 0 } {
set insidepwd 1
}
expect -re $tprompt {send "grep $ulogin /etc/mail/aliases\r"}
expect -re $tprompt
send "echo $?\r"
expect -re "\r\n(.*)\r\n"
set returnval $expect_out(1,string)
if { $returnval == 0 } {
set insidealias 1
}
expect -re $tprompt {
send "exit\r"
}
return "$hostname $insidepwd $insidealias"
}

proc sco_q {tprompt login lpass userlogin host} {
set timeout 5
set log $login
set opass $lpass
set prompt $tprompt
set ulogin $userlogin
set hostname $host
set insidealias none
set insidepwd none

send_user "Scanning $hostname... "

expect -re $tprompt {send "\r"}
expect -re $tprompt {send "grep $ulogin /etc/passwd\r"}
expect -re $tprompt
send "echo $?\r"
expect -re "\r\n(.*)\r\n"
set returnval $expect_out(1,string)
if { $returnval == 0 } {
set insidepwd 1
}
expect -re $tprompt {send "grep $ulogin /usr/mmdf/table/alias.user\r"}
expect -re $tprompt
send "echo $?\r"
expect -re "\r\n(.*)\r\n"
set returnval $expect_out(1,string)
if { $returnval == 0 } {
set insidealias 1
}
expect -re $tprompt {
send "exit\r"
}
return "$hostname $insidepwd $insidealias"
}

#########################################################################
# The main process loop
#########################################################################

#########################################################################
# Check command line args
#########################################################################

if { [llength $argv] < 2 } {
puts "Usage: $argv0 <-h> \[User Login\] \[Your Login\]"
exit
}

#########################################################################
# Set list of servers, passwords and other globals
#########################################################################

set sun [list sun1 sun2]
set sco [list sco1 sco2]
set hp [list hp1 hp2]

set userlogin [lindex $argv 0]
set login [lindex $argv 1]
set tprompt "(%|#|\\\/|\\\$|\\\/|\\\/\\\/#|mgolvach|\\\)|\\\>|\\\}) ?$"

#########################################################################
# Grab user password.
#########################################################################

send_user "Enter your login password : "
stty -echo
expect -re "(.*)\n"
stty echo
set lpass $expect_out(1,string)

send_user "\n"

#########################################################################
# Do It To It.
#########################################################################

foreach host $sun {
spawn telnet $host
sun_login $tprompt $login $lpass
lappend finalists [sunhp_q $tprompt $login $lpass $userlogin $host]
}

foreach host $hp {
spawn telnet $host
hp_login $tprompt $login $lpass
lappend finalists [sunhp_q $tprompt $login $lpass $userlogin $host]
}

foreach host $sco {
spawn telnet $host
sco_login $tprompt $login $lpass
lappend finalists [sco_q $tprompt $login $lpass $userlogin $host]
}

puts ""
puts "#################################################################"
puts "$userlogin is a valid user on the following machines:"
puts "#################################################################"
set counter 0
foreach item $finalists {
if { [lindex $item 1] == 1 } {
incr counter
puts [lindex $item 0]
}
}
if { $counter == 0 } {
puts "No valid accounts found for $userlogin"
}
puts "#################################################################"
puts "$userlogin has mail alias entries on the following machines:"
puts "#################################################################"
set counter 0
foreach item $finalists {
if { [lindex $item 2] == 1 } {
incr counter
puts [lindex $item 0]
}
}
if { $counter == 0 } {
puts "No alias file entries found for $userlogin"
}
puts "#################################################################"
puts "$userlogin mail server sql user report:"
puts "#################################################################"
set counter 0
set squealer [open "|isql userdb 2>/dev/null >/tmp/tattle1.tmp" w]
puts $squealer "select * from mail_users where mail_user_id = \"$userlogin\""
close $squealer
set stooly [open "/tmp/tattle1.tmp"]
while {[gets $stooly buffer]!=-1} {
if { [string match *$userlogin* $buffer] == 1 } {
puts "Mail Dir : $buffer"
incr counter
}
}
if { $counter == 0 } {
puts "No Mail Dir entries found for $userlogin"
}
system "rm /tmp/tattle1.tmp"
puts "#################################################################"
puts "$userlogin excess files and directories found on mail server"
puts "#################################################################"
set counter 0
if { [file exists "/var/mail/$userlogin"] == 1 } {
incr counter
puts "File: /var/mail/$userlogin"
}
if { [file isdirectory "/var/imsp/user/$userlogin"] == 1 } {
incr counter
puts "Directory: /var/imsp/user/$userlogin"
}
if { $counter == 0 } {
puts "No excess mail server files found for $userlogin"
}
puts "#################################################################"
puts "DON'T FORGET TO:"
puts " 1. Do this".
puts " 2. Do that."
puts " 3. Do everything else."
puts "#################################################################"

exit



, Mike




Monday, March 24, 2008

Doing Network Wide Updates Using Expect

Hey there,

It's been a while since we took a look at Expect on this blog, so I'm throwing a monster out there today ;)

Today's script is a bit long-in-the-tooth, and may be complicated looking, but it's basically a simple script to enable you to do updates across your network all at once; only having to login once (or twice, but all at the same time ;)

I put a small section in the beginning that checks whether or not a user invoking the script is allowed to use it (although this script doesn't allow anyone to do anything that they don't have the privilege or access and knowledge to do already). It's a simple check against an exec of the logname command, to make sure that the user's logname is among the list of lognames allowed to use the script. This whole section can be removed without affecting the script's functionality. Simply delete the section with the header: "Weed out the undesirables pronto" and it'll no longer be an issue.

I also added separate login functions for Solaris, Linux, HP-UX and SCO Unix for variety (Note that you may need to tweak these Expect routines, in case the regular expression matching doesn't suit your flavor). You may not need all of these functions, and you may only need one. In any case, you can either call the script specifically to use it the way in which you desire, or edit it so that it will only run the way you desire. If you call it with the "-h" flag it should give you more help than you could ever possibly want ;)

Ex:
host # ./update.exp -h


Hope you enjoy this and find it useful. Good Times! :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/local/bin/expect

#########################################################################
# update.exp - do mass updates across network
# without lifing a finger
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# Usage: update.exp <-h> <-d>
# [ +host1 +hostn | (+sco &| +sun &| +hp &| +linux) | +all ]
# [ login ] [ passwd | <command> <args> ]
#########################################################################

#########################################################################
# Weed out the undesirables pronto
#########################################################################

set gauntlet [exec logname]
if { [string compare $gauntlet "user1"] != 0 && [string compare $gauntlet "user2"] != 0 && [string compare $gauntlet "user3"] != 0 } {
puts "Sorry, $gauntlet. You may not use update.exp."
exit
}

#########################################################################
# Go for the help short-circuit
#########################################################################

foreach member $argv {
if { $member == "-h" } {
puts "#############################################################################"
puts "USAGE:"
puts " update.exp <-h> <-d>"
puts " \[ +host1 +hostn | (+sco &| +sun &| +hp &| +linux) | +all \]"
puts " \[ login \] \[ passwd | <command> <args> \]"
puts "#############################################################################"
puts "SCO Hosts:"
puts " sco1 sco2 sco3"
puts "SUN Hosts:"
puts " sun1 sun2 sun3"
puts "HP Hosts:"
puts " hp1 hp2 hp3"
puts "LINUX Hosts:"
puts " linux1 linux2 linux3"
puts "#############################################################################"
puts "FLAGS:"
puts " -h: Show this message."
puts " -d: Force extremely verbose output during operation."
puts " +host\[1-n\]: The plus symbol followed by any vaild hostname."
puts " +sco: Process all sco hosts."
puts " +sun: Process all sun hosts."
puts " +hp: Process all hp hosts."
puts " +linux: Process all linux hosts."
puts " +all: Process all hosts."
puts " login: Your own."
puts " passwd: Change password on specified machines."
puts " command: Any architecture-supported command, with optional arguments."
puts "#############################################################################"
puts "A FEW VALID COMMAND LINE EXPRESSIONS:"
puts " update.exp -h"
puts " \(The -h command invalidates all others\)"
puts " update.exp -d *****"
puts " \(The -d command may be used in conjunction with any other\)"
puts " update.exp +host1 +host2 login command args"
puts " \(Multiple hosts may be named on command line\)"
puts " update.exp +host1 +sun login command args"
puts " \(Specific host may be coupled with groups\)"
puts " update.exp +sun +hp login command"
puts " \(Groups may be combined. Args to command are trivial\)"
puts " update.exp +all login passwd"
puts " \(+all obviates any other hosts named, but may be combined for"
puts " a specific purpose\)"
puts "#############################################################################"
exit
}
}

#########################################################################
# Hide the gory details unless they want blood
#########################################################################

if { [llength $argv] == 0 } {
set debug 0
} else {
foreach entrail $argv {
if { $entrail == "-d" } {
set debug 1
break
} else {
set debug 0
}
}
}

if { $debug } {
log_user 1
} else {
log_user 0
}

#########################################################################
# Generic login procs divided by architecture
#########################################################################

proc sun_login {name tprompt login lpass} {
send_user "$name\n"

set timeout 3
set hostname $name
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word: " {send "$qpass\r"}
}

proc linux_login {name tprompt login lpass} {
send_user "$name\n"

set timeout 3
set hostname $name
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word: " {send "$qpass\r"}
}

proc hp_login {name tprompt login lpass} {
send_user "$name\n"

set timeout 12
set hostname $name
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word:" {send "$qpass\r"}
}

proc sco_login {name tprompt login lpass} {
send_user "$name\n"

set timeout 6
set hostname $name
set prompt $tprompt
set qlogin $login
set qpass $lpass

expect "ogin: " {send "$qlogin\r"}
expect "word:" {send "$qpass\r"}
expect -re $tprompt {send "\r"}
}

#########################################################################
# Command and user passwd procs by architecture
#########################################################################

proc sunhp_q {command arglist tprompt debug login lpass} {
set timeout 5
set log $login
set opass $lpass
set argvec $arglist
set commvec $command
set prompt $tprompt
set db $debug

if { $command == "passwd" } {
set username $log
set oldpass $opass
set newpass $argvec

expect -re $prompt {send "$commvec $username\r"}
expect -re "word: ?$" {send "$oldpass\r"}
expect -re "New password: ?$" {send "$newpass\r"}
expect {
-re "New password: ?$" {
send "\r"
send_user "Password not accepted!\n"
expect -re "New password: ?$" {send "\r"}
expect -re "New password: ?$" {send "\r"}
}
-re "word: ?$" {send "$newpass\r"}
}
expect -re $prompt {send "exit\r"}
} else {
if { $argvec != 0 } {
expect -re $tprompt {send "$commvec $argvec\r"}
} else {
expect -re $tprompt {send "$commvec\r"}
}
log_user 1
expect -re $tprompt {
if { $db } {
log_user 1
} else {
log_user 0
}
send "exit\r"
}
}
}

proc linux_q {command arglist tprompt debug login lpass} {
set timeout 5
set log $login
set opass $lpass
set argvec $arglist
set commvec $command
set prompt $tprompt
set db $debug

if { $command == "passwd" } {
set username $log
set oldpass $opass
set newpass $argvec

expect -re $prompt {send "$commvec\r"}
expect -re "word: ?$" {send "$oldpass\r"}
expect -re "assword: ?$" {send "$newpass\r"}
expect {
-re "BAD.*$" {
send "\r"
send_user "Password not accepted!\n"
expect -re "BAD.*$" {send "\r"}
expect -re "BAD.*$" {send "\r"}
}
-re "word: ?$" {send "$newpass\r"}
}
expect -re $prompt {send "exit\r"}
} else {
if { $argvec != 0 } {
expect -re $tprompt {send "$commvec $argvec\r"}
} else {
expect -re $tprompt {send "$commvec\r"}
}
log_user 1
expect -re $tprompt {
if { $db } {
log_user 1
} else {
log_user 0
}
send "exit\r"
}
}
}

proc sco_q {command arglist tprompt debug login lpass} {
set timeout 5
set log $login
set opass $lpass
set argvec $arglist
set commvec $command
set prompt $tprompt
set db $debug

if { $db } {
log_user 1
} else {
log_user 0
}
if { $command == "passwd" } {
set username $log
set oldpass $opass
set newpass $argvec

expect -re $prompt {send "$commvec $username\r"}
expect -re "word: ?$" {send "$oldpass\r"}
expect -re "1\\\): ?$" {send "\r"}
expect -re "New password: ?$" {send "$newpass\r"}
expect {
-re "New password: ?$" {
send "$newpass\r"
expect -re "New password: ?$" {send "$newpass\r"}
send_user "Password not accepted!\n"
}
-re "word: ?$" {send "$newpass\r"}
}
expect -re $prompt {send "exit\r"}
} else {
expect -re $tprompt {send "\r"}
expect -re $tprompt {
if { $argvec != 0 } {
send "$commvec $argvec\r"
} else {
send "$commvec\r"
}
log_user 1
}
expect -re $tprompt {
if { $db } {
log_user 1
} else {
log_user 0
}
send "exit\r"
}
}
}

#########################################################################
# The main process loop
#########################################################################

#########################################################################
# Check command line args
#########################################################################

if { [llength $argv] < 3 } {
puts "Usage: $argv0 <-h> <-d> \\ "
puts "\t\[+host1 +hostn | \(+sun &| +sco &| +hp &| +linux\) | +all\] \\ "
puts "\t\[login\] \[rpasswd | passwd | <command> <args>\]"
exit
}

set gotserv 0
set gotlog 0

foreach name $argv {
if { [string match "+*" $name ] } {
set gotserv 1
} elseif { [string match "+*" $name ] && $gotserv } {
continue
} elseif { $gotserv } {
set gotlog 1
}
}

if { [expr $gotlog+$gotserv] != 2 } {
puts "Usage: $argv0 <-h> <-d> \\ "
puts "\t\[+host1 +hostn | \(+sun &| +sco &| +hp &| +linux\) | +all\] \\ "
puts "\t\[login\] \[rpasswd | passwd| <command> <args>\]"
exit
}

#########################################################################
# Set list of servers, passwords and other globals
#########################################################################

set sun [list sun1 sun2 sun3]
set sco [list sco1 sco2 sco3]
set hp [list hp1 hp2 hp3]
set linux [list linux1 linux2 linux3]

list servers
list arglist
set allservers 0
set sunservers 0
set linuxservers 0
set scoservers 0
set hpservers 0
set needargs 0
set gotargs 0
set verify 0
set tprompt "(%|#|\\\/|\\\$|\\\/|\\\/\\\/#|user1|user2|user3|\\\)|\\\>|\\\}) ?$"

#########################################################################
# Parse arg vector and delegate procs
#########################################################################

foreach name $argv {
if { [string match "+*" $name] == 1 } {
if { [string match "+all" $name] == 1 } {
set allservers 1
} elseif { [string match "+sun" $name] == 1 } {
set sunservers 1
} elseif { [string match "+linux" $name] == 1 } {
set linuxservers 1
} elseif { [string match "+sco" $name] == 1 } {
set scoservers 1
} elseif { [string match "+hp" $name] == 1 } {
set hpservers 1
} else {
lappend servers [string range $name 1 [expr [string length $name]-1]]
}
} else {
if { $needargs == 1 } {
lappend arglist $name
set gotargs 1
} elseif { $verify == 1 } {
set command $name
set needargs 1
} elseif { $name == "-d" } {
continue
} else {
set login $name
incr verify
}
}
}

set ssshservers [expr $sunservers + $linuxservers + $scoservers + $hpservers]

if { $gotargs == 0 } {
set arglist 0
}

#########################################################################
# Grab user password and new password if necessary.
#########################################################################

send_user "Enter your login password : "
stty -echo
expect -re "(.*)\n"
stty echo
set lpass $expect_out(1,string)

send_user "\n"

if { $command == "passwd" } {
send_user "Enter your new password : "
stty -echo
expect -re "(.*)\n"
stty echo
set arglist $expect_out(1,string)
}

send_user "\n"

#########################################################################
# Loop until all conditions are satisfied
#########################################################################

for {} {1} {} {
if {$allservers} {
foreach host $sun {
spawn telnet $host
sun_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
}
foreach host $linux {
spawn telnet $host
linux_login $host $tprompt $login $lpass
linux_q $command $arglist $tprompt $debug $login $lpass
}
foreach host $hp {
spawn telnet $host
hp_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
}
foreach host $sco {
spawn telnet $host
sco_login $host $tprompt $login $lpass
sco_q $command $arglist $tprompt $debug $login $lpass
}
break
} elseif {$sunservers} {
foreach host $sun {
spawn telnet $host
sun_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
}
if { $ssshservers > 1 } {
set sunservers 0
incr ssshservers -1
continue
}
break
} elseif {$linuxservers} {
foreach host $linux {
spawn telnet $host
linux_login $host $tprompt $login $lpass
linux_q $command $arglist $tprompt $debug $login $lpass
}
if { $ssshservers > 1 } {
set linuxservers 0
incr ssshservers -1
continue
}
break
} elseif {$scoservers} {
foreach host $sco {
spawn telnet $host
sco_login $host $tprompt $login $lpass
sco_q $command $arglist $tprompt $debug $login $lpass
}
if { $ssshservers > 1 } {
set scoservers 0
incr ssshservers -1
continue
}
break
} elseif {$hpservers} {
foreach host $hp {
spawn telnet $host
hp_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
}
if { $ssshservers > 1 } {
set hpservers 0
incr ssshservers -1
continue
}
break
} else {
foreach host $servers {
if { [lsearch $sun $host] >= 0 } {
spawn telnet $host
sun_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
} elseif { [lsearch $hp $host] >= 0 } {
spawn telnet $host
hp_login $host $tprompt $login $lpass
sunhp_q $command $arglist $tprompt $debug $login $lpass
} elseif { [lsearch $linux $host] >= 0 } {
spawn telnet $host
linux_login $host $tprompt $login $lpass
linux_q $command $arglist $tprompt $debug $login $lpass
} elseif { [lsearch $sco $host] >= 0 } {
spawn telnet $host
sco_login $host $tprompt $login $lpass
sco_q $command $arglist $tprompt $debug $login $lpass
}
}
if { $ssshservers > 1 } {
incr ssshservers -1
continue
}
break
}
}

exit


, Mike




Saturday, January 12, 2008

Taking a Look At Expect on Linux and Unix

Hey There,

Another lazy Saturday (actually, I'm just finishing a fun 7pm to 5am shift), so I thought I'd kick it off by touching on a subject we've never addressed in any of our Unix or Linux shell scripts. I expect you know what I'm talking about if you've read the title (There was almost no way around making a bad joke there ;)

Expect is a great tool to incorporate in a shell script when you need to fake some form of human interaction. For instance, if you need to do some complex work over Telnet or SSH. We'll get into more complicated scripts later, and tailor them for both Linux and Unix. Today, I snapped out a quick Expect script to introduce the language. If you've been around long enough (or had the pleasure) to use chat, Expect is probably going to seem fairly familiar. Its name explains the core functionality. It "expects" some sort of input (That you determine or decide) and reacts on it when it finds it (which action you also dictate).

For today, just take a look at this script, and you can see what I mean. The language, itself, is an extension of the TCL language, which makes it easy to program in all on its own (and get very creative with ;) Most of the commands are self-explanatory, and you'll note that lines like:

expect -re "220 .*\r\n"

are instructions to the Expect shell telling it to react when it recognizes that particular regular expression (as denoted with the "-re" flag). Basically, it's going to wait for a line to pass into its buffer that reads "220 whatever" ending with a carriage return and then react on that.

Another interesting thing, that ties into the above, and should be a mainstay of Unix and Linux Expect scripting, is the "timeout" variable. This is set in the script at the top:

set timeout 10

but can be set multiple times in the script and (given the default value I've chosen) can be left out if you want to go with the standard 10 second timeout. This directive tells Expect that, when it's expecting something, if it doesn't find it in 10 seconds, it should move on to the next line.

The little script below simply connects to a server's SMTP port and attempts to issue the VRFY command to determine if a user name is good or not. Now, most servers won't respond to that command, due to growing security problems with its use (or misuse ;), but this is just an example. And it may just work.

The last line ("interact") tells Expect it's time for you the user to do your thing. You can escape out of this by ending the currently spawned process (the SMTP prompt). You can just type "quit", or ctl-C, and the remainder of script will process. In this case, you'll get a shell prompt, as the script ends with that line :)

Enjoy,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/local/bin/expect

#
# lame example script. Connect
# to a mail server and see if we
# can verify usernames
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

set timeout 10
send_user "Checking...\n"
log_user 0
set wordfile $argv
eval spawn telnet localhost 25
expect -re "220 .*\r\n"
set wf [open $wordfile]
while {[gets $wf buf]!=-1} {
set word $buf
send_user "$word... "
send "VRFY $word\r"
expect {
-re "2.. .*\r\n" {
send_user "Good!\n"
}
-re "5.. .*\r\n" {
send_user "Bad!\n"
}
}
continue
}
interact