예제 #1
0
    def checkOtp(self, anOtpVal, counter, window, options=None):
        '''
        Here we contact the Yubico Cloud server to validate the OtpVal.
        '''

        pparams = {}

        yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL)

        if yubico_url == DEPRECATED_YUBICO_URL:

            log.warning("Usage of YUBICO_URL %r is deprecated!! ",
                        DEPRECATED_YUBICO_URL)

            # setups with old YUBICO URLS will be broken on yubico side
            # after 3th of February 2019
            third_feb_2019 = datetime.datetime(year=2019, month=2, day=3)

            if datetime.datetime.now() >= third_feb_2019:
                raise Exception("Usage of YUBICO_URL %r is deprecated!! " %
                                DEPRECATED_YUBICO_URL)

        apiId = getFromConfig("yubico.id") or DEFAULT_CLIENT_ID
        apiKey = getFromConfig("yubico.secret") or DEFAULT_API_KEY

        if apiKey == DEFAULT_API_KEY or apiId == DEFAULT_CLIENT_ID:
            log.warning("Usage of default apiKey or apiId not recomended!!")
            log.warning("Please register your own apiKey and apiId at "
                                                        "yubico website !!")
            log.warning("Configure of apiKey and apiId at the "
                                             "linotp manage config menue!!")

        tokenid = self.getFromTokenInfo("yubico.tokenid")
        if len(anOtpVal) < 12:
            log.warning("[checkOtp] The otpval is too short: %r" % anOtpVal)
            return -1

        if anOtpVal[:12] != tokenid:
            log.warning("[checkOtp] the tokenid in the OTP value does "
                        "not match the assigned token!")
            return -1

        timeout = getFromConfig("yubico.timeout")
        if timeout:
            pparams['timeout']= parse_timeout(timeout)

        nonce = binascii.hexlify(os.urandom(20))

        p = urllib.urlencode({
            'nonce': nonce,
            'otp':anOtpVal,
            'id':apiId
        })

        yubico_urls = [x.strip() for x in yubico_url.split(',')]

        res_scheduler = ResourceScheduler(
                        tries=2, uri_list=yubico_urls)


        for uri in res_scheduler.next():

            try:
                URL = "%s?%s" % (uri, p)

                response = requests.get(URL, **pparams)

                if response.ok:
                    return self._check_yubico_response(
                                nonce, apiKey, response.content)

                log.info("Failed to validate yubico request %r", response)

                return -1


            except (Timeout, ConnectTimeout, ReadTimeout,
                    ConnectionError, TooManyRedirects) as exx:

                log.exception('resource %r not available!', uri)

                # mark the url as blocked

                res_scheduler.block(uri, delay=30)

                log.error("[checkOtp] Error getting response from "
                          "Yubico Cloud Server (%r)" % uri)


            except Exception as exx:

                log.exception('unknown exception for uri %r!', uri)

                raise exx

        # ------------------------------------------------------------------ --

        # if we reach here, no resource has been availabel

        log.error('non of the resources %r available!', yubico_urls)

        raise AllResourcesUnavailable('non of the resources %r available!' %
                                      yubico_urls)
예제 #2
0
    def checkOtp(self, anOtpVal, counter, window, options=None):
        '''
        Here we contact the Yubico Cloud server to validate the OtpVal.
        '''

        pparams = {}

        yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL)

        if yubico_url == DEPRECATED_YUBICO_URL:

            log.warning("Usage of YUBICO_URL %r is deprecated!! ",
                        DEPRECATED_YUBICO_URL)

            # setups with old YUBICO URLS will be broken on yubico side
            # after 3th of February 2019
            third_feb_2019 = datetime.datetime(year=2019, month=2, day=3)

            if datetime.datetime.now() >= third_feb_2019:
                raise Exception("Usage of YUBICO_URL %r is deprecated!! " %
                                DEPRECATED_YUBICO_URL)

        apiId = getFromConfig("yubico.id") or DEFAULT_CLIENT_ID
        apiKey = getFromConfig("yubico.secret") or DEFAULT_API_KEY

        if apiKey == DEFAULT_API_KEY or apiId == DEFAULT_CLIENT_ID:
            log.warning("Usage of default apiKey or apiId not recomended!!")
            log.warning("Please register your own apiKey and apiId at "
                        "yubico website !!")
            log.warning("Configure of apiKey and apiId at the "
                        "linotp manage config menue!!")

        tokenid = self.getFromTokenInfo("yubico.tokenid")
        if len(anOtpVal) < 12:
            log.warning("[checkOtp] The otpval is too short: %r" % anOtpVal)
            return -1

        if anOtpVal[:12] != tokenid:
            log.warning("[checkOtp] the tokenid in the OTP value does "
                        "not match the assigned token!")
            return -1

        timeout = getFromConfig("yubico.timeout")
        if timeout:
            pparams['timeout'] = parse_timeout(timeout)

        nonce = binascii.hexlify(os.urandom(20)).decode()

        p = urllib.parse.urlencode({
            'nonce': nonce,
            'otp': anOtpVal,
            'id': apiId
        })

        yubico_urls = [x.strip() for x in yubico_url.split(',')]

        res_scheduler = ResourceScheduler(tries=2, uri_list=yubico_urls)

        for uri in next(res_scheduler):

            try:
                URL = "%s?%s" % (uri, p)

                response = requests.get(URL, **pparams)

                if response.ok:
                    return self._check_yubico_response(
                        nonce, apiKey, response.content.decode())

                log.info("Failed to validate yubico request %r", response)

                return -1

            except (Timeout, ConnectTimeout, ReadTimeout, ConnectionError,
                    TooManyRedirects) as exx:

                log.exception('resource %r not available!', uri)

                # mark the url as blocked

                res_scheduler.block(uri, delay=30)

                log.error("[checkOtp] Error getting response from "
                          "Yubico Cloud Server (%r)" % uri)

            except Exception as exx:

                log.exception('unknown exception for uri %r!', uri)

                raise exx

        # ------------------------------------------------------------------ --

        # if we reach here, no resource has been availabel

        log.error('non of the resources %r available!', yubico_urls)

        raise AllResourcesUnavailable('non of the resources %r available!' %
                                      yubico_urls)
예제 #3
0
    def httplib_request(self, url, parameter,
                        username=None, password=None, method='GET'):
        """
        build the urllib request and check the response for success or fail

        :param url: target url
        :param parameter: additonal parameter to append to the url request
        :param username: basic authentication with username (optional)
        :param password: basic authentication with password (optional)
        :param method: run an GET or POST request

        :return: False or True
        """

        #httplib2.debuglevel = 4

        ret = False
        http_params = {}
        headers = {}

        log.debug("Do the request to %s with %s" % (url, parameter))

        if 'PROXY' in self.config:
            proxy_url = None

            proxy = self.config['PROXY']

            if isinstance(proxy, dict):
                if url.startswith('https') and 'https' in proxy:
                    proxy_url = proxy['https']
                elif url.startswith('http') and 'http' in proxy:
                    proxy_url = proxy['http']

            elif isinstance(proxy, (str, unicode)):
                proxy_url = proxy

            if proxy_url:
                http_params['proxy_info'] = http2lib_get_proxy_info(proxy_url)

        if 'timeout' in self.config:

            parsed_timeout = parse_timeout(self.config['timeout'])

            if isinstance(parsed_timeout, tuple):
                timeout = int(parsed_timeout[0])
            else:
                timeout = int(parsed_timeout)

            http_params['timeout'] = timeout

        http_params["disable_ssl_certificate_validation"] = True

        try:
            # test if httplib is compiled with ssl - will raise a TypeError
            # TypeError: __init__() got an unexpected keyword argument
            # 'disable_ssl_certificate_validation'
            http = httplib2.Http(**http_params)

        except TypeError as exx:
            log.warning("httplib2 'disable_ssl_certificate_validation' "
                        "attribute error: %r" % exx)
            # so we remove the ssl param from the arguments
            del http_params["disable_ssl_certificate_validation"]
            # and retry
            http = httplib2.Http(**http_params)

        # for backward compatibility we have to support url with the format
        # http://user:pass@server:port/path
        # so we extract the url_user and the url_pass and use them if
        # not overruled by the explicit parameters username and password
        url_user = None
        url_pass = None
        parsed_url = urlparse(url)

        if "@" in parsed_url[1]:
            puser, server = parsed_url[1].split('@')
            url_user, url_pass = puser.split(':')

            # now rewrite the url to not contain the user anymore
            url = url.replace(parsed_url[1], server)

        if username and password is not None:
            http.add_credentials(name=username, password=password)
        elif url_user and url_pass is not None:
            http.add_credentials(name=url_user, password=url_pass)

        #! the parameters to the httplib / proxy must be of type str()
        encoded_params = ''
        if parameter is not None and len(parameter) > 0:
            encoded_params = self.urlencode(parameter)

        call_url = str(url)

        try:
            # do a GET request - which has no body but all params
            # added to the url
            if method == 'GET':
                call_data = None
                if len(encoded_params) > 0:
                    # extend the url with our parameters
                    call_url = "%s?%s" % (call_url, encoded_params)

            # or do a POST request - the more secure default and fallback
            else:
                method = 'POST'
                headers["Content-type"] = "application/x-www-form-urlencoded"
                call_data = encoded_params

            # using httplib2:
            # the proxy spec and url + enc. parameters must be of
            # type string str() - otherwise the following error will occur:
            # : GeneralProxyError: (5, 'bad input') :

            (_resp, reply) = http.request(call_url, method=method,
                                          headers=headers,
                                          body=call_data)

            # some providers like clickatell have no response.status!
            log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:])
            ret = self._check_success(reply)

        except (httplib2.HttpLib2Error, socket.error) as exc:
            raise ProviderNotAvailable(
                        "Failed to send SMS - timed out %r" % exc)

        except Exception as exc:
            log.exception("Failed to send SMS")
            raise ProviderNotAvailable("Failed to send SMS. %r" % exc)

        return ret
예제 #4
0
    def urllib_request(self, url, parameter,
                       username=None, password=None, method='GET'):
        """
        build the urllib request and check the response for success or fail

        :param url: target url
        :param parameter: additonal parameter to append to the url request
        :param username: basic authentication with username (optional)
        :param password: basic authentication with password (optional)
        :param method: run an GET or POST request

        :return: False or True
        """
        try:
            headers = {}
            handlers = []
            pparams = {}

            if 'PROXY' in self.config and self.config['PROXY']:

                proxy_handler = None

                if isinstance(self.config['PROXY'], (str, unicode)):
                    # for simplicity we set both protocols
                    proxy_handler = urllib2.ProxyHandler({
                        "http": self.config['PROXY'],
                        "https": self.config['PROXY']}
                    )

                elif isinstance(self.config['PROXY'], dict):
                    proxy_defintion = self.config['PROXY']
                    proxy_handler = urllib2.ProxyHandler(proxy_defintion)

                if proxy_handler:
                    handlers.append(proxy_handler)
                    log.debug("using Proxy: %r" % self.config['PROXY'])

            if username and password is not None:

                password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
                password_mgr.add_password(None, url, username, password)
                auth_handler = urllib2.HTTPBasicAuthHandler(password_mgr)
                handlers.append(auth_handler)

            timeout = None
            if 'timeout' in self.config and self.config['timeout']:
                timeout = parse_timeout(self.config['timeout'])

            opener = urllib2.build_opener(*handlers)
            urllib2.install_opener(opener)

            full_url = str(url)

            encoded_params = None
            if parameter is not None and len(parameter) > 0:
                encoded_params = self.urlencode(parameter)

            if method == 'GET':
                c_data = None
                if encoded_params:
                    full_url = "%s?%s" % (url, encoded_params)
            else:
                headers["Content-type"] = "application/x-www-form-urlencoded"
                c_data = encoded_params

            requ = urllib2.Request(full_url, data=c_data, headers=headers)
            if username and password is not None:
                base64string = base64.encodestring(
                    '%s:%s' % (username, password)).replace('\n', '')
                requ.add_header("Authorization", "Basic %s" % base64string)

            response = urllib2.urlopen(requ, timeout=timeout)
            reply = response.read()

            # some providers like clickatell have no response.status!
            log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:])
            ret = self._check_success(reply)

        except (urllib2.URLError, socket.timeout) as exc:
            log.exception("HttpSMSProvider urllib timeout exception")
            raise ProviderNotAvailable("Failed to send SMS -timed out %r" % exc)

        except Exception as exc:
            log.exception("HttpSMSProvider urllib")
            raise Exception("Failed to send SMS. %r" % exc)

        return ret
예제 #5
0
    def requests_request(self, url, parameter,
                         username=None, password=None, method='GET'):

        try:
            pparams = {}

            if 'timeout' in self.config and self.config['timeout']:
                pparams['timeout'] = parse_timeout(self.config['timeout'])

            if 'PROXY' in self.config and self.config['PROXY']:

                if isinstance(self.config['PROXY'], (str, unicode)):
                    proxy_defintion = {
                        "http": self.config['PROXY'],
                        "https": self.config['PROXY']
                        }

                elif isinstance(self.config['PROXY'], dict):
                    proxy_defintion = self.config['PROXY']

                pparams['proxies'] = proxy_defintion

            if username and password is not None:
                auth = None
                auth_type = self.config.get(
                    'AUTH_TYPE', 'basic').lower().strip()

                if auth_type == 'basic':
                    auth = HTTPBasicAuth(username, password)

                if auth_type == 'digest':
                    auth = HTTPDigestAuth(username, password)

                if auth:
                    pparams['auth'] = auth

            # -------------------------------------------------------------- --

            # fianly execute the request

            if method == 'GET':
                response = requests.get(url, params=parameter, **pparams)
            else:
                response = requests.post(url, data=parameter, **pparams)

            reply = response.text
            # some providers like clickatell have no response.status!
            log.debug("HttpSMSProvider >>%s...%s<<", reply[:20], reply[-20:])
            ret = self._check_success(reply)

        except (requests.exceptions.ConnectTimeout,
                requests.exceptions.ConnectionError,
                requests.exceptions.Timeout,
                requests.exceptions.ReadTimeout,
                requests.exceptions.TooManyRedirects) as exc:

            log.exception("HttpSMSProvider timed out")
            raise ProviderNotAvailable("Failed to send SMS - timed out %r" % exc)

        except Exception as exc:
            log.error("HttpSMSProvider %r" % exc)
            raise Exception("Failed to send SMS. %r" % exc)

        return ret
예제 #6
0
    def request(self,
                url,
                parameter,
                username=None,
                password=None,
                method='GET'):

        try:
            pparams = {}

            if 'timeout' in self.config and self.config['timeout']:
                pparams['timeout'] = parse_timeout(self.config['timeout'])

            if 'PROXY' in self.config and self.config['PROXY']:

                if isinstance(self.config['PROXY'], str):
                    proxy_defintion = {
                        "http": self.config['PROXY'],
                        "https": self.config['PROXY']
                    }

                elif isinstance(self.config['PROXY'], dict):
                    proxy_defintion = self.config['PROXY']

                pparams['proxies'] = proxy_defintion

            if username and password is not None:
                auth = None
                auth_type = self.config.get('AUTH_TYPE',
                                            'basic').lower().strip()

                if auth_type == 'basic':
                    auth = HTTPBasicAuth(username, password)

                if auth_type == 'digest':
                    auth = HTTPDigestAuth(username, password)

                if auth:
                    pparams['auth'] = auth

            # -------------------------------------------------------------- --

            # fianly execute the request

            if method == 'GET':
                response = requests.get(url, params=parameter, **pparams)
            else:
                response = requests.post(url, data=parameter, **pparams)

            reply = response.text
            # some providers like clickatell have no response.status!
            log.debug("HttpSMSProvider >>%r...%r<<", reply[:20], reply[-20:])
            ret = self._check_success(reply)

        except (requests.exceptions.ConnectTimeout,
                requests.exceptions.ConnectionError,
                requests.exceptions.Timeout, requests.exceptions.ReadTimeout,
                requests.exceptions.TooManyRedirects) as exc:

            log.exception("HttpSMSProvider timed out")
            raise ProviderNotAvailable("Failed to send SMS - timed out %r" %
                                       exc)

        except Exception as exc:
            log.error("HttpSMSProvider %r", exc)
            raise Exception("Failed to send SMS. %r" % exc)

        return ret
예제 #7
0
    def request(self,
                url,
                parameter,
                username=None,
                password=None,
                method="GET"):

        try:
            pparams = {}

            pparams["timeout"] = HttpSMSProvider.DEFAULT_TIMEOUT
            if "timeout" in self.config and self.config["timeout"]:
                pparams["timeout"] = parse_timeout(self.config["timeout"])

            if "PROXY" in self.config and self.config["PROXY"]:

                if isinstance(self.config["PROXY"], str):
                    proxy_defintion = {
                        "http": self.config["PROXY"],
                        "https": self.config["PROXY"],
                    }

                elif isinstance(self.config["PROXY"], dict):
                    proxy_defintion = self.config["PROXY"]

                pparams["proxies"] = proxy_defintion

            if username and password is not None:
                auth = None
                auth_type = (self.config.get("AUTH_TYPE",
                                             "basic").lower().strip())

                if auth_type == "basic":
                    auth = HTTPBasicAuth(username, password)

                if auth_type == "digest":
                    auth = HTTPDigestAuth(username, password)

                if auth:
                    pparams["auth"] = auth

            # --------------------------------------------------------------

            # set server certificate validation policy

            server_certificate = self.load_server_cert(
                self.config, server_cert_key="SERVER_CERTIFICATE")

            if server_certificate is False:
                pparams["verify"] = False

            if server_certificate:
                pparams["verify"] = server_certificate

            # -------------------------------------------------------------- --

            # finally execute the request

            if method == "GET":
                response = requests.get(url, params=parameter, **pparams)
            else:
                response = requests.post(url, data=parameter, **pparams)

            reply = response.text
            # some providers like clickatell have no response.status!
            log.debug("HttpSMSProvider >>%r...%r<<", reply[:20], reply[-20:])
            ret = self._check_success(reply)

        except (
                requests.exceptions.ConnectTimeout,
                requests.exceptions.ConnectionError,
                requests.exceptions.Timeout,
                requests.exceptions.ReadTimeout,
                requests.exceptions.TooManyRedirects,
        ) as exc:

            log.error("HttpSMSProvider timed out")
            raise ProviderNotAvailable("Failed to send SMS - timed out %r" %
                                       exc)

        except Exception as exc:
            log.error("HttpSMSProvider %r", exc)
            raise Exception("Failed to send SMS. %r" % exc)

        return ret
예제 #8
0
    def checkOtp(self, anOtpVal, counter, window, options=None):
        """
        Here we contact the Yubico Cloud server to validate the OtpVal.
        """

        pparams = {}

        yubico_url = getFromConfig("yubico.url", FALLBACK_YUBICO_URL)

        if yubico_url == DEPRECATED_YUBICO_URL:

            log.warning(
                "Usage of YUBICO_URL %r is deprecated!! ",
                DEPRECATED_YUBICO_URL,
            )

            # setups with old YUBICO URLS will be broken on yubico side
            # after 3th of February 2019
            third_feb_2019 = datetime.datetime(year=2019, month=2, day=3)

            if datetime.datetime.now() >= third_feb_2019:
                raise Exception("Usage of YUBICO_URL %r is deprecated!! " %
                                DEPRECATED_YUBICO_URL)

        apiId = getFromConfig("yubico.id")
        apiKey = getFromConfig("yubico.secret")

        if not apiKey or not apiId:
            log.error(APIKEY_UNCONFIGURED_ERROR)
            raise YubicoApikeyException(
                "Yubico apiKey or apiId not configured!")

        tokenid = self.getFromTokenInfo("yubico.tokenid")
        if len(anOtpVal) < 12:
            log.warning("[checkOtp] The otpval is too short: %r", anOtpVal)
            return -1

        if anOtpVal[:12] != tokenid:
            log.warning("[checkOtp] the tokenid in the OTP value does "
                        "not match the assigned token!")
            return -1

        timeout = getFromConfig("yubico.timeout")
        if timeout:
            pparams["timeout"] = parse_timeout(timeout)

        nonce = binascii.hexlify(os.urandom(20)).decode()

        p = urllib.parse.urlencode({
            "nonce": nonce,
            "otp": anOtpVal,
            "id": apiId
        })

        yubico_urls = [x.strip() for x in yubico_url.split(",")]

        res_scheduler = ResourceScheduler(tries=2, uri_list=yubico_urls)

        for uri in next(res_scheduler):

            try:
                URL = "%s?%s" % (uri, p)

                response = requests.get(URL, **pparams)

                if response.ok:
                    return self._check_yubico_response(
                        nonce, apiKey, response.content.decode())

                log.info("Failed to validate yubico request %r", response)

                return -1

            except (
                    Timeout,
                    ConnectTimeout,
                    ReadTimeout,
                    ConnectionError,
                    TooManyRedirects,
            ) as exx:

                log.error("resource %r not available!", uri)

                # mark the url as blocked

                res_scheduler.block(uri, delay=30)

                log.error(
                    "[checkOtp] Error getting response from "
                    "Yubico Cloud Server (%r)",
                    uri,
                )

            except Exception as exx:

                log.error("unknown exception for uri %r!", uri)

                raise exx

        # ------------------------------------------------------------------ --

        # if we reach here, no resource has been availabel

        log.error("non of the resources %r available!", yubico_urls)

        raise AllResourcesUnavailable("non of the resources %r available!" %
                                      yubico_urls)
예제 #9
0
    def loadConfig(self, configDict):

        if not configDict:
            raise Exception("missing configuration")

        self.config = configDict

        self.username = configDict.get("USERNAME", None)
        self.password = configDict.get("PASSWORD", None)

        # proxy defintion must be in the following format:
        #
        #    . . .
        #    PROXY : {
        #        "http": "http://10.10.1.10:3128",
        #        "https": "http://10.10.1.10:1080",
        #    }
        #    . . .
        #    but we can as well provide Proxy via socks:
        #
        #    PROXY = {
        #        'http': 'socks5://user:pass@host:port',
        #        'https': 'socks5://user:pass@host:port'
        #    }
        #

        self.proxy = configDict.get("PROXY", None)

        self.auth_type = configDict.get("AUTHENTICATION", "BASIC").lower()
        if self.auth_type not in ["basic", "digest"]:
            raise Exception("no valid Authentication type provided")

        # support for multiple urls

        self.url_config = configDict["URL"]
        self.urls = [x.strip() for x in self.url_config.split(",")]

        # ------------------------------------------------------------------ --

        # timeout (float or tuple) -- (optional)
        # How many seconds to wait for the server to send data before giving
        # up, as a float, or a (connect timeout, read timeout) tuple.

        #!!! we set the timeout by default so that linotp wont block

        self.timeout = parse_timeout(
            configDict.get("TIMEOUT", RestSMSProvider.DEFAULT_TIMEOUT))

        # ------------------------------------------------------------------ --

        # parameter is our json payload, which will provide the
        # keys where the phone and the message is replaced within

        self.payload = configDict["PAYLOAD"]
        self.headers = configDict.get("HEADERS", {})
        self.sms_text_key = configDict["SMS_TEXT_KEY"]
        self.sms_phone_key = configDict["SMS_PHONENUMBER_KEY"]

        self.client_cert = configDict.get("CLIENT_CERTIFICATE_FILE")

        self.server_cert = self.load_server_cert(
            configDict, server_cert_key="SERVER_CERTIFICATE")