def create_keys(self): """ Connect to the HSM and create the encryption keys. The HSM connection should already be configured in pi.cfg. We will create new keys with new key labels :return: a dictionary of the created key labels """ # We need a new read/write session session = self.pkcs11.openSession(self.slot, PyKCS11.CKF_SERIAL_SESSION | PyKCS11.CKF_RW_SESSION) # We need to logout, otherwise we get CKR_USER_ALREADY_LOGGED_IN session.logout() session.login(self.password) key_labels = {"token": "", "config": "", "value": ""} for kl in key_labels.keys(): label = "{0!s}_{1!s}".format(kl, get_alphanum_str()) aesTemplate = [ (PyKCS11.CKA_CLASS, PyKCS11.CKO_SECRET_KEY), (PyKCS11.CKA_KEY_TYPE, PyKCS11.CKK_AES), (PyKCS11.CKA_VALUE_LEN, 32), (PyKCS11.CKA_LABEL, label), (PyKCS11.CKA_ID, label), (PyKCS11.CKA_TOKEN, PyKCS11.CK_TRUE), (PyKCS11.CKA_PRIVATE, True), (PyKCS11.CKA_SENSITIVE, True), (PyKCS11.CKA_ENCRYPT, True), (PyKCS11.CKA_DECRYPT, True), (PyKCS11.CKA_TOKEN, True), (PyKCS11.CKA_WRAP, True), (PyKCS11.CKA_UNWRAP, True), (PyKCS11.CKA_EXTRACTABLE, False) ] aesKey = session.generateKey(aesTemplate) key_labels[kl] = label session.logout() session.closeSession() return key_labels
def create_challenge(self): """ Depending on the self.challenge_type and the self.challenge_length we create a challenge :return: a challenge string """ ret = None if self.challenge_type == "QH": ret = geturandom(length=self.challenge_length, hex=True) elif self.challenge_type == "QA": ret = get_alphanum_str(self.challenge_length) elif self.challenge_type == "QN": ret = get_rand_digit_str(length=self.challenge_length) if not ret: # pragma: no cover raise Exception("OCRA.create_challenge failed. Obviously no good " "challenge_type!") return ret
def create_challenge(self): """ Depending on the self.challenge_type and the self.challenge_length we create a challenge :return: a challenge string """ ret = None if self.challenge_type == "QH": ret = geturandom(length=self.challenge_length, hex=True) elif self.challenge_type == "QA": ret = get_alphanum_str(self.challenge_length) elif self.challenge_type == "QN": ret = get_rand_digit_str(length=self.challenge_length) if not ret: # pragma: no cover raise Exception("OCRA.create_challenge failed. Obviously no good " "challenge_type!") return ret
def test_05_get_alphanum_str(self): r = get_alphanum_str(20) self.assertEqual(len(r), 20)
def create_challenge(self, transactionid=None, options=None): """ This method creates a challenge, which is submitted to the user. The submitted challenge will be preserved in the challenge database. If no transaction id is given, the system will create a transaction id and return it, so that the response can refer to this transaction. :param transactionid: the id of this challenge :param options: the request context parameters / data :type options: dict :return: tuple of (bool, message, transactionid, reply_dict) :rtype: tuple The return tuple builds up like this: ``bool`` if submit was successful; ``message`` which is displayed in the JSON response; additional challenge ``reply_dict``, which are displayed in the JSON challenges response. """ options = options or {} message = 'Please answer the challenge' attributes = {} # Get ValidityTime=120s. Maybe there is a OCRAChallengeValidityTime... validity = int(get_from_config('DefaultChallengeValidityTime', 120)) tokentype = self.get_tokentype().lower() lookup_for = tokentype.capitalize() + 'ChallengeValidityTime' validity = int(get_from_config(lookup_for, validity)) # Get the OCRASUITE from the token information ocrasuite = self.get_tokeninfo("ocrasuite") or OCRA_DEFAULT_SUITE challenge = options.get("challenge") # TODO: we could add an additional parameter to hash the challenge # cleartext -> sha1 if not challenge: # If no challenge is given in the Request, we create a random # challenge based on the OCRA-SUITE os = OCRASuite(ocrasuite) challenge = os.create_challenge() else: # Add a random challenge if options.get("addrandomchallenge"): challenge += get_alphanum_str( int(options.get("addrandomchallenge"))) attributes["original_challenge"] = challenge attributes["qrcode"] = create_img(challenge) if options.get("hashchallenge", "").lower() == "sha256": challenge = hexlify_and_unicode( hashlib.sha256(to_bytes(challenge)).digest()) elif options.get("hashchallenge", "").lower() == "sha512": challenge = hexlify_and_unicode( hashlib.sha512(to_bytes(challenge)).digest()) elif options.get("hashchallenge"): challenge = hexlify_and_unicode( hashlib.sha1(to_bytes(challenge)).digest()) # Create the challenge in the database db_challenge = Challenge(self.token.serial, transaction_id=None, challenge=challenge, data=None, session=None, validitytime=validity) db_challenge.save() attributes["challenge"] = challenge reply_dict = {"attributes": attributes} return True, message, db_challenge.transaction_id, reply_dict
def create_challenge(self, transactionid=None, options=None): """ This method creates a challenge, which is submitted to the user. The submitted challenge will be preserved in the challenge database. If no transaction id is given, the system will create a transaction id and return it, so that the response can refer to this transaction. :param transactionid: the id of this challenge :param options: the request context parameters / data :type options: dict :return: tuple of (bool, message, transactionid, attributes) :rtype: tuple The return tuple builds up like this: ``bool`` if submit was successful; ``message`` which is displayed in the JSON response; additional ``attributes``, which are displayed in the JSON response. """ options = options or {} message = 'Please answer the challenge' attributes = {} # Get ValidityTime=120s. Maybe there is a OCRAChallengeValidityTime... validity = int(get_from_config('DefaultChallengeValidityTime', 120)) tokentype = self.get_tokentype().lower() lookup_for = tokentype.capitalize() + 'ChallengeValidityTime' validity = int(get_from_config(lookup_for, validity)) # Get the OCRASUITE from the token information ocrasuite = self.get_tokeninfo("ocrasuite") or OCRA_DEFAULT_SUITE challenge = options.get("challenge") # TODO: we could add an additional parameter to hash the challenge # cleartext -> sha1 if not challenge: # If no challenge is given in the Request, we create a random # challenge based on the OCRA-SUITE os = OCRASuite(ocrasuite) challenge = os.create_challenge() else: # Add a random challenge if options.get("addrandomchallenge"): challenge += get_alphanum_str(int(options.get( "addrandomchallenge"))) attributes["original_challenge"] = challenge attributes["qrcode"] = create_img(challenge) if options.get("hashchallenge", "").lower() == "sha256": challenge = binascii.hexlify(hashlib.sha256(challenge).digest()) elif options.get("hashchallenge", "").lower() == "sha512": challenge = binascii.hexlify(hashlib.sha512(challenge).digest()) elif options.get("hashchallenge"): challenge = binascii.hexlify(hashlib.sha1(challenge).digest()) # Create the challenge in the database db_challenge = Challenge(self.token.serial, transaction_id=None, challenge=challenge, data=None, session=None, validitytime=validity) db_challenge.save() attributes["challenge"] = challenge return True, message, db_challenge.transaction_id, attributes
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: """ ret = True g = options.get("g") request = options.get("request") response = options.get("response") content = self._get_response_content(response) handler_def = options.get("handler_def") handler_options = handler_def.get("options", {}) notify_type = handler_options.get("To", NOTIFY_TYPE.TOKENOWNER) reply_to_type = handler_options.get("reply_to") try: logged_in_user = g.logged_in_user except Exception: logged_in_user = {} tokenowner = self._get_tokenowner(request) log.debug(u"Executing event for action {0!r}, user {1!r}, " u"logged_in_user {2!r}".format(action, tokenowner, logged_in_user)) # Determine recipient recipient = None reply_to = None if reply_to_type == NOTIFY_TYPE.NO_REPLY_TO: reply_to = "" elif reply_to_type == NOTIFY_TYPE.TOKENOWNER and not tokenowner.is_empty( ): reply_to = tokenowner.info.get("email") elif reply_to_type == NOTIFY_TYPE.INTERNAL_ADMIN: username = handler_options.get("reply_to " + NOTIFY_TYPE.INTERNAL_ADMIN) internal_admin = get_db_admin(username) reply_to = internal_admin.email if internal_admin else "" elif reply_to_type == NOTIFY_TYPE.ADMIN_REALM: # Adds all email addresses from a specific admin realm to the reply-to-header admin_realm = handler_options.get("reply_to " + NOTIFY_TYPE.ADMIN_REALM) attr = is_attribute_at_all() ulist = get_user_list({"realm": admin_realm}, custom_attributes=attr) # create a list of all user-emails, if the user has an email emails = [u.get("email") for u in ulist if u.get("email")] reply_to = ",".join(emails) elif reply_to_type == NOTIFY_TYPE.LOGGED_IN_USER: # Add email address from the logged in user into the reply-to header if logged_in_user.get( "username") and not logged_in_user.get("realm"): # internal admins have no realm internal_admin = get_db_admin(logged_in_user.get("username")) if internal_admin: reply_to = internal_admin.email if internal_admin else "" else: # Try to find the user in the specified realm user_obj = User(logged_in_user.get("username"), logged_in_user.get("realm")) if user_obj: reply_to = user_obj.info.get("email") if user_obj else "" elif reply_to_type == NOTIFY_TYPE.EMAIL: email = handler_options.get("reply_to " + NOTIFY_TYPE.EMAIL, "").split(",") reply_to = email[0] else: log.warning("Was not able to determine the email for the reply-to " "header: {0!s}".format(handler_def)) if notify_type == NOTIFY_TYPE.TOKENOWNER and not tokenowner.is_empty(): recipient = { "givenname": tokenowner.info.get("givenname"), "surname": tokenowner.info.get("surname"), "username": tokenowner.login, "userrealm": tokenowner.realm, "email": tokenowner.info.get("email"), "mobile": tokenowner.info.get("mobile") } elif notify_type == NOTIFY_TYPE.INTERNAL_ADMIN: username = handler_options.get("To " + NOTIFY_TYPE.INTERNAL_ADMIN) internal_admin = get_db_admin(username) recipient = { "givenname": username, "email": internal_admin.email if internal_admin else "" } elif notify_type == NOTIFY_TYPE.ADMIN_REALM: # Send emails to all the users in the specified admin realm admin_realm = handler_options.get("To " + NOTIFY_TYPE.ADMIN_REALM) attr = is_attribute_at_all() ulist = get_user_list({"realm": admin_realm}, custom_attributes=attr) # create a list of all user-emails, if the user has an email emails = [u.get("email") for u in ulist if u.get("email")] recipient = { "givenname": "admin of realm {0!s}".format(admin_realm), "email": emails } elif notify_type == NOTIFY_TYPE.LOGGED_IN_USER: # Send notification to the logged in user if logged_in_user.get( "username") and not logged_in_user.get("realm"): # internal admins have no realm internal_admin = get_db_admin(logged_in_user.get("username")) if internal_admin: recipient = { "givenname": logged_in_user.get("username"), "email": internal_admin.email if internal_admin else "" } else: # Try to find the user in the specified realm user_obj = User(logged_in_user.get("username"), logged_in_user.get("realm")) if user_obj: recipient = { "givenname": user_obj.info.get("givenname"), "surname": user_obj.info.get("surname"), "email": user_obj.info.get("email"), "mobile": user_obj.info.get("mobile") } elif notify_type == NOTIFY_TYPE.EMAIL: email = handler_options.get("To " + NOTIFY_TYPE.EMAIL, "").split(",") recipient = {"email": email} else: log.warning("Was not able to determine the recipient for the user " "notification: {0!s}".format(handler_def)) if recipient or action.lower() == "savefile": # In case of "savefile" we do not need a recipient # Collect all data body = handler_options.get("body") or DEFAULT_BODY if body.startswith("file:"): # pragma no cover # We read the template from the file. filename = body[5:] try: with open(filename, "r", encoding="utf-8") as f: body = f.read() except Exception as e: log.warning( u"Failed to read email template from file {0!r}: {1!r}" .format(filename, e)) log.debug(u"{0!s}".format(traceback.format_exc())) subject = handler_options.get("subject") or \ "An action was performed on your token." serial = request.all_data.get("serial") or \ content.get("detail", {}).get("serial") or \ g.audit_object.audit_data.get("serial") registrationcode = content.get("detail", {}).get("registrationcode") pin = content.get("detail", {}).get("pin") googleurl_value = content.get("detail", {}).get("googleurl", {}).get("value") googleurl_img = content.get("detail", {}).get("googleurl", {}).get("img") tokentype = None 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, pin=pin, googleurl_value=googleurl_value, recipient=recipient, tokenowner=tokenowner, serial=serial, tokentype=tokentype, registrationcode=registrationcode, escape_html=action.lower() == "sendmail" and handler_options.get("mimetype", "").lower() == "html") body = to_unicode(body).format(googleurl_img=googleurl_img, **tags) subject = subject.format(**tags) # Send notification if action.lower() == "sendmail": emailconfig = handler_options.get("emailconfig") mimetype = handler_options.get("mimetype", "plain") useremail = recipient.get("email") attach_qrcode = handler_options.get("attach_qrcode", False) if attach_qrcode and googleurl_img: # get the image part of the googleurl googleurl = urlopen(googleurl_img) mail_body = MIMEMultipart('related') mail_body.attach(MIMEText(body, mimetype)) mail_img = MIMEImage(googleurl.read()) mail_img.add_header('Content-ID', '<token_image>') mail_img.add_header( 'Content-Disposition', 'inline; filename="{0!s}.png"'.format(serial)) mail_body.attach(mail_img) body = mail_body try: ret = send_email_identifier(emailconfig, recipient=useremail, subject=subject, body=body, reply_to=reply_to, mimetype=mimetype) except Exception as exx: log.error("Failed to send email: {0!s}".format(exx)) ret = False if ret: log.info("Sent a notification email to user {0}".format( recipient)) else: log.warning("Failed to send a notification email to user " "{0}".format(recipient)) elif action.lower() == "savefile": spooldir = get_app_config_value( "PI_NOTIFICATION_HANDLER_SPOOLDIRECTORY", "/var/lib/privacyidea/notifications/") filename = handler_options.get("filename") random = get_alphanum_str(16) filename = filename.format(random=random, **tags).lstrip(os.path.sep) outfile = os.path.normpath(os.path.join(spooldir, filename)) if not outfile.startswith(spooldir): log.error( u'Cannot write outside of spooldir {0!s}!'.format( spooldir)) else: try: with open(outfile, "w") as f: f.write(body) except Exception as err: log.error( u"Failed to write notification file: {0!s}".format( err)) elif action.lower() == "sendsms": smsconfig = handler_options.get("smsconfig") userphone = recipient.get("mobile") try: ret = send_sms_identifier(smsconfig, userphone, body) except Exception as exx: log.error("Failed to send sms: {0!s}".format(exx)) ret = False if ret: log.info("Sent a notification sms to user {0}".format( recipient)) else: log.warning("Failed to send a notification email to user " "{0}".format(recipient)) return ret
def test_05_get_alphanum_str(self): r = get_alphanum_str(20) self.assertEqual(len(r), 20)