#!/usr/bin/env python # -*- coding: utf-8 """ ################################################################# # python script to create webform to change password # ################################################################# # # This script is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This script is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public # License along with this script; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # or check http://www.gnu.org/licenses/ for more information. # # NAME # wpwd # # USAGE # Call http://[: # # This script uses pexpect $Id: pexpect.py 516 2008-05-23 20:46:01Z noah $ # Pexpect Copyright (c) 2008 Noah Spurrier # http://pexpect.sourceforge.net/ # """ ###################################################### # Global variables which should be changed/customized ###################################################### form_title = "Password change form" # password rules pwd_len = 12 # desired password length pwd_upper = True # should password contain at least one uppercase character pwd_lower = True # should password contain at least one lowercase character pwd_digit = True # should password contain at least one digit pwd_spezi = True # should password contain at least one special character like "!@#$%&.*" ###################################################### # Change anything below this line on your own risk ###################################################### import os, platform, cgi, time, sys, string, re try: import pexpect except ImportError, e: raise ImportError (str(e) + """ The module pexpect could not be found. Either copy the file pexpect.py into the current directory or install pexpect globally. See https://pypi.python.org/pypi/pexpect/ or http://pexpect.sourceforge.net/pexpect.html """) import cgitb cgitb.enable() def print_out( *args ): sys.stdout.write(' '.join( map( str, args ) ) + '\n') sys.stdout.flush() def print_err( *args ): sys.stderr.write(' '.join( map( str, args ) ) + '\n') sys.stderr.flush() def log( *args ): print_err( "wpwd - [", time.asctime( time.localtime(time.time()) ), "] : ", ' '.join( map( str, args ) ) ) # send all parameters to stderr, i.e. webserver def txt2html ( *args ): """ ###################################################################### # function txt2html # Purpose: replace problematic characters into proper HTML # Usage: txt2html( max" # Return: "max < min && min > max" ###################################################################### """ # character to translate chars = ( '&' , '&', '<' , '<', '>' , '>', '€' , '€', ' ' , ' ', '\t' , '        ', '\n' , '
', ) html_str = ' '.join( map( str, args ) ) # put all in one string ind = 0 while ind < len( chars ): html_str = html_str.replace( chars[ind], chars[ind + 1] ) ind += 2 return html_str def print_header(): print "Content-type: text/html\r\n" print \ """ %s


%s

""" % ( form_title, form_title ) def print_footer(): print \ """


wpwd - Copyright 2014 by Hans-Georg Bork, hgb <at> users.sourceforge.net
Check on sourceforge.net
""" def print_pwd_rules(): rule_list = [] try: rule_file = open( "password_rules", "r" ) except: try: rule_file = open( "password_rules.default", "r" ) log( "Using rules file \"password_rules.default\"" ) except: # rule_list has to be a list, so that no substitution is needed below; readlines deliveres also a list rule_list = [ "There are no password rules specified for this system.\n", "However, you should alway strive for a strong password with a length of at least 12 characters\n", "and it should contain uppercase and lowercase letters, digits and special characters.\n" ] if len( rule_list ) == 0: rule_list = rule_file.readlines(); rule_file.close() rule_line = "" for line in rule_list: rule_line += txt2html( line ) # Create single html string from rules print \ """


%s



""" % ( rule_line ) def print_login_form( user = "", msg = "" ): if len( msg ) > 0: msg="""

%s %s


""" % ( txt2html( msg ), user ) # message to be shown in login form, e.g. wrong password ... print_header() print \ """
%s
Username :
Password :
 
""" % ( thisscript, msg ) print_footer() def print_change_form( user, passwd, msg = "" ): if len( msg ) > 0: msg="""

%s


""" % ( txt2html( msg ) ) # message to be shown in login form, e.g. wrong password ... print_header() print """
%s
Username :
Current Password :
New Password :
Retype new Password :
""" % ( thisscript, msg, user, passwd ) print_pwd_rules() print_footer() def print_error( action, txt = "" ): if action == "changepwd": msg=""" Your password could not be changed. A common reason is, that you changed your password less than 24 hours ago; in that case, just wait till the last change is more than 24 hours ago. If you did not recently change your password, contact your system administrator so that the system and your account can be checked. """ elif action == "pwdchanged": msg=""" Your password has been changed successfully. You will need to wait at least one day before you can change it again. """ else: msg = txt print_header() print """
%s
""" % ( txt2html( msg ) ) print_footer() def checknew( user, opasswd, npasswd, rnpasswd ): """ Check new password if it is according to the rules. Parameters: npasswd # new password rnpasswd # retyped new password """ if npasswd != rnpasswd: return 1 # passwords do not match if len( npasswd ) < pwd_len: return 2 # password too short if pwd_upper and not re.search( '[A-Z]', npasswd ): return 3 # No uppercase char if pwd_lower and not re.search( '[a-z]', npasswd ): return 4 # No lowercase char if pwd_digit and not re.search( '[0-9]', npasswd ): return 5 # No digit if pwd_spezi and not re.search( '[^A-Za-z0-9]', npasswd ): return 6 # No special character if opasswd.upper() == npasswd.upper(): return 7 # same as old password if npasswd.upper().find( user.upper() ) != -1: return 8 # username in password return 0 def validate_login( user, passwd ): "Validate user/password pair from login form" session = pexpect.spawn( """/bin/su -c "exit" %s""" % (user) ) try: session.expect( '(?i)password' ) except: session.close() if session.exitstatus == 125: log( "Invalid user", user, "error :", session.exitstatus ) log( "Invalid user", user, "error :", session.exitstatus ) return session.exitstatus session.sendline( "%s\n" % (passwd) ) session.close() return session.exitstatus def changepwd( user, passwd, npasswd ): "Change current password with new one" if platform.dist()[0] == 'debian': interactive = { "Password": "%s\n" % (passwd), "\(current\) UNIX password": "%s\n" % (passwd), "Enter new UNIX password": "%s\n" % (npasswd), "Retype new UNIX password": "%s\n" % (npasswd), } elif int( float( platform.dist()[1] ) ) < 6: # as of RHEL 6 the term UNIX is not used anymore when asking for new password ... interactive = { "Password": "%s\n" % (passwd), "\(current\) UNIX password": "%s\n" % (passwd), "New UNIX password": "%s\n" % (npasswd), "Retype new UNIX password": "%s\n" % (npasswd), } else: interactive = { "Password": "%s\n" % (passwd), "\(current\) UNIX password": "%s\n" % (passwd), "New password": "%s\n" % (npasswd), "Retype new password": "%s\n" % (npasswd), } (output, exitstatus) = pexpect.run ( '/bin/su -c passwd %s' % (user), events=interactive, withexitstatus=True) return exitstatus def main(): "main function for wpwd" # get all POST variables form_val = cgi.FieldStorage() try: wpwd_action = form_val.getvalue( 'wpwd_action' ) except: wpwd_action = "" if wpwd_action == "login": res = validate_login( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ) ) if res == None: print_login_form( form_val.getvalue( 'wpwd_user' ), "Wrong password for user " ) elif res == 1 or res == 125: # 1 on RHEL 5, 125 on RHEL 6 print_login_form( form_val.getvalue( 'wpwd_user' ), "Unknown username" ) elif res == 0: log( "Session opened for user:", form_val.getvalue( 'wpwd_user' ) ) print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ) ) else: print_login_form( "", "General error or access denied" ) log( "ERROR : login failed for unknown reason.", "Username/Password were probably correct" ) elif wpwd_action == "change": res = checknew( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), form_val.getvalue( 'wpwd_npasswd' ), form_val.getvalue( 'wpwd_rnpasswd' ) ) if res == 0: if changepwd( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), form_val.getvalue( 'wpwd_npasswd' ) ) == 0: print_error( "pwdchanged" ) else: print_error( "changepwd" ) log( "Session finished for user:", form_val.getvalue( 'wpwd_user' ) ) elif res == 1: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New Passwords do not match" ) elif res == 2: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password is too short (minimum is %s characters)" % ( pwd_len ) ) elif res == 3: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password has no UPPERCASE character (must have at least one)" ) elif res == 4: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password has no lowercase character (must have at leasr one)" ) elif res == 5: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password has no digit (must have at least one)" ) elif res == 6: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password has no speacial character like (*&^%$#@!) (must have at least one)" ) elif res == 7: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password may not be the same as the old password" ) elif res == 8: print_change_form( form_val.getvalue( 'wpwd_user' ), form_val.getvalue( 'wpwd_passwd' ), "New password may not contain the username" ) else: print_login_form() # main prog if __name__ == '__main__': try: thisscript = os.environ['SCRIPT_NAME'] # system path to this script; might be different than just $0, since it is started via webserver except: thisscript = "./test.py" main()