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'))
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")
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
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])
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
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
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
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
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")
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)
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)
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)
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
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 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)))
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
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
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
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')
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
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
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])
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
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
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
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
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
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
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
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
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
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)
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)
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()
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
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)
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
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
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
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
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)
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)
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)
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
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))
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