Esempio n. 1
0
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>
&nbsp;&nbsp;&nbsp;&#8226; Place <tt>%s</tt> in group %d.<br>
&nbsp;&nbsp;&nbsp;&#8226; Assign permissions 640 to <tt>%s</tt>.<br>
&nbsp;&nbsp;&nbsp;&#8226; 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()
Esempio n. 2
0
    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
Esempio n. 3
0
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>
&nbsp;&nbsp;&nbsp;&#8226; Place <tt>%s</tt> in group %d.<br>
&nbsp;&nbsp;&nbsp;&#8226; Assign permissions 640 to <tt>%s</tt>.<br>
&nbsp;&nbsp;&nbsp;&#8226; 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()
Esempio n. 4
0
  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