# Copyright (C) 1998 by the Free Software Foundation, Inc. # # This program 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 program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Handle passwords and sanitize approved messages.""" import os import string import time import types import Crypt import Errors import Utils import mm_cfg # TBD: is this the best location for the site password? SITE_PW_FILE = os.path.join(mm_cfg.DATA_DIR, 'adm.pw') class SecurityManager: def SetSiteAdminPassword(self, pw): old = os.umask(0022) f = open(SITE_PW_FILE, "w+") f.write(Crypt.crypt(pw, Utils.GetRandomSeed())) f.close() os.umask(old) def CheckSiteAdminPassword(self, str): try: f = open(SITE_PW_FILE, "r") pw = f.read() f.close() return Crypt.crypt(str, pw) == pw # There probably is no site admin password if there was an exception except: return 0 def InitVars(self, crypted_password): # Configurable, however, we don't pass this back in GetConfigInfo # because it's a special case as it requires confirmation to change. self.password = crypted_password # Non configurable self.passwords = {} def ValidAdminPassword(self, pw): if self.CheckSiteAdminPassword(pw): return 1 return ((type(pw) == types.StringType) and (Crypt.crypt(pw, self.password) == self.password)) def ConfirmAdminPassword(self, pw): if(not self.ValidAdminPassword(pw)): raise Errors.MMBadPasswordError return 1 def MakeCookie(self): client_ip = os.environ.get('REMOTE_ADDR') or '0.0.0.0' issued = int(time.time()) expires = issued + mm_cfg.ADMIN_COOKIE_LIFE secret = self.password mac = hash(secret + client_ip + `issued` + `expires`) return [client_ip, issued, expires, mac] def CheckCookie(self, cookie): if type(cookie) <> type([]): return 0 if len(cookie) <> 4: return 0 client_ip = os.environ.get('REMOTE_ADDR') or '0.0.0.0' [for_ip, issued, expires, received_mac] = cookie if for_ip <> client_ip: return 0 now = time.time() if not issued < now < expires: return 0 secret = self.password mac = hash(secret + client_ip + `issued` + `expires`) if mac <> received_mac: return 0 return 1 def ConfirmUserPassword(self, user, pw): """True if password is valid for site, list admin, or specific user.""" if self.ValidAdminPassword(pw): return 1 addr = self.FindUser(user) if addr is None: raise Errors.MMNotAMemberError storedpw = self.passwords.get(addr) if storedpw is None: raise Errors.MMBadUserError if storedpw <> pw: raise Errors.MMBadPasswordError return 1 def ChangeUserPassword(self, user, newpw, confirm): self.IsListInitialized() addr = self.FindUser(user) if not addr: raise Errors.MMNotAMemberError if newpw <> confirm: raise Errors.MMPasswordsMustMatch self.passwords[addr] = newpw self.Save() def ExtractApproval(self, msg): """True if message has valid administrator approval. Approval line is always stripped from message as a side effect.""" p = msg.getheader('approved') if p == None: return 0 del msg['approved'] # Mustn't deliver this line!! return self.ValidAdminPassword(p)