Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 03 Mar 2002 21:31:39 -0500
From:      Eric Harris <eric@majorgeek.net>
To:        freebsd-security@FreeBSD.ORG
Subject:   Re: Changing Passwords through the web (fwd)
Message-ID:  <5.1.0.14.2.20020303212925.02343788@insanity.majorgeek.net>

next in thread | raw e-mail | index | archive | help

Hi,

I use a CGI script that uses the poppassd and python ports on my small 
system (FreeBSD 4.5-S), so users can change their passwords via an web 
based interface.   You MUST have poppassd AND python installed for this to 
work!

This seems pretty safe to me (as safe as having users send their password 
to my POP3 server every 60 seconds).  I didn't write the script, just using 
it from a friend that passed it on.  If you use it, use it at your own risk 
:)

----------------------------------------------------------------------------------------------------------------------------------
Here is the HTML page (put this somewhere that is accessible on the web on 
your server):
NOTE: You must change the FORM ACTION line 
"http://somesite.com/cgi-bin/change-password.cgi" to the actual URL the CGI 
script lives on.
If your email client translates the HTML source into an actual HTML page, 
just view the source of the email to get it.
-----------------------------------------------------------------------------------------------------------------------------------
BEGIN HTML SOURCE
----
<HTML>
<HEAD>
         <TITLE>Password Update</TITLE>
<SCRIPT>
<!--
minlength = 4;
maxlength = 20;
strOtherthan = "abcdefghijklmnopqrstuvwxyz";
strErrmsg = "Please use capital letters and/or numbers in your password in 
addition to lower case letters."

function checksubmission() {
         if (document.forms[0].username.value == "") {
                 alert ("Please enter your user ID.");
                 document.forms[0].username.focus();
                 return;
                 }
         if (document.forms[0].oldpass.value == "") {
                 alert ("Please enter your current password.");
                 document.forms[0].oldpass.focus();
                 return;
                 }
         if (document.forms[0].newpass1.value == "") {
                 alert ("Please enter your new password.");
                 document.forms[0].newpass1.focus();
                 return;
                 }
         if (document.forms[0].newpass2.value == "") {
                 alert ("Please confirm the new password.");
                 document.forms[0].newpass2.focus();
                 return;
                 }
         if (document.forms[0].newpass1.value.length < minlength) {
                 alert ("Please use a longer password");
                 document.forms[0].newpass1.focus();
                 return;
                 }
         if (document.forms[0].newpass1.value.length > maxlength) {
                 alert ("Please use a shorter password");
                 document.forms[0].newpass1.focus();
                 return;
                 }
         if (poorpasswd(document.forms[0].newpass1.value)) {
                 alert (strErrmsg);
                 document.forms[0].newpass1.value = "";
                 document.forms[0].newpass2.value = "";
                 document.forms[0].newpass1.focus();
                 return;
                 }
         if (document.forms[0].newpass1.value != 
document.forms[0].newpass2.value) {
                 alert ("Password Mismatch. Please retype the new passwords.");
                 document.forms[0].newpass1.value = "";
                 document.forms[0].newpass2.value = "";
                 document.forms[0].newpass1.focus();
                 return;
                 }
         else {
                 document.forms[0].submit();
                 }
         }

function poorpasswd(strPasswd) {
         for (i=0; i<strPasswd.length; i++) {
                 if (strOtherthan.indexOf(strPasswd.charAt(i)) == -1) {
                         return false;
                         }
                 }
         return true;
         }
// Yeah, I know, I have a silly way of indenting my parantheses.
// -->
</SCRIPT>
</HEAD>
<BODY>
<BR>
<CENTER>
<H4>Change your password</H4>
<FORM ACTION="http://somesite.com/cgi-bin/change-password.cgi" METHOD="POST">
<TABLE BORDER=1 CELLPADDING=1 CELLSPACING=0>
<TR>
         <TD>User ID :</TD>
         <TD><INPUT TYPE = "TEXT" NAME="username" SIZE="15"></TD>
</TR>
<TR>
         <TD>Current Password :</TD>
         <TD><INPUT TYPE = "PASSWORD" NAME="oldpass" SIZE="15"></TD>
</TR>
<TR>
         <TD>New Password :</TD>
         <TD><INPUT TYPE = "PASSWORD" NAME="newpass1" SIZE="15"></TD>
</TR>
<TR>
         <TD>Confirm New :</TD>
         <TD><INPUT TYPE = "PASSWORD" NAME="newpass2" SIZE="15"></TD>
</TR>
<TR>
         <TD COLSPAN=2 BGCOLOR="#C0C0C0" ALIGN="CENTER">
                 <INPUT TYPE = "BUTTON" onClick="checksubmission()" 
VALUE="submit"><INPUT TYPE = "RESET" VALUE="clear">
         </TD>
</TR>
</TABLE>
<BR>
<BR>
<BR>
<TABLE BORDER=0 WIDTH="420">
<TR>
         <TD ALIGN = "LEFT">
         <P>Please observe the following when choosing your new password
         <DL>
                 <DT>Make it at least 6 characters long.</DT>
                 <DT>Do not use names.  Especially your's, your family's, 
or your pet's.</DT>
                     <DD>For example, "Christie" or "William" might be too 
obvious.</DD>
                 <DT>Do not use words that are found in the dictionary.</DT>
                     <DD>For example, "pumpkin" would not be a good 
password.</DD>
                 <DT>Do not use all lowercase letters.</DT>
                     <DD>With only lowercase letters, it is easy to read 
over your shoulder.</DD>
                 <DT>Examples of a sufficiently complex passwords are 
"MyPass", "caNTguess", and "not4You".</DT>
                     <DD>It is recommended that you do not use the 
examples.  If someone were to try to break in, these
are likely to be their first guesses.</DD>
                 <DT>Do not use the "Stop" button on your web browser.</DT>
                     <DD>Once you press the "submit" button, the command 
executes.  Pressing the "Stop" button only
stops the feedback.</DD>
         </DL>
         </TD>
</TR>
</TABLE>

</CENTER>
</FORM>
</BODY>
</HTML>

------
END HTML SOURCE
------


----------------------------------------------------------------------------------------------------------------------------------
Here is the CGI script (put this somewhere where you can run CGI scripts on 
your server):
NOTE: You must change the HOST line from '127.0.0.1' to whatever IP/host 
that poppassd runs on.
-----------------------------------------------------------------------------------------------------------------------------------
BEGIN CGI SOURCE
----

#!/usr/local/bin/python

# Yeah, go ahead and use this script as you wish.
# It would be great if you could leave these comments
# line in though....  Why ? I'd really appreciate
# it if you could email any bugs or feature requests
# so that I can improve it. Thanks indeed, chas.
# sweeting@neuronet.com.my
#
# ps. You need python - a gem of a scripting language.
#     Get it from  www.python.org.

from socket import *
import cgi, string, os, time
import sys

# ++++++++  Customisation : +++++++++++++++
# Your mail server :
HOST = '127.0.0.1'

# The following 3 variables need only be set if you wish
# error messages to be mailed to you. (Just in case someone's
# trying to guess somebody else's password - although there
# would be much quicker ways for them to run a script themselves
# to do this.)
# Your date and mail functions. (Type 'whereis date' and
# 'whereis mail' if you are not sure.)
# The address to mail these messages to.
# NB: if you do not want error messages mailed to you, use : 'mailto=""'
#     Do not comment out the variable 'mailto'
datefn = "/bin/date"
mailfn = "/usr/bin/mail"
mailto = " root@some.domain.com"

# Probably do not want to change this (poppassd should run on port 106)
PORT = 106
crlf = "\r\n"

# -------------- Functions : --------------
#
def outputhtml(msg):
         print "<BR><BR><CENTER><P><B>" + msg + "</B></CENTER></BODY></HTML>"

def mailadmin(msgtosend):
         # only email us if there is a email address there.
         if (mailto <> "") :
                 f = os.popen (mailfn + mailto, "w")
                 mydate = os.popen(datefn).read()
                 f.write("Date : " + mydate)
                 f.write(msgtosend)
                 f.close()

def cgierror (msg):
         outputhtml(msg)
         f = os.popen (mailfn + mailto, "w")
         mydate = os.popen(datefn).read()
         f.write("Date : " + mydate)
         f.write("Error reading in data from the form :")
         f.write(msg)
         f.close()
         sys.exit()

# ----------------- Main : -----------------
# (goes after the functions since we did not create a main as such)
# Send out the HTTP header for error checking and also to prevent a time out :
print "Content-type: text/html"
print
print "<HTML><HEAD><TITLE>Changing Password</TITLE></HEAD><BODY 
BGCOLOR=\"#FFFFFF\">"

# ---- Process the CGI variables ----
myform = cgi.SvFormContentDict()
if myform.has_key('username'):
         cgiuser = myform['username']
else:
         cgierror("CGI error reading in User ID from form.")
if myform.has_key('oldpass'):
         cgipass = myform['oldpass']
else:
         cgierror("CGI error reading in old passwd from form.")
if myform.has_key('newpass1'):
         cginew1 = myform['newpass1']
else:
         cgierror("CGI error reading in new passwd #1 from form.")
if myform.has_key('newpass2'):
         cginew2 = myform['newpass2']
else:
         cgierror("CGI error reading in new passwd #2 from form.")

# print "<P>Form variables received :"
# print "<UL>"
# print "<LI>" + cgiuser
# print "<LI>" + cgipass
# print "<LI>" + cginew1
# print "<LI>" + cginew2
# print "</UL>"

# ---- Check that the two new passwords match ----
if (cginew1 != cginew2) :
         errmsg = "New passwords do not match."
         outputhtml(errmsg)
         mailadmin(errmsg)
         s.close()
         sys.exit()

# ---- Poppassd Step #0 : Open a connection  ----
s = socket(AF_INET,SOCK_STREAM)
s.connect((HOST,PORT))
data = s.recv(1024)
if (string.find(data,'200') == -1) :
         errmsg = "An error occured opening the socket."
         # outputhtml(errmsg)
         mailadmin(errmsg)
         s.close()
         sys.exit()

# print "Connection opened OK"

# ---- Poppassd Step #1 : Send the user ID ----
s.send('user ' + cgiuser + crlf)
data = s.recv(1024)
if (string.find(data,'200') == -1) :
         errmsg = "An error occured while handling the User ID"
         # outputhtml(errmsg)
         mailadmin(errmsg)
         s.close()
         sys.exit()

# print "Username sent OK<BR>"

# ---- Poppassd Step #2 : Send the old passwd ----
s.send('pass ' + cgipass + crlf)
data = s.recv(1024)
if (string.find(data,'200') == -1) :
         # actually, either the password or userid could be wrong, but 
let's assume
         # the guy knows his userid.
         errmsg = "Sorry, an error occurred. It looks like you entered 
<BR>either your username or current password incorrectly.<BR> Please try 
again."
         outputhtml(errmsg)
         mailadmin(errmsg)
         s.close()
         sys.exit()

# print "Old password sent OK<BR>"

# ---- Poppassd Step #3 : Send the new passwd ----
s.send('newpass ' + cginew1 + crlf)
data = s.recv(1024)
if (string.find(data,'200') == -1) :
         errmsg = "An error occured with the new password. Please check 
with your system administrator."
         outputhtml(errmsg)
         mailadmin(errmsg)
         s.close()
         sys.exit()

# ---- If we have got this far, then I assume that the passwd was changed : 
----
outputhtml("Password Changed Successfully<P><HR width=90%><P><A 
href=\"http://apollo.cairodurham.org/\">Return to Email System</A>")
s.close()


# --------------- LESSONS LEARNED ---------------
# 01. Socket commands must end in "\r\n"
# 02. If you use "import string" then later you need to qualify 
"string.find(...)"
#     But if you use "from string import *" then later you just use "find(...)"

# --------------- REASONING ---------------------
#
# The shell output from telneting to the 106 port (poppassd) is
#
# Trying 202.184.153.15...
# Connected to peace.com.my.
# Escape character is '^]'.
# 200 poppassd v1.2 hello, who are you?
# user medusa
# 200 your password please.
# pass ElphtNse
# 200 your new password please.
# newpass Dnkyface
# 200 Password changed, thank-you.
#
# If things go awfully Pete Tong then we get :
# 200 poppassd v1.2 hello, who are you?
# user freddy
# 200 your password please.
# pass Junglez
# 500 Unknown user, freddu.
#
# In this case, neither the user or passwd existed.
#
# You can also get error messages from silly passwds.
# You could write the CGI to check the security of a
# passwd but I'll just leave it to the poppassd.
# eg.
# newpass happy
# 500  Please enter a longer password.
#
# From which we could define some substrings to look out for :
# prompt1 = 'who are you?'
# prompt2 = 'your password please.'
# etc etc etc
#
# But it is easier just to look out for error codes '500'
# and OK codes '200' and respond accordingly.
------
END CGI SOURCE
------


That's about it.  It works fine for my needs.  Let me know if you have 
questions or problems running or obtaining the scripts I pasted above.

Have a good day!


Regards,

Eric Harris
eric@majorgeek.net


At 09:08 AM 2/25/2002 -0500, you wrote:

>  Hello friends...
>  I was using webmin to create users by the web... but i need
>  to do an interface for users can change them passwords by the
>  web too.
>  I can not use webmin, because the webmin user need a password...
>  i need an open interface, for everyone who wants change his own
>  password, can do it by the web...
>  I was thinking on suexec apache service... but in the web site
>  i found that suexec doesn't support root scripts anymore...
>  so, i get lost...
>
>  Any question or sugestion is welcome.
>  Thank you
>
>=======================================================================
>  Buliwyf McGraw
>  Administrador del Servidor Libertad
>  Centro de Servicios de Informacion
>  Universidad del Valle
>=======================================================================
>
>
>
>
>To Unsubscribe: send mail to majordomo@FreeBSD.org
>with "unsubscribe freebsd-security" in the body of the message


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?5.1.0.14.2.20020303212925.02343788>