If you work at a place like my company, your passwords expire regularly. If you have a job similar to mine, you have a whole mess of systems that you have to change your password on. If you have a personality like mine, this is a really boring task that you’d rather not deal with, but you have to.
Well, I have a solution for you. It’s based on my last post, that magical Python pexpect script. It’s stripped down a little more, but I’m sure you’ll find most of it very familiar if you read through the last one. So, without further ado, I introduce to you my magically delicious password change script:
#!/usr/bin/python # # Change a user's password on multiple systems, ensuring that given user # has valid sudo access. (2 birds, 1 stone) # # It requires that you have sudo available on the target(s) and that you # can run the given command under sudo. It does not require SSH keys be set # up, since it handles the password dialogs for both SSH login and sudo # access. # # Any vulgarities in this code are the result of being lazy about # case sensitivity checks, and are not deliberate. If you decide to be # offended, you need to get over it. import pexpect from optparse import OptionParser import os import getpass import signal import sys from datetime import datetime DEBUG = 0 jams = [] misses = [] hits = [] def getTargets(hostspec): global DEBUG if os.path.isfile(hostspec): if DEBUG: print "Reading hosts from file "+hostspec fh = open(hostspec, 'r') hosts=fh.read() else: if DEBUG: print "Using hosts from command line." hosts=hostspec return hosts.split() def pullTrigger(target, oldpass, newpass, username): global DEBUG, jams, misses, hits rangeHot = "\$ " # First, we launch the ssh process and get logged in to the target # Set a 5 minute timeout on commands, not 30 seconds proc = pexpect.spawn("ssh "+target) while True: index = proc.expect(["The authenticity of host", "assword:", "Permission denied", rangeHot, pexpect.EOF, pexpect.TIMEOUT]) if index == 0: proc.sendline("yes") elif index == 1: proc.sendline(oldpass) elif index == 2: jams.append(target) if DEBUG: print "Dud cartridge. Clearing chamber, proceeding with firing plan..." proc.kill(signal.SIGKILL) return elif index == 3: break elif index == 4: jams.append(target) if DEBUG: print "Cartridge jammed, clearing chamber, proceeding with firing plan." proc.kill(signal.SIGKILL) return elif index == 5: jams.append(target) if DEBUG: print "Squib load. clearing chamber, proceeding with firing plan." proc.kill(signal.SIGKILL) return # Go root if DEBUG: print "Becoming root inside expect spawn." rangeHot = becomeRoot(proc, oldpass) if rangeHot == "EOF": misses.append(target) if DEBUG: print "Missed target low. Proceeding with firing plan." proc.kill(signal.SIGKILL) return if rangeHot == "TIMEOUT": misses.append(target) if DEBUG: print "Missed target high. Proceeding with firing plan." proc.kill(signal.SIGKILL) return # Change password proc.sendline("passwd "+username) proc.expect(":") proc.sendline(newpass) proc.expect(":") proc.sendline(newpass) index = proc.expect([rangeHot, pexpect.EOF, pexpect.TIMEOUT]) if index != 0: misses.append(target) if DEBUG: print "Missed wide left. Proceeding with firing plan." proc.kill(signal.SIGKILL) return # A hit! A veritable hit! O frabjious day! hits.append(target) rangeHot = exitRoot(proc) proc.sendline("exit") def exitRoot(proc): global DEBUG # Quick and dirty. This should really be nicer, but I'm lazy and it's # almost guaranteed to work if you actually got this far. if DEBUG: print "Leaving root shell." proc.sendline("exit") proc.expect("\$ ") return "\$ " def becomeRoot(proc, passwd): proc.sendline("uname -s") index = proc.expect(["SunOS", "Linux"]) if index == 0: proc.sendline("super root-shell") elif index == 1: proc.sendline("sudo su -") while True: index = proc.expect(["assword", "\# ", pexpect.EOF, pexpect.TIMEOUT]) if index == 0: proc.sendline(passwd) elif index == 1: return "\# " elif index == 2: return "EOF" elif index == 3: return "TIMEOUT" def main(): global DEBUG, jams, misses, hits # Set up command line options / arguments parser = OptionParser() parser.disable_interspersed_args() parser.set_defaults(saveResults=True) parser.add_option("-H", "--hosts", dest="hostspec", help="hosts to run the command(s) on", metavar="HOSTSPEC", default="pyexphosts") parser.add_option ("-d", "--debug", action="store_true", dest="debug", help="print debugging messages") (options, args) = parser.parse_args() if options.debug: DEBUG=1 targets = getTargets(options.hostspec) username = raw_input("User name to change password for: ") oldpass = getpass.getpass("Old password: ") newpass = getpass.getpass("New password: ") for target in targets: if DEBUG: print "Launching commands at target "+target pullTrigger(target, oldpass, newpass, username) if (len(jams)): print "Jams noticed:" for target in jams: print "Target "+target if (len(misses)): print "Misses noticed:" for target in misses: print "Target: "+target print "Done changing passwords." if __name__ == "__main__": main()