Пример #1
0
 def test_05_encode_decode(self):
     b_str = b'Hello World'
     self.assertEqual(to_unicode(b_str), b_str.decode('utf8'))
     u_str = u'Hello Wörld'
     self.assertEqual(to_unicode(u_str), u_str)
     self.assertEqual(to_bytes(b_str), b_str)
     self.assertEqual(to_bytes(u_str), u_str.encode('utf8'))
Пример #2
0
 def test_05_encode_decode(self):
     b_str = b'Hello World'
     self.assertEqual(to_unicode(b_str), b_str.decode('utf8'))
     u_str = u'Hello Wörld'
     self.assertEqual(to_unicode(u_str), u_str)
     self.assertEqual(to_bytes(b_str), b_str)
     self.assertEqual(to_bytes(u_str), u_str.encode('utf8'))
Пример #3
0
    def test_14_to_unicode(self):
        s = "kölbel"
        su = to_unicode(s)
        self.assertEqual(su, u"kölbel")

        s = u"kölbel"
        su = to_unicode(s)
        self.assertEqual(su, u"kölbel")
Пример #4
0
    def test_14_to_unicode(self):
        s = "kölbel"
        su = to_unicode(s)
        self.assertEqual(su, u"kölbel")

        s = u"kölbel"
        su = to_unicode(s)
        self.assertEqual(su, u"kölbel")
Пример #5
0
    def checkOtp(self, anOtpVal, window=10, options=None):
        '''
        check a provided otp value

        :param anOtpVal: the to be tested otp value
        :type anOtpVal: str
        :param window: the +/- window around the test time
        :param options: generic container for additional values \
                        here only used for seltest: setting the initTime

        :return: -1 for fail else the identified counter/time 
        '''
        res = -1
        window = window * 2

        initTime = 0
        if options is not None and type(options) == dict:
            initTime = int(options.get('initTime', 0))

        if initTime == 0:
            otime = int(time.time() // 10)
        else:
            otime = initTime

        if self.secretObject is None:
            key = self.key
            pin = self.pin
        else:
            key = self.secretObject.getKey()
            pin = self.secPin.getKey()

        for i in range(otime - window, otime + window):
            otp = self.calcOtp(i, to_unicode(key), to_unicode(pin))
            if anOtpVal == otp:
                res = i
                log.debug("otpvalue {0!r} found at: {1!r}".format(
                    anOtpVal, res))
                break

        if self.secretObject is not None:
            zerome(key)
            zerome(pin)
            del key
            del pin

        ## prevent access twice with last motp
        if res <= self.oldtime:
            log.warning(
                "otpvalue {0!s} checked once before ({1!r}<={2!r})".format(
                    anOtpVal, res, self.oldtime))
            res = -1
        if res == -1:
            msg = 'checking motp failed'
        else:
            msg = 'checking motp sucess'

        log.debug("end. {0!s} : returning result: {1!r}, ".format(msg, res))
        return res
Пример #6
0
def x509name_to_string(x509name):
    """
    converts a X509Name to a string as in a DN

    :param x509name: THe X509Name object
    :return:
    """
    components = x509name.get_components()
    return ",".join(["{0}={1}".format(to_unicode(c[0]), to_unicode(c[1])) for c in components])
Пример #7
0
    def checkOtp(self, anOtpVal, window=10, options=None):
        '''
        check a provided otp value

        :param anOtpVal: the to be tested otp value
        :type anOtpVal: str
        :param window: the +/- window around the test time
        :param options: generic container for additional values \
                        here only used for seltest: setting the initTime

        :return: -1 for fail else the identified counter/time 
        '''
        res = -1
        window = window * 2

        initTime = 0
        if options is not None and type(options) == dict:
            initTime = int(options.get('initTime', 0))

        if initTime == 0:
            otime = int(time.time() // 10)
        else:
            otime = initTime

        if self.secretObject is None:
            key = self.key
            pin = self.pin
        else:
            key = self.secretObject.getKey()
            pin = self.secPin.getKey()

        for i in range(otime - window, otime + window):
            otp = self.calcOtp(i, to_unicode(key), to_unicode(pin))
            if anOtpVal == otp:
                res = i
                log.debug("otpvalue {0!r} found at: {1!r}".format(anOtpVal, res))
                break

        if self.secretObject is not None:
            zerome(key)
            zerome(pin)
            del key
            del pin

        ## prevent access twice with last motp
        if res <= self.oldtime:
            log.warning("otpvalue {0!s} checked once before ({1!r}<={2!r})".format(anOtpVal, res, self.oldtime))
            res = -1
        if res == -1:
            msg = 'checking motp failed'
        else:
            msg = 'checking motp sucess'

        log.debug("end. {0!s} : returning result: {1!r}, ".format(msg, res))
        return res
Пример #8
0
def decryptPassword(cryptPass, convert_unicode=False):
    """
    Decrypt the encrypted password ``cryptPass`` and return it.
    If an error occurs during decryption, return FAILED_TO_DECRYPT_PASSWORD.

    :param cryptPass: bytestring
    :param convert_unicode: If true, interpret the decrypted password as an UTF-8 string
                            and convert it to unicode. If an error occurs here,
                            the original bytestring is returned.
    """
    # NOTE: Why do we have the ``convert_unicode`` parameter?
    # Up until now, this always returned bytestrings. However, this breaks
    # LDAP and SQL resolvers, which expect this to return an unicode string
    # (and this makes more sense, because ``encryptPassword`` also
    # takes unicode strings!). But always returning unicode might break
    # other call sites of ``decryptPassword``. So we add the
    # keyword argument to avoid breaking compatibility.
    from privacyidea.lib.utils import to_unicode
    hsm = _get_hsm()
    try:
        ret = hsm.decrypt_password(cryptPass)
    except Exception as exx:  # pragma: no cover
        log.warning(exx)
        ret = FAILED_TO_DECRYPT_PASSWORD
    try:
        if convert_unicode:
            ret = to_unicode(ret)
    except Exception as exx:  # pragma: no cover
        log.warning(exx)
        # just keep ``ret`` as a bytestring in that case
    return ret
Пример #9
0
def decryptPassword(cryptPass, convert_unicode=False):
    """
    Decrypt the encrypted password ``cryptPass`` and return it.
    If an error occurs during decryption, return FAILED_TO_DECRYPT_PASSWORD.

    :param cryptPass: bytestring
    :param convert_unicode: If true, interpret the decrypted password as an UTF-8 string
                            and convert it to unicode. If an error occurs here,
                            the original bytestring is returned.
    """
    # NOTE: Why do we have the ``convert_unicode`` parameter?
    # Up until now, this always returned bytestrings. However, this breaks
    # LDAP and SQL resolvers, which expect this to return an unicode string
    # (and this makes more sense, because ``encryptPassword`` also
    # takes unicode strings!). But always returning unicode might break
    # other call sites of ``decryptPassword``. So we add the
    # keyword argument to avoid breaking compatibility.
    from privacyidea.lib.utils import to_unicode
    hsm = get_hsm()
    try:
        ret = hsm.decrypt_password(cryptPass)
    except Exception as exx:  # pragma: no cover
        log.warning(exx)
        ret = FAILED_TO_DECRYPT_PASSWORD
    try:
        if convert_unicode:
            ret = to_unicode(ret)
    except Exception as exx:  # pragma: no cover
        log.warning(exx)
        # just keep ``ret`` as a bytestring in that case
    return ret
Пример #10
0
    def search(self, search_base=None, search_scope=None,
               search_filter=None, attributes=None, paged_size=5,
               size_limit=0, paged_cookie=None):
        s_filter = list()
        candidates = list()
        self.response = list()
        self.result = dict()

        try:
            if isinstance(search_filter, bytes):
                # We need to convert to unicode otherwise pyparsing will not
                # find the u"ö"
                search_filter = to_unicode(search_filter)
            expr = Connection._parse_filter()
            s_filter = expr.parseString(search_filter).asList()[0]
        except pyparsing.ParseBaseException as exx:
            # Just for debugging purposes
            s = "{!s}".format(exx)

        for item in s_filter:
            if item[0] in self.operation:
                candidates = self.operation.get(item[0])(search_base,
                                                         s_filter)
        self.response = Connection._deDuplicate(candidates)

        return True
Пример #11
0
    def request(config, user, password):
        """
        Perform an HTTP test request to the privacyIDEA server.
        The privacyIDEA configuration contains the URL and the TLS verify.

        * config.url
        * config.tls

        :param config: The privacyIDEA configuration
        :type config: PrivacyIDEAServer Database Model
        :param user: the username to test
        :param password: the password/OTP to test
        :return: True or False. If any error occurs, an exception is raised.
        """
        response = requests.post(config.url + "/validate/check",
                          data={"user": user, "pass": password},
                          verify=config.tls
                          )
        log.debug("Sent request to privacyIDEA server. status code returned: "
                  "{0!s}".format(response.status_code))
        if response.status_code != 200:
            log.warning("The request to the remote privacyIDEA server {0!s} "
                        "returned a status code: {1!s}".format(config.url,
                                                               response.status_code))
            return False

        j_response = json.loads(to_unicode(response.content))
        result = j_response.get("result")
        return result.get("status") and result.get("value")
Пример #12
0
 def test_05_templates(self):
     cwd = os.getcwd()
     cacon = LocalCAConnector(
         "localCA", {
             "cakey": CAKEY,
             "cacert": CACERT,
             "openssl.cnf": OPENSSLCNF,
             "WorkingDir": cwd + "/" + WORKINGDIR,
             ATTR.TEMPLATE_FILE: "templates.yaml"
         })
     templates = cacon.get_templates()
     self.assertTrue("user" in templates)
     self.assertTrue("webserver" in templates)
     self.assertTrue("template3" in templates)
     cert = cacon.sign_request(SPKAC,
                               options={
                                   "spkac": 1,
                                   "template": "webserver"
                               })
     expires = to_unicode(cert.get_notAfter())
     import datetime
     dt = datetime.datetime.strptime(expires, "%Y%m%d%H%M%SZ")
     ddiff = dt - datetime.datetime.now()
     # The certificate is signed for 750 days
     self.assertTrue(ddiff.days > 740, ddiff.days)
     self.assertTrue(ddiff.days < 760, ddiff.days)
Пример #13
0
    def _decrypt_value(self, crypt_value, slot_id):
        """
        base method to decrypt a value
        - used one slot id to encrypt a string with leading iv, separated by ':'

        :param crypt_value: the the value that is to be decrypted
        :type crypt_value: str

        :param slot_id: slot of the key array
        :type slot_id: int

        :return: decrypted data
        :rtype: str
        """
        # split at ":"
        pos = crypt_value.find(':')
        bIV = crypt_value[:pos]
        bData = crypt_value[pos + 1:]

        iv = binascii.unhexlify(bIV)
        data = binascii.unhexlify(bData)

        clear_data = self.decrypt(data, iv, slot_id)

        return to_unicode(clear_data)
Пример #14
0
 def test_03_realm_dropdown(self):
     set_policy("realmdrop", scope=SCOPE.WEBUI,
                action="{0!s}=Hello World".format(ACTION.REALMDROPDOWN))
     with self.app.test_request_context('/', method='GET'):
         res = self.app.full_dispatch_request()
         self.assertTrue(res.status_code == 200, res)
         self.assertIsNotNone(re.search(r'id="REALMS" value=".*World.*"',
                                        to_unicode(res.data)), res)
Пример #15
0
def _daplug2digit(daplug_otp):
    hex_otp = ""
    for i in daplug_otp:
        digit = MAPPING.get(i)
        hex_otp += digit
    # we know the result is a string of digits
    otp = to_unicode(binascii.unhexlify(hex_otp))
    return otp
Пример #16
0
def _daplug2digit(daplug_otp):
    hex_otp = ""
    for i in daplug_otp:
        digit = MAPPING.get(i)
        hex_otp += digit
    # we know the result is a string of digits
    otp = to_unicode(binascii.unhexlify(hex_otp))
    return otp
Пример #17
0
 def test_03_realm_dropdown(self):
     set_policy("realmdrop", scope=SCOPE.WEBUI,
                action="{0!s}=Hello World".format(ACTION.REALMDROPDOWN))
     with self.app.test_request_context('/', method='GET'):
         res = self.app.full_dispatch_request()
         self.assertTrue(res.status_code == 200, res)
         self.assertIsNotNone(re.search(r'id="REALMS" value=".*World.*"',
                                        to_unicode(res.data)), res)
Пример #18
0
    def test_02_sign_cert(self):
        cacon = LocalCAConnector("localCA", {"cacert": "...",
                                             "cakey": "..."})
        # set the parameters:
        cwd = os.getcwd()
        cacon.set_config({"cakey": CAKEY, "cacert": CACERT,
                          "openssl.cnf": OPENSSLCNF,
                          "WorkingDir": cwd + "/" + WORKINGDIR})

        cert = cacon.sign_request(REQUEST,
                                  {"CSRDir": "",
                                   "CertificateDir": "",
                                   "WorkingDir": cwd + "/" + WORKINGDIR})
        serial = cert.get_serial_number()

        self.assertEqual("{0!r}".format(cert.get_issuer()),
                         "<X509Name object "
                         "'/C=DE/ST=Hessen/O=privacyidea/CN=CA001'>")
        self.assertEqual("{0!r}".format(cert.get_subject()),
                         "<X509Name object "
                         "'/C=DE/ST=Hessen/O=privacyidea/CN=requester"
                         ".localdomain'>")

        # Revoke certificate
        r = cacon.revoke_cert(cert)
        serial_hex = int_to_hex(serial)
        self.assertEqual(r, serial_hex)

        # Create the CRL
        r = cacon.create_crl()
        self.assertEqual(r, "crl.pem")
        # Check if the serial number is contained in the CRL!
        filename = os.path.join(cwd, WORKINGDIR, "crl.pem")
        f = open(filename)
        buff = f.read()
        f.close()
        crl = crypto.load_crl(crypto.FILETYPE_PEM, buff)
        revoked_certs = crl.get_revoked()
        found_revoked_cert = False
        for revoked_cert in revoked_certs:
            s = to_unicode(revoked_cert.get_serial())
            if s == serial_hex:
                found_revoked_cert = True
                break
        self.assertTrue(found_revoked_cert)

        # Create the CRL and check the overlap period. But no need to create
        # a new CRL.
        r = cacon.create_crl(check_validity=True)
        self.assertEqual(r, None)

        # Now we overlap at any cost!
        cacon.set_config({"cakey": CAKEY, "cacert": CACERT,
                          "openssl.cnf": OPENSSLCNF,
                          "WorkingDir": cwd + "/" + WORKINGDIR,
                          ATTR.CRL_OVERLAP_PERIOD: 1000})
        r = cacon.create_crl(check_validity=True)
        self.assertEqual(r, "crl.pem")
    def test_02_sign_cert(self):
        cacon = LocalCAConnector("localCA", {"cacert": "...",
                                             "cakey": "..."})
        # set the parameters:
        cwd = os.getcwd()
        cacon.set_config({"cakey": CAKEY, "cacert": CACERT,
                          "openssl.cnf": OPENSSLCNF,
                          "WorkingDir": cwd + "/" + WORKINGDIR})

        cert = cacon.sign_request(REQUEST,
                                  {"CSRDir": "",
                                   "CertificateDir": "",
                                   "WorkingDir": cwd + "/" + WORKINGDIR})
        serial = cert.get_serial_number()

        self.assertEqual("{0!r}".format(cert.get_issuer()),
                         "<X509Name object "
                         "'/C=DE/ST=Hessen/O=privacyidea/CN=CA001'>")
        self.assertEqual("{0!r}".format(cert.get_subject()),
                         "<X509Name object "
                         "'/C=DE/ST=Hessen/O=privacyidea/CN=requester"
                         ".localdomain'>")

        # Revoke certificate
        r = cacon.revoke_cert(cert)
        serial_hex = int_to_hex(serial)
        self.assertEqual(r, serial_hex)

        # Create the CRL
        r = cacon.create_crl()
        self.assertEqual(r, "crl.pem")
        # Check if the serial number is contained in the CRL!
        filename = os.path.join(cwd, WORKINGDIR, "crl.pem")
        f = open(filename)
        buff = f.read()
        f.close()
        crl = crypto.load_crl(crypto.FILETYPE_PEM, buff)
        revoked_certs = crl.get_revoked()
        found_revoked_cert = False
        for revoked_cert in revoked_certs:
            s = to_unicode(revoked_cert.get_serial())
            if s == serial_hex:
                found_revoked_cert = True
                break
        self.assertTrue(found_revoked_cert)

        # Create the CRL and check the overlap period. But no need to create
        # a new CRL.
        r = cacon.create_crl(check_validity=True)
        self.assertEqual(r, None)

        # Now we overlap at any cost!
        cacon.set_config({"cakey": CAKEY, "cacert": CACERT,
                          "openssl.cnf": OPENSSLCNF,
                          "WorkingDir": cwd + "/" + WORKINGDIR,
                          ATTR.CRL_OVERLAP_PERIOD: 1000})
        r = cacon.create_crl(check_validity=True)
        self.assertEqual(r, "crl.pem")
Пример #20
0
def encryptPin(cryptPin):
    """
    :param cryptPin: the pin to encrypt
    :type cryptPin: bytes or str
    :return: the encrypted pin
    :rtype: str
    """
    hsm = get_hsm()
    return to_unicode(hsm.encrypt_pin(to_bytes(cryptPin)))
Пример #21
0
 def get_init_detail(self, params=None, user=None):
     """
     At the end of the initialization we return the registration code.
     """
     response_detail = TokenClass.get_init_detail(self, params, user)
     secretHOtp = self.token.get_otpkey()
     password = secretHOtp.getKey()
     response_detail[self.password_detail_key] = to_unicode(password)
     return response_detail
Пример #22
0
    def _match_equal_to(search_base, attribute, value, candidates):
        matches = list()
        match_using_regex = False

        if "*" in value:
            match_using_regex = True
            #regex = check_escape(value)
            regex = value.replace('*', '.*')
            regex = "^{0}$".format(regex)

        for entry in candidates:
            dn = to_unicode(entry.get("dn"))

            if attribute not in entry.get(
                    "attributes") or not dn.endswith(search_base):
                continue

            values_from_directory = entry.get("attributes").get(attribute)
            if isinstance(values_from_directory, list):
                for item in values_from_directory:
                    if attribute == "objectGUID":
                        item = _convert_objectGUID(item)

                    if match_using_regex:
                        m = re.match(regex, str(item), re.I)
                        if m:
                            entry["type"] = "searchResEntry"
                            matches.append(entry)
                    else:
                        if item == value:
                            entry["type"] = "searchResEntry"
                            matches.append(entry)

            else:
                if attribute == "objectGUID":
                    values_from_directory = _convert_objectGUID(
                        values_from_directory)

                if match_using_regex:
                    m = re.match(regex, str(values_from_directory), re.I)
                    if m:
                        entry["type"] = "searchResEntry"
                        matches.append(entry)
                else:
                    # The value, which we compare is unicode, so we convert
                    # the values_from_directory to unicode rather than str.
                    if isinstance(values_from_directory, bytes):
                        values_from_directory = values_from_directory.decode(
                            "utf-8")
                    elif type(values_from_directory) == int:
                        values_from_directory = u"{0!s}".format(
                            values_from_directory)
                    if value == values_from_directory:
                        entry["type"] = "searchResEntry"
                        matches.append(entry)

        return matches
Пример #23
0
    def getUserList(self, searchDict):
        """
        :param searchDict: A dictionary with search parameters
        :type searchDict: dict
        :return: list of users, where each user is a dictionary
        """
        ret = []
        self._bind()
        attributes = self.userinfo.values()
        ad_timestamp = get_ad_timestamp_now()
        if self.uidtype.lower() != "dn":
            attributes.append(str(self.uidtype))

        # do the filter depending on the searchDict
        filter = u"(&" + self.searchfilter
        for search_key in searchDict.keys():
            # convert to unicode
            searchDict[search_key] = to_unicode(searchDict[search_key])
            if search_key == "accountExpires":
                comperator = ">="
                if searchDict[search_key] in ["1", 1]:
                    comperator = "<="
                filter += u"(&({0!s}{1!s}{2!s})(!({3!s}=0)))".format(
                    self.userinfo[search_key], comperator,
                    get_ad_timestamp_now(), self.userinfo[search_key])
            else:
                filter += u"({0!s}={1!s})".format(self.userinfo[search_key],
                                                  searchDict[search_key])
        filter += ")"

        g = self.l.extend.standard.paged_search(search_base=self.basedn,
                                                search_filter=filter,
                                                search_scope=self.scope,
                                                attributes=attributes,
                                                paged_size=100,
                                                size_limit=self.sizelimit,
                                                generator=True)
        # returns a generator of dictionaries
        for entry in ignore_sizelimit_exception(self.l, g):
            # Simple fix for ignored sizelimit with Active Directory
            if len(ret) >= self.sizelimit:
                break
            # Fix for searchResRef entries which have no attributes
            if entry.get('type') == 'searchResRef':
                continue
            try:
                attributes = entry.get("attributes")
                user = self._ldap_attributes_to_user_object(attributes)
                user['userid'] = self._get_uid(entry, self.uidtype)
                ret.append(user)
            except Exception as exx:  # pragma: no cover
                log.error(
                    "Error during fetching LDAP objects: {0!r}".format(exx))
                log.debug("{0!s}".format(traceback.format_exc()))

        return ret
Пример #24
0
    def test_25_encodings(self):
        u = u'Hello Wörld'
        b = b'Hello World'
        self.assertEquals(to_utf8(None), None)
        self.assertEquals(to_utf8(u), u.encode('utf8'))
        self.assertEquals(to_utf8(b), b)

        self.assertEquals(to_unicode(u), u)
        self.assertEquals(to_unicode(b), b.decode('utf8'))
        self.assertEquals(to_unicode(None), None)
        self.assertEquals(to_unicode(10), 10)

        self.assertEquals(to_bytes(u), u.encode('utf8'))
        self.assertEquals(to_bytes(b), b)
        self.assertEquals(to_bytes(10), 10)

        self.assertEquals(to_byte_string(u), u.encode('utf8'))
        self.assertEquals(to_byte_string(b), b)
        self.assertEquals(to_byte_string(10), b'10')
Пример #25
0
 def get_init_detail(self, params=None, user=None):
     """
     At the end of the initialization we return the registration code.
     """
     response_detail = PasswordTokenClass.get_init_detail(self, params, user)
     params = params or {}
     secretHOtp = self.token.get_otpkey()
     registrationcode = secretHOtp.getKey()
     response_detail["registrationcode"] = to_unicode(registrationcode)
     return response_detail
Пример #26
0
    def test_25_encodings(self):
        u = u'Hello Wörld'
        b = b'Hello World'
        self.assertEquals(to_utf8(None), None)
        self.assertEquals(to_utf8(u), u.encode('utf8'))
        self.assertEquals(to_utf8(b), b)

        self.assertEquals(to_unicode(u), u)
        self.assertEquals(to_unicode(b), b.decode('utf8'))
        self.assertEquals(to_unicode(None), None)
        self.assertEquals(to_unicode(10), 10)

        self.assertEquals(to_bytes(u), u.encode('utf8'))
        self.assertEquals(to_bytes(b), b)
        self.assertEquals(to_bytes(10), 10)

        self.assertEquals(to_byte_string(u), u.encode('utf8'))
        self.assertEquals(to_byte_string(b), b)
        self.assertEquals(to_byte_string(10), b'10')
Пример #27
0
 def get_init_detail(self, params=None, user=None):
     """
     At the end of the initialization we return the registration code.
     """
     response_detail = PasswordTokenClass.get_init_detail(self, params, user)
     params = params or {}
     secretHOtp = self.token.get_otpkey()
     registrationcode = secretHOtp.getKey()
     response_detail["registrationcode"] = to_unicode(registrationcode)
     return response_detail
Пример #28
0
    def getUserList(self, searchDict):
        """
        :param searchDict: A dictionary with search parameters
        :type searchDict: dict
        :return: list of users, where each user is a dictionary
        """
        ret = []
        self._bind()
        attributes = list(self.userinfo.values())
        ad_timestamp = get_ad_timestamp_now()
        if self.uidtype.lower() != "dn":
            attributes.append(str(self.uidtype))

        # do the filter depending on the searchDict
        filter = u"(&" + self.searchfilter
        for search_key in searchDict.keys():
            # convert to unicode
            searchDict[search_key] = to_unicode(searchDict[search_key])
            if search_key == "accountExpires":
                comperator = ">="
                if searchDict[search_key] in ["1", 1]:
                    comperator = "<="
                filter += u"(&({0!s}{1!s}{2!s})(!({3!s}=0)))".format(
                    self.userinfo[search_key], comperator,
                    get_ad_timestamp_now(), self.userinfo[search_key])
            else:
                filter += u"({0!s}={1!s})".format(self.userinfo[search_key],
                                                  searchDict[search_key])
        filter += ")"

        g = self.l.extend.standard.paged_search(search_base=self.basedn,
                                                search_filter=filter,
                                                search_scope=self.scope,
                                                attributes=attributes,
                                                paged_size=100,
                                                size_limit=self.sizelimit,
                                                generator=True)
        # returns a generator of dictionaries
        for entry in ignore_sizelimit_exception(self.l, g):
            # Simple fix for ignored sizelimit with Active Directory
            if len(ret) >= self.sizelimit:
                break
            # Fix for searchResRef entries which have no attributes
            if entry.get('type') == 'searchResRef':
                continue
            try:
                attributes = entry.get("attributes")
                user = self._ldap_attributes_to_user_object(attributes)
                user['userid'] = self._get_uid(entry, self.uidtype)
                ret.append(user)
            except Exception as exx:  # pragma: no cover
                log.error("Error during fetching LDAP objects: {0!r}".format(exx))
                log.debug("{0!s}".format(traceback.format_exc()))

        return ret
Пример #29
0
 def _filename_from_x509(x509_name, file_extension="pem"):
     """
     return a filename from the subject from an x509 object
     :param x509_name: The X509Name object
     :type x509_name: X509Name object
     :param file_extension:
     :type file_extension: str
     :return: filename
     :rtype: str
     """
     name_components = x509_name.get_components()
     filename = "_".join([to_unicode(value) for (key, value) in name_components])
     return '.'.join([filename, file_extension])
Пример #30
0
 def _filename_from_x509(x509_name, file_extension="pem"):
     """
     return a filename from the subject from an x509 object
     :param x509_name: The X509Name object
     :type x509_name: X509Name object
     :param file_extension:
     :type file_extension: str
     :return: filename
     :rtype: str
     """
     name_components = x509_name.get_components()
     filename = "_".join([to_unicode(value) for (key, value) in name_components])
     return '.'.join([filename, file_extension])
Пример #31
0
    def do(self, action, options=None):
        """
        This method executes the defined action in the given event.

        :param action:
        :param options: Contains the flask parameters g, request, response
            and the handler_def configuration
        :type options: dict
        :return:
        :rtype: bool
        """
        ret = False

        if action.lower() == 'logging':
            tokentype = None
            g = options.get("g")
            handler_def = options.get("handler_def")
            handler_options = handler_def.get("options", {})
            logged_in_user = g.logged_in_user if hasattr(g, 'logged_in_user') else {}
            request = options.get("request")
            response = options.get("response")
            tokenowner = self._get_tokenowner(request)
            content = self._get_response_content(response)
            serial = (request.all_data.get("serial")
                      or content.get("detail", {}).get("serial")
                      or g.audit_object.audit_data.get("serial"))
            if serial:
                tokens = get_tokens(serial=serial)
                if tokens:
                    tokentype = tokens[0].get_tokentype()
            else:
                token_objects = get_tokens(user=tokenowner)
                serial = ','.join([tok.get_serial() for tok in token_objects])

            tags = create_tag_dict(logged_in_user=logged_in_user,
                                   request=request,
                                   client_ip=g.client_ip,
                                   tokenowner=tokenowner,
                                   serial=serial,
                                   tokentype=tokentype)

            logger_name = handler_options.get('name', DEFAULT_LOGGER_NAME)
            log_action = logging.getLogger(logger_name)
            log_level = getattr(logging,
                                handler_options.get('level', DEFAULT_LOGLEVEL),
                                logging.INFO)
            log_template = handler_options.get('message') or DEFAULT_LOGMSG
            log_action.log(log_level, to_unicode(log_template).format(**tags))
            ret = True

        return ret
Пример #32
0
    def get_access_token(server=None, client=None, secret=None):

        auth = to_unicode(base64.b64encode(to_bytes(client + ':' + secret)))

        url = "{0!s}/oauth/token?grant_type=client_credentials".format(server)
        resp = requests.get(url, headers={'Authorization': 'Basic ' + auth})

        if resp.status_code != 200:
            info = "Could not get access token: {0!s}".format(resp.status_code)
            log.error(info)
            raise Exception(info)

        access_token = yaml.safe_load(resp.content).get('access_token')
        return access_token
Пример #33
0
def generate_keypair(rsa_keysize=2048):
    """
    This creates an RSA key pair

    # TODO: The HSM should be used.

    The public key and private keys are returned in PKCS#1 Format.

    :return: tuple of (pubkey, privkey)
    """
    private_key = rsa.generate_private_key(public_exponent=65537,
                                           key_size=rsa_keysize,
                                           backend=default_backend())
    public_key = private_key.public_key()
    pem_priv = to_unicode(
        private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()))
    pem_pub = to_unicode(
        public_key.public_bytes(encoding=serialization.Encoding.PEM,
                                format=serialization.PublicFormat.PKCS1))
    return pem_pub, pem_priv
Пример #34
0
def url_decode(url):
    """
    Decodes a base64 encoded, not padded string as used in FIDO U2F
    :param url: base64 urlsafe encoded string
    :type url: basestring or bytes
    :return: the decoded string
    :rtype: bytes
    """
    # remove all non base64 characters (newline, CR) from the string before
    # calculating the padding length
    pad_len = -len(re.sub('[^A-Za-z0-9-_+/]+', '', to_unicode(url))) % 4

    padding = pad_len * "="
    res = base64.urlsafe_b64decode(to_bytes(url) + to_bytes(padding))
    return res
Пример #35
0
    def get_access_token(server=None, client=None, secret=None):

        auth = to_unicode(base64.b64encode(to_bytes(client + ':' + secret)))

        url = "{0!s}/oauth/token?grant_type=client_credentials".format(server)
        resp = requests.get(url,
                            headers={'Authorization': 'Basic ' + auth})

        if resp.status_code != 200:
            info = "Could not get access token: {0!s}".format(resp.status_code)
            log.error(info)
            raise Exception(info)

        access_token = yaml.safe_load(resp.content).get('access_token')
        return access_token
Пример #36
0
    def getUserId(self, LoginName):
        """
        resolve the loginname to the userid.

        :param LoginName: The login name from the credentials
        :type LoginName: string
        :return: UserId as found for the LoginName
        """
        userid = ""
        self._bind()
        LoginName = to_unicode(LoginName)
        login_name = self._escape_loginname(LoginName)

        if len(self.loginname_attribute) > 1:
            loginname_filter = u""
            for l_attribute in self.loginname_attribute:
                loginname_filter += u"({!s}={!s})".format(
                    l_attribute.strip(), login_name)
            loginname_filter = u"|" + loginname_filter
        else:
            loginname_filter = u"{!s}={!s}".format(self.loginname_attribute[0],
                                                   login_name)

        log.debug("login name filter: {!r}".format(loginname_filter))
        filter = u"(&{0!s}({1!s}))".format(self.searchfilter, loginname_filter)

        # create search attributes
        attributes = self.userinfo.values()
        if self.uidtype.lower() != "dn":
            attributes.append(str(self.uidtype))

        log.debug("Searching user {0!r} in LDAP.".format(LoginName))
        self.l.search(search_base=self.basedn,
                      search_scope=self.scope,
                      search_filter=filter,
                      attributes=attributes)

        r = self.l.response
        r = self._trim_result(r)
        if len(r) > 1:  # pragma: no cover
            raise Exception(
                "Found more than one object for Loginname {0!r}".format(
                    LoginName))

        for entry in r:
            userid = self._get_uid(entry, self.uidtype)

        return userid
Пример #37
0
    def getUserId(self, LoginName):
        """
        resolve the loginname to the userid.

        :param LoginName: The login name from the credentials
        :type LoginName: string
        :return: UserId as found for the LoginName
        """
        userid = ""
        self._bind()
        LoginName = to_unicode(LoginName)
        login_name = self._escape_loginname(LoginName)

        if len(self.loginname_attribute) > 1:
            loginname_filter = u""
            for l_attribute in self.loginname_attribute:
                loginname_filter += u"({!s}={!s})".format(l_attribute.strip(),
                                                          login_name)
            loginname_filter = u"|" + loginname_filter
        else:
            loginname_filter = u"{!s}={!s}".format(self.loginname_attribute[0],
                                                   login_name)

        log.debug("login name filter: {!r}".format(loginname_filter))
        filter = u"(&{0!s}({1!s}))".format(self.searchfilter, loginname_filter)

        # create search attributes
        attributes = self.userinfo.values()
        if self.uidtype.lower() != "dn":
            attributes.append(str(self.uidtype))

        log.debug("Searching user {0!r} in LDAP.".format(LoginName))
        self.l.search(search_base=self.basedn,
                      search_scope=self.scope,
                      search_filter=filter,
                      attributes=attributes)

        r = self.l.response
        r = self._trim_result(r)
        if len(r) > 1:  # pragma: no cover
            raise Exception("Found more than one object for Loginname {0!r}".format(
                            LoginName))

        for entry in r:
            userid = self._get_uid(entry, self.uidtype)

        return userid
Пример #38
0
def get_all_params(param, body):
    """
    Combine parameters from GET and POST requests
    """
    return_param = {}
    for key in param.keys():
        return_param[key] = param[key]

    # In case of serialized JSON data in the body, add these to the values.
    try:
        json_data = json.loads(to_unicode(body))
        for k, v in json_data.items():
            return_param[k] = v
    except Exception as exx:
        log.debug("Can not get param: {0!s}".format(exx))

    return return_param
Пример #39
0
def geturandom(length=20, hex=False):
    '''
    get random - from the security module

    :param length: length of the returned bytes - default is 20 bytes
    :type length: int
    :param hex: convert result to hexstring
    :type hex: bool

    :return:
    :rtype: bytes, unicode

    '''
    hsm = get_hsm()
    ret = hsm.random(length)

    if hex:
        ret = to_unicode(binascii.hexlify(ret))
    return ret
Пример #40
0
def get_all_params(request):
    """
    Retrieve all parameters from a request, no matter if these are GET or POST requests
    or parameters are contained as viewargs like the serial in DELETE /token/<serial>

    :param request: The flask request object
    """
    param = request.values
    body = request.data
    return_param = {}
    if param:
        log.debug(u"Update params in request {0!s} {1!s} with values.".format(
            request.method, request.base_url))
        # Add the unquoted HTML and form parameters
        return_param = {key: unquote(value) for (key, value) in param.items()}

    if request.json:
        log.debug(
            u"Update params in request {0!s} {1!s} with JSON data.".format(
                request.method, request.base_url))
        # Add the original JSON data
        return_param.update(request.json)
    elif body:
        # In case of serialized JSON data in the body, add these to the values.
        try:
            json_data = json.loads(to_unicode(body))
            for k, v in json_data.items():
                return_param[k] = v
        except Exception as exx:
            log.debug("Can not get param: {0!s}".format(exx))

    if request.view_args:
        log.debug(
            u"Update params in request {0!s} {1!s} with view_args.".format(
                request.method, request.base_url))
        # We add the unquoted view_args
        return_param.update({
            key: unquote(value)
            for (key, value) in request.view_args.items()
        })

    return return_param
Пример #41
0
    def decrypt(self, input_data):
        """
        Decrypts the input data with one of the private keys.

        Since this functionality is only used for decrypting import lists, the
        decrypted data is assumed to be of type text und thus converted to unicode.

        :param input_data: The data to decrypt
        :type input_data: str or bytes
        :return: The decrypted input_data
        :rtype: str
        """
        decrypted = self.gpg.decrypt(message=input_data)

        if not decrypted.ok:
            log.error(u"Decrpytion failed: {0!s}. {1!s}".format(
                decrypted.status, decrypted.stderr))
            raise Exception(decrypted.stderr)

        return to_unicode(decrypted.data)
Пример #42
0
    def decrypt(self, input_data):
        """
        Decrypts the input data with one of the private keys.

        Since this functionality is only used for decrypting import lists, the
        decrypted data is assumed to be of type text und thus converted to unicode.

        :param input_data: The data to decrypt
        :type input_data: str or bytes
        :return: The decrypted input_data
        :rtype: str
        """
        decrypted = self.gpg.decrypt(message=input_data)

        if not decrypted.ok:
            log.error(u"Decrpytion failed: {0!s}. {1!s}".format(
                decrypted.status, decrypted.stderr))
            raise Exception(decrypted.stderr)

        return to_unicode(decrypted.data)
Пример #43
0
 def _on_Connection(self,
                    server,
                    user,
                    password,
                    auto_bind=None,
                    client_strategy=None,
                    authentication=None,
                    check_names=None,
                    auto_referrals=None,
                    receive_timeout=None):
     """
     We need to create a Connection object with
     methods:
         add()
         modify()
         search()
         unbind()
     and object
         response
     """
     # check the password
     correct_password = False
     # Anonymous bind
     # Reload the directory just in case a change has been made to
     # user credentials
     self.directory = self._load_data(DIRECTORY)
     if authentication == ldap3.ANONYMOUS and user == "":
         correct_password = True
     for entry in self.directory:
         if to_unicode(entry.get("dn")) == user:
             pw = entry.get("attributes").get("userPassword")
             # password can be unicode
             if to_bytes(pw) == to_bytes(password):
                 correct_password = True
             elif pw.startswith('{SSHA}'):
                 correct_password = ldap_salted_sha1.verify(password, pw)
             else:
                 correct_password = False
     self.con_obj = Connection(self.directory)
     self.con_obj.bound = correct_password
     return self.con_obj
 def test_05_templates(self):
     cwd = os.getcwd()
     cacon = LocalCAConnector("localCA",
                              {"cakey": CAKEY,
                               "cacert": CACERT,
                               "openssl.cnf": OPENSSLCNF,
                               "WorkingDir": cwd + "/" + WORKINGDIR,
                               ATTR.TEMPLATE_FILE: "templates.yaml"})
     templates = cacon.get_templates()
     self.assertTrue("user" in templates)
     self.assertTrue("webserver" in templates)
     self.assertTrue("template3" in templates)
     cert = cacon.sign_request(SPKAC, options={"spkac": 1,
                                               "template": "webserver"})
     expires = to_unicode(cert.get_notAfter())
     import datetime
     dt = datetime.datetime.strptime(expires, "%Y%m%d%H%M%SZ")
     ddiff = dt - datetime.datetime.now()
     # The certificate is signed for 750 days
     self.assertTrue(ddiff.days > 740, ddiff.days)
     self.assertTrue(ddiff.days < 760, ddiff.days)
Пример #45
0
    def test_39_generate_sym_key(self):
        db_token = Token("symkey", tokentype="no_matter", userid=1000)
        db_token.save()
        token_obj = TokenClass(db_token)
        key = token_obj.generate_symmetric_key("1234567890", "abc")
        self.assertEqual(key, "1234567abc")
        self.assertRaises(Exception, token_obj.generate_symmetric_key,
                          "1234", "1234")
        self.assertRaises(Exception, token_obj.generate_symmetric_key,
                          "1234", "12345")

        # Now run the init/update process
        # 1. step
        token_obj.update({"2stepinit": "1",
                          "genkey": "1"
                          })

        self.assertEqual(db_token.rollout_state, "clientwait")
        self.assertEqual(db_token.active, False)
        serial = db_token.serial
        details = token_obj.init_details

        # 2. step
        client_component = "AAAAAA"
        token_obj.update({"serial": serial,
                          "otpkey": client_component
                          })
        self.assertEqual(db_token.rollout_state, "")
        self.assertEqual(db_token.active, True)

        # Given a client component of K bytes, the base algorithm
        # simply replaces the last K bytes of the server component
        # with the client component.
        server_component = details.get("otpkey")[:-len(client_component)]
        expected_otpkey = server_component + client_component

        self.assertEqual(to_unicode(db_token.get_otpkey().getKey()),
                         expected_otpkey)

        token_obj.delete_token()
Пример #46
0
    def test_39_generate_sym_key(self):
        db_token = Token("symkey", tokentype="no_matter", userid=1000)
        db_token.save()
        token_obj = TokenClass(db_token)
        key = token_obj.generate_symmetric_key("1234567890", "abc")
        self.assertEqual(key, "1234567abc")
        self.assertRaises(Exception, token_obj.generate_symmetric_key,
                          "1234", "1234")
        self.assertRaises(Exception, token_obj.generate_symmetric_key,
                          "1234", "12345")

        # Now run the init/update process
        # 1. step
        token_obj.update({"2stepinit": "1",
                          "genkey": "1"
                          })

        self.assertEqual(db_token.rollout_state, "clientwait")
        self.assertEqual(db_token.active, False)
        serial = db_token.serial
        details = token_obj.init_details

        # 2. step
        client_component = "AAAAAA"
        token_obj.update({"serial": serial,
                          "otpkey": client_component
                          })
        self.assertEqual(db_token.rollout_state, "")
        self.assertEqual(db_token.active, True)

        # Given a client component of K bytes, the base algorithm
        # simply replaces the last K bytes of the server component
        # with the client component.
        server_component = details.get("otpkey")[:-len(client_component)]
        expected_otpkey = server_component + client_component

        self.assertEqual(to_unicode(db_token.get_otpkey().getKey()),
                         expected_otpkey)

        token_obj.delete_token()
Пример #47
0
    def getUserInfo(self, userId):
        """
        This function returns all user info for a given userid/object.

        :param userId: The userid of the object
        :type userId: string
        :return: A dictionary with the keys defined in self.userinfo
        :rtype: dict
        """
        ret = {}
        self._bind()

        if self.uidtype.lower() == "dn":
            # encode utf8, so that also german ulauts work in the DN
            self.l.search(search_base=to_utf8(userId),
                          search_scope=self.scope,
                          search_filter=u"(&" + self.searchfilter + u")",
                          attributes=self.userinfo.values())
        else:
            search_userId = to_unicode(self._trim_user_id(userId))
            filter = u"(&{0!s}({1!s}={2!s}))".format(self.searchfilter,
                                                     self.uidtype,
                                                     search_userId)
            self.l.search(search_base=self.basedn,
                              search_scope=self.scope,
                              search_filter=filter,
                              attributes=self.userinfo.values())

        r = self.l.response
        r = self._trim_result(r)
        if len(r) > 1:  # pragma: no cover
            raise Exception("Found more than one object for uid {0!r}".format(userId))

        for entry in r:
            attributes = entry.get("attributes")
            ret = self._ldap_attributes_to_user_object(attributes)

        return ret
Пример #48
0
    def calcOtp(self, counter, key=None, pin=None):
        '''
        calculate an otp value from counter/time, key and pin
        
        :param counter:    counter/time to be checked
        :type counter:     int
        :param key:        the secret key
        :type key:         str
        :param pin:        the secret pin
        :type pin:         str
        
        :return:           the otp value
        :rtype:            str
        '''
        ## ref impl from https://github.com/szimszon/motpy/blob/master/motpy
        if pin is None:
            pin = self.pin
        if key is None:
            key = self.key

        vhash = u"{0:d}{1!s}{2!s}".format(counter, key, pin)
        motp = md5(to_bytes(vhash)).hexdigest()[:self.digits]
        return to_unicode(motp)
Пример #49
0
def _get_crl_next_update(filename):
    """
    Read the CRL file and return the next update as datetime
    :param filename:
    :return:
    """
    dt = None
    f = open(filename)
    crl_buff = f.read()
    f.close()
    crl_obj = crypto.load_crl(crypto.FILETYPE_PEM, crl_buff)
    # Get "Next Update" of CRL
    # Unfortunately pyOpenSSL does not support this. so we dump the
    # CRL and parse the text :-/
    # We do not want to add dependency to pyasn1
    crl_text = to_unicode(crypto.dump_crl(crypto.FILETYPE_TEXT, crl_obj))
    for line in crl_text.split("\n"):
        if "Next Update: " in line:
            key, value = line.split(":", 1)
            date = value.strip()
            dt = datetime.datetime.strptime(date, "%b %d %X %Y %Z")
            break
    return dt
Пример #50
0
    def check_otp(self, otpval, counter=None, window=None, options=None):
        """
        run the RADIUS request against the RADIUS server

        :param otpval: the OTP value
        :param counter: The counter for counter based otp values
        :type counter: int
        :param window: a counter window
        :type counter: int
        :param options: additional token specific options
        :type options: dict
        :return: counter of the matching OTP value.
        :rtype: int
        """
        otp_count = -1
        options = options or {}

        radius_dictionary = None
        radius_identifier = self.get_tokeninfo("radius.identifier")
        radius_user = self.get_tokeninfo("radius.user")
        system_radius_settings = self.get_tokeninfo("radius.system_settings")
        if radius_identifier:
            # New configuration
            radius_server_object = get_radius(radius_identifier)
            radius_server = radius_server_object.config.server
            radius_port = radius_server_object.config.port
            radius_server = u"{0!s}:{1!s}".format(radius_server, radius_port)
            radius_secret = radius_server_object.get_secret()
            radius_dictionary = radius_server_object.config.dictionary

        elif system_radius_settings:
            # system configuration
            radius_server = get_from_config("radius.server")
            radius_secret = get_from_config("radius.secret")
        else:
            # individual token settings
            radius_server = self.get_tokeninfo("radius.server")
            # Read the secret
            secret = self.token.get_otpkey()
            radius_secret = binascii.unhexlify(secret.getKey())

        # here we also need to check for radius.user
        log.debug(u"checking OTP len:{0!s} on radius server: "
                  u"{1!s}, user: {2!r}".format(len(otpval), radius_server,
                                               radius_user))

        try:
            # pyrad does not allow to set timeout and retries.
            # it defaults to retries=3, timeout=5

            # TODO: At the moment we support only one radius server.
            # No round robin.
            server = radius_server.split(':')
            r_server = server[0]
            r_authport = 1812
            if len(server) >= 2:
                r_authport = int(server[1])
            nas_identifier = get_from_config("radius.nas_identifier",
                                             "privacyIDEA")
            if not radius_dictionary:
                radius_dictionary = get_from_config("radius.dictfile",
                                                    "/etc/privacyidea/dictionary")
            log.debug(u"NAS Identifier: %r, "
                      u"Dictionary: %r" % (nas_identifier, radius_dictionary))
            log.debug(u"constructing client object "
                      u"with server: %r, port: %r, secret: %r" %
                      (r_server, r_authport, to_unicode(radius_secret)))

            srv = Client(server=r_server,
                         authport=r_authport,
                         secret=to_bytes(radius_secret),
                         dict=Dictionary(radius_dictionary))

            req = srv.CreateAuthPacket(code=pyrad.packet.AccessRequest,
                                       User_Name=radius_user.encode('utf-8'),
                                       NAS_Identifier=nas_identifier.encode('ascii'))

            req["User-Password"] = req.PwCrypt(otpval)
            if "transactionid" in options:
                req["State"] = str(options.get("transactionid"))

            response = srv.SendPacket(req)
            # TODO: handle the RADIUS challenge
            """
            if response.code == pyrad.packet.AccessChallenge:
                opt = {}
                for attr in response.keys():
                    opt[attr] = response[attr]
                res = False
                log.debug("challenge returned %r " % opt)
                # now we map this to a privacyidea challenge
                if "State" in opt:
                    reply["transactionid"] = opt["State"][0]
                if "Reply-Message" in opt:
                    reply["message"] = opt["Reply-Message"][0]
            """
            if response.code == pyrad.packet.AccessAccept:
                log.info("Radiusserver %s granted "
                         "access to user %s." % (r_server, radius_user))
                otp_count = 0
            else:
                log.warning("Radiusserver %s rejected "
                            "access to user %s." % (r_server, radius_user))

        except Exception as ex:  # pragma: no cover
            log.error("Error contacting radius Server: {0!r}".format((ex)))
            log.debug("{0!s}".format(traceback.format_exc()))

        return otp_count
Пример #51
0
def export_pskc(tokenobj_list, psk=None):
    """
    Take a list of token objects and create a beautifulsoup xml object.

    If no preshared key is given, we create one and return it.

    :param tokenobj_list: list of token objects
    :param psk: pre-shared-key for AES-128-CBC in hex format
    :return: tuple of (psk, number of tokens, beautifulsoup)
    """
    if psk:
        psk = binascii.unhexlify(psk)
    else:
        psk = geturandom(16)

    mackey = geturandom(20)
    encrypted_mackey = aes_encrypt_b64(psk, mackey)
    number_of_exported_tokens = 0

    # define the header
    soup = BeautifulSoup("""<KeyContainer Version="1.0"
     xmlns="urn:ietf:params:xml:ns:keyprov:pskc"
     xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
     xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
     <EncryptionKey>
         <ds:KeyName>Pre-shared-key</ds:KeyName>
     </EncryptionKey>
     <MACMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1">
         <MACKey>
             <xenc:EncryptionMethod
             Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
             <xenc:CipherData>
                 <xenc:CipherValue>{encrypted_mackey}</xenc:CipherValue>
             </xenc:CipherData>
         </MACKey>
     </MACMethod>
""".format(encrypted_mackey=encrypted_mackey), "html.parser")

    for tokenobj in tokenobj_list:
        if tokenobj.type.lower() not in ["totp", "hotp", "pw"]:
            continue
        type = tokenobj.type.lower()
        issuer = "privacyIDEA"
        try:
            manufacturer = tokenobj.token.description.encode("ascii", "replace")
            manufacturer = to_unicode(manufacturer)
        except UnicodeEncodeError:
            manufacturer = "deleted during export"
        serial = tokenobj.token.serial
        otplen = tokenobj.token.otplen
        counter = tokenobj.token.count
        suite = tokenobj.get_tokeninfo("hashlib", default="sha1")
        if type == "totp":
            timestep = tokenobj.get_tokeninfo("timeStep")
            timedrift = tokenobj.get_tokeninfo("timeShift")
        else:
            timestep = 0
            timedrift = 0
        otpkey = tokenobj.token.get_otpkey().getKey()
        try:
            if tokenobj.type.lower() in ["totp", "hotp"]:
                encrypted_otpkey = aes_encrypt_b64(psk, binascii.unhexlify(otpkey))
            elif tokenobj.type.lower() in ["pw"]:
                encrypted_otpkey = aes_encrypt_b64(psk, otpkey)
            else:
                encrypted_otpkey = aes_encrypt_b64(psk, otpkey)
        except TypeError:
            # Some keys might be odd string length
            continue
        try:
            kp2 = BeautifulSoup("""<KeyPackage>
        <DeviceInfo>
          <Manufacturer>{manufacturer}</Manufacturer>
          <SerialNo>{serial}</SerialNo>
        </DeviceInfo>
        <Key Id="{serial}"
             Algorithm="urn:ietf:params:xml:ns:keyprov:pskc:{type}">
                 <Issuer>{issuer}</Issuer>
                 <AlgorithmParameters>
                     <ResponseFormat Length="{otplen}" Encoding="DECIMAL"/>
                     <Suite hashalgo="{suite}" />
                 </AlgorithmParameters>
                 <Data>
                    <Secret>
                         <EncryptedValue>
                             <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
                             <xenc:CipherData>
                                 <xenc:CipherValue>{encrypted_otpkey}</xenc:CipherValue>
                             </xenc:CipherData>
                         </EncryptedValue>
                     </Secret>
                     <ValueMAC>TODOmissing</ValueMAC>
                    <Time>
                        <PlainValue>0</PlainValue>
                    </Time>
                    <TimeInterval>
                        <PlainValue>{timestep}</PlainValue>
                    </TimeInterval>
                    <Counter>
                        <PlainValue>{counter}</PlainValue>
                    </Counter>
                    <TimeDrift>
                        <PlainValue>{timedrift}</PlainValue>
                    </TimeDrift>
                </Data>
        </Key>
        </KeyPackage>""".format(serial=cgi.escape(serial), type=cgi.escape(type), otplen=otplen,
                                issuer=cgi.escape(issuer), manufacturer=cgi.escape(manufacturer),
                                counter=counter, timestep=timestep, encrypted_otpkey=encrypted_otpkey,
                                timedrift=timedrift,
                                suite=cgi.escape(suite)), "html.parser")

            soup.macmethod.insert_after(kp2)
            number_of_exported_tokens += 1
        except Exception as e:
            log.warning(u"Failed to export the token {0!s}: {1!s}".format(serial, e))
            tb = traceback.format_exc()
            log.debug(tb)

    return hexlify_and_unicode(psk), number_of_exported_tokens, soup
Пример #52
0
def parsePSKCdata(xml_data,
                  preshared_key_hex=None,
                  password=None,
                  do_checkserial=False):
    """
    This function parses XML data of a PSKC file, (RFC6030)
    It can read
    * AES-128-CBC encrypted (preshared_key_bin) data
    * password based encrypted data
    * plain text data

    :param xml_data: The XML data
    :type xml_data: basestring
    :param preshared_key_hex: The preshared key, hexlified
    :param password: The password that encrypted the keys
    :param do_checkserial: Check if the serial numbers conform to the OATH
        specification (not yet implemented)

    :return: a dictionary of token dictionaries
        { serial : { otpkey , counter, .... }}
    """

    tokens = {}
    #xml = BeautifulSoup(xml_data, "lxml")
    xml = strip_prefix_from_soup(BeautifulSoup(xml_data, "lxml"))

    if xml.keycontainer.encryptionkey and \
            xml.keycontainer.encryptionkey.derivedkey:
        # If we have a password we also need a tag EncryptionKey in the
        # KeyContainer
        preshared_key_hex = derive_key(xml, password)

    key_packages = xml.keycontainer.findAll("keypackage")
    for key_package in key_packages:
        token = {}
        key = key_package.key
        try:
            token["description"] = key_package.deviceinfo.manufacturer.string
        except Exception as exx:
            log.debug("Can not get manufacturer string {0!s}".format(exx))
        serial = key["id"]
        try:
            serial = key_package.deviceinfo.serialno.string.strip()
        except Exception as exx:
            log.debug("Can not get serial string from device info {0!s}".format(exx))
        algo = key["algorithm"]
        token["type"] = algo.split(":")[-1].lower()
        parameters = key.algorithmparameters
        token["otplen"] = parameters.responseformat["length"] or 6
        try:
            token["hashlib"] = parameters.suite["hashalgo"] or "sha1"
        except Exception as exx:
            log.warning("No compatible suite contained.")
        try:
            if key.data.secret.plainvalue:
                secret = key.data.secret.plainvalue.string
                token["otpkey"] = hexlify_and_unicode(base64.b64decode(secret))
            elif key.data.secret.encryptedvalue:
                encryptionmethod = key.data.secret.encryptedvalue.encryptionmethod
                enc_algorithm = encryptionmethod["algorithm"].split("#")[-1]
                if enc_algorithm.lower() != "aes128-cbc":
                    raise ImportException("We only import PSKC files with "
                                          "AES128-CBC.")
                enc_data = key.data.secret.encryptedvalue.ciphervalue.text
                enc_data = enc_data.strip()
                secret = aes_decrypt_b64(binascii.unhexlify(preshared_key_hex), enc_data)
                if token["type"].lower() in ["hotp", "totp"]:
                    token["otpkey"] = hexlify_and_unicode(secret)
                elif token["type"].lower() in ["pw"]:
                    token["otpkey"] = to_unicode(secret)
                else:
                    token["otpkey"] = to_unicode(secret)
        except Exception as exx:
            log.error("Failed to import tokendata: {0!s}".format(exx))
            log.debug(traceback.format_exc())
            raise ImportException("Failed to import tokendata. Wrong "
                                  "encryption key? %s" % exx)
        if token["type"] in ["hotp", "totp"] and key.data.counter:
            token["counter"] = key.data.counter.text.strip()
        if token["type"] == "totp":
            if key.data.timeinterval:
                token["timeStep"] = key.data.timeinterval.text.strip()
            if key.data.timedrift:
                token["timeShift"] = key.data.timedrift.text.strip()

        tokens[serial] = token
    return tokens
Пример #53
0
    def update(self, param):
        """
        This method is called during the initialization process.
        :param param: parameters from the token init
        :type param: dict
        :return: None
        """
        TokenClass.update(self, param)

        request = getParam(param, "request", optional)
        spkac = getParam(param, "spkac", optional)
        certificate = getParam(param, "certificate", optional)
        generate = getParam(param, "genkey", optional)
        template_name = getParam(param, "template", optional)
        if request or generate:
            # If we do not upload a user certificate, then we need a CA do
            # sign the uploaded request or generated certificate.
            ca = getParam(param, "ca", required)
            self.add_tokeninfo("CA", ca)
            cacon = get_caconnector_object(ca)
        if request:
            # During the initialization process, we need to create the
            # certificate
            x509object = cacon.sign_request(request,
                                            options={"spkac": spkac,
                                                     "template": template_name})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
        elif generate:
            # Create the certificate on behalf of another user.
            # Now we need to create the key pair,
            # the request
            # and the certificate
            # We need the user for whom the certificate should be created
            user = get_user_from_param(param, optionalOrRequired=required)

            keysize = getParam(param, "keysize", optional, 2048)
            key = crypto.PKey()
            key.generate_key(crypto.TYPE_RSA, keysize)
            req = crypto.X509Req()
            req.get_subject().CN = user.login
            # Add email to subject
            if user.info.get("email"):
                req.get_subject().emailAddress = user.info.get("email")
            req.get_subject().organizationalUnitName = user.realm
            # TODO: Add Country, Organization, Email
            # req.get_subject().countryName = 'xxx'
            # req.get_subject().stateOrProvinceName = 'xxx'
            # req.get_subject().localityName = 'xxx'
            # req.get_subject().organizationName = 'xxx'
            req.set_pubkey(key)
            req.sign(key, "sha256")
            csr = to_unicode(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))
            x509object = cacon.sign_request(csr, options={"template": template_name})
            certificate = crypto.dump_certificate(crypto.FILETYPE_PEM,
                                                  x509object)
            # Save the private key to the encrypted key field of the token
            s = crypto.dump_privatekey(crypto.FILETYPE_PEM, key)
            self.add_tokeninfo("privatekey", s, value_type="password")

        if "pin" in param:
            self.set_pin(param.get("pin"), encrypt=True)

        if certificate:
            self.add_tokeninfo("certificate", certificate)
Пример #54
0
def import_policy_api(filename=None):
    """
    This function is used to import policies from a file.

    :jsonparam filename: The name of the file in the request

    :formparam file: The uploaded file contents

    :return: A json response with the number of imported policies.

    :status 200: Policy created or modified.
    :status 401: Authentication failed

    **Example request**:

    .. sourcecode:: http

       POST /policy/import/backup-policy.cfg HTTP/1.1
       Host: example.com
       Accept: application/json

    **Example response**:

    .. sourcecode:: http

       HTTP/1.0 200 OK
       Content-Type: application/json

        {
          "id": 1,
          "jsonrpc": "2.0",
          "result": {
            "status": true,
            "value": 2
          },
          "version": "privacyIDEA unknown"
        }


    """
    policy_file = request.files['file']
    file_contents = ""
    # In case of form post requests, it is a "instance" of FieldStorage
    # i.e. the Filename is selected in the browser and the data is
    # transferred
    # in an iframe. see: http://jquery.malsup.com/form/#sample4
    #
    if type(policy_file) == FieldStorage:  # pragma: no cover
        log.debug("Field storage file: %s", policy_file)
        file_contents = policy_file.value
    elif type(policy_file) == FileStorage:
        log.debug("Werkzeug File storage file: %s", policy_file)
        file_contents = policy_file.read()
    else:  # pragma: no cover
        file_contents = policy_file

    # The policy file should contain readable characters
    file_contents = to_unicode(file_contents)

    if file_contents == "":
        log.error("Error loading/importing policy file. file {0!s} empty!".format(
                  filename))
        raise ParameterError("Error loading policy. File empty!")

    policy_num = import_policies(file_contents=file_contents)
    g.audit_object.log({"success": True,
                        'info': u"imported {0:d} policies from file {1!s}".format(
                            policy_num, filename)})

    return send_result(policy_num)
Пример #55
0
def parse_registration_data(reg_data, verify_cert=True):
    """
    returns the parsed registration data in a tuple
    attestation_cert, user_pub_key, key_handle, signature, description

     * attestation_cert is a x509 object
     * user_pub_key is a hex string
     * key_handle is a hex string
     * signature is a hex string
     * description is a basestring

    see
    https://fidoalliance.org/specs/fido-u2f-v1.0-nfc-bt-amendment
    -20150514/fido-u2f-raw-message-formats.html#registration-messages

    :param reg_data: base64 encoded registration data
    :param verify_cert: whether the attestation certificate should be verified
    :return: tuple
    """
    reg_data_bin = url_decode(reg_data)
    reserved_byte = six.int2byte(six.indexbytes(reg_data_bin, 0))  # must be '\x05'
    if reserved_byte != b'\x05':
        raise Exception("The registration data is in a wrong format. It must"
                        "start with 0x05")
    user_pub_key = reg_data_bin[1:66]
    key_handle_len = six.indexbytes(reg_data_bin, 66)
    # We need to save the key handle
    key_handle = reg_data_bin[67:67+key_handle_len]

    certificate = reg_data_bin[67+key_handle_len:]
    attestation_cert = crypto.load_certificate(crypto.FILETYPE_ASN1,
                                               certificate)
    cert_len = len(crypto.dump_certificate(crypto.FILETYPE_ASN1,
                                           attestation_cert))
    # TODO: Check the issuer of the certificate
    issuer = attestation_cert.get_issuer()
    log.debug("The attestation certificate is signed by {0!r}".format(issuer))
    not_after = to_unicode(attestation_cert.get_notAfter())
    not_before = to_unicode(attestation_cert.get_notBefore())
    log.debug("The attestation certificate "
              "is valid from %s to %s" % (not_before, not_after))
    start_time = time.strptime(not_before, "%Y%m%d%H%M%SZ")
    end_time = time.strptime(not_after, "%Y%m%d%H%M%SZ")
    # check the validity period of the certificate
    if verify_cert:
        if start_time > time.localtime() or \
                        end_time < time.localtime():  #pragma no cover
            log.error("The certificate is not valid. {0!s} -> {1!s}".format(not_before,
                                                                  not_after))
            raise Exception("The time of the attestation certificate is not "
                            "valid.")

    # Get the subject as description
    subj_x509name = attestation_cert.get_subject()
    subj_list = subj_x509name.get_components()
    description = ""
    cdump = to_unicode(crypto.dump_certificate(crypto.FILETYPE_PEM, attestation_cert))
    log.debug("This attestation certificate registered: {0!s}".format(cdump))

    for component in subj_list:
        # each component is a tuple. We are looking for CN
        if component[0].upper() == b"CN":
            description = to_unicode(component[1])
            break

    signature = reg_data_bin[67+key_handle_len+cert_len:]
    return (attestation_cert, hexlify_and_unicode(user_pub_key),
            hexlify_and_unicode(key_handle), hexlify_and_unicode(signature),
            description)
Пример #56
0
    def sign_request(self, csr, options=None):
        """
        Signs a certificate request with the key of the CA.

        options can be
        WorkingDir: The directory where the configuration like openssl.cnf
        can be found.
        CSRDir: The directory, where to save the certificate signing
        requests. This is relative to the WorkingDir.
        CertificateDir: The directory where to save the certificates. This is
        relative to the WorkingDir.

        :param csr: Certificate signing request
        :type csr: PEM string or SPKAC
        :param options: Additional options like the validity time or the
            template or spkac=1
        :type options: dict
        :return: Returns the certificate object
        :rtype: X509
        """
        # Sign the certificate for one year
        options = options or {}
        days = options.get("days", 365)
        spkac = options.get("spkac")
        config = options.get(ATTR.OPENSSL_CNF,
                             self.config.get(
                                 ATTR.OPENSSL_CNF, "/etc/ssl/openssl.cnf"))
        extension = options.get("extension", "server")
        template_name = options.get("template")
        workingdir = options.get(ATTR.WORKING_DIR,
                                 self.config.get(ATTR.WORKING_DIR))
        csrdir = options.get(ATTR.CSR_DIR,
                             self.config.get(ATTR.CSR_DIR, ""))
        certificatedir = options.get(ATTR.CERT_DIR,
                                     self.config.get(ATTR.CERT_DIR, ""))
        if workingdir:
            if not csrdir.startswith("/"):
                # No absolut path
                csrdir = workingdir + "/" + csrdir
            if not certificatedir.startswith("/"):
                certificatedir = workingdir + "/" + certificatedir

        if template_name:
            t_data = self.templates.get(template_name)
            extension = t_data.get("extensions", extension)
            days = t_data.get("days", days)

        # Determine filename from the CN of the request
        if spkac:
            common_name = re.search("CN=(.*)", csr).group(0).split('=')[1]
            csr_filename = common_name + ".txt"
            certificate_filename = common_name + ".der"
        else:
            csr_obj = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr)
            csr_filename = self._filename_from_x509(csr_obj.get_subject(),
                                                    file_extension="req")
            certificate_filename = self._filename_from_x509(
                csr_obj.get_subject(), file_extension="pem")
            #csr_extensions = csr_obj.get_extensions()
        csr_filename = csr_filename.replace(" ", "_")
        certificate_filename = certificate_filename.replace(" ", "_")
        # dump the file
        csr_filename = to_unicode(csr_filename.encode('ascii', 'ignore'))
        with open(os.path.join(csrdir, csr_filename), "w") as f:
            f.write(csr)

        # TODO: use the template name to set the days and the extention!
        if spkac:
            cmd = CA_SIGN_SPKAC.format(cakey=self.cakey, cacert=self.cacert,
                                       days=days, config=config,
                                       extension=extension,
                                       spkacfile=os.path.join(csrdir, csr_filename),
                                       certificate=os.path.join(certificatedir,
                                                                certificate_filename))
        else:
            cmd = CA_SIGN.format(cakey=self.cakey, cacert=self.cacert,
                                 days=days, config=config, extension=extension,
                                 csrfile=os.path.join(csrdir, csr_filename),
                                 certificate=os.path.join(certificatedir,
                                                          certificate_filename))
        # run the command
        args = shlex.split(cmd)
        p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=workingdir)
        result, error = p.communicate()
        if p.returncode != 0:  # pragma: no cover
            # Some error occurred
            raise CAError(error)

        with open(os.path.join(certificatedir, certificate_filename), "rb") as f:
            certificate = f.read()

        # We return the cert_obj.
        if spkac:
            filetype = crypto.FILETYPE_ASN1
        else:
            filetype = crypto.FILETYPE_PEM
        cert_obj = crypto.load_certificate(filetype, certificate)
        return cert_obj
Пример #57
0
def loadtokens_api(filename=None):
    """
    The call imports the given file containing token definitions.
    The file can be an OATH CSV file, an aladdin XML file or a Yubikey CSV file
    exported from the yubikey initialization tool.

    The function is called as a POST request with the file upload.

    :jsonparam filename: The name of the token file, that is imported
    :jsonparam type: The file type. Can be "aladdin-xml",
        "oathcsv" or "yubikeycsv".
    :jsonparam tokenrealms: comma separated list of tokens.
    :jsonparam psk: Pre Shared Key, when importing PSKC
    :return: The number of the imported tokens
    :rtype: int
    """
    if not filename:
        filename = getParam(request.all_data, "filename", required)
    known_types = ['aladdin-xml', 'oathcsv', "OATH CSV", 'yubikeycsv',
                   'Yubikey CSV', 'pskc']
    file_type = getParam(request.all_data, "type", required)
    hashlib = getParam(request.all_data, "aladdin_hashlib")
    aes_psk = getParam(request.all_data, "psk")
    aes_password = getParam(request.all_data, "password")
    if aes_psk and len(aes_psk) != 32:
        raise TokenAdminError("The Pre Shared Key must be 128 Bit hex "
                              "encoded. It must be 32 characters long!")
    trealms = getParam(request.all_data, "tokenrealms") or ""
    tokenrealms = []
    if trealms:
        tokenrealms = trealms.split(",")

    TOKENS = {}
    token_file = request.files['file']
    file_contents = ""
    # In case of form post requests, it is a "instance" of FieldStorage
    # i.e. the Filename is selected in the browser and the data is
    # transferred
    # in an iframe. see: http://jquery.malsup.com/form/#sample4
    #
    if type(token_file) == FieldStorage:  # pragma: no cover
        log.debug("Field storage file: %s", token_file)
        file_contents = token_file.value
    elif type(token_file) == FileStorage:
        log.debug("Werkzeug File storage file: %s", token_file)
        file_contents = token_file.read()
    else:  # pragma: no cover
        file_contents = token_file

    file_contents = to_unicode(file_contents)

    if file_contents == "":
        log.error("Error loading/importing token file. file {0!s} empty!".format(
                  filename))
        raise ParameterError("Error loading token file. File empty!")

    if file_type not in known_types:
        log.error("Unknown file type: >>{0!s}<<. We only know the types: {1!s}".format(file_type, ', '.join(known_types)))
        raise TokenAdminError("Unknown file type: >>%s<<. We only know the "
                              "types: %s" % (file_type,
                                             ', '.join(known_types)))

    # Decrypt file, if necessary
    if file_contents.startswith("-----BEGIN PGP MESSAGE-----"):
        GPG = GPGImport(current_app.config)
        file_contents = GPG.decrypt(file_contents)

    # Parse the tokens from file and get dictionary
    if file_type == "aladdin-xml":
        TOKENS = parseSafeNetXML(file_contents)
    elif file_type in ["oathcsv", "OATH CSV"]:
        TOKENS = parseOATHcsv(file_contents)
    elif file_type in ["yubikeycsv", "Yubikey CSV"]:
        TOKENS = parseYubicoCSV(file_contents)
    elif file_type in ["pskc"]:
        TOKENS = parsePSKCdata(file_contents, preshared_key_hex=aes_psk,
                               password=aes_password)

    # Now import the Tokens from the dictionary
    ret = ""
    for serial in TOKENS:
        log.debug("importing token {0!s}".format(TOKENS[serial]))

        log.info("initialize token. serial: {0!s}, realm: {1!s}".format(serial,
                                                              tokenrealms))

        import_token(serial,
                     TOKENS[serial],
                     tokenrealms=tokenrealms,
                     default_hashlib=hashlib)

    g.audit_object.log({'info': u"{0!s}, {1!s} (imported: {2:d})".format(file_type,
                                                           token_file,
                                                           len(TOKENS)),
                        'serial': ', '.join(TOKENS)})
    # logTokenNum()

    return send_result(len(TOKENS))
Пример #58
0
    def getUserId(self, LoginName):
        """
        resolve the loginname to the userid.

        :param LoginName: The login name from the credentials
        :type LoginName: string
        :return: UserId as found for the LoginName
        """
        userid = ""
        self._bind()
        LoginName = to_unicode(LoginName)
        login_name = self._escape_loginname(LoginName)

        if len(self.loginname_attribute) > 1:
            loginname_filter = u""
            for l_attribute in self.loginname_attribute:
                # Special case if we have a guid
                try:
                    if l_attribute.lower() == "objectguid":
                        search_login_name = trim_objectGUID(login_name)
                    else:
                        search_login_name = login_name
                    loginname_filter += u"({!s}={!s})".format(l_attribute.strip(),
                                                              search_login_name)
                except ValueError:
                    # This happens if we have a self.loginname_attribute like ["sAMAccountName","objectGUID"],
                    # the user logs in with his sAMAccountName, which can
                    # not be transformed to a UUID
                    log.debug(u"Can not transform {0!s} to a objectGUID.".format(login_name))

            loginname_filter = u"|" + loginname_filter
        else:
            if self.loginname_attribute[0].lower() == "objectguid":
                search_login_name = trim_objectGUID(login_name)
            else:
                search_login_name = login_name
            loginname_filter = u"{!s}={!s}".format(self.loginname_attribute[0],
                                                   search_login_name)

        log.debug("login name filter: {!r}".format(loginname_filter))
        filter = u"(&{0!s}({1!s}))".format(self.searchfilter, loginname_filter)

        # create search attributes
        attributes = list(self.userinfo.values())
        if self.uidtype.lower() != "dn":
            attributes.append(str(self.uidtype))

        log.debug("Searching user {0!r} in LDAP.".format(LoginName))
        self.l.search(search_base=self.basedn,
                      search_scope=self.scope,
                      search_filter=filter,
                      attributes=attributes)

        r = self.l.response
        r = self._trim_result(r)
        if len(r) > 1:  # pragma: no cover
            raise Exception("Found more than one object for Loginname {0!r}".format(
                            LoginName))

        for entry in r:
            userid = self._get_uid(entry, self.uidtype)

        return userid