Exemple #1
0
 def test_preauth_01(self):
     """ preauth that can't work returns None """
     self.main_object.duo_client_args['host'] = 'badhost'
     tmplibrary = DuoAPIAuth(**self.main_object.duo_client_args)
     tmplibrary.load_user_to_verify(self.user_data['push'])
     res = tmplibrary._preauth()
     self.assertIsNone(res, '_preauth on a damaged server returns None')
 def test_fail_open_02(self):
     """ _fail_open performs as expected on a False """
     tmplibrary = DuoAPIAuth(fail_open=False,
                             **self.main_object.duo_client_args)
     res = tmplibrary._fail_open
     self.assertIsInstance(res, bool, '_fail_open must return a bool')
     self.assertFalse(res, '_fail_open must return an expected False')
Exemple #3
0
 def test_preflight_01(self):
     """ preflight fails for bad host """
     self.main_object.duo_client_args['host'] = 'badhost'
     tmplibrary = DuoAPIAuth(**self.main_object.duo_client_args)
     # No user data needs to be loaded to be preflighted
     res = tmplibrary._preflight()
     self.assertFalse(res, '_preflight must be False for a bad host')
Exemple #4
0
 def test_preflight_02(self):
     """ preflight fails for bad skey """
     if not self.deep_test_rawauth:  # pragma: no cover
         return self.skipTest('because of .deep_testing preference')
     self.main_object.duo_client_args['skey'] = 'wrong-passcode'
     tmplibrary = DuoAPIAuth(**self.main_object.duo_client_args)
     # No user data needs to be loaded to be preflighted
     res = tmplibrary._preflight()
     self.assertFalse(res, '_preflight must be False for a bad skey')
Exemple #5
0
 def test_log_good(self):
     """ Test sending a log message - all good """
     # There is no raise or return.  We're just poking at
     # the function and making sure it doesn't raise.
     tmplibrary = DuoAPIAuth(log_func=self.main_object.log,
                             **self.main_object.duo_client_args)
     tmplibrary.log(
         summary='TEST message',
         severity='DEBUG',
     )
Exemple #6
0
 def test_main_01(self):
     """ main_auth with no connection goes fail_open """
     self.main_object.duo_client_args['host'] = 'badhost'
     for state in (True, False):
         tmplibrary = DuoAPIAuth(fail_open=state,
                                 **self.main_object.duo_client_args)
         tmplibrary.load_user_to_verify(self.user_data['auto'])
         res = tmplibrary.main_auth()
         self.assertEqual(
             res, state, 'main_auth with no connection must return '
             'the fail_open state')
Exemple #7
0
 def setUp(self):
     """ Preparing test rig """
     # To get a decent test, we're going to need items from the config
     # file in order to test.
     with mock.patch.object(DuoOpenVPN,
                            'CONFIG_FILE_LOCATIONS',
                            new=[
                                'duo_openvpn.conf',
                                '/usr/local/etc/duo_openvpn.conf',
                                '/etc/openvpn/duo_openvpn.conf',
                                '/etc/duo_openvpn.conf'
                            ]):
         self.main_object = DuoOpenVPN()
     try:
         self.normal_user = self.main_object.configfile.get(
             'testing', 'normal_user')
     except (configparser.NoOptionError,
             configparser.NoSectionError):  # pragma: no cover
         return self.skipTest('No testing/normal_user defined')
     try:
         self.deep_test_rawauth = self.main_object.configfile.getboolean(
             'testing', 'deep_testing_rawauth')
     except (configparser.NoOptionError,
             configparser.NoSectionError):  # pragma: no cover
         self.deep_test_rawauth = False
     try:
         self.deep_test_mfa = self.main_object.configfile.getboolean(
             'testing', 'deep_testing_mfa')
     except (configparser.NoOptionError,
             configparser.NoSectionError):  # pragma: no cover
         self.deep_test_mfa = False
     try:
         self.deep_test_main = self.main_object.configfile.getboolean(
             'testing', 'deep_testing_mainauth')
     except (configparser.NoOptionError,
             configparser.NoSectionError):  # pragma: no cover
         self.deep_test_main = False
     #
     os.environ['untrusted_ip'] = 'testing-ip-Unknown-is-OK'
     os.environ['common_name'] = self.normal_user
     user_creds = dict()
     for varname in OpenVPNCredentials.DUO_RESERVED_WORDS:
         os.environ['password'] = varname
         res = OpenVPNCredentials()
         res.load_variables_from_environment()
         user_creds[varname] = res
     self.user_data = user_creds
     #
     self.library = DuoAPIAuth(**self.main_object.duo_client_args)
    def setUp(self):
        """ Preparing test rig """
        # To get a decent test, we're going to need items from the config
        # file in order to test.
        config = configparser.ConfigParser()
        config.add_section('duo-credentials')
        config.set('duo-credentials', 'IKEY', 'DI9QQ99X9MK4H99RJ9FF')
        config.set('duo-credentials', 'SKEY',
                   '2md9rw5xeyxt8c648dgkmdrg3zpvnhj5b596mgku')
        config.set('duo-credentials', 'HOST', 'api-9f134ff9.duosekurity.com')
        with open(self.testing_conffile, 'w') as configfile:
            config.write(configfile)

        with mock.patch.object(DuoOpenVPN,
                               'CONFIG_FILE_LOCATIONS',
                               new=[
                                   'duo_openvpn.conf',
                                   '/usr/local/etc/duo_openvpn.conf',
                                   '/etc/openvpn/duo_openvpn.conf',
                                   '/etc/duo_openvpn.conf',
                                   self.testing_conffile
                               ]):
            self.main_object = DuoOpenVPN()
        os.environ['untrusted_ip'] = 'testing-ip-Unknown-is-OK'
        os.environ['common_name'] = 'bob'
        user_creds = dict()
        for varname in OpenVPNCredentials.DUO_RESERVED_WORDS:
            os.environ['password'] = varname
            res = OpenVPNCredentials()
            res.load_variables_from_environment()
            user_creds[varname] = res
        self.user_data = user_creds
        #
        with mock.patch.object(DuoOpenVPN,
                               'CONFIG_FILE_LOCATIONS',
                               new=[
                                   'duo_openvpn.conf',
                                   '/usr/local/etc/duo_openvpn.conf',
                                   '/etc/openvpn/duo_openvpn.conf',
                                   '/etc/duo_openvpn.conf',
                                   self.testing_conffile
                               ]):
            self.library = DuoAPIAuth(**self.main_object.duo_client_args)
Exemple #9
0
    def main_authentication(self):  # pylint: disable=too-many-return-statements
        """
            The main authentication function.
            Return True if the user successfully authenticated.
            Return False if they didn't.
            It's expected that we'll handle errors within this and not raise.
        """
        # Set up a user object based on the environmental variables.
        user_data = OpenVPNCredentials()
        try:
            user_data.load_variables_from_environment()
        except ValueError:  # pragma: no cover
            # This happens when we have a total mismatch, like, openvpn
            # didn't send valid environmental variables to the plugin,
            # or someone got here without a certificate(!?!)
            self.log(
                summary='FAIL: VPN environmental load, software bug',
                severity='ERROR',
                details={
                    'error': 'true',
                    'success': 'false',
                },
            )
            traceback.print_exc()
            return False

        username = user_data.username
        client_ipaddr = user_data.client_ipaddr
        password = user_data.password

        try:
            iam_searcher = iamvpnlibrary.IAMVPNLibrary()
        except RuntimeError:  # pragma: no cover
            # Couldn't connect to the IAM service:
            self.log(
                summary=('FAIL: Unable to connect to IAM'),
                severity='INFO',
                details={
                    'username': username,
                    'sourceipaddress': client_ipaddr,
                    'success': 'false',
                },
            )
            return False

        if not iam_searcher.user_allowed_to_vpn(username):
            # Here we have a user not allowed to VPN in at all.
            # This is some form of "their account is disabled" and/or
            # they aren't in the approved ACL list.
            self.log(
                summary=('FAIL: VPN user "{}" administratively '
                         'denied'.format(username)),
                severity='INFO',
                details={
                    'username': username,
                    'sourceipaddress': client_ipaddr,
                    'success': 'false',
                },
            )
            return False

        if not iam_searcher.does_user_require_vpn_mfa(username):
            # We've hit a special snowflake user who does not require MFA.
            if password is None:
                # If a password of 'None' makes it through, auth will fail.
                # 'None' can happen if someone tries using a Duo keyword as a
                # password.  In the unbelievably small chance that password
                # is correct, it's an unacceptably bad password.  Punt them.
                # I mean, really, you've got a non-MFA user, who then has no
                # password / a horrible one.  That's zero factors.
                allow_1fa = False
            else:
                allow_1fa = iam_searcher.non_mfa_vpn_authentication(
                    username, password)

            if allow_1fa:
                summary = 'SUCCESS: non-MFA user "{}" accepted by password'
            else:
                summary = 'FAIL: non-MFA user "{}" denied by password'

            self.log(
                summary=summary.format(username),
                severity='INFO',
                details={
                    'username': username,
                    'sourceipaddress': client_ipaddr,
                    'success': str(allow_1fa).lower(),
                },
            )
            return allow_1fa

        # We don't establish a Duo object until we need it.
        duo = DuoAPIAuth(fail_open=self.failopen,
                         log_func=self.log,
                         **self.duo_client_args)

        if not duo.load_user_to_verify(
                user_config=user_data):  # pragma: no cover
            # The load_user_to_verify method is benign, so we should
            # never fail to load, but if we do it'll be hard to find,
            # so this 'if' block captures an edge case we've never seen,
            # just in case.
            self.log(
                summary='FAIL: VPN user failed MFA pre-load',
                severity='INFO',
                details={
                    'username': username,
                    'sourceipaddress': client_ipaddr,
                    'success': 'false',
                },
            )
            return False

        try:
            return duo.main_auth()
        except Exception:  # pragma: no cover  pylint: disable=broad-except
            # Deliberately catch all errors until we can find what can
            # go wrong.
            self.log(
                summary='FAIL: VPN User auth failed, software bug',
                severity='ERROR',
                details={
                    'username': username,
                    'error': 'true',
                    'sourceipaddress': client_ipaddr,
                    'success': 'false',
                },
            )
            traceback.print_exc()
            return False

        # We should never get here.
        self.log(
            summary='FAIL: VPN User fell through all possible '
            'Duo checks, software bug',
            severity='ERROR',
            details={
                'username': username,
                'error': 'true',
                'success': 'false',
            },
        )  # pragma: no cover
        return False  # pragma: no cover