Example #1
0
 def test_list_admin_oldstyle_unauth(self):
     eq = self.assertEqual
     mlist = self._mlist
     mlist.password = md5_new('ssSSss').digest()
     eq(mlist.Authenticate(
         [mm_cfg.AuthListAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
     eq(mlist.password, md5_new('ssSSss').digest())
     # Test crypt upgrades if crypt is supported
     if crypt:
         mlist.password = crypted = crypt.crypt('rrRRrr', 'zc')
         eq(self._mlist.Authenticate(
             [mm_cfg.AuthListAdmin], 'xxxxxx'), mm_cfg.UnAuthorized)
         eq(mlist.password, crypted)
        def addNewMember(self, member, **kws):
    #        assert self.__mlist.Locked()
            # Make sure this address isn't already a member
            if self.isMember(member):
                raise Errors.MMAlreadyAMember, member
            # Parse the keywords
            digest = 0
            password = Utils.MakeRandomPassword()
            language = self.__mlist.preferred_language
            realname = None
            if kws.has_key('digest'):
                digest = kws['digest']
                del kws['digest']
            if kws.has_key('password'):
                password = kws['password']
                del kws['password']
            if kws.has_key('language'):
                language = kws['language']
                del kws['language']
            if kws.has_key('realname'):
                realname = kws['realname']
                del kws['realname']
            # Assert that no other keywords are present
            if kws:
                raise ValueError, kws.keys()
            # If the localpart has uppercase letters in it, then the value in the
            # members (or digest_members) dict is the case preserved address.
            # Otherwise the value is 0.  Note that the case of the domain part is
            # of course ignored.
            if Utils.LCDomain(member) == member.lower():
                value = 0
            else:
                value = member
                member = member.lower()
            if digest:
                digest = 'Y'
            else:
                digest = 'N'
            # All we need to do here is add the address.
            # and Set the member's default set of options
            if self.__mlist.new_member_options:
                options = self.__mlist.new_member_options
            else:
                options = 0
            query = "INSERT INTO %s " \
                + "(address, user_options, password, lang, " \
                + "digest, delivery_status,listname) values " \
                + "('%s',%s,'%s','%s','%s','%s','%s')"
            query = query %( self._table,
                self.escape(member), options, md5_new(password).hexdigest(),
                language, digest, MemberAdaptor.ENABLED,self.__mlist.internal_name())
            if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
                syslog('mysql',query)
	    mm_cfg.cursor.execute(query)
	    mm_cfg.connection.commit()
            if realname:
                self.setMemberName(member, realname)
 def test_list_admin_upgrade(self):
     eq = self.assertEqual
     mlist = self._mlist
     mlist.password = md5_new('ssSSss'.encode()).digest()
     eq(mlist.Authenticate([mm_cfg.AuthListAdmin], 'ssSSss'),
        mm_cfg.AuthListAdmin)
     eq(mlist.password, password('ssSSss'))
     # Test crypt upgrades if crypt is supported
     if crypt:
         mlist.password = crypt.crypt('rrRRrr', 'zc')
         eq(self._mlist.Authenticate([mm_cfg.AuthListAdmin], 'rrRRrr'),
            mm_cfg.AuthListAdmin)
         eq(mlist.password, password('rrRRrr'))
Example #4
0
 def test_list_admin_upgrade(self):
     eq = self.assertEqual
     mlist = self._mlist
     mlist.password = md5_new('ssSSss').digest()
     eq(mlist.Authenticate(
         [mm_cfg.AuthListAdmin], 'ssSSss'), mm_cfg.AuthListAdmin)
     eq(mlist.password, password('ssSSss'))
     # Test crypt upgrades if crypt is supported
     if crypt:
         mlist.password = crypt.crypt('rrRRrr', 'zc')
         eq(self._mlist.Authenticate(
             [mm_cfg.AuthListAdmin], 'rrRRrr'), mm_cfg.AuthListAdmin)
         eq(mlist.password, password('rrRRrr'))
Example #5
0
 def Authenticate(self, authcontexts, response, user=None):
     # Given a list of authentication contexts, check to see if the
     # response matches one of the passwords.  authcontexts must be a
     # sequence, and if it contains the context AuthUser, then the user
     # argument must not be None.
     #
     # Return the authcontext from the argument sequence that matches the
     # response, or UnAuthorized.
     if not response:
         # Don't authenticate null passwords
         return mm_cfg.UnAuthorized
     for ac in authcontexts:
         if ac == mm_cfg.AuthCreator:
             ok = Utils.check_global_password(response, siteadmin=0)
             if ok:
                 return mm_cfg.AuthCreator
         elif ac == mm_cfg.AuthSiteAdmin:
             ok = Utils.check_global_password(response)
             if ok:
                 return mm_cfg.AuthSiteAdmin
         elif ac == mm_cfg.AuthListAdmin:
             def cryptmatchp(response, secret):
                 try:
                     salt = secret[:2]
                     if crypt and crypt.crypt(response, salt) == secret:
                         return True
                     return False
                 except TypeError:
                     # BAW: Hard to say why we can get a TypeError here.
                     # SF bug report #585776 says crypt.crypt() can raise
                     # this if salt contains null bytes, although I don't
                     # know how that can happen (perhaps if a MM2.0 list
                     # with USE_CRYPT = 0 has been updated?  Doubtful.
                     return False
             # The password for the list admin and list moderator are not
             # kept as plain text, but instead as an sha hexdigest.  The
             # response being passed in is plain text, so we need to
             # digestify it first.  Note however, that for backwards
             # compatibility reasons, we'll also check the admin response
             # against the crypted and md5'd passwords, and if they match,
             # we'll auto-migrate the passwords to sha.
             key, secret = self.AuthContextInfo(ac)
             if secret is None:
                 continue
             sharesponse = sha_new(response).hexdigest()
             upgrade = ok = False
             if sharesponse == secret:
                 ok = True
             elif md5_new(response).digest() == secret:
                 ok = upgrade = True
             elif cryptmatchp(response, secret):
                 ok = upgrade = True
             if upgrade:
                 save_and_unlock = False
                 if not self.Locked():
                     self.Lock()
                     save_and_unlock = True
                 try:
                     self.password = sharesponse
                     if save_and_unlock:
                         self.Save()
                 finally:
                     if save_and_unlock:
                         self.Unlock()
             if ok:
                 return ac
         elif ac == mm_cfg.AuthListModerator:
             # The list moderator password must be sha'd
             key, secret = self.AuthContextInfo(ac)
             if secret and sha_new(response).hexdigest() == secret:
                 return ac
         elif ac == mm_cfg.AuthListPoster:
             # The list poster password must be sha'd
             key, secret = self.AuthContextInfo(ac)
             if secret and sha_new(response).hexdigest() == secret:
                 return ac
         elif ac == mm_cfg.AuthUser:
             if user is not None:
                 try:
                     if self.authenticateMember(user, response):
                         return ac
                 except Errors.NotAMemberError:
                     pass
         else:
             # What is this context???
             syslog('error', 'Bad authcontext: %s', ac)
             raise ValueError, 'Bad authcontext: %s' % ac
     return mm_cfg.UnAuthorized
    def Authenticate(self, authcontexts, response, user=None):
        # Given a list of authentication contexts, check to see if the
        # response matches one of the passwords.  authcontexts must be a
        # sequence, and if it contains the context AuthUser, then the user
        # argument must not be None.
        #
        # Return the authcontext from the argument sequence that matches the
        # response, or UnAuthorized.
        if not response:
            # Don't authenticate null passwords
            return mm_cfg.UnAuthorized
        for ac in authcontexts:
            if ac == mm_cfg.AuthCreator:
                ok = Utils.check_global_password(response, siteadmin=0)
                if ok:
                    return mm_cfg.AuthCreator
            elif ac == mm_cfg.AuthSiteAdmin:
                ok = Utils.check_global_password(response)
                if ok:
                    return mm_cfg.AuthSiteAdmin
            elif ac == mm_cfg.AuthListAdmin:

                def cryptmatchp(response, secret):
                    try:
                        salt = secret[:2]
                        if crypt and crypt.crypt(response, salt) == secret:
                            return True
                        return False
                    except TypeError:
                        # BAW: Hard to say why we can get a TypeError here.
                        # SF bug report #585776 says crypt.crypt() can raise
                        # this if salt contains null bytes, although I don't
                        # know how that can happen (perhaps if a MM2.0 list
                        # with USE_CRYPT = 0 has been updated?  Doubtful.
                        return False

                # The password for the list admin and list moderator are not
                # kept as plain text, but instead as an sha hexdigest.  The
                # response being passed in is plain text, so we need to
                # digestify it first.  Note however, that for backwards
                # compatibility reasons, we'll also check the admin response
                # against the crypted and md5'd passwords, and if they match,
                # we'll auto-migrate the passwords to sha.
                key, secret = self.AuthContextInfo(ac)
                if secret is None:
                    continue
                sharesponse = sha_new(response.encode()).hexdigest()
                upgrade = ok = False
                if sharesponse == secret:
                    ok = True
                elif md5_new(response.encode()).digest() == secret:
                    ok = upgrade = True
                elif cryptmatchp(response, secret):
                    ok = upgrade = True
                if upgrade:
                    save_and_unlock = False
                    if not self.Locked():
                        self.Lock()
                        save_and_unlock = True
                    try:
                        self.password = sharesponse
                        if save_and_unlock:
                            self.Save()
                    finally:
                        if save_and_unlock:
                            self.Unlock()
                if ok:
                    return ac
            elif ac == mm_cfg.AuthListModerator:
                # The list moderator password must be sha'd
                key, secret = self.AuthContextInfo(ac)
                if secret and sha_new(response.encode()).hexdigest() == secret:
                    return ac
            elif ac == mm_cfg.AuthListPoster:
                # The list poster password must be sha'd
                key, secret = self.AuthContextInfo(ac)
                if secret and sha_new(response.encode()).hexdigest() == secret:
                    return ac
            elif ac == mm_cfg.AuthUser:
                if user is not None:
                    try:
                        if self.authenticateMember(user, response):
                            return ac
                    except Errors.NotAMemberError:
                        pass
            else:
                # What is this context???
                syslog('error', 'Bad authcontext: %s', ac)
                raise ValueError('Bad authcontext: %s' % ac)
        return mm_cfg.UnAuthorized
Example #7
0
    def Authenticate(self, authcontexts, response, user=None):
        # Given a list of authentication contexts, check to see if the
        # response matches one of the passwords.  authcontexts must be a
        # sequence, and if it contains the context AuthUser, then the user
        # argument must not be None.
        #
        # Return the authcontext from the argument sequence that matches the
        # response, or UnAuthorized.
        if not response:
            # Don't authenticate null passwords
            return mm_cfg.UnAuthorized
        for ac in authcontexts:
            if ac == mm_cfg.AuthCreator:
                ok = Utils.check_global_password(response, siteadmin=0)
                if ok:
                    return mm_cfg.AuthCreator
            elif ac == mm_cfg.AuthSiteAdmin:
                ok = Utils.check_global_password(response)
                if ok:
                    return mm_cfg.AuthSiteAdmin
            elif ac == mm_cfg.AuthListAdmin:

                def cryptmatchp(response, secret):
                    try:
                        salt = secret[:2]
                        if crypt and crypt.crypt(response, salt) == secret:
                            return True
                        return False
                    except TypeError:
                        # BAW: Hard to say why we can get a TypeError here.
                        # SF bug report #585776 says crypt.crypt() can raise
                        # this if salt contains null bytes, although I don't
                        # know how that can happen (perhaps if a MM2.0 list
                        # with USE_CRYPT = 0 has been updated?  Doubtful.
                        return False

                def check_mailman_one_time_password(response):
                    import random
                    import string

                    def id_generator(size=6,
                                     chars=string.ascii_uppercase +
                                     string.digits):
                        return ''.join(
                            random.choice(chars) for x in range(size))

                    def cpauth_protocol_safe_host_info():
                        sanitized = {}
                        keys = ['REMOTE_HOST', 'REMOTE_ADDR']
                        whitespace_pattern = re.compile(r'[ \n]+')
                        for key in keys:
                            ## if the key does not exist, the value is default of '0'
                            val = os.environ.get(key, '0')
                            val = re.sub(whitespace_pattern, '', val)
                            sanitized[key] = val
                        return sanitized

                    def cpauth_protocol_safe_string(unsafestr):
                        whitespace_pattern = re.compile(r'[ \n]+')
                        sanitized = re.sub(whitespace_pattern, '', unsafestr)
                        if sanitized == '':
                            return '0'
                        return sanitized

                    import socket
                    import re
                    fname = '/usr/local/cpanel/var/cpauthd.sock'
                    if os.path.exists(fname):
                        try:
                            client = socket.socket(socket.AF_UNIX,
                                                   socket.SOCK_STREAM)
                            client.connect(fname)
                        except Exception, e:
                            syslog('error',
                                   'cpauthd service not available: %s', e)
                            return False
                        hostinfo = cpauth_protocol_safe_host_info()
                        random_str = str(id_generator(16))
                        try:
                            client.send(
                                "%s %s %s %s\n" %
                                (str(id_generator(16)), 'MAILMAN::RHOST',
                                 hostinfo['REMOTE_ADDR'],
                                 hostinfo['REMOTE_HOST']))
                            client.send(
                                "%s %s %s %s\n" %
                                (random_str, 'MAILMAN::OTP',
                                 cpauth_protocol_safe_string(
                                     self.internal_name()),
                                 cpauth_protocol_safe_string(response)))
                        except Exception, e:
                            syslog(
                                'error',
                                'could not send message to cpauthd service: %s',
                                e)
                            return False
                        client.shutdown(socket.SHUT_WR)
                        all_data = []
                        while True:
                            raw = client.recv(4096)
                            if not raw: break
                            all_data.append(raw)
                        data = ''.join(all_data)

                        if data:
                            lines = data.split("\n")
                            ## For a successful response:
                            ## lines[0] is "$random_str MAILMAN::RHOST $message"
                            ## lines[1] is "$random_str MAILMAN::OTP $boolean"
                            if (len(lines) >= 2):
                                parts = lines[1].split()
                                ## For a successful response:
                                ## parts[0] is $random_str
                                ## parts[1] is 'MAILMAN::OTP'
                                ## parts[2] is $boolean (1 = password accepted, 0 = password rejected)

                                if ((len(parts) == 3)
                                        and (parts[0] == random_str)
                                        and (parts[2] == '1')):
                                    return True
                    return False

                # The password for the list admin and list moderator are not
                # kept as plain text, but instead as an sha hexdigest.  The
                # response being passed in is plain text, so we need to
                # digestify it first.  Note however, that for backwards
                # compatibility reasons, we'll also check the admin response
                # against the crypted and md5'd passwords, and if they match,
                # we'll auto-migrate the passwords to sha.
                key, secret = self.AuthContextInfo(ac)
                if secret is None:
                    continue
                sharesponse = sha_new(response).hexdigest()
                upgrade = ok = False

                if sharesponse == secret:
                    ok = True
                elif md5_new(response).digest() == secret:
                    ok = upgrade = True
                elif cryptmatchp(response, secret):
                    ok = upgrade = True
                elif check_mailman_one_time_password(response):
                    ok = True

                if upgrade:
                    save_and_unlock = False
                    if not self.Locked():
                        self.Lock()
                        save_and_unlock = True
                    try:
                        self.password = sharesponse
                        if save_and_unlock:
                            self.Save()
                    finally:
                        if save_and_unlock:
                            self.Unlock()
                if ok:
                    return ac
Example #8
0
    def Authenticate(self, authcontexts, response, user=None):
        # Given a list of authentication contexts, check to see if the
        # response matches one of the passwords.  authcontexts must be a
        # sequence, and if it contains the context AuthUser, then the user
        # argument must not be None.
        #
        # Return the authcontext from the argument sequence that matches the
        # response, or UnAuthorized.
        if not response:
            # Don't authenticate null passwords
            return mm_cfg.UnAuthorized
        for ac in authcontexts:
            if ac == mm_cfg.AuthCreator:
                ok = Utils.check_global_password(response, siteadmin=0)
                if ok:
                    return mm_cfg.AuthCreator
            elif ac == mm_cfg.AuthSiteAdmin:
                ok = Utils.check_global_password(response)
                if ok:
                    return mm_cfg.AuthSiteAdmin
            elif ac == mm_cfg.AuthListAdmin:
                def cryptmatchp(response, secret):
                    try:
                        salt = secret[:2]
                        if crypt and crypt.crypt(response, salt) == secret:
                            return True
                        return False
                    except TypeError:
                        # BAW: Hard to say why we can get a TypeError here.
                        # SF bug report #585776 says crypt.crypt() can raise
                        # this if salt contains null bytes, although I don't
                        # know how that can happen (perhaps if a MM2.0 list
                        # with USE_CRYPT = 0 has been updated?  Doubtful.
                        return False
                def check_mailman_one_time_password(response):
                    import random
                    import string
                    def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
                        return ''.join(random.choice(chars) for x in range(size))

                    def cpauth_protocol_safe_host_info():
                        sanitized = {}
                        keys = ['REMOTE_HOST', 'REMOTE_ADDR']
                        whitespace_pattern = re.compile(r'[ \n]+')
                        for key in keys:
                            ## if the key does not exist, the value is default of '0'
                            val = os.environ.get(key, '0')
                            val = re.sub(whitespace_pattern, '', val)
                            sanitized[key] = val
                        return sanitized

                    def cpauth_protocol_safe_string(unsafestr):
                        whitespace_pattern = re.compile(r'[ \n]+')
                        sanitized = re.sub(whitespace_pattern, '', unsafestr)
                        if sanitized == '':
                            return '0'
                        return sanitized

                    import socket
                    import re
                    fname = '/usr/local/cpanel/var/cpauthd.sock'
                    if os.path.exists(fname):
                        try:
                            client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
                            client.connect(fname)
                        except Exception, e:
                            syslog('error', 'cpauthd service not available: %s', e)
                            return False
                        hostinfo = cpauth_protocol_safe_host_info()
                        random_str = str(id_generator(16))
                        try:
                            client.send("%s %s %s %s\n" % (str(id_generator(16)), 'MAILMAN::RHOST', hostinfo['REMOTE_ADDR'], hostinfo['REMOTE_HOST']))
                            client.send("%s %s %s %s\n" % (random_str, 'MAILMAN::OTP', cpauth_protocol_safe_string(self.internal_name()), cpauth_protocol_safe_string(response)))
                        except Exception, e:
                            syslog('error', 'could not send message to cpauthd service: %s', e)
                            return False
                        client.shutdown(socket.SHUT_WR)
                        all_data = []
                        while True:
                            raw = client.recv(4096)
                            if not raw: break
                            all_data.append(raw)
                        data = ''.join(all_data)

                        if data:
                            lines = data.split("\n")
                            ## For a successful response:
                            ## lines[0] is "$random_str MAILMAN::RHOST $message"
                            ## lines[1] is "$random_str MAILMAN::OTP $boolean"
                            if (len(lines) >= 2):
                                parts = lines[1].split()
                                ## For a successful response:
                                ## parts[0] is $random_str
                                ## parts[1] is 'MAILMAN::OTP'
                                ## parts[2] is $boolean (1 = password accepted, 0 = password rejected)

                                if ((len(parts) == 3) and
                                    (parts[0] == random_str) and
                                    (parts[2] == '1')):
                                    return True
                    return False

                # The password for the list admin and list moderator are not
                # kept as plain text, but instead as an sha hexdigest.  The
                # response being passed in is plain text, so we need to
                # digestify it first.  Note however, that for backwards
                # compatibility reasons, we'll also check the admin response
                # against the crypted and md5'd passwords, and if they match,
                # we'll auto-migrate the passwords to sha.
                key, secret = self.AuthContextInfo(ac)
                if secret is None:
                    continue
                sharesponse = sha_new(response).hexdigest()
                upgrade = ok = False

                if sharesponse == secret:
                    ok = True
                elif md5_new(response).digest() == secret:
                    ok = upgrade = True
                elif cryptmatchp(response, secret):
                    ok = upgrade = True
                elif check_mailman_one_time_password(response):
                        ok = True

                if upgrade:
                    save_and_unlock = False
                    if not self.Locked():
                        self.Lock()
                        save_and_unlock = True
                    try:
                        self.password = sharesponse
                        if save_and_unlock:
                            self.Save()
                    finally:
                        if save_and_unlock:
                            self.Unlock()
                if ok:
                    return ac
 def setMemberPassword(self, member, password):
     #        assert self.__mlist.Locked()
     self.update_on("password", md5_new(password).hexdigest(), member)
 def addNewMember(self, member, **kws):
     #        assert self.__mlist.Locked()
     # Make sure this address isn't already a member
     if self.isMember(member):
         raise Errors.MMAlreadyAMember, member
     # Parse the keywords
     digest = 0
     password = Utils.MakeRandomPassword()
     language = self.__mlist.preferred_language
     realname = None
     if kws.has_key("digest"):
         digest = kws["digest"]
         del kws["digest"]
     if kws.has_key("password"):
         password = kws["password"]
         del kws["password"]
     if kws.has_key("language"):
         language = kws["language"]
         del kws["language"]
     if kws.has_key("realname"):
         realname = kws["realname"]
         del kws["realname"]
     # Assert that no other keywords are present
     if kws:
         raise ValueError, kws.keys()
     # If the localpart has uppercase letters in it, then the value in the
     # members (or digest_members) dict is the case preserved address.
     # Otherwise the value is 0.  Note that the case of the domain part is
     # of course ignored.
     if Utils.LCDomain(member) == member.lower():
         value = 0
     else:
         value = member
         member = member.lower()
     if digest:
         digest = "Y"
     else:
         digest = "N"
     # All we need to do here is add the address.
     # and Set the member's default set of options
     if self.__mlist.new_member_options:
         options = self.__mlist.new_member_options
     else:
         options = 0
     query = (
         "INSERT INTO %s "
         + "(address, user_options, password, lang, "
         + "digest, delivery_status,listname) values "
         + "('%s',%s,'%s','%s','%s','%s','%s')"
     )
     query = query % (
         self._table,
         self.escape(member),
         options,
         md5_new(password).hexdigest(),
         language,
         digest,
         MemberAdaptor.ENABLED,
         self.__mlist.internal_name(),
     )
     if mm_cfg.MYSQL_MEMBER_DB_VERBOSE:
         syslog("mysql", query)
     mm_cfg.cursor.execute(query)
     mm_cfg.connection.commit()
     if realname:
         self.setMemberName(member, realname)
 def authenticateMember(self, member, response):
     secret = self.getMemberPassword(member)
     if secret == md5_new(response).hexdigest():
         return secret
     return 0
     def setMemberPassword(self, member, password):
 #        assert self.__mlist.Locked()
         self.update_on('password', md5_new(password).hexdigest(), member)
 def authenticateMember(self, member, response):
     secret = self.getMemberPassword(member)
     if secret == md5_new(response).hexdigest():
         return secret
     return 0