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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -10
  3. data/Rakefile +16 -8
  4. data/bin/schleuder +2 -1
  5. data/bin/schleuder-api-daemon +3 -2
  6. data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +30 -0
  7. data/db/schema.rb +2 -2
  8. data/etc/list-defaults.yml +4 -2
  9. data/etc/schleuder.yml +11 -0
  10. data/lib/schleuder-api-daemon.rb +9 -354
  11. data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +143 -0
  12. data/lib/schleuder-api-daemon/routes/key.rb +40 -0
  13. data/lib/schleuder-api-daemon/routes/list.rb +69 -0
  14. data/lib/schleuder-api-daemon/routes/status.rb +5 -0
  15. data/lib/schleuder-api-daemon/routes/subscription.rb +99 -0
  16. data/lib/schleuder-api-daemon/routes/version.rb +5 -0
  17. data/lib/schleuder.rb +2 -3
  18. data/lib/schleuder/cli.rb +24 -0
  19. data/lib/schleuder/cli/subcommand_fix.rb +1 -1
  20. data/lib/schleuder/conf.rb +7 -1
  21. data/lib/schleuder/errors/active_model_error.rb +2 -5
  22. data/lib/schleuder/errors/decryption_failed.rb +2 -7
  23. data/lib/schleuder/errors/key_adduid_failed.rb +1 -5
  24. data/lib/schleuder/errors/key_generation_failed.rb +1 -8
  25. data/lib/schleuder/errors/keyword_admin_only.rb +1 -5
  26. data/lib/schleuder/errors/list_not_found.rb +1 -5
  27. data/lib/schleuder/errors/listdir_problem.rb +2 -7
  28. data/lib/schleuder/errors/loading_list_settings_failed.rb +2 -5
  29. data/lib/schleuder/errors/message_empty.rb +1 -5
  30. data/lib/schleuder/errors/message_not_from_admin.rb +2 -5
  31. data/lib/schleuder/errors/message_sender_not_subscribed.rb +2 -5
  32. data/lib/schleuder/errors/message_too_big.rb +2 -5
  33. data/lib/schleuder/errors/message_unauthenticated.rb +1 -4
  34. data/lib/schleuder/errors/message_unencrypted.rb +2 -5
  35. data/lib/schleuder/errors/message_unsigned.rb +2 -5
  36. data/lib/schleuder/errors/too_many_keys.rb +1 -8
  37. data/lib/schleuder/filters/{request_filter.rb → post_decryption/10_request.rb} +0 -0
  38. data/lib/schleuder/filters/{max_message_size.rb → post_decryption/20_max_message_size.rb} +0 -0
  39. data/lib/schleuder/filters/{forward_filter.rb → post_decryption/30_forward_to_owner.rb} +0 -0
  40. data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +10 -0
  41. data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +10 -0
  42. data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +10 -0
  43. data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +10 -0
  44. data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +10 -0
  45. data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb} +0 -0
  46. data/lib/schleuder/filters/{forward_incoming.rb → pre_decryption/20_forward_all_incoming_to_admins.rb} +0 -0
  47. data/lib/schleuder/filters/{send_key_filter.rb → pre_decryption/30_send_key.rb} +0 -0
  48. data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb} +5 -3
  49. data/lib/schleuder/filters/{strip_alternative_filter.rb → pre_decryption/50_strip_html_from_alternative.rb} +1 -1
  50. data/lib/schleuder/filters_runner.rb +41 -31
  51. data/lib/schleuder/gpgme/ctx.rb +1 -1
  52. data/lib/schleuder/gpgme/import_status.rb +13 -7
  53. data/lib/schleuder/gpgme/key.rb +4 -0
  54. data/lib/schleuder/list.rb +7 -4
  55. data/lib/schleuder/mail/encrypted_part.rb +14 -0
  56. data/lib/schleuder/mail/gpg.rb +15 -0
  57. data/lib/schleuder/mail/message.rb +70 -30
  58. data/lib/schleuder/plugins/key_management.rb +32 -7
  59. data/lib/schleuder/plugins/subscription_management.rb +70 -3
  60. data/lib/schleuder/runner.rb +19 -8
  61. data/lib/schleuder/subscription.rb +5 -9
  62. data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
  63. data/lib/schleuder/version.rb +1 -1
  64. data/locales/de.yml +96 -8
  65. data/locales/en.yml +102 -10
  66. metadata +48 -27
  67. data/lib/schleuder/errors/file_not_found.rb +0 -14
  68. data/lib/schleuder/errors/invalid_listname.rb +0 -13
  69. data/lib/schleuder/errors/list_exists.rb +0 -13
  70. data/lib/schleuder/errors/unknown_list_option.rb +0 -14
  71. data/lib/schleuder/filters/auth_filter.rb +0 -39
@@ -5,12 +5,14 @@ module Schleuder
5
5
  include Singleton
6
6
 
7
7
  EMAIL_REGEXP = /\A.+@[[:alnum:]_.-]+\z/i
8
- FINGERPRINT_REGEXP = /\A(0x)?[a-f0-9]{32,}\z/i
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
- @errors = errors
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
- @list = list
7
- end
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
- @errmsg = errmsg
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
- @listdir = listdir
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
- @keyword = keyword
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,11 +2,7 @@ module Schleuder
2
2
  module Errors
3
3
  class ListNotFound < Base
4
4
  def initialize(recipient)
5
- @recipient = recipient
6
- end
7
-
8
- def message
9
- t('errors.list_not_found', email: @recipient)
5
+ super t('errors.list_not_found', email: recipient)
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
- @dir = dir
6
- @problem = problem
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
- @config_file = ENV['SCHLEUDER_LIST_DEFAULTS']
6
- end
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
- @request_address = list.request_address
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 MessageNotFromAdmin < Base
4
- def initialize(list)
4
+ def initialize
5
5
  set_default_locale
6
- end
7
-
8
- def message
9
- t('errors.message_not_from_admin')
6
+ super t('errors.message_not_from_admin')
10
7
  end
11
8
  end
12
9
  end
@@ -1,12 +1,9 @@
1
1
  module Schleuder
2
2
  module Errors
3
3
  class MessageSenderNotSubscribed < Base
4
- def initialize(list)
4
+ def initialize
5
5
  set_default_locale
6
- end
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
- @allowed_size = list.max_message_size_kb
7
- end
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
@@ -3,10 +3,7 @@ module Schleuder
3
3
  class MessageUnauthenticated < Base
4
4
  def initialize
5
5
  set_default_locale
6
- end
7
-
8
- def message
9
- t('errors.message_unauthenticated')
6
+ super t('errors.message_unauthenticated')
10
7
  end
11
8
  end
12
9
  end
@@ -1,12 +1,9 @@
1
1
  module Schleuder
2
2
  module Errors
3
3
  class MessageUnencrypted < Base
4
- def initialize(list)
4
+ def initialize
5
5
  set_default_locale
6
- end
7
-
8
- def message
9
- t('errors.message_unencrypted')
6
+ super t('errors.message_unencrypted')
10
7
  end
11
8
  end
12
9
  end
@@ -1,12 +1,9 @@
1
1
  module Schleuder
2
2
  module Errors
3
3
  class MessageUnsigned < Base
4
- def initialize(list)
4
+ def initialize
5
5
  set_default_locale
6
- end
7
-
8
- def message
9
- t('errors.message_unsigned')
6
+ super t('errors.message_unsigned')
10
7
  end
11
8
  end
12
9
  end
@@ -2,14 +2,7 @@ module Schleuder
2
2
  module Errors
3
3
  class TooManyKeys < Base
4
4
  def initialize(listdir, listname)
5
- @listdir = listdir
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
@@ -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_signed_only(list, mail)
4
+ if list.receive_signed_only? && ! mail.was_validly_signed?
5
+ list.logger.info "Rejecting mail as unsigned"
6
+ return Errors::MessageUnsigned.new
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Schleuder
2
+ module Filters
3
+ def self.receive_encrypted_only(list, mail)
4
+ if list.receive_encrypted_only? && ! mail.was_encrypted?
5
+ list.logger.info "Rejecting mail as unencrypted"
6
+ return Errors::MessageUnencrypted.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
@@ -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
- # See #211 and #246 for background
10
- def self.fix_hotmail_messages!(list, mail)
11
- if mail.header['X-OriginatorOrg'].to_s.match(/(hotmail|outlook).com/) &&
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!(list, mail)
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
- # To define priority sort this.
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
- attr_reader :list
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, filters)
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