def proof_of_work_check(self, resolved_proof_of_work): """ :param resolved_proof_of_work: a string, that has to be an integer :return: """ HASH_ENDS_WITH = '00' try: int_pow = int(resolved_proof_of_work) resolved = "%s%d" % (self.proof_of_work['question'], int_pow) x = sha256(bytes(resolved)) if not x.endswith(HASH_ENDS_WITH): log.debug("Failed proof of work validation: expected '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], int_pow)) return True log.debug("Successful proof of work validation! got '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], int_pow)) # mark the proof_of_work as solved self.proof_of_work = False return False except Exception as e: log.debug("Exception while validating the proof of work: %s" % e) return True
def proof_of_work_check(self, resolved_proof_of_work): """ :param resolved_proof_of_work: a string, that has to be an integer :return: """ HASH_ENDS_WITH = '00' try: int_pow = int(resolved_proof_of_work) resolved = "%s%d" % (self.proof_of_work['question'], int_pow) x = sha256(bytes(resolved)) if not x.endswith(HASH_ENDS_WITH): log.debug( "Failed proof of work validation: expected '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], int_pow)) return True log.debug( "Successful proof of work validation! got '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], int_pow)) # mark the proof_of_work as solved self.proof_of_work = False return False except Exception as e: log.debug("Exception while validating the proof of work: %s" % e) return True
def send_exception_email(mail_body, mail_reason="GlobaLeaks Exception"): if (GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit): return if isinstance(mail_body, str) or isinstance(mail_body, unicode): mail_body = bytes(mail_body) if mail_reason.endswith("Exceeded"): log.debug("Skipping email [%s] because switch -S is present" % mail_reason) return if not hasattr(GLSettings.memory_copy, 'notif_source_name') or \ not hasattr(GLSettings.memory_copy, 'notif_source_email') or \ not hasattr(GLSettings.memory_copy, 'exception_email'): log.err( "Error: Cannot send mail exception before complete initialization." ) return sha256_hash = sha256(mail_body) if sha256_hash in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: # if the threshold has been exceeded log.err( "exception mail suppressed for exception (%s) [reason: threshold exceeded]" % sha256_hash) return else: GLSettings.exceptions[sha256_hash] = 1 GLSettings.exceptions_email_count += 1 try: mail_subject = "%s %s" % (mail_reason, __version__) if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name message = MIME_mail_build(GLSettings.memory_copy.notif_source_name, GLSettings.memory_copy.notif_source_email, GLSettings.memory_copy.exception_email, GLSettings.memory_copy.exception_email, mail_subject, mail_body) sendmail(authentication_username=GLSettings.memory_copy.notif_username, authentication_password=GLSettings.memory_copy.notif_password, from_address=GLSettings.memory_copy.notif_username, to_address=GLSettings.memory_copy.exception_email, message_file=message, smtp_host=GLSettings.memory_copy.notif_server, smtp_port=GLSettings.memory_copy.notif_port, security=GLSettings.memory_copy.notif_security) except Exception as excep: # we strongly need to avoid raising exception inside email logic to avoid chained errors log.err("Unexpected exception in process_mail_exception: %s" % excep)
def schedule_exception_email(exception_text, *args): try: from globaleaks.transactions import schedule_email if not hasattr(GLSettings.memory_copy, 'notif'): log.err( "Error: Cannot send mail exception before complete initialization." ) return if GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit: return exception_text = (exception_text % args) if args else exception_text sha256_hash = sha256(bytes(exception_text)) if sha256_hash not in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] = 1 else: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: log.err( "Exception mail suppressed for (%s) [reason: threshold exceeded]", sha256_hash) return GLSettings.exceptions_email_count += 1 mail_subject = "GlobaLeaks Exception" delivery_list = GLSettings.memory_copy.notif.exception_delivery_list if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name delivery_list = [("*****@*****.**", '')] exception_text = bytes("Platform: %s (%s)\nVersion: %s\n\n%s" \ % (GLSettings.memory_copy.hostname, GLSettings.memory_copy.onionservice, __version__, exception_text)) for mail_address, pub_key in delivery_list: mail_body = exception_text # Opportunisticly encrypt the mail body. NOTE that mails will go out # unencrypted if one address in the list does not have a public key set. if pub_key: mail_body = encrypt_message(pub_key, mail_body) # avoid waiting for the notification to send and instead rely on threads to handle it schedule_email(mail_address, mail_subject, mail_body) except Exception as excep: # Avoid raising exception inside email logic to avoid chaining errors log.err("Unexpected exception in send_exception_mail: %s", excep)
def send_exception_email(mail_body, mail_reason="GlobaLeaks Exception"): if GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit: return if isinstance(mail_body, str) or isinstance(mail_body, unicode): mail_body = bytes(mail_body) if ( not hasattr(GLSettings.memory_copy, "notif_source_name") or not hasattr(GLSettings.memory_copy, "notif_source_email") or not hasattr(GLSettings.memory_copy, "exception_email_address") ): log.err("Error: Cannot send mail exception before complete initialization.") return sha256_hash = sha256(mail_body) if sha256_hash in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: # if the threshold has been exceeded log.err("exception mail suppressed for exception (%s) [reason: threshold exceeded]" % sha256_hash) return else: GLSettings.exceptions[sha256_hash] = 1 GLSettings.exceptions_email_count += 1 try: mail_subject = "%s %s" % (mail_reason, __version__) if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name # If the receiver has encryption enabled (for notification), encrypt the mail body if GLSettings.memory_copy.exception_email_pgp_key_status == u"enabled": gpob = GLBPGP() try: gpob.load_key(GLSettings.memory_copy.exception_email_pgp_key_public) mail_body = gpob.encrypt_message(GLSettings.memory_copy.exception_email_pgp_key_fingerprint, mail_body) except Exception as excep: # If exception emails are configured to be subject to encryption an the key # expires the only thing to do is to disable the email. # TODO: evaluate if notificate an alert in plaintext to the exception email # this could be done simply here replacing the email subject and body. log.err("Error while encrypting exception email: %s" % str(excep)) return None finally: # the finally statement is always called also if # except contains a return or a raise gpob.destroy_environment() # avoid to wait for the notification to happen but rely on background completion sendmail(GLSettings.memory_copy.exception_email_address, mail_subject, mail_body) except Exception as excep: # we strongly need to avoid raising exception inside email logic to avoid chained errors log.err("Unexpected exception in process_mail_exception: %s" % excep)
def send_exception_email(mail_body, mail_reason="GlobaLeaks Exception"): if (GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit): return if isinstance(mail_body, str) or isinstance(mail_body, unicode): mail_body = bytes(mail_body) if mail_reason.endswith("Exceeded"): log.debug("Skipping email [%s] because switch -S is present" % mail_reason) return if not hasattr(GLSettings.memory_copy, 'notif_source_name') or \ not hasattr(GLSettings.memory_copy, 'notif_source_email') or \ not hasattr(GLSettings.memory_copy, 'exception_email'): log.err("Error: Cannot send mail exception before complete initialization.") return sha256_hash = sha256(mail_body) if sha256_hash in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: # if the threshold has been exceeded log.err("exception mail suppressed for exception (%s) [reason: threshold exceeded]" % sha256_hash) return else: GLSettings.exceptions[sha256_hash] = 1 GLSettings.exceptions_email_count += 1 try: mail_subject = "%s %s" % (mail_reason, __version__) if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name message = MIME_mail_build(GLSettings.memory_copy.notif_source_name, GLSettings.memory_copy.notif_source_email, GLSettings.memory_copy.exception_email, GLSettings.memory_copy.exception_email, mail_subject, mail_body) sendmail(authentication_username=GLSettings.memory_copy.notif_username, authentication_password=GLSettings.memory_copy.notif_password, from_address=GLSettings.memory_copy.notif_username, to_address=GLSettings.memory_copy.exception_email, message_file=message, smtp_host=GLSettings.memory_copy.notif_server, smtp_port=GLSettings.memory_copy.notif_port, security=GLSettings.memory_copy.notif_security) except Exception as excep: # we strongly need to avoid raising exception inside email logic to avoid chained errors log.err("Unexpected exception in process_mail_exception: %s" % excep)
def proof_of_work_valid(self, request_answer): """ :param resolved_proof_of_work: a string, that has to be an integer :return: """ HASH_ENDS_WITH = '00' resolved = "%s%d" % (self.proof_of_work['question'], request_answer) x = sha256(bytes(resolved)) if not x.endswith(HASH_ENDS_WITH): log.debug("Failed proof of work validation: expected '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], request_answer)) return log.debug("Successful proof of work validation! got '%s' at the end of the hash %s (seeds %s + %d)" % (HASH_ENDS_WITH, x, self.proof_of_work['question'], request_answer)) self.proof_of_work['solved'] = True
def handle_internaltip_fixes(self, new_obj, old_obj): old_node = self.store_old.find(self.model_from['Node']).one() new_archivedschema_model = self.model_to['ArchivedSchema'] questionnaire, answers = self.extract_answers_from_wb_steps( old_obj.wb_steps) new_obj.questionnaire_hash = sha256(json.dumps(questionnaire)) aqs = self.store_new.find( new_archivedschema_model, new_archivedschema_model.hash == unicode( new_obj.questionnaire_hash), new_archivedschema_model.type == u'questionnaire', new_archivedschema_model.language == unicode( old_node.default_language)).one() if not aqs: for lang in old_node.languages_enabled: aqs = new_archivedschema_model() aqs.hash = new_obj.questionnaire_hash aqs.type = u'questionnaire' aqs.language = lang aqs.schema = questionnaire self.store_new.add(aqs) preview = [] for s in aqs.schema: for f in s['children']: if f['preview']: preview.append(f) aqsp = new_archivedschema_model() aqsp.hash = new_obj.questionnaire_hash aqsp.type = u'preview' aqsp.language = lang aqsp.schema = preview self.store_new.add(aqsp) db_save_questionnaire_answers(self.store_new, new_obj.id, answers) new_obj.preview = extract_answers_preview(questionnaire, answers)
def proof_of_work_valid(self, request_answer): """ :param resolved_proof_of_work: a string, that has to be an integer :return: """ HASH_ENDS_WITH = '00' resolved = "%s%d" % (self.proof_of_work['question'], request_answer) x = sha256(bytes(resolved)) if not x.endswith(HASH_ENDS_WITH): log.debug( "Failed proof of work validation: expected '%s' at the end of the hash %s (seeds %s + %d)", HASH_ENDS_WITH, x, self.proof_of_work['question'], request_answer) return log.debug( "Successful proof of work validation! got '%s' at the end of the hash %s (seeds %s + %d)", HASH_ENDS_WITH, x, self.proof_of_work['question'], request_answer) self.proof_of_work['solved'] = True
def handle_internaltip_fixes(self, new_obj, old_obj): old_node = self.store_old.find(self.model_from['Node']).one() new_archivedschema_model = self.model_to['ArchivedSchema'] questionnaire, answers = self.extract_answers_from_wb_steps(old_obj.wb_steps) new_obj.questionnaire_hash = sha256(json.dumps(questionnaire)) aqs = self.store_new.find(new_archivedschema_model, new_archivedschema_model.hash == unicode(new_obj.questionnaire_hash), new_archivedschema_model.type == u'questionnaire', new_archivedschema_model.language == unicode(old_node.default_language)).one() if not aqs: for lang in old_node.languages_enabled: aqs = new_archivedschema_model() aqs.hash = new_obj.questionnaire_hash aqs.type = u'questionnaire' aqs.language = lang aqs.schema = questionnaire self.store_new.add(aqs) preview = [] for s in aqs.schema: for f in s['children']: if f['preview']: preview.append(f) aqsp = new_archivedschema_model() aqsp.hash = new_obj.questionnaire_hash aqsp.type = u'preview' aqsp.language = lang aqsp.schema = preview self.store_new.add(aqsp) db_save_questionnaire_answers(self.store_new, new_obj.id, answers) new_obj.preview = extract_answers_preview(questionnaire, answers)
def send_exception_email(mail_body, mail_reason="GlobaLeaks Exception"): if (GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit): return if isinstance(mail_body, str) or isinstance(mail_body, unicode): mail_body = bytes(mail_body) if not hasattr(GLSettings.memory_copy, 'notif_source_name') or \ not hasattr(GLSettings.memory_copy, 'notif_source_email') or \ not hasattr(GLSettings.memory_copy, 'exception_email_address'): log.err( "Error: Cannot send mail exception before complete initialization." ) return sha256_hash = sha256(mail_body) if sha256_hash in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: # if the threshold has been exceeded log.err( "exception mail suppressed for exception (%s) [reason: threshold exceeded]" % sha256_hash) return else: GLSettings.exceptions[sha256_hash] = 1 GLSettings.exceptions_email_count += 1 try: mail_subject = "%s %s" % (mail_reason, __version__) if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name # If the receiver has encryption enabled (for notification), encrypt the mail body if GLSettings.memory_copy.exception_email_pgp_key_status == u'enabled': gpob = GLBPGP() try: gpob.load_key( GLSettings.memory_copy.exception_email_pgp_key_public) mail_body = gpob.encrypt_message( GLSettings.memory_copy.exception_email_pgp_key_fingerprint, mail_body) except Exception as excep: # If exception emails are configured to be subject to encryption an the key # expires the only thing to do is to disable the email. # TODO: evaluate if notificate an alert in plaintext to the exception email # this could be done simply here replacing the email subject and body. log.err("Error while encrypting exception email: %s" % str(excep)) return None finally: # the finally statement is always called also if # except contains a return or a raise gpob.destroy_environment() # avoid to wait for the notification to happen but rely on background completion sendmail(GLSettings.memory_copy.exception_email_address, mail_subject, mail_body) except Exception as excep: # we strongly need to avoid raising exception inside email logic to avoid chained errors log.err("Unexpected exception in process_mail_exception: %s" % excep)
def db_create_submission(store, request, uploaded_files, client_using_tor): answers = request['answers'] context, questionnaire = store.find( (models.Context, models.Questionnaire), models.Context.id == request['context_id'], models.Questionnaire.id == models.Context.questionnaire_id).one() if not context: raise errors.ModelNotFound(models.Context) steps = db_get_questionnaire(store, questionnaire.id, None)['steps'] questionnaire_hash = unicode(sha256(json.dumps(steps))) submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) if context.tip_timetolive > -1: submission.expiration_date = get_expiration(context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The status tor2web is used to keep track of the security level adopted by the whistleblower submission.tor2web = not client_using_tor submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request[ 'identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(steps, answers) store.add(submission) db_archive_questionnaire_schema(store, steps, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) for filedesc in uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)", new_file.name, new_file.content_type, new_file.size) receipt = db_create_whistleblowertip(store, submission) if context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > context.maximum_selectable_receivers: raise errors.SubmissionValidationFailure( "selected an invalid number of recipients") rtips_count = 0 for receiver, user, in store.find( (models.Receiver, models.User), In(models.Receiver.id, request['receivers']), models.ReceiverContext.receiver_id == models.Receiver.id, models.ReceiverContext.context_id == context.id, models.User.id == models.Receiver.id): if user.pgp_key_public or GLSettings.memory_copy.allow_unencrypted: db_create_receivertip(store, receiver, submission) rtips_count += 1 if rtips_count == 0: raise errors.SubmissionValidationFailure("need at least one recipient") log.debug("The finalized submission had created %d models.ReceiverTip(s)", rtips_count) return {'receipt': receipt}
def db_create_submission(store, token_id, request, t2w, language): # the .get method raise an exception if the token is invalid token = TokenList.get(token_id) token.use() answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) submission.expiration_date = utc_future_date(seconds=context.tip_timetolive) # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The use of Tor2Web is detected by the basehandler and the status forwared here; # The status is used to keep track of the security level adopted by the whistleblower submission.tor2web = t2w submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request['identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: import_receivers(store, submission, request['receivers']) except Exception as excep: log.err("Submission create: receivers import fail: %s" % excep) raise excep try: for filedesc in token.uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['filename'] new_file.description = "" new_file.content_type = filedesc['content_type'] new_file.size = filedesc['body_len'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['encrypted_path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)" % ( new_file.name, new_file.content_type, new_file.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblower_tip(store, submission) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(store, token_id, request, t2w, language): # the .get method raise an exception if the token is invalid token = TokenList.get(token_id) if not token.context_associated == request['context_id']: raise errors.InvalidInputFormat( "Token context does not match the one specified in submission payload" ) token.validate(request) TokenList.delete(token_id) answers = request['answers'] context = store.find(Context, Context.id == token.context_associated).one() if not context: # this can happen only if the context is removed # between submission POST and PUT.. :) that's why is better just # ignore this check, take che cached and wait the reference below fault log.err("Context requested: [%s] not found!" % token.context_associated) raise errors.ContextIdNotFound submission = InternalTip() submission.expiration_date = utc_future_date( seconds=context.tip_timetolive) submission.context_id = context.id submission.creation_date = datetime_now() # Tor2Web is spot in the handler and passed here, is done to keep track of the # security level adopted by the whistleblower submission.tor2web = t2w try: questionnaire = db_get_context_steps( store, context.id, GLSettings.memory_copy.default_language) questionnaire_hash = sha256(json.dumps(questionnaire)) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, submission) db_save_questionnaire_answers(store, submission, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: import_receivers(store, submission, request['receivers']) except Exception as excep: log.err("Submission create: receivers import fail: %s" % excep) raise excep try: for filedesc in token.uploaded_files: associated_f = InternalFile() associated_f.name = filedesc['filename'] associated_f.description = "" associated_f.content_type = filedesc['content_type'] associated_f.size = filedesc['body_len'] associated_f.internaltip_id = submission.id associated_f.file_path = filedesc['encrypted_path'] store.add(associated_f) log.debug("=> file associated %s|%s (%d bytes)" % (associated_f.name, associated_f.content_type, associated_f.size)) except Exception as excep: log.err("Unable to create a DB entry for file! %s" % excep) raise excep receipt = db_create_whistleblower_tip(store, submission) submission_dict = wb_serialize_internaltip(store, submission) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(store, token_id, request, t2w, language): # the .get method raise an exception if the token is invalid token = TokenList.get(token_id) token.use() answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) submission.expiration_date = utc_future_date(seconds=context.tip_timetolive) # The use of Tor2Web is detected by the basehandler and the status forwared here; # The status is used to keep track of the security level adopted by the whistleblower submission.tor2web = t2w submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.enable_whistleblower_identity if context.enable_whistleblower_identity and request['identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: import_receivers(store, submission, request['receivers']) except Exception as excep: log.err("Submission create: receivers import fail: %s" % excep) raise excep try: for filedesc in token.uploaded_files: associated_f = models.InternalFile() associated_f.name = filedesc['filename'] associated_f.description = "" associated_f.content_type = filedesc['content_type'] associated_f.size = filedesc['body_len'] associated_f.internaltip_id = submission.id associated_f.file_path = filedesc['encrypted_path'] store.add(associated_f) log.debug("=> file associated %s|%s (%d bytes)" % ( associated_f.name, associated_f.content_type, associated_f.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblower_tip(store, submission) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def send_exception_email(exception_text): if not hasattr(GLSettings.memory_copy.notif, 'exception_delivery_list'): log.err( "Error: Cannot send mail exception before complete initialization." ) return if GLSettings.exceptions_email_count >= GLSettings.exceptions_email_hourly_limit: return mail_subject = "GlobaLeaks Exception" delivery_list = GLSettings.memory_copy.notif.exception_delivery_list if GLSettings.devel_mode: mail_subject += " [%s]" % GLSettings.developer_name delivery_list = [("*****@*****.**", '') ] exception_text = bytes("GlobaLeaks version: %s\n\n%s" % (__version__, exception_text)) sha256_hash = sha256(exception_text) if sha256_hash in GLSettings.exceptions: GLSettings.exceptions[sha256_hash] += 1 if GLSettings.exceptions[sha256_hash] > 5: # if the threshold has been exceeded log.err( "exception mail suppressed for exception (%s) [reason: threshold exceeded]" % sha256_hash) return else: GLSettings.exceptions[sha256_hash] = 1 GLSettings.exceptions_email_count += 1 try: for mail_address, pub_key in delivery_list: mail_body = exception_text # Opportunisticly encrypt the mail body. NOTE that mails will go out # unencrypted if one address in the list does not have a public key set. if len(pub_key): gpob = GLBPGP() try: r = gpob.load_key(pub_key) mail_body = gpob.encrypt_message(r['fingerprint'], mail_body) gpob.destroy_environment() except Exception as excep: # If this exception email is configured to be subject to encryption # and the encryption step throws, log the error and move on. log.err("Error while encrypting exception email: %s" % str(excep)) gpob.destroy_environment() continue # avoid waiting for the notification to send and instead rely on threads to handle it sendmail(mail_address, mail_subject, mail_body) except Exception as excep: # Avoid raising exception inside email logic to avoid chaining errors log.err("Unexpected exception in send_exception_mail: %s" % excep)
def db_create_submission(store, request, uploaded_files, t2w, language): answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) if context.tip_timetolive > -1: submission.expiration_date = utc_future_date(days=context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The use of Tor2Web is detected by the basehandler and the status forwared here; # The status is used to keep track of the security level adopted by the whistleblower submission.tor2web = t2w submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request['identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: for filedesc in uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)" % ( new_file.name, new_file.content_type, new_file.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblowertip(store, submission) if submission.context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > submission.context.maximum_selectable_receivers: raise errors.SubmissionValidationFailure("provided an invalid number of receivers") rtips = [] for receiver in store.find(models.Receiver, In(models.Receiver.id, request['receivers'])): if submission.context not in receiver.contexts: continue if not GLSettings.memory_copy.allow_unencrypted and len(receiver.user.pgp_key_public) == 0: continue rtips.append(db_create_receivertip(store, receiver, submission)) if len(rtips) == 0: raise errors.SubmissionValidationFailure("needed almost one receiver") log.debug("The finalized submission had created %d models.ReceiverTip(s)" % len(rtips)) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(store, request, uploaded_files, client_using_tor, language): answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) if context.tip_timetolive > -1: submission.expiration_date = get_expiration(context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The status tor2web is used to keep track of the security level adopted by the whistleblower submission.tor2web = not client_using_tor submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request[ 'identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: for filedesc in uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)" % (new_file.name, new_file.content_type, new_file.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblowertip(store, submission) if submission.context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > submission.context.maximum_selectable_receivers: raise errors.SubmissionValidationFailure( "selected an invalid number of recipients") rtips = [] for receiver in store.find(models.Receiver, In(models.Receiver.id, request['receivers'])): if submission.context not in receiver.contexts: continue if not GLSettings.memory_copy.allow_unencrypted and len( receiver.user.pgp_key_public) == 0: continue rtips.append(db_create_receivertip(store, receiver, submission)) if len(rtips) == 0: raise errors.SubmissionValidationFailure("need at least one recipient") log.debug("The finalized submission had created %d models.ReceiverTip(s)" % len(rtips)) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(store, token_id, request, t2w, language): # the .get method raise an exception if the token is invalid token = TokenList.get(token_id) if not token.context_associated == request['context_id']: raise errors.InvalidInputFormat("Token context does not match the one specified in submission payload") token.validate(request) TokenList.delete(token_id) answers = request['answers'] context = store.find(Context, Context.id == token.context_associated).one() if not context: # this can happen only if the context is removed # between submission POST and PUT.. :) that's why is better just # ignore this check, take che cached and wait the reference below fault log.err("Context requested: [%s] not found!" % token.context_associated) raise errors.ContextIdNotFound submission = InternalTip() submission.expiration_date = utc_future_date(seconds=context.tip_timetolive) submission.context_id = context.id submission.creation_date = datetime_now() # Tor2Web is spot in the handler and passed here, is done to keep track of the # security level adopted by the whistleblower submission.tor2web = t2w try: questionnaire = db_get_context_steps(store, context.id, GLSettings.memory_copy.default_language) questionnaire_hash = sha256(json.dumps(questionnaire)) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, submission) db_save_questionnaire_answers(store, submission, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: import_receivers(store, submission, request['receivers']) except Exception as excep: log.err("Submission create: receivers import fail: %s" % excep) raise excep try: for filedesc in token.uploaded_files: associated_f = InternalFile() associated_f.name = filedesc['filename'] associated_f.description = "" associated_f.content_type = filedesc['content_type'] associated_f.size = filedesc['body_len'] associated_f.internaltip_id = submission.id associated_f.file_path = filedesc['encrypted_path'] store.add(associated_f) log.debug("=> file associated %s|%s (%d bytes)" % ( associated_f.name, associated_f.content_type, associated_f.size)) except Exception as excep: log.err("Unable to create a DB entry for file! %s" % excep) raise excep receipt = db_create_whistleblower_tip(store, submission) submission_dict = wb_serialize_internaltip(store, submission) submission_dict.update({'receipt': receipt}) return submission_dict