Update of /cvsroot/tmda/tmda-cgi
In directory sc8-pr-cvs1:/tmp/cvs-serv13366
Modified Files:
Authenticate.py Session.py compile tmda-cgi.c
Log Message:
* Authenticate.py now uses Auth.py for checkpassword and remote authentication.
* Cleaned up file authentication a bit in Authenticate.py
* Session.py now initializes the authentication scheme using the environment set
up via the new compile programs. By default, the original file authentication
proceeds.
Note: Delete existing compile.ini before running compile again.
Original file authentication should not be affected, I hope.
Warning: Checkpassword appears to work, but has not been tested robustly.
Caution: Imap authentication appears to work, but other remote protocols have
not been tested.
Index: Authenticate.py
===================================================================
RCS file: /cvsroot/tmda/tmda-cgi/Authenticate.py,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -r1.1.1.1 -r1.2
--- Authenticate.py 29 Mar 2003 22:46:28 -0000 1.1.1.1
+++ Authenticate.py 1 Apr 2003 18:15:58 -0000 1.2
@@ -27,106 +27,139 @@
import os.path
import pwd
import random
+import sys
-def ComparePassword(Filename, User, Password):
- """Checks password against a given filename.
+from TMDA import Auth
+from TMDA import Errors
-Returns:
- 1: File read, user found, password authenticated
- 0: File read, user found, login deactivated
- -1: File read, user found, password wrong
- -2: File read, user not found
- -3: File couldn't be read"""
+# For now, output all Auth.py errors to http error log
+Auth.DEBUGSTREAM = sys.stderr
+
+authinit = False
+
+def InitProgramAuth( Program, trueProg = "/usr/bin/true" ):
+ """Initializes the authentication scheme with a checkpw-style program.
+(Implemented by Auth.py)"""
+ global authinit
+ Auth.authtype = 'prog'
+ if not os.access( Program, os.F_OK ):
+ authinit = False
+ raise ValueError, "'%s' does not exist" % Program
+ if not os.access( trueProg, os.F_OK ):
+ authinit = False
+ raise ValueError, "'%s' does not exist", trueProg
+ if not os.access( Program, os.X_OK ):
+ authinit = False
+ raise ValueError, "'%s' is not executable" % Program
+ if not os.access( trueProg, os.X_OK ):
+ authinit = False
+ raise ValueError, "'%s' is not executable", trueProg
+ # Now initialize the authprog with the checkpasswd program and "true"
+ Auth.authprog = "%s %s" % ( Program, trueProg )
+ authinit = True
+
+def InitFileAuth( Filename="/etc/tmda-cgi" ):
+ """Initializes the authentication scheme with a flat file
+(Not implemented by Auth.py yet)"""
+ global authinit
+ Auth.authtype = 'file'
+ if not os.access( Filename, os.F_OK ):
+ authinit = False
+ raise ValueError, "File '%s' does not exist" % Filename
+ Auth.authfile = Filename
+ authinit = True
+
+def InitRemoteAuth( URI ):
+ """Initialaze the authentication scheme with a remote URL
+(Implemented by Auth.py)"""
+ global authinit
+ Auth.authtype = 'remote'
try:
- F = open(Filename)
- except IOError:
- return -3
-
- RetVal = -2
- while (1):
- PasswordRecord = F.readline()
-
- # End of file?
- if PasswordRecord == "":
- break
- Temp = PasswordRecord.strip().split(":")
-
- # Have we found the correct user record?
- if Temp[0] == User:
- if Temp[1] == "":
- RetVal = 0
- break
-
- Perm = os.stat(Filename)[0] & 07777
-
- # Is the password in the file encrypted?
- if (Perm != 0400) and (Perm != 0600):
- if crypt.crypt(Password, Temp[1][:2]) == Temp[1]:
- RetVal = 1
- else:
- RetVal = -1
- break
- else:
- if Temp[1] == Password:
- RetVal = 1
- else:
- RetVal = -1
- break
- F.close()
+ Auth.parse_auth_uri( URI )
+ Auth.init_auth_method()
+ authinit = True
+ except ValueError, err:
+ authinit = False
+ raise Errors.AuthError, "Bad URI: %s" % err.value
+ except ImportError, err:
+ authinit = False
+ raise Errors.AuthError, "URI scheme not supported: %s" % err.value
+
+def Authenticate(User, Password):
+ """Checks password against initialized authentication scheme filename.
+
+ - Returns True or False, depending on authentication.
+
+ - May raise Errors.AuthError if something "funny" happens."""
+ global authinit
+ RetVal = False
+ if authinit:
+ if Auth.authtype == 'prog' or Auth.authtype == 'remote':
+ try:
+ if Auth.authenticate_plain( User, Password ):
+ RetVal = True
+ except Errors.AuthError:
+ pass
+ elif Auth.authtype == 'file':
+ Filename = Auth.authfile
+ # Revert to original code since Auth.py doesn't implement files yet.
+ try:
+ F = open(Filename)
+ except IOError:
+ raise Errors.AuthError, \
+ "Cannot open file '%s' for reading." % Filename, \
+ "Check file permissions"
+
+ PasswordRecord = F.readline()
+ while PasswordRecord != "":
+
+ # Split about the :
+ Temp = PasswordRecord.strip().split(":")
+
+ # Have we found the correct user record?
+ if Temp[0] == User:
+ if Temp[1] == "":
+ raise Errors.AuthError, \
+ "User %s is denied login (blank password in file)" % Temp[0], \
+ "Blank password in file"
+
+ Perm = os.stat(Filename)[0] & 07777
+
+ # Any file may have encrypted passwords in it.
+ # Even though this is a Bad Idea.
+ if crypt.crypt(Password, Temp[1][:2]) == Temp[1]:
+ RetVal = True
+ break
+ # Only <secret> files may have cleartext passwords in it.
+ if Perm == 0400 or Perm == 0600:
+ if Temp[1] == Password:
+ RetVal = True
+ break
+ PasswordRecord = F.readline()
+ F.close()
+ else:
+ raise Errors.AuthError, \
+ "Authentication mechanism '%s' unknown." % Auth.authtype
+ else:
+ raise Errors.AuthError, "No authentication mechanism initialized."
+ # If we made it this far, we're either returning True or False.
return RetVal
def CheckPassword(Form):
- "Checks a password against password files."
-
- try:
- # Find the requested home directory
- os.environ["HOME"] = pwd.getpwnam(Form["user"].value)[5]
+ "Checks a password from a form."
- # Look in same directory as TMDARC file
- if os.environ.has_key("TMDARC"):
- # Given location?
- FN = os.path.join(os.path.split(os.environ["TMDARC"])[0], "tmda-cgi")
- else:
- # No given location, try ~/.tmda/tmda-cgi
- FN = os.path.expanduser("~/.tmda/tmda-cgi")
-
- # Login succeed?
- RetVal = ComparePassword(FN, Form["user"].value, Form["password"].value)
- if RetVal > 0:
- return RetVal
- except KeyError:
- RetVal = -4
- FN = "<i>n/a</i>"
-
- # Login help?
- if int(Form["debug"].value):
- Errors = ["Logins for user %(user)s have been deactivated in file
<tt>%(file)s</tt>",
- "Password incorrect for user %(user)s in file <tt>%(file)s</tt>",
- "User %(user)s was not found in file <tt>%(file)s</tt>",
- "Could not read file <tt>%(file)s</tt>",
- "User %(user)s does not exist"]
- Err = Errors[-RetVal] % {"user": Form["user"].value, "file": FN}
- Err += "<br>" + CgiUtil.FileDetails("Local password", FN)
- if RetVal > -2:
- CgiUtil.TermError("Login failed", "Bad pass / login disabled.", "validate
password",
- Err, "Correct entry for %s in file <tt>%s</tt>" % (Form["user"].value, FN))
- if RetVal > -2:
- return RetVal
-
- # Login succeed?
- FN = "/etc/tmda-cgi"
+ errMsg = "Password incorrect for user %s" % Form["user"].value
+ errHelp = "Reset password or correct file permissions"
try:
- RetVal = ComparePassword(FN, Form["user"].value, Form["password"].value)
- except KeyError:
- RetVal = -4
- if RetVal > 0:
- return RetVal
+ if Authenticate( Form["user"].value, Form["password"].value ):
+ return True
+ except Errors.AuthError, error:
+ errMsg = error.msg
+ if error.help != "":
+ errHelp = error.help
- # Login help?
if int(Form["debug"].value):
- Err += "<br>" + Errors[-RetVal] % {"user": Form["user"].value, "file": FN}
- Err += "<br>" + CgiUtil.FileDetails("Global password", FN)
- CgiUtil.TermError("Login failed", "Password / password file error.",
- "validate password", Err, "Reset password or correct file permissions")
- return RetVal
-
\ No newline at end of file
+ CgiUtil.TermError("Login failed", "Authentication error",
+ "validate password", errMsg, errHelp)
+ else:
+ return False
Index: Session.py
===================================================================
RCS file: /cvsroot/tmda/tmda-cgi/Session.py,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- Session.py 31 Mar 2003 19:33:51 -0000 1.3
+++ Session.py 1 Apr 2003 18:15:59 -0000 1.4
@@ -31,7 +31,6 @@
import sys
import time
import CgiUtil
-import Authenticate
Rands = random.Random()
@@ -155,8 +154,40 @@
os.environ["TMDARC"] = os.environ["TMDARC"].replace("/~/",
"/%s/" % Form["user"].value)
+ # Initialize the auth mechanism
+ import Authenticate
+ try:
+ if os.environ.has_key( "TMDA_AUTH_TYPE" ):
+ if os.environ["TMDA_AUTH_TYPE"] == "program":
+ Authenticate.InitProgramAuth( os.environ["TMDA_AUTH_ARG"],
+ os.environ["TMDA_AUTH_TRUE"] )
+ elif os.environ["TMDA_AUTH_TYPE"] == "remote":
+ Authenticate.InitRemoteAuth( os.environ["TMDA_AUTH_ARG"] )
+ elif os.environ["TMDA_AUTH_TYPE"] == "file":
+ Authenticate.InitFileAuth( os.environ["TMDA_AUTH_ARG"] )
+ else:
+ # Default to regular flat file.
+ # Order of preference:
+ # 1) $TMDARC/tmda-cgi
+ # 2) $HOME/tmda-cgi
+ # 3) /etc/tmda-cgi
+ if os.environ.has_key("TMDARC"):
+ File = os.path.join(os.path.split(os.environ["TMDARC"])[0],
+ "tmda-cgi")
+ else:
+ # FIXME: "getpwnam" will not work for virtual users!
+ os.environ["HOME"] = pwd.getpwnam(Form["user"].value)[5]
+ File = os.path.expanduser("~/.tmda/tmda-cgi")
+ if not os.access( File, os.F_OK ):
+ File = "/etc/tmda-cgi"
+
+ Authenticate.InitFileAuth( File )
+ except ValueError, err:
+ CgiUtil.TermError( "Auth Initialization Failed", "ValueError caught",
+ "init auth type %s" % os.environ["TMDA_AUTH_TYPE"], err, "Fix the code." )
+
# Validate the new session
- if Authenticate.CheckPassword(Form) > 0:
+ if Authenticate.CheckPassword(Form):
self.Vars["User"] = Form["user"].value
PasswordRecord = pwd.getpwnam(self.Vars["User"])
self.Vars["UID"] = PasswordRecord[2]
Index: compile
===================================================================
RCS file: /cvsroot/tmda/tmda-cgi/compile,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- compile 31 Mar 2003 02:27:42 -0000 1.2
+++ compile 1 Apr 2003 18:15:59 -0000 1.3
@@ -19,6 +19,11 @@
# along with TMDA; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# TODO: Perhaps a check should be put in for the various authentication schemes
+# that the arguments are valid. This is currently done at runtime within
+# "Authenticate.py", but it may be better to check at least some of the
+# values at compile-time anyway.
+
"""Pending queue manipulation tool.
Usage: %(Program)s [OPTIONS]
@@ -37,6 +42,10 @@
Specify a different directory for supplimental display files (icons and
stylesheet).
+ -f <authfile>
+ --file-auth <authfile>
+ Specify a different authentication file than the default.
+
-h
--help
Print this help message and exit.
@@ -53,10 +62,37 @@
--no-su
Compile a CGI to run in no-su mode. Forces option "-m no-su".
+ -p <checkpassword>
+ --program-auth <checkpassword>
+ ** Warning *********************************************************
+ * The code used to do this is ALPHA stable, so do not rely on this *
+ * functionality yet. *
+ ********************************************************************
+ Specify checkpassword-style authentication, where "checkpw" is the full
+ path to the desired checkpassword-style program.
+ - Ensure that --true-prog is set correctly for your system as well.
+
+ -r <uri>
+ --remote-auth <uri>
+ ** Warning *********************************************************
+ * The code used to do this is ALPHA stable, so do not rely on this *
+ * functionality yet. *
+ ********************************************************************
+ Specify remote authentication, where "uri" is the remote authentication
+ mechanism and host in the format:
+ protocol://host.domain.com[:port][/ldap_domain]
+ Currently supported protocols:
+ imap, imaps, apop, pop3, ldap
+
-t <file>
--target <file>
Compile as a file other than ./tmda-cgi.
+ -T <true>
+ --true-prog <true>
+ Specify the path to the "true" program (which returns 0)
+ Default is '/usr/bin/true'
+
-u <user>
--user <user>
Overrides the value of CGI_USER found in the configuration files.
@@ -71,6 +107,23 @@
wide mode. For the resulting code to run correctly, root will have to chown
the resulting program.
+Authentication is done by default using a file of the format username:password.
+The default location for this file is chosen in the following order:
+
+ 1) File tmda-cgi in the same directory as the config file specified by the '-c'
+ option (See below for discussion of -c)
+ 2) File tmda-cgi in ~/.tmda/tmda-cgi for the user attempting to log in.
+ 3) File /etc/tmda-cgi
+
+The password in this file should probably not be plaintext (although this is
+allowed in files of mode 400 or 600), but should be generated by the included
+script "genpass.py"
+
+The -f, -p, and -r options are used to change the default authentication. Be
+warned that the options -p and -r are EXPERIMENTAL at this point.
+
+When specifying -p, ensure that the value of -T is correct for your system.
+
The target may be specified on the command line with the -t option.
You can specify the location of the supplimental display files (icons and
@@ -118,6 +171,21 @@
Ask("Path to tmda-cgi Python files", OptD, "Path")
Ask('User config file location (or "None" for system default)', OptD,
"Config")
+ Ask("Authentication Type [file, program, remote, default]", OptD, "AuthType")
+ if OptD["AuthType"] == "file":
+ Ask('Authentication File (or "None" for system default)', OptD, "AuthFile")
+ OptD["AuthArg"] = OptD["AuthFile"]
+ elif OptD["AuthType"] == "program":
+ Ask('Authentication (checkpassword-style) program (full path)', OptD,
+ "AuthProg")
+ OptD["AuthArg"] = OptD["AuthProg"]
+ Ask('Full path to "true" program', OptD, "AuthTrue")
+ elif OptD["AuthType"] == "remote":
+ Ask('Authentication URI (protocol://host.domain.com[:port][/dn])', OptD,
+ "AuthURI")
+ OptD["AuthArg"] = OptD["AuthURI"]
+ elif OptD["AuthType"] == "default":
+ OptD["AuthArg"] = "None"
Ask("Relative or absolute web path from CGI to display directory", OptD,
"DispDir")
if OptD["DispDir"][-1] != "/": OptD["DispDir"] += "/"
@@ -132,12 +200,18 @@
# Keep options in one handy dictionary
OptD = {}
-OptD["Python"] = sys.executable
-OptD["Target"] = "tmda-cgi"
-OptD["Base"] = "../tmda/"
-OptD["Path"] = "."
-OptD["DispDir"] = "../display/"
-OptD["User"] = "nobody"
+OptD["Python"] = sys.executable
+OptD["Target"] = "tmda-cgi"
+OptD["Base"] = "../tmda/"
+OptD["Path"] = "."
+OptD["DispDir"] = "../display/"
+OptD["User"] = "nobody"
+OptD["AuthType"] = "default"
+OptD["AuthFile"] = "None"
+OptD["AuthProg"] = "/usr/sbin/checkpassword"
+OptD["AuthURI"] = "imap://localhost"
+OptD["AuthArg"] = "None"
+OptD["AuthTrue"] = "/usr/bin/true"
if os.geteuid():
OptD["Mode"] = "single-user"
else:
@@ -154,9 +228,10 @@
sys.exit(Code)
try:
- Opts, Args = getopt.getopt(sys.argv[1:], "b:c:d:i:m:nht:u:",
- ["base-dir=", "config-file=", "display-dir=", "help", "install-prefix=",
- "mode=", "no-su", "target=", "user="])
+ Opts, Args = getopt.getopt(sys.argv[1:], "b:c:d:f:i:m:nhp:r:t:T:u:",
+ ["base-dir=", "config-file=", "display-dir=", "file-auth=", "help",
+ "install-prefix=", "mode=", "no-su", "program-auth=", "remote-auth=",
+ "target=", "true-prog=", "user="])
except getopt.error, Msg:
Usage(1, Msg)
@@ -173,10 +248,24 @@
OptD["DispDir"] = Arg
else:
OptD["DispDir"] = Arg + "/"
+ elif Opt in ("-f", "--file-auth"):
+ OptD["AuthType"] = "file"
+ OptD["AuthFile"] = Arg
+ OptD["AuthArg"] = Arg
elif Opt in ("-i", "--install-prefix"):
OptD["Path"] = Arg
+ elif Opt in ("-p", "--program-auth"):
+ OptD["AuthType"] = "program"
+ OptD["AuthProg"] = Arg
+ OptD["AuthArg"] = Arg
+ elif Opt in ("-r", "--remote-auth"):
+ OptD["AuthType"] = "remote"
+ OptD["AuthURI"] = Arg
+ OptD["AuthArg"] = Arg
elif Opt in ("-t", "--target"):
OptD["Target"] = Arg
+ elif Opt in ("-T", "--true-prog"):
+ OptD["AuthTrue"] = Arg
elif Opt in ("-m", "--mode"):
if not Arg in ("system-wide", "single-user", "no-su"):
Usage(1, "Valid modes are system-wide, single-user, and no-su")
@@ -261,6 +350,14 @@
""" % string.replace(OptD["Config"], "/~/", "/<user>/")
F.write("""#define TMDARC "TMDARC=%(Config)s"
""" % OptD)
+if OptD["AuthArg"] != "None":
+ print "Users will be authenticated using %(AuthType)s %(AuthArg)s" % OptD
+ F.write("""#define AUTH_TYPE "TMDA_AUTH_TYPE=%(AuthType)s"
+#define AUTH_ARG "TMDA_AUTH_ARG=%(AuthArg)s"
+""" % OptD)
+ if OptD["AuthType"] == "program":
+ F.write( """#define AUTH_TRUE "TMDA_AUTH_TRUE=%(AuthTrue)s"
+""" % OptD)
F.close()
print "Compiling..."
@@ -287,3 +384,23 @@
compileall.compile_dir(OptD["Path"])
print "Compilation done."
+
+# Unsafe authentication type warnings!
+if OptD["AuthType"] == "remote":
+ print """
+ ** Warning *********************************************************
+ * You have selected "remote" authentication. *
+ * The code used to do this is ALPHA stable, and not every *
+ * reportedly supported protocol has been tested, so do not rely on *
+ * this functionality yet. *
+ ********************************************************************
+"""
+elif OptD["AuthType"] == "program":
+ print """
+ ** Warning *********************************************************
+ * You have selected "program" authentication. *
+ * Initial testing seems to imply that this authentication method *
+ * works properly, but the code used to do so is ALPHA stable, so *
+ * not relay on this functionality yet.
+ ********************************************************************
+"""
Index: tmda-cgi.c
===================================================================
RCS file: /cvsroot/tmda/tmda-cgi/tmda-cgi.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- tmda-cgi.c 31 Mar 2003 02:24:53 -0000 1.2
+++ tmda-cgi.c 1 Apr 2003 18:15:59 -0000 1.3
@@ -28,6 +28,13 @@
#ifdef TMDARC
putenv(TMDARC);
#endif
+#ifdef AUTH_ARG
+ putenv(AUTH_TYPE);
+ putenv(AUTH_ARG);
+ #ifdef AUTH_TRUE
+ putenv(AUTH_TRUE);
+ #endif
+#endif
putenv(MODE);
putenv(USER);
putenv(DISP_DIR);
_______________________________________
tmda-cvs mailing list
http://tmda.net/lists/listinfo/tmda-cvs