Esempio n. 1
0
    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
Esempio n. 2
0
    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
Esempio n. 3
0
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)
Esempio n. 4
0
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)
Esempio n. 5
0
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)
Esempio n. 6
0
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)
Esempio n. 7
0
    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
Esempio n. 8
0
    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)
Esempio n. 9
0
    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
Esempio n. 10
0
    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)
Esempio n. 11
0
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)
Esempio n. 12
0
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}
Esempio n. 13
0
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
Esempio n. 14
0
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
Esempio n. 15
0
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
Esempio n. 16
0
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)
Esempio n. 17
0
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
Esempio n. 18
0
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
Esempio n. 19
0
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