schleuder 3.2.2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -10
- data/Rakefile +16 -8
- 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/schema.rb +2 -2
- data/etc/list-defaults.yml +4 -2
- 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 +2 -3
- data/lib/schleuder/cli.rb +24 -0
- 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/{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 +1 -1
- data/lib/schleuder/gpgme/import_status.rb +13 -7
- data/lib/schleuder/gpgme/key.rb +4 -0
- data/lib/schleuder/list.rb +7 -4
- data/lib/schleuder/mail/encrypted_part.rb +14 -0
- data/lib/schleuder/mail/gpg.rb +15 -0
- data/lib/schleuder/mail/message.rb +70 -30
- data/lib/schleuder/plugins/key_management.rb +32 -7
- data/lib/schleuder/plugins/subscription_management.rb +70 -3
- data/lib/schleuder/runner.rb +19 -8
- 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 +96 -8
- data/locales/en.yml +102 -10
- metadata +48 -27
- 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
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/{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
|