def cleanup(self): if not self.exists(): return lifetimesecs = Util.seconds(Defaults.PENDING_LIFETIME) cwd = os.getcwd() os.chdir(Defaults.PENDING_DIR) msgs = glob.glob('*.*.msg') os.chdir(cwd) for msg in msgs: now = '%d' % time.time() min_time = int(now) - int(lifetimesecs) msg_time = int(msg.split('.')[0]) if msg_time > min_time: # skip this message continue # delete this message fpath = os.path.join(Defaults.PENDING_DIR, msg) if Defaults.PENDING_DELETE_APPEND: try: msgobj = Util.msg_from_file(open(fpath, 'r')) except IOError: # in case of concurrent cleanups pass else: rp = parseaddr(msgobj.get('return-path'))[1] Util.append_to_file(rp, Defaults.PENDING_DELETE_APPEND) try: os.unlink(fpath) except OSError: # in case of concurrent cleanups pass
def CreateTgz(Archive, Filelist): """Create a .tgz of the listed files, but take care with ../ files. Return 0 on success, error code on error.""" if not Util.CanWrite(os.environ["HOME"], os.geteuid(), os.getegid(), 0): CgiUtil.TermError("Can't write.", "No write permissions.", "create backup", CgiUtil.FileDetails("home directory", os.environ["HOME"]), "Check file permissions in home directory.") Files = [] Parents = [] for Filename in Filelist: SrcFn = os.path.join(os.environ["HOME"], Filename) if os.path.isfile(SrcFn): if (Filename[:len(Dict["Parent"])] == Dict["Parent"]) and \ (Filename[:len(Dict["Home"])] != Dict["Home"]): try: os.mkdir(os.path.join(os.environ["HOME"], "%(Parent)s")) except OSError: pass NewFilename = "%(Parent)s" + Filename[len(Dict["Parent"]):] DstFn = os.path.join(os.environ["HOME"], NewFilename) Parents.append((SrcFn, DstFn)) os.rename(SrcFn, DstFn) Files.append(NewFilename) else: Files.append(Filename) TarCmd = [PVars[("NoOverride", "WhichTar")], "-C", os.environ["HOME"], "-czf", Archive] + Files try: Util.RunTask(TarCmd) except OSError, ( eno, estr ): CgiUtil.TermError("CreateTgz failed.", "Error: %s (%d)" % (estr, eno), "create backup", " ".join(TarCmd), "Check file permissions in home directory.")
def insert_message(self, msg, mailid, recipient): fname = mailid + ".msg" # Create ~/.tmda/ and friends if necessary. self._create() # X-TMDA-Recipient is used by release_pending() del msg['X-TMDA-Recipient'] msg['X-TMDA-Recipient'] = recipient # Write ~/.tmda/pending/MAILID.msg fcontents = Util.msg_as_string(msg) fpath = os.path.join(Defaults.PENDING_DIR, fname) Util.writefile(fcontents, fpath) del msg['X-TMDA-Recipient']
def ReadTgz(Archive): "Return the files listed in an archive." TarCmd = [PVars[("NoOverride", "WhichTar")], "-C", os.environ["HOME"], "-tzf", Archive] Files = Util.RunTask(TarCmd) for i in range(len(Files)): Files[i] = Files[i].strip() return Files
def testNonV6(self): urls = [ 'http://foo.com/path', 'http://foo.com:33/path?query#fragment', 'ftp://111.222.0.1/path', 'ftp://111.222.0.1:99/path?query#fragment', ] for url in urls: result = Util.urlsplit(url) self.assertTrue(isinstance(result, urlparse.SplitResult))
def insert_message(self, msg, mailid, recipient): # Create the Maildir if necessary. self._create() # X-TMDA-Recipient is used by release_pending() del msg['X-TMDA-Recipient'] msg['X-TMDA-Recipient'] = recipient # Write message time, pid = mailid.split('.') self.__deliver_maildir(Util.msg_as_string(msg), time, pid, Defaults.PENDING_DIR) del msg['X-TMDA-Recipient']
def fetch_message(self, mailid, fullParse=False): msgs = (glob(os.path.join(Defaults.PENDING_DIR, 'new/') + '1*.[0-9]*.*')) + \ (glob(os.path.join(Defaults.PENDING_DIR, 'cur/') + '1*.[0-9]*.*')) for m in msgs: if mailid in m: msg = Util.msg_from_file(file(m, 'r'),fullParse=fullParse) return msg else: # couldn't find message, defer and retry until we find it raise IOError, "couldn't locate %s, will retry" % m
def testV6(self): tests = [ ( # First test shows all the parts for reference 'http://*****:*****@[::1]:33/path?query#fragment', ('http', 'user:pass@[::1]:33', '/path', 'query', 'fragment', 'user', 'pass', '::1', 33) ), ( '//[::1]', ('', '[::1]', '', '', '', None, None, '::1', None) ), ( # Case-insensitivity 'ftp://[dead:BEEF:1234:5678:aA09::1]', ('ftp', '[dead:BEEF:1234:5678:aA09::1]', '', '', '', None, None, 'dead:BEEF:1234:5678:aA09::1', None) ), ( 'http://*****:*****@[0000:0000:0000:0000:0000:0000:0000:0001]:33/path', ('http', 'user:pass@[0000:0000:0000:0000:0000:0000:0000:0001]:33', '/path', '', '', 'user', 'pass', '0000:0000:0000:0000:0000:0000:0000:0001', 33) ), ( 'http://[::ffff:127.0.0.1]/', ('http', '[::ffff:127.0.0.1]', '/', '', '', None, None, '::ffff:127.0.0.1', None) ), ( 'http://*****:*****@[0000:0000:0000:0000:0000:ffff:127.0.0.1]:33/path?query#fragment', ('http', 'user:pass@[0000:0000:0000:0000:0000:ffff:127.0.0.1]:33', '/path', 'query', 'fragment', 'user', 'pass', '0000:0000:0000:0000:0000:ffff:127.0.0.1', 33) ), ] print for (url, expected) in tests: print 'Testing url', url result = Util.urlsplit(url) self.checkSplitUrl(result, expected)
def ExtractTgz(Archive): """Extract all from a .tgz, but take care with ../ files. Return file list on success, None on error.""" TarCmd = [PVars[("NoOverride", "WhichTar")], "-C", os.environ["HOME"], "-xzf", Archive] try: Util.RunTask(TarCmd) except OSError: return None Files = ReadTgz(Archive) for i in range(len(Files)): if Files[i][:10] == "%(Parent)s": NewFilename = Dict["Parent"] + Files[i][10:] SrcFn = os.path.join(os.environ["HOME"], Files[i]) DstFn = os.path.join(os.environ["HOME"], NewFilename) os.rename(SrcFn, DstFn) Files[i] = NewFilename try: os.rmdir(os.path.join(os.environ["HOME"], "%(Parent)s")) except OSError: pass return Files
def __init__(self, Form): "Reload an existing SID or create a new one." # Existing, valid looking session? if Form.has_key("SID") and \ re.compile("^[a-zA-Z0-9]{8}$").search(Form["SID"].value): # Provide SID to templates Template.Template.Dict["SID"] = self.SID = Form["SID"].value # Resurrect session try: self.__suid__("web") Filename = os.environ["TMDA_SESSION_PREFIX"] + self.SID if os.stat(Filename)[4] != os.geteuid(): CgiUtil.TermError("CGI_USER does not own session file.", "Something suspicious is going on here. This should not happen.", "open file", CgiUtil.FileDetails("Session data", Filename), "No recommendation.") try: F = open(Filename) self.Vars = pickle.load(F) F.close() except (IOError, EOFError): self.Vars = {} # Make sure the session has not been hijacked if os.environ["REMOTE_ADDR"] != self.Vars["IP"]: CgiUtil.TermError("User's IP address has changed.", "Your IP address has changed. This is not allowed.", "read session data", "%s->%s" % (self.Vars["IP"], os.environ["REMOTE_ADDR"]), '<a href="%s">Log back in</a>.' % os.environ["SCRIPT_NAME"]) # Are they logging out? if Form.has_key("cmd") and (Form["cmd"].value == "logout"): os.unlink(Filename) return # Touch session file to keep it from getting cleaned too soon os.utime(Filename, None) # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it if os.environ.has_key("TMDA_DOMAIN_CONFIG"): userinfo = self.Vars["User"].split('@', 1) user = userinfo[0] if len(userinfo) > 1: domain = userinfo[1] else: domain = '' os.environ["TMDARC"] = os.environ["TMDARC"].replace("/~/", "/%s/%s/" % (domain, user)) else: os.environ["TMDARC"] = os.environ["TMDARC"].replace("/~/", "/%s/" % self.Vars["User"]) # Load system defaults self.LoadSysDefaults() # Become the user self.BecomeUser() # Done! return # Failed to resurrect session, fall through to make new SID except (IOError, OSError): pass # New session SessionChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \ "0123456789" self.SID = "" for i in range(8): self.SID += SessionChars[self.Rands.randrange(len(SessionChars))] Template.Template.Dict["SID"] = self.SID self.Vars = {} # Are they logging in? if not Form.has_key("user"): Template.Template.Dict["ErrMsg"] = "No user name supplied." return if not Form.has_key("password"): Template.Template.Dict["ErrMsg"] = "No password supplied." return # Get IP, User, UID, & Home directory self.Vars["IP"] = os.environ["REMOTE_ADDR"] self.Vars["User"] = Form["user"].value.lower() self.__suid__("root") try: if os.environ.has_key("TMDA_VLOOKUP"): VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], self.Vars["User"]) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": self.Vars["User"]} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError("Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], "Recompile CGI.") Params = Sandbox["getuserparams"](List) self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = Params[0:3] if len(Params) > 3: self.Vars["NAME"] = Params[3] else: self.Vars["NAME"] = None else: self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = \ Util.getuserparams(self.Vars["User"]) self.Vars["NAME"] = pwd.getpwuid(self.Vars["UID"])[4] except KeyError, str: Template.Template.Dict["ErrMsg"] = \ "Username %s not found in system.\nstr=%s" % (self.Vars["User"], str) return
def Show(): "Handle installation." from TMDA import Util # Make a substitution dictionary if os.environ.has_key( "USER" ): user = os.environ["USER"] elif os.environ.has_key( "LOGNAME" ): user = os.environ["LOGNAME"] global Dict Dict = \ { "Base": os.path.abspath(os.environ["TMDA_BASE_DIR"]), "CryptKey": KeyGen(), "Domain": Util.gethostname(), "Home": os.environ["HOME"], "Name": repr(PVars["NAME"]), "Parent": "..", "RealHome": pwd.getpwuid(os.geteuid())[5], "UrlDomain": os.environ["SERVER_NAME"], "User": user, "VPop": PVars[("NoOverride", "VPop")], "VPopBin": PVars[("NoOverride", "VPopBin")] } Dict["ShortUrlDom"] = re.sub("^www\.", "", Dict["UrlDomain"], re.I) Dict["qUser"] = re.sub("\.", ":", Dict["User"]) Match = re.search(".*/([^\./]+\.[^/]+)/[^/]+/?$", Dict["Home"]) if Match: Dict["Domain"] = Match.group(1) # Load the display template if Form["cmd"].value == "conf-example": TemplateFN = "conf-example.html" elif Form["cmd"].value == "faq": TemplateFN = "faq.html" elif Form["cmd"].value == "install": if not Util.CanWrite(os.environ["HOME"]): CgiUtil.TermError("Can't write to home dir.", "No write permissions.", "installing", CgiUtil.FileDetails("home directory", os.environ["HOME"]), "Check file permissions in home directory.") Install() # Does not return. elif Form["cmd"].value == "restore": Restore() # Does not return. elif Form["cmd"].value == "uninstall": if PVars[("NoOverride", "MayInstall")][0].lower() == "n": CgiUtil.TermError("No permission.", "Uninstallation disabled by sysadmin.", "uninstall", "", "Contact system administrator.") if Form.has_key("subcmd"): try: ReleaseAndDelete(Form["release"].value) except Errors.QueueError: pass Uninstall() # Does not return. else: TemplateFN = "uninstall.html" elif Form["cmd"].value == "welcome": TemplateFN = "welcome.html" else: # Have they installed before? if Util.CanRead(os.path.join(os.environ["HOME"], PVars[("NoOverride", "UninstallBackupTGZ")]), os.geteuid(), os.getegid()): TemplateFN = "re-enroll.html" else: TemplateFN = "welcome.html" # Load template T = Template.Template(TemplateFN) # Javascript confirmation? if PVars.has_key(["General", "UseJSConfirm"]) and \ PVars[("General", "UseJSConfirm")] == "Yes": T["OnSubmit"] = 'onSubmit="return JSConfirm()"' else: T["OnSubmit"] = "" # Display template print T
def __init__(self, Form): "Reload an existing SID or create a new one." global Defaults # Existing, valid looking session? if Form.has_key("SID") and \ re.compile("^[a-zA-Z0-9]{8}$").search(Form["SID"].value): # Provide SID to templates Template.Template.Dict["SID"] = self.SID = Form["SID"].value # Resurrect session try: self.__suid__("web") Filename = os.environ["TMDA_SESSION_PREFIX"] + self.SID if os.stat(Filename)[4] != os.geteuid(): CgiUtil.TermError( "CGI_USER does not own session file.", "Something suspicious is going on here. This should not happen.", "open file", CgiUtil.FileDetails("Session data", Filename), "No recommendation.") try: F = open(Filename) self.Vars = pickle.load(F) F.close() except (IOError, EOFError): self.Vars = {} # Make sure the session has not been hijacked if os.environ["REMOTE_ADDR"] != self.Vars["IP"]: CgiUtil.TermError( "User's IP address has changed.", "Your IP address has changed. This is not allowed.", "read session data", "%s->%s" % (self.Vars["IP"], os.environ["REMOTE_ADDR"]), '<a href="%s">Log back in</a>.' % os.environ["SCRIPT_NAME"]) # Are they logging out? if Form.has_key("cmd") and (Form["cmd"].value == "logout"): os.unlink(Filename) return # Touch session file to keep it from getting cleaned too soon os.utime(Filename, None) # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it os.environ["TMDARC"] = os.environ["TMDARC"].replace( "/~/", "/%s/" % self.Vars["User"]) # Load system defaults self.LoadSysDefaults() # Become the user self.BecomeUser() # Done! return # Failed to resurrect session, fall through to make new SID except (IOError, OSError): pass # New session SessionChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" \ "0123456789" self.SID = "" for i in range(8): self.SID += SessionChars[self.Rands.randrange(len(SessionChars))] Template.Template.Dict["SID"] = self.SID self.Vars = {} # Are they logging in? if not Form.has_key("user"): Template.Template.Dict["ErrMsg"] = "No user name supplied." return if not Form.has_key("password"): Template.Template.Dict["ErrMsg"] = "No password supplied." return # Get IP, User, UID, & Home directory self.Vars["IP"] = os.environ["REMOTE_ADDR"] self.Vars["User"] = Form["user"].value.lower() self.__suid__("root") try: if os.environ.has_key("TMDA_VLOOKUP"): VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], self.Vars["User"]) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": self.Vars["User"]} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError( "Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], "Recompile CGI.") Params = Sandbox["getuserparams"](List) self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = Params[ 0:3] if len(Params) > 3: self.Vars["NAME"] = Params[3] else: self.Vars["NAME"] = None else: self.Vars["HOME"], self.Vars["UID"], self.Vars["GID"] = \ Util.getuserparams(self.Vars["User"]) self.Vars["NAME"] = pwd.getpwuid(self.Vars["UID"])[4] except KeyError, str: Template.Template.Dict["ErrMsg"] = \ "Username %s not found in system.\nstr=%s" % (self.Vars["User"], str) return
def Release(QueryString): """Release the message represented in the QueryString. QueryString is in one of two formats, real users MAY confirm with: <UID>.<confirm_cookie> Virtual users MUST confirm with: <UID>&<recipient_address>&<confirm_cookie> Where <UID> is the UID of the TMDA account, <recipient_address> is the untagged address of the original message recipient, and <confirm_cookie> is used to find and validate the pending email in question.""" # Prepare the traceback in case of uncaught exception MyCgiTb.ErrTemplate = "prog_err2.html" CgiUtil.ErrTemplate = "error2.html" try: UID, Recipient, Cookie = QueryString.split("&") UID = int(UID) OldStyle = 0 # Get base address from Recipient RecipUser, RecipDomain = Recipient.split("@") User = RecipUser.split('-')[0] + "@" + RecipDomain except (ValueError, KeyError): try: # Check for old-style format UID, Cookie = QueryString.split(".", 1) UID = int(UID) User = pwd.getpwuid(UID)[0] OldStyle = 1 except (ValueError, KeyError): CgiUtil.TermError("Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") try: # Get real user from UID Timestamp, PID, HMAC = Cookie.split(".") except ValueError: CgiUtil.TermError("Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") MsgID = "%s.%s" % (Timestamp, PID) # Check to make sure they're not trying to access anything other than email if not re.compile("^\d+\.\d+$").search(MsgID): CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") # Set up the user's home directory. try: os.seteuid(0) os.setegid(0) os.setuid(0) except OSError: pass try: if os.environ.has_key("TMDA_VLOOKUP") and not OldStyle: VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], User ) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": User} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError("Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") Home, UID, GID = Sandbox["getuserparams"](List)[0:3] else: Home, UID, GID = Util.getuserparams(pwd.getpwuid(UID)[0]) except KeyError: CgiUtil.TermError("No such user", "User %s not found" % User, "find user %s" % User, "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") if UID < 2: PasswordRecord = pwd.getpwnam(os.environ["TMDA_VUSER"]) UID = PasswordRecord[2] GID = PasswordRecord[3] if not int(UID): CgiUtil.TermError("TMDA_VUSER is UID 0.", "It is not safe to run " "tmda-cgi as root.", "set euid", "TMDA_VUSER = %s" % os.environ["TMDA_VUSER"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") # We now have the home directory and the User. Set this in the environment. os.environ["USER"] = User os.environ["LOGNAME"] = User os.environ["HOME"] = Home # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it if os.environ.has_key("TMDA_DOMAIN_CONFIG"): userinfo = User.split('@', 1) user = userinfo[0] if len(userinfo) > 1: domain = userinfo[1] else: domain = '' os.environ["TMDARC"] = os.environ["TMDARC"].replace("/~/", "/%s/%s/" % (domain, user)) else: os.environ["TMDARC"] = os.environ["TMDARC"].replace("/~/", "/%s/" % User) # Try to change users try: os.seteuid(0) os.setegid(0) os.setgid(int(GID)) os.setuid(int(UID)) except OSError: pass # Now that we know who we are, get our defaults try: from TMDA import Defaults except Errors.ConfigError: CgiUtil.TermError("Confirm Failed", "Old-style URL is not compatible with virtual users", "use incompatible URL", "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") from TMDA import Pending from TMDA import Cookie try: Defaults.CRYPT_KEY except AttributeError: CgiUtil.TermError("Could not read CRYPT_KEY.", "CRYPT_KEY can not be read by group %d." % os.getegid(), "read CRYPT_KEY", "ALLOW_MODE_640 = %d<br>%s" % (Defaults.ALLOW_MODE_640, CgiUtil.FileDetails("Cryptography key", Defaults.CRYPT_KEY_FILE)), """Any of the following solutions:<br> 1. Place <tt>%s</tt> in any of the groups that user %d belongs to.<br> 2. Do all three of the following:<br> • Place <tt>%s</tt> in group %d.<br> • Assign permissions 640 to <tt>%s</tt>.<br> • Set ALLOW_MODE_640 = 1 in your configuration file.<br> 3. Disable URL confirmation in your confirmation template.""" % (Defaults.CRYPT_KEY_FILE, os.geteuid(), Defaults.CRYPT_KEY_FILE, os.getegid(), Defaults.CRYPT_KEY_FILE)) # Validate the HMAC if Cookie.confirmationmac(Timestamp, PID, "accept") != HMAC: CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", "Recheck link or contact TMDA programmers.") # Read in e-mail try: MsgObj = Pending.Message(MsgID) except Errors.MessageError: CgiUtil.TermError("Message could not be fetched.", "Message has already been released or deleted.", "retrieve pending e-mail", "", "Inquire with recipient about e-mail.") T = Template.Template("released.html") # Fetch row Row = T["Row"] # Generate header rows for Header in Defaults.SUMMARY_HEADERS: T["Name"] = Header.capitalize() T["Value"] = CgiUtil.Escape(MsgObj.msgobj[Header]) Row.Add() # Can we add this address to a do-not-confirm-again list? if Defaults.CONFIRM_APPEND: ConfirmAddr = Util.confirm_append_address \ ( parseaddr(MsgObj.msgobj["x-primary-address"])[1], parseaddr(MsgObj.msgobj["return-path"])[1] ) if ConfirmAddr: Util.append_to_file(ConfirmAddr, Defaults.CONFIRM_APPEND) T["Address"] = ConfirmAddr else: T["Future"] else: T["Future"] print T # Make sure release does not write to PENDING_RELEASE_APPEND Defaults.PENDING_RELEASE_APPEND = None MsgObj.release()
def fetch_message(self, mailid, fullParse=False): fpath = os.path.join(Defaults.PENDING_DIR, mailid + '.msg') msg = Util.msg_from_file(file(fpath, 'r'),fullParse=fullParse) return msg
def fetch_message(self, mailid, fullParse=False): fpath = os.path.join(Defaults.PENDING_DIR, mailid + '.msg') msg = Util.msg_from_file(file(fpath, 'r'), fullParse=fullParse) return msg
"Password checker for tmda-cgi." import CgiUtil import crypt import os import random import socket import sys import Template from TMDA import Auth from TMDA import Errors from TMDA import Util DebugStringOutput = Util.StringOutput() authobj = Auth.Auth(debugObject=DebugStringOutput) authinit = 0 def InitProgramAuth(Program): """Initializes the authentication scheme with a checkpw-style program. (Implemented by Auth.py)""" # Populate TCPLOCALHOST for checkpw-stype programs that need it # (i.e. checkvpw) if os.environ.has_key("HTTP_HOST"): os.putenv('TCPLOCALHOST', os.environ["HTTP_HOST"]) global authinit
def Release(QueryString): """Release the message represented in the QueryString. QueryString is in one of two formats, real users MAY confirm with: <UID>.<confirm_cookie> Virtual users MUST confirm with: <UID>&<recipient_address>&<confirm_cookie> Where <UID> is the UID of the TMDA account, <recipient_address> is the untagged address of the original message recipient, and <confirm_cookie> is used to find and validate the pending email in question.""" # Prepare the traceback in case of uncaught exception MyCgiTb.ErrTemplate = "prog_err2.html" CgiUtil.ErrTemplate = "error2.html" try: UID, Recipient, Cookie = QueryString.split("&") UID = int(UID) OldStyle = 0 # Get base address from Recipient RecipUser, RecipDomain = Recipient.split("@") User = RecipUser.split('-')[0] + "@" + RecipDomain except (ValueError, KeyError): try: # Check for old-style format UID, Cookie = QueryString.split(".", 1) UID = int(UID) User = pwd.getpwuid(UID)[0] OldStyle = 1 except (ValueError, KeyError): CgiUtil.TermError( "Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") try: # Get real user from UID Timestamp, PID, HMAC = Cookie.split(".") except ValueError: CgiUtil.TermError( "Unable to parse query string.", "Program error / corrupted link.", "locate pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") MsgID = "%s.%s.msg" % (Timestamp, PID) # Check to make sure they're not trying to access anything other than email if not re.compile("^\d+\.\d+\.msg$").search(MsgID): CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", """Please check the link you followed and make sure that it is typed in exactly as it was sent to you.""") # Set up the user's home directory. try: os.seteuid(0) os.setegid(0) os.setuid(0) except OSError: pass try: if os.environ.has_key("TMDA_VLOOKUP") and not OldStyle: VLookup = \ CgiUtil.ParseString(os.environ["TMDA_VLOOKUP"], User ) List = Util.RunTask(VLookup[1:]) Sandbox = {"User": User} Filename = os.path.join("stubs", "%s.py" % VLookup[0]) try: execfile(Filename, Sandbox) except IOError: CgiUtil.TermError( "Can't load virtual user stub.", "Cannot execute %s" % Filename, "execute stub", "TMDA_VLOOKUP = %s" % os.environ["TMDA_VLOOKUP"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") Home, UID, GID = Sandbox["getuserparams"](List)[0:3] else: Home, UID, GID = Util.getuserparams(pwd.getpwuid(UID)[0]) except KeyError: CgiUtil.TermError( "No such user", "User %s not found" % User, "find user %s" % User, "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") if UID < 2: PasswordRecord = pwd.getpwnam(os.environ["TMDA_VUSER"]) UID = PasswordRecord[2] GID = PasswordRecord[3] if not int(UID): CgiUtil.TermError( "TMDA_VUSER is UID 0.", "It is not safe to run " "tmda-cgi as root.", "set euid", "TMDA_VUSER = %s" % os.environ["TMDA_VUSER"], """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") # We now have the home directory and the User. Set this in the environment. os.environ["USER"] = User os.environ["LOGNAME"] = User os.environ["HOME"] = Home # Is there a TMDARC variable? if os.environ.has_key("TMDARC"): # Yes, replace it os.environ["TMDARC"] = os.environ["TMDARC"].replace( "/~/", "/%s/" % User) # Try to change users try: os.seteuid(0) os.setegid(0) os.setgid(int(GID)) os.setuid(int(UID)) except OSError: pass # Now that we know who we are, get our defaults try: from TMDA import Defaults except Errors.ConfigError: CgiUtil.TermError( "Confirm Failed", "Old-style URL is not compatible with virtual users", "use incompatible URL", "", """Contact this message's sender by an alternate means and inform them of this error, or try confirming your message using an alternate method.""") from TMDA import Pending from TMDA import Cookie try: Defaults.CRYPT_KEY except AttributeError: CgiUtil.TermError( "Could not read CRYPT_KEY.", "CRYPT_KEY can not be read by group %d." % os.getegid(), "read CRYPT_KEY", "ALLOW_MODE_640 = %d<br>%s" % (Defaults.ALLOW_MODE_640, CgiUtil.FileDetails("Cryptography key", Defaults.CRYPT_KEY_FILE)), """Any of the following solutions:<br> 1. Place <tt>%s</tt> in any of the groups that user %d belongs to.<br> 2. Do all three of the following:<br> • Place <tt>%s</tt> in group %d.<br> • Assign permissions 640 to <tt>%s</tt>.<br> • Set ALLOW_MODE_640 = 1 in your configuration file.<br> 3. Disable URL confirmation in your confirmation template.""" % (Defaults.CRYPT_KEY_FILE, os.geteuid(), Defaults.CRYPT_KEY_FILE, os.getegid(), Defaults.CRYPT_KEY_FILE)) # Validate the HMAC if Cookie.confirmationmac(Timestamp, PID, "accept") != HMAC: CgiUtil.TermError("<tt>%s.%s.%s</tt> is not a valid message ID." % \ (Timestamp, PID, HMAC), "Program error / corrupted link.", "retrieve pending e-mail", "", "Recheck link or contact TMDA programmers.") # Read in e-mail try: MsgObj = Pending.Message(MsgID) except Errors.MessageError: CgiUtil.TermError("Message could not be fetched.", "Message has already been released or deleted.", "retrieve pending e-mail", "", "Inquire with recipient about e-mail.") T = Template.Template("released.html") # Fetch row Row = T["Row"] # Generate header rows for Header in Defaults.SUMMARY_HEADERS: T["Name"] = Header.capitalize() T["Value"] = CgiUtil.Escape(MsgObj.msgobj[Header]) Row.Add() # Can we add this address to a do-not-confirm-again list? if Defaults.CONFIRM_APPEND: ConfirmAddr = Util.confirm_append_address \ ( parseaddr(MsgObj.msgobj["x-primary-address"])[1], parseaddr(MsgObj.msgobj["return-path"])[1] ) if ConfirmAddr: Util.append_to_file(ConfirmAddr, Defaults.CONFIRM_APPEND) T["Address"] = ConfirmAddr else: T["Future"] else: T["Future"] print T # Make sure release does not write to PENDING_RELEASE_APPEND Defaults.PENDING_RELEASE_APPEND = None MsgObj.release()