schleuder 3.2.2 → 3.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +21 -11
  3. data/Rakefile +18 -10
  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/migrate/20180723173900_add_deliver_selfsent_to_list.rb +11 -0
  8. data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +11 -0
  9. data/db/schema.rb +4 -2
  10. data/etc/list-defaults.yml +13 -3
  11. data/etc/schleuder.yml +11 -0
  12. data/lib/schleuder-api-daemon.rb +9 -354
  13. data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +143 -0
  14. data/lib/schleuder-api-daemon/routes/key.rb +40 -0
  15. data/lib/schleuder-api-daemon/routes/list.rb +69 -0
  16. data/lib/schleuder-api-daemon/routes/status.rb +5 -0
  17. data/lib/schleuder-api-daemon/routes/subscription.rb +99 -0
  18. data/lib/schleuder-api-daemon/routes/version.rb +5 -0
  19. data/lib/schleuder.rb +12 -3
  20. data/lib/schleuder/cli.rb +33 -3
  21. data/lib/schleuder/cli/subcommand_fix.rb +1 -1
  22. data/lib/schleuder/conf.rb +7 -1
  23. data/lib/schleuder/errors/active_model_error.rb +2 -5
  24. data/lib/schleuder/errors/decryption_failed.rb +2 -7
  25. data/lib/schleuder/errors/key_adduid_failed.rb +1 -5
  26. data/lib/schleuder/errors/key_generation_failed.rb +1 -8
  27. data/lib/schleuder/errors/keyword_admin_only.rb +1 -5
  28. data/lib/schleuder/errors/list_not_found.rb +1 -5
  29. data/lib/schleuder/errors/listdir_problem.rb +2 -7
  30. data/lib/schleuder/errors/loading_list_settings_failed.rb +2 -5
  31. data/lib/schleuder/errors/message_empty.rb +1 -5
  32. data/lib/schleuder/errors/message_not_from_admin.rb +2 -5
  33. data/lib/schleuder/errors/message_sender_not_subscribed.rb +2 -5
  34. data/lib/schleuder/errors/message_too_big.rb +2 -5
  35. data/lib/schleuder/errors/message_unauthenticated.rb +1 -4
  36. data/lib/schleuder/errors/message_unencrypted.rb +2 -5
  37. data/lib/schleuder/errors/message_unsigned.rb +2 -5
  38. data/lib/schleuder/errors/too_many_keys.rb +1 -8
  39. data/lib/schleuder/filters/{request_filter.rb → post_decryption/10_request.rb} +0 -0
  40. data/lib/schleuder/filters/{max_message_size.rb → post_decryption/20_max_message_size.rb} +0 -0
  41. data/lib/schleuder/filters/{forward_filter.rb → post_decryption/30_forward_to_owner.rb} +0 -0
  42. data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +10 -0
  43. data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +10 -0
  44. data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +10 -0
  45. data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +10 -0
  46. data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +10 -0
  47. data/lib/schleuder/filters/post_decryption/90_strip_html_from_alternative_if_keywords_present.rb +21 -0
  48. data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb} +0 -0
  49. data/lib/schleuder/filters/{forward_incoming.rb → pre_decryption/20_forward_all_incoming_to_admins.rb} +0 -0
  50. data/lib/schleuder/filters/{send_key_filter.rb → pre_decryption/30_send_key.rb} +0 -0
  51. data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb} +5 -3
  52. data/lib/schleuder/filters/{strip_alternative_filter.rb → pre_decryption/50_strip_html_from_alternative.rb} +1 -1
  53. data/lib/schleuder/filters_runner.rb +41 -31
  54. data/lib/schleuder/gpgme/ctx.rb +24 -3
  55. data/lib/schleuder/gpgme/import_status.rb +13 -7
  56. data/lib/schleuder/gpgme/key.rb +8 -0
  57. data/lib/schleuder/list.rb +26 -4
  58. data/lib/schleuder/logger_notifications.rb +8 -1
  59. data/lib/schleuder/mail/encrypted_part.rb +14 -0
  60. data/lib/schleuder/mail/gpg.rb +15 -0
  61. data/lib/schleuder/mail/message.rb +97 -49
  62. data/lib/schleuder/plugins/attach_listkey.rb +6 -10
  63. data/lib/schleuder/plugins/key_management.rb +34 -26
  64. data/lib/schleuder/plugins/resend.rb +14 -11
  65. data/lib/schleuder/plugins/subscription_management.rb +70 -3
  66. data/lib/schleuder/runner.rb +49 -10
  67. data/lib/schleuder/subscription.rb +5 -9
  68. data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
  69. data/lib/schleuder/version.rb +1 -1
  70. data/locales/de.yml +101 -9
  71. data/locales/en.yml +107 -11
  72. metadata +72 -34
  73. data/lib/schleuder/errors/file_not_found.rb +0 -14
  74. data/lib/schleuder/errors/invalid_listname.rb +0 -13
  75. data/lib/schleuder/errors/list_exists.rb +0 -13
  76. data/lib/schleuder/errors/unknown_list_option.rb +0 -14
  77. 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 invokations of the help-output.
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
@@ -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
@@ -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
+
@@ -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