schleuder 3.2.2 → 3.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -11
- data/Rakefile +18 -10
- data/bin/schleuder +2 -1
- data/bin/schleuder-api-daemon +3 -2
- data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +30 -0
- data/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb +11 -0
- data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +11 -0
- data/db/schema.rb +4 -2
- data/etc/list-defaults.yml +13 -3
- data/etc/schleuder.yml +11 -0
- data/lib/schleuder-api-daemon.rb +9 -354
- data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +143 -0
- data/lib/schleuder-api-daemon/routes/key.rb +40 -0
- data/lib/schleuder-api-daemon/routes/list.rb +69 -0
- data/lib/schleuder-api-daemon/routes/status.rb +5 -0
- data/lib/schleuder-api-daemon/routes/subscription.rb +99 -0
- data/lib/schleuder-api-daemon/routes/version.rb +5 -0
- data/lib/schleuder.rb +12 -3
- data/lib/schleuder/cli.rb +33 -3
- data/lib/schleuder/cli/subcommand_fix.rb +1 -1
- data/lib/schleuder/conf.rb +7 -1
- data/lib/schleuder/errors/active_model_error.rb +2 -5
- data/lib/schleuder/errors/decryption_failed.rb +2 -7
- data/lib/schleuder/errors/key_adduid_failed.rb +1 -5
- data/lib/schleuder/errors/key_generation_failed.rb +1 -8
- data/lib/schleuder/errors/keyword_admin_only.rb +1 -5
- data/lib/schleuder/errors/list_not_found.rb +1 -5
- data/lib/schleuder/errors/listdir_problem.rb +2 -7
- data/lib/schleuder/errors/loading_list_settings_failed.rb +2 -5
- data/lib/schleuder/errors/message_empty.rb +1 -5
- data/lib/schleuder/errors/message_not_from_admin.rb +2 -5
- data/lib/schleuder/errors/message_sender_not_subscribed.rb +2 -5
- data/lib/schleuder/errors/message_too_big.rb +2 -5
- data/lib/schleuder/errors/message_unauthenticated.rb +1 -4
- data/lib/schleuder/errors/message_unencrypted.rb +2 -5
- data/lib/schleuder/errors/message_unsigned.rb +2 -5
- data/lib/schleuder/errors/too_many_keys.rb +1 -8
- data/lib/schleuder/filters/{request_filter.rb → post_decryption/10_request.rb} +0 -0
- data/lib/schleuder/filters/{max_message_size.rb → post_decryption/20_max_message_size.rb} +0 -0
- data/lib/schleuder/filters/{forward_filter.rb → post_decryption/30_forward_to_owner.rb} +0 -0
- data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/90_strip_html_from_alternative_if_keywords_present.rb +21 -0
- data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{forward_incoming.rb → pre_decryption/20_forward_all_incoming_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{send_key_filter.rb → pre_decryption/30_send_key.rb} +0 -0
- data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb} +5 -3
- data/lib/schleuder/filters/{strip_alternative_filter.rb → pre_decryption/50_strip_html_from_alternative.rb} +1 -1
- data/lib/schleuder/filters_runner.rb +41 -31
- data/lib/schleuder/gpgme/ctx.rb +24 -3
- data/lib/schleuder/gpgme/import_status.rb +13 -7
- data/lib/schleuder/gpgme/key.rb +8 -0
- data/lib/schleuder/list.rb +26 -4
- data/lib/schleuder/logger_notifications.rb +8 -1
- data/lib/schleuder/mail/encrypted_part.rb +14 -0
- data/lib/schleuder/mail/gpg.rb +15 -0
- data/lib/schleuder/mail/message.rb +97 -49
- data/lib/schleuder/plugins/attach_listkey.rb +6 -10
- data/lib/schleuder/plugins/key_management.rb +34 -26
- data/lib/schleuder/plugins/resend.rb +14 -11
- data/lib/schleuder/plugins/subscription_management.rb +70 -3
- data/lib/schleuder/runner.rb +49 -10
- data/lib/schleuder/subscription.rb +5 -9
- data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +101 -9
- data/locales/en.yml +107 -11
- metadata +72 -34
- data/lib/schleuder/errors/file_not_found.rb +0 -14
- data/lib/schleuder/errors/invalid_listname.rb +0 -13
- data/lib/schleuder/errors/list_exists.rb +0 -13
- data/lib/schleuder/errors/unknown_list_option.rb +0 -14
- data/lib/schleuder/filters/auth_filter.rb +0 -39
@@ -2,7 +2,7 @@ module Schleuder
|
|
2
2
|
module SubcommandFix
|
3
3
|
|
4
4
|
# Fixing a bug in Thor where the actual subcommand wouldn't show up
|
5
|
-
# with some
|
5
|
+
# with some invocations of the help-output.
|
6
6
|
def banner(task, namespace = true, subcommand = true)
|
7
7
|
"#{basename} #{task.formatted_usage(self, true, subcommand).split(':').join(' ')}"
|
8
8
|
end
|
data/lib/schleuder/conf.rb
CHANGED
@@ -5,12 +5,14 @@ module Schleuder
|
|
5
5
|
include Singleton
|
6
6
|
|
7
7
|
EMAIL_REGEXP = /\A.+@[[:alnum:]_.-]+\z/i
|
8
|
-
|
8
|
+
# TODO: drop v3 keys and only accept length of 40
|
9
|
+
FINGERPRINT_REGEXP = /\A(0x)?[a-f0-9]{32}([a-f0-9]{8})?\z/i
|
9
10
|
|
10
11
|
DEFAULTS = {
|
11
12
|
'lists_dir' => '/var/lib/schleuder/lists',
|
12
13
|
'listlogs_dir' => '/var/lib/schleuder/lists',
|
13
14
|
'plugins_dir' => '/etc/schleuder/plugins',
|
15
|
+
'filters_dir' => '/usr/local/lib/schleuder/filters',
|
14
16
|
'log_level' => 'warn',
|
15
17
|
'superadmin' => 'root@localhost',
|
16
18
|
'keyserver' => 'hkp://pool.sks-keyservers.net',
|
@@ -58,6 +60,10 @@ module Schleuder
|
|
58
60
|
instance.config['plugins_dir']
|
59
61
|
end
|
60
62
|
|
63
|
+
def self.filters_dir
|
64
|
+
instance.config['filters_dir']
|
65
|
+
end
|
66
|
+
|
61
67
|
def self.database
|
62
68
|
instance.config['database'][ENV['SCHLEUDER_ENV']]
|
63
69
|
end
|
@@ -2,13 +2,10 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class ActiveModelError < Base
|
4
4
|
def initialize(errors)
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def message
|
9
|
-
@errors.messages.map do |message|
|
5
|
+
messages = errors.messages.map do |message|
|
10
6
|
message.join(' ')
|
11
7
|
end.join("\n")
|
8
|
+
super messages
|
12
9
|
end
|
13
10
|
end
|
14
11
|
end
|
@@ -3,13 +3,8 @@ module Schleuder
|
|
3
3
|
class DecryptionFailed < Base
|
4
4
|
def initialize(list)
|
5
5
|
set_default_locale
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def message
|
10
|
-
t('errors.decryption_failed',
|
11
|
-
{ key: @list.key.to_s,
|
12
|
-
email: @list.sendkey_address })
|
6
|
+
super t('errors.decryption_failed',
|
7
|
+
{ key: list.key.to_s, email: list.sendkey_address })
|
13
8
|
end
|
14
9
|
end
|
15
10
|
end
|
@@ -2,11 +2,7 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class KeyAdduidFailed < Base
|
4
4
|
def initialize(errmsg)
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def message
|
9
|
-
t('errors.key_adduid_failed', { errmsg: @errmsg })
|
5
|
+
super t('errors.key_adduid_failed', { errmsg: errmsg })
|
10
6
|
end
|
11
7
|
end
|
12
8
|
end
|
@@ -2,14 +2,7 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class KeyGenerationFailed < Base
|
4
4
|
def initialize(listdir, listname)
|
5
|
-
|
6
|
-
@listname = listname
|
7
|
-
end
|
8
|
-
|
9
|
-
def message
|
10
|
-
t('errors.key_generation_failed',
|
11
|
-
{ listdir: @listdir,
|
12
|
-
listname: @listname })
|
5
|
+
super t('errors.key_generation_failed', {listdir: listdir, listname: listname})
|
13
6
|
end
|
14
7
|
end
|
15
8
|
end
|
@@ -2,11 +2,7 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class KeywordAdminOnly < Base
|
4
4
|
def initialize(keyword)
|
5
|
-
|
6
|
-
end
|
7
|
-
|
8
|
-
def message
|
9
|
-
t('errors.keyword_admin_only', keyword: @keyword)
|
5
|
+
super t('errors.keyword_admin_only', keyword: keyword)
|
10
6
|
end
|
11
7
|
end
|
12
8
|
end
|
@@ -2,13 +2,8 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class ListdirProblem < Base
|
4
4
|
def initialize(dir, problem)
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def message
|
10
|
-
problem = t("errors.listdir_problem.#{@problem}")
|
11
|
-
t('errors.listdir_problem.message', dir: @dir, problem: problem)
|
5
|
+
problem = t("errors.listdir_problem.#{problem}")
|
6
|
+
super t('errors.listdir_problem.message', dir: dir, problem: problem)
|
12
7
|
end
|
13
8
|
end
|
14
9
|
end
|
@@ -2,11 +2,8 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class LoadingListSettingsFailed < Base
|
4
4
|
def initialize
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def message
|
9
|
-
t('errors.loading_list_settings_failed', config_file: @config_file)
|
5
|
+
config_file = ENV['SCHLEUDER_LIST_DEFAULTS']
|
6
|
+
super t('errors.loading_list_settings_failed', config_file: config_file)
|
10
7
|
end
|
11
8
|
end
|
12
9
|
end
|
@@ -3,11 +3,7 @@ module Schleuder
|
|
3
3
|
class MessageEmpty < Base
|
4
4
|
def initialize(list)
|
5
5
|
set_default_locale
|
6
|
-
|
7
|
-
end
|
8
|
-
|
9
|
-
def message
|
10
|
-
t('errors.message_empty', { request_address: @request_address })
|
6
|
+
super t('errors.message_empty', { request_address: list.request_address })
|
11
7
|
end
|
12
8
|
end
|
13
9
|
end
|
@@ -1,12 +1,9 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module Errors
|
3
3
|
class MessageSenderNotSubscribed < Base
|
4
|
-
def initialize
|
4
|
+
def initialize
|
5
5
|
set_default_locale
|
6
|
-
|
7
|
-
|
8
|
-
def message
|
9
|
-
t('errors.message_sender_not_subscribed')
|
6
|
+
super t('errors.message_sender_not_subscribed')
|
10
7
|
end
|
11
8
|
end
|
12
9
|
end
|
@@ -3,11 +3,8 @@ module Schleuder
|
|
3
3
|
class MessageTooBig < Base
|
4
4
|
def initialize(list)
|
5
5
|
set_default_locale
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def message
|
10
|
-
t('errors.message_too_big', { allowed_size: @allowed_size })
|
6
|
+
allowed_size = list.max_message_size_kb
|
7
|
+
super t('errors.message_too_big', { allowed_size: allowed_size })
|
11
8
|
end
|
12
9
|
end
|
13
10
|
end
|
@@ -2,14 +2,7 @@ module Schleuder
|
|
2
2
|
module Errors
|
3
3
|
class TooManyKeys < Base
|
4
4
|
def initialize(listdir, listname)
|
5
|
-
|
6
|
-
@listname = listname
|
7
|
-
end
|
8
|
-
|
9
|
-
def message
|
10
|
-
t('errors.too_many_keys',
|
11
|
-
{ listdir: @listdir,
|
12
|
-
listname: @listname })
|
5
|
+
super t('errors.too_many_keys', {listdir: listdir, listname: listname})
|
13
6
|
end
|
14
7
|
end
|
15
8
|
end
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module Filters
|
3
|
+
def self.receive_admin_only(list, mail)
|
4
|
+
if list.receive_admin_only? && ( ! mail.was_validly_signed? || ! mail.signer.admin? )
|
5
|
+
list.logger.info "Rejecting mail as not from admin."
|
6
|
+
return Errors::MessageNotFromAdmin.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module Filters
|
3
|
+
def self.receive_authenticated_only(list, mail)
|
4
|
+
if list.receive_authenticated_only? && ( ! mail.was_encrypted? || ! mail.was_validly_signed? )
|
5
|
+
list.logger.info "Rejecting mail as unauthenticated"
|
6
|
+
return Errors::MessageUnauthenticated.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module Filters
|
3
|
+
def self.receive_from_subscribed_emailaddresses_only(list, mail)
|
4
|
+
if list.receive_from_subscribed_emailaddresses_only? && list.subscriptions.where(email: mail.from.first).blank?
|
5
|
+
list.logger.info "Rejecting mail as not from subscribed address."
|
6
|
+
return Errors::MessageSenderNotSubscribed.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
data/lib/schleuder/filters/post_decryption/90_strip_html_from_alternative_if_keywords_present.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module Filters
|
3
|
+
def self.strip_html_from_alternative_if_keywords_present(list, mail)
|
4
|
+
if mail[:content_type].blank? ||
|
5
|
+
mail[:content_type].content_type != 'multipart/alternative' ||
|
6
|
+
mail.keywords.blank?
|
7
|
+
return false
|
8
|
+
end
|
9
|
+
|
10
|
+
Schleuder.logger.debug 'Stripping html-part from multipart/alternative-message because it contains keywords'
|
11
|
+
mail.parts.delete_if do |part|
|
12
|
+
part[:content_type].content_type == 'text/html'
|
13
|
+
end
|
14
|
+
mail.content_type = 'multipart/mixed'
|
15
|
+
mail.add_pseudoheader(:note, I18n.t('pseudoheaders.stripped_html_from_multialt_with_keywords'))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb}
RENAMED
File without changes
|
File without changes
|
File without changes
|
data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb}
RENAMED
@@ -6,9 +6,11 @@ module Schleuder
|
|
6
6
|
# it problematic to correctly detect the message as a valid pgp/mime-mail.
|
7
7
|
# Here we fix the mail to be a proper pgp/mime aka. multipart/encrypted
|
8
8
|
# message, so further processing will detect it properly.
|
9
|
-
#
|
10
|
-
|
11
|
-
|
9
|
+
# This problem seems to be in fact related to the use of Microsoft
|
10
|
+
# Exchange. Accordingly, check if the headers contain 'X-MS-Exchange'.
|
11
|
+
# See #211, #246, #331 and #333 for background.
|
12
|
+
def self.fix_exchange_messages(list, mail)
|
13
|
+
if mail.header_fields.any?{|f| f.name =~ /^X-MS-Exchange-/i } &&
|
12
14
|
!mail[:content_type].blank? &&
|
13
15
|
mail[:content_type].content_type == 'multipart/mixed' && mail.parts.size > 2 &&
|
14
16
|
mail.parts[0][:content_type].content_type == 'text/plain' &&
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module Filters
|
3
3
|
|
4
|
-
def self.strip_html_from_alternative
|
4
|
+
def self.strip_html_from_alternative(list, mail)
|
5
5
|
if mail[:content_type].blank? ||
|
6
6
|
mail[:content_type].content_type != 'multipart/alternative' ||
|
7
7
|
! mail.to_s.include?('BEGIN PGP ')
|
@@ -1,40 +1,14 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module Filters
|
3
3
|
class Runner
|
4
|
-
|
5
|
-
# The method `setup` parses, decrypts etc.
|
6
|
-
# the mail sent to the list. So before
|
7
|
-
# calling setup we do all the things
|
8
|
-
# that won't require e.g. validation of
|
9
|
-
# the sender.
|
10
|
-
PRE_SETUP_FILTERS = %w[
|
11
|
-
forward_bounce_to_admins
|
12
|
-
forward_all_incoming_to_admins
|
13
|
-
send_key
|
14
|
-
fix_hotmail_messages!
|
15
|
-
strip_html_from_alternative!
|
16
|
-
]
|
17
|
-
# message size must be checked after
|
18
|
-
# decryption as gpg heavily compresses
|
19
|
-
# messages.
|
20
|
-
POST_SETUP_FILTERS = %w[
|
21
|
-
request
|
22
|
-
max_message_size
|
23
|
-
forward_to_owner
|
24
|
-
receive_admin_only
|
25
|
-
receive_authenticated_only
|
26
|
-
receive_signed_only
|
27
|
-
receive_encrypted_only
|
28
|
-
receive_from_subscribed_emailaddresses_only
|
29
|
-
]
|
4
|
+
attr_reader :list, :filter_type
|
30
5
|
|
31
|
-
|
32
|
-
|
33
|
-
def initialize(list)
|
6
|
+
def initialize(list, filter_type)
|
34
7
|
@list = list
|
8
|
+
@filter_type = filter_type
|
35
9
|
end
|
36
10
|
|
37
|
-
def run(mail
|
11
|
+
def run(mail)
|
38
12
|
filters.map do |cmd|
|
39
13
|
list.logger.debug "Calling filter #{cmd}"
|
40
14
|
response = Filters.send(cmd, list, mail)
|
@@ -48,8 +22,12 @@ module Schleuder
|
|
48
22
|
end
|
49
23
|
nil
|
50
24
|
end
|
51
|
-
private
|
52
25
|
|
26
|
+
def filters
|
27
|
+
@filters ||= load_filters
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
53
31
|
def stop?(response)
|
54
32
|
response.kind_of?(StandardError)
|
55
33
|
end
|
@@ -78,6 +56,38 @@ module Schleuder
|
|
78
56
|
list.logger.notify_admin reason, original_message, I18n.t('notice')
|
79
57
|
end
|
80
58
|
end
|
59
|
+
|
60
|
+
def load_filters
|
61
|
+
list.logger.debug "Loading #{filter_type}_decryption filters"
|
62
|
+
sorted_filters.map do |filter_name|
|
63
|
+
require all_filter_files[filter_name]
|
64
|
+
filter_name.split('_',2).last
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def sorted_filters
|
69
|
+
@sorted_filters ||= all_filter_files.keys.sort do |a,b|
|
70
|
+
a.split('_',2).first.to_i <=> b.split('_',2).first.to_i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def all_filter_files
|
75
|
+
@all_filter_files ||= begin
|
76
|
+
files_in_filter_dirs = Dir[*filter_dirs]
|
77
|
+
files_in_filter_dirs.inject({}) do |res,file|
|
78
|
+
filter_name = File.basename(file,'.rb')
|
79
|
+
res[filter_name] = file
|
80
|
+
res
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def filter_dirs
|
86
|
+
@filter_dirs ||= [File.join(File.dirname(__FILE__),"filters"),
|
87
|
+
Schleuder::Conf.filters_dir].map do |d|
|
88
|
+
File.join(d,"#{filter_type}_decryption/[0-9]*_*.rb")
|
89
|
+
end
|
90
|
+
end
|
81
91
|
end
|
82
92
|
end
|
83
93
|
end
|