schleuder 3.3.0 → 3.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 637d9f0b81f2cb7aaee30a9761eeb0f09f49d95649a00d735ce13ecd008abdad
4
- data.tar.gz: ca41b305951ef8ac0fb7edfb42e87521cf1d2ce5e51be6ceaa3c57c933846067
3
+ metadata.gz: 8aff8f8ecff4958e630158f6fc5811db96d26699ab0946d16b4fc430e2897387
4
+ data.tar.gz: daa4d6fbe4941a311dcfc54d5544b1771d809ca2f570f6060473b0f94f4bc577
5
5
  SHA512:
6
- metadata.gz: 3b7f8cd46761314484df53f64ba3d929c75eccdfd4c7a2c7ce87d76720b6fa1025d59f370126b107b08a19b9916fe7e160563739a1058dc88a537acd744af212
7
- data.tar.gz: 149620dc7fc6549391af197396b68c8cf42b990e037d2224e337db57af00f90edb293e23120a8f2f8156e40cbfadcd35bca1009d066ba2126f98c4b7c90ae29a
6
+ metadata.gz: b125a6734601100a1a768b9295848c4d3c1de0fb8336e7a6cf070972c78913f71c2ff52418a8a16cff705a546ca27de29155cae0c1f30736f4f8127bffe5b823
7
+ data.tar.gz: 55bd88009950e4dbc52a3f17f3566ee8f4bd7d43751237f19da514d00e8e3d2dc92817c15660b0d826bb8072cb537f3652cf5e93ebeca43fcc424faf33964978
data/README.md CHANGED
@@ -16,11 +16,11 @@ Requirements
16
16
  * sqlite3
17
17
  * openssl
18
18
 
19
- *If you use Debian stretch or CentOS 7, please have a look at the [installation docs](https://schleuder.org/docs/#installation). We do provide packages for those platforms, which simplify the installation a lot.*
19
+ *If you use Debian buster or CentOS 7, please have a look at the [installation docs](https://schleuder.org/schleuder/docs/server-admins.html#installation). We do provide packages for those platforms, which simplify the installation a lot.*
20
20
 
21
21
  *🛈 A note regarding Ubuntu: All Ubuntu versions up to and including 17.10 don't meet the requirements with their packaged versions of gnupg! To run Schleuder on Ubuntu you currently have to install a more recent version of gnupg manually. Only Ubuntu 18.04 ("bionic") provides modern enough versions of Schleuder's requirements.*
22
22
 
23
- On systems that base on Debian 9 ("stretch"), install the dependencies via
23
+ On systems that base on Debian 10 ("buster"), install the dependencies via
24
24
 
25
25
  apt-get install ruby-dev gnupg2 libgpgme-dev libsqlite3-dev libssl-dev build-essential
26
26
 
@@ -47,15 +47,15 @@ Additionally these **rubygems** are required (will be installed automatically un
47
47
  Installing Schleuder
48
48
  ------------
49
49
 
50
- 1. Download [the gem](https://schleuder.org/download/schleuder-3.3.0.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.3.0.gem.sig) and verify:
50
+ 1. Download [the gem](https://schleuder.org/download/schleuder-3.5.3.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.5.3.gem.sig) and verify:
51
51
  ```
52
52
  gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
53
- gpg --verify schleuder-3.3.0.gem.sig
53
+ gpg --verify schleuder-3.5.3.gem.sig
54
54
  ```
55
55
 
56
56
  2. If all went well install the gem:
57
57
  ```
58
- gem install schleuder-3.3.0.gem
58
+ gem install schleuder-3.5.3.gem
59
59
  ```
60
60
 
61
61
  3. Set up schleuder:
@@ -65,7 +65,7 @@ Installing Schleuder
65
65
  This creates necessary directories, copies example configs, etc. If you see errors about missing write permissions please follow the advice given.
66
66
 
67
67
 
68
- For further information on setup and configuration please read <https://schleuder.org/docs/#setup>.
68
+ For further information on setup and configuration please read <https://schleuder.org/schleuder/docs/server-admins.html>.
69
69
 
70
70
 
71
71
  Command line usage
@@ -112,7 +112,7 @@ To execute the test suite run:
112
112
 
113
113
  bundle exec rspec
114
114
 
115
- Please note: Some of the specs use 'pgrep'. On systems that base on Debian 9 ("stretch") install it via
115
+ Please note: Some of the specs use 'pgrep'. On systems that base on Debian 10 ("buster") install it via
116
116
 
117
117
  apt-get install procps
118
118
 
@@ -145,4 +145,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
145
145
  Alternative Download
146
146
  --------------------
147
147
 
148
- Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.3.0.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.3.0.tar.gz.sig).
148
+ Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.5.3.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.5.3.tar.gz.sig).
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require_relative "lib/#{project}.rb"
3
3
 
4
4
  @version = Schleuder::VERSION
5
5
  @tagname = "#{project}-#{@version}"
6
- @gpguid = 'team@schleuder.org'
6
+ @gpguid = 'B3D190D5235C74E1907EACFE898F2C91E2E6E1F3'
7
7
  @filename_gem = "#{@tagname}.gem"
8
8
  @filename_tarball = "#{@tagname}.tar.gz"
9
9
 
@@ -110,7 +110,7 @@ end
110
110
  desc 'Publish gem-file to rubygems.org'
111
111
  task :publish_gem do
112
112
  puts "Really push #{@filename_gem} to rubygems.org? [yN]"
113
- if gets.match(/^y/i)
113
+ if $stdin.gets.match(/^y/i)
114
114
  puts "Pushing..."
115
115
  `gem push #{@filename_gem}`
116
116
  else
@@ -132,7 +132,7 @@ desc 'Check if version-tag already exists'
132
132
  task :check_version do
133
133
  # Check if Schleuder::VERSION has been updated since last release
134
134
  if `git tag`.match?(/^#{@tagname}$/)
135
- $stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update #{project}/version.rb?"
135
+ $stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update lib/#{project}/version.rb?"
136
136
  $stderr.print "Delete tag to continue? [yN] "
137
137
  if $stdin.gets.match(/^y/i)
138
138
  `git tag -d #{@tagname}`
@@ -0,0 +1,11 @@
1
+ class AddDeliverSelfsentToList < ActiveRecord::Migration
2
+ def up
3
+ if ! column_exists?(:lists, :deliver_selfsent)
4
+ add_column :lists, :deliver_selfsent, :boolean, default: true
5
+ end
6
+ end
7
+
8
+ def down
9
+ remove_column(:lists, :deliver_selfsent)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ class AddAutocryptHeaderToList < ActiveRecord::Migration
2
+ def up
3
+ if ! column_exists?(:lists, :include_autocrypt_header)
4
+ add_column :lists, :include_autocrypt_header, :boolean, default: true
5
+ end
6
+ end
7
+
8
+ def down
9
+ remove_column(:lists, :include_autocrypt_header)
10
+ end
11
+ end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20180110203100) do
14
+ ActiveRecord::Schema.define(version: 20190906194820) do
15
15
 
16
16
  create_table "lists", force: :cascade do |t|
17
17
  t.datetime "created_at"
@@ -37,6 +37,7 @@ ActiveRecord::Schema.define(version: 20180110203100) do
37
37
  t.boolean "keep_msgid", default: true
38
38
  t.boolean "bounces_drop_all", default: false
39
39
  t.boolean "bounces_notify_admins", default: true
40
+ t.boolean "deliver_selfsent", default: true
40
41
  t.boolean "include_list_headers", default: true
41
42
  t.boolean "include_openpgp_header", default: true
42
43
  t.integer "max_message_size_kb", default: 10240
@@ -44,6 +45,7 @@ ActiveRecord::Schema.define(version: 20180110203100) do
44
45
  t.boolean "forward_all_incoming_to_admins", default: false
45
46
  t.integer "logfiles_to_keep", default: 2
46
47
  t.text "internal_footer", default: ""
48
+ t.boolean "include_autocrypt_header", default: true
47
49
  end
48
50
 
49
51
  create_table "subscriptions", force: :cascade do |t|
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # Options are listed with the behaviour encoded in the database schema.
8
8
 
9
- # Only send out enrypted emails to subscriptions?
9
+ # Only send out encrypted emails to subscriptions?
10
10
  # (This setting does not affect resend-messages.)
11
11
  send_encrypted_only: true
12
12
 
@@ -93,6 +93,9 @@ bounces_drop_on_headers:
93
93
  # Send a notice to the list-admins whenever an email is bounced or dropped?
94
94
  bounces_notify_admins: true
95
95
 
96
+ # Include Autocrypt header into emails?
97
+ include_autocrypt_header: true
98
+
96
99
  # Include RFC-compliant List-* Headers into emails?
97
100
  include_list_headers: true
98
101
 
@@ -123,3 +126,8 @@ language: en
123
126
  # Forward a raw copy of all incoming emails to the list-admins?
124
127
  # Mainly useful for debugging.
125
128
  forward_all_incoming_to_admins: false
129
+
130
+ # Should e-mails be delivered to the original subscribed sender?
131
+ # Disabling this only works for signed e-mails; any e-mail that is unsigned
132
+ # sent to the list is treated as coming from an unknown source
133
+ deliver_selfsent: true
@@ -1,3 +1,10 @@
1
+ # default to UTF-8 encoding as early as possible for external
2
+ # data.
3
+ #
4
+ # this should ensure we are able to parse most incoming
5
+ # plain text mails in different charsets.
6
+ Encoding.default_external = Encoding::UTF_8
7
+
1
8
  # Stdlib
2
9
  require 'fileutils'
3
10
  require 'singleton'
@@ -61,6 +68,9 @@ ENV["SCHLEUDER_CONFIG"] ||= '/etc/schleuder/schleuder.yml'
61
68
  ENV["SCHLEUDER_LIST_DEFAULTS"] ||= '/etc/schleuder/list-defaults.yml'
62
69
  ENV["SCHLEUDER_ENV"] ||= 'production'
63
70
  ENV["SCHLEUDER_ROOT"] = rootdir.to_s
71
+ # Ensure that gnupg never-ever tries to ask for a passphrase.
72
+ ENV["GPG_TTY"] = "/nonexistant-#{rand}"
73
+ ENV["DISPLAY"] = nil
64
74
 
65
75
  GPGME::Ctx.set_gpg_path_from_env
66
76
  GPGME::Ctx.check_gpg_version
@@ -1,6 +1,7 @@
1
1
  require 'thor'
2
2
  require 'yaml'
3
3
  require 'gpgme'
4
+ require 'charlock_holmes'
4
5
 
5
6
  require_relative '../schleuder'
6
7
  require 'schleuder/cli/subcommand_fix'
@@ -67,6 +68,7 @@ module Schleuder
67
68
 
68
69
  desc 'refresh_keys [list1@example.com]', "Refresh all keys of all list from the keyservers sequentially (one by one or on the passed list). (This is supposed to be run from cron weekly.)"
69
70
  def refresh_keys(list=nil)
71
+ GPGME::Ctx.send_notice_if_gpg_does_not_know_import_filter
70
72
  work_on_lists(:refresh_keys,list)
71
73
  permission_notice
72
74
  end
@@ -319,11 +321,15 @@ Please notify the users and admins of this list of these changes.
319
321
  private
320
322
 
321
323
  def work_on_lists(subj, list=nil)
322
- selected_lists = if list.nil?
323
- List.all
324
+ if list.nil?
325
+ selected_lists = List.all
324
326
  else
325
- List.where(email: list)
327
+ selected_lists = List.where(email: list)
328
+ if selected_lists.blank?
329
+ error("No list with this address exists: #{list.inspect}")
330
+ end
326
331
  end
332
+
327
333
  selected_lists.each do |list|
328
334
  I18n.locale = list.language
329
335
  output = list.send(subj)
@@ -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
+
@@ -103,7 +103,7 @@ module GPGME
103
103
  end
104
104
 
105
105
  def refresh_key(fingerprint)
106
- args = "#{keyserver_arg} --refresh-keys #{fingerprint}"
106
+ args = "#{keyserver_arg} #{import_filter_arg} --refresh-keys #{fingerprint}"
107
107
  gpgerr, gpgout, exitcode = self.class.gpgcli(args)
108
108
 
109
109
  if exitcode > 0
@@ -136,7 +136,8 @@ module GPGME
136
136
  arguments, error = fetch_key_gpg_arguments_for(input)
137
137
  return error if error
138
138
 
139
- gpgerr, gpgout, exitcode = self.class.gpgcli(arguments)
139
+ self.class.send_notice_if_gpg_does_not_know_import_filter
140
+ gpgerr, gpgout, exitcode = self.class.gpgcli("#{import_filter_arg} #{arguments}")
140
141
 
141
142
  # Unfortunately gpg doesn't exit with code > 0 if `--fetch-key` fails.
142
143
  if exitcode > 0 || gpgerr.grep(/ unable to fetch /).presence
@@ -270,5 +271,25 @@ module GPGME
270
271
  ""
271
272
  end
272
273
  end
274
+
275
+ def self.gpg_knows_import_filter?
276
+ sufficient_gpg_version?('2.1.15')
277
+ end
278
+
279
+ def import_filter_arg
280
+ if self.class.gpg_knows_import_filter?
281
+ %{ --import-filter drop-sig='sig_created_d > 0000-00-00'}
282
+ end
283
+ end
284
+
285
+ def self.send_notice_if_gpg_does_not_know_import_filter
286
+ if ! gpg_knows_import_filter?
287
+ Schleuder.logger.notify_superadmin(
288
+ subject: 'Schleuder installation problem',
289
+ message: "Your version of GnuPG is very old, please update!\n\nWith your version of GnuPG we can not protect your setup against signature flooding. Please update to at least version 2.1.15 to fix this problem. See <https://dkg.fifthhorseman.net/blog/openpgp-certificate-flooding.html> for details on the background."
290
+ )
291
+ ''
292
+ end
293
+ end
273
294
  end
274
295
  end
@@ -57,6 +57,10 @@ module GPGME
57
57
  "#{self.to_s}\n\n#{export(armor: true).read}"
58
58
  end
59
59
 
60
+ def minimal
61
+ export(minimal: true).to_s
62
+ end
63
+
60
64
  # Force encoding, some databases save "ASCII-8BIT" as binary data.
61
65
  alias_method :orig_fingerprint, :fingerprint
62
66
  def fingerprint
@@ -19,6 +19,7 @@ module Schleuder
19
19
  :receive_admin_only,
20
20
  :keep_msgid,
21
21
  :bounces_drop_all,
22
+ :deliver_selfsent,
22
23
  :bounces_notify_admins,
23
24
  :include_list_headers,
24
25
  :include_openpgp_header,
@@ -146,6 +147,16 @@ module Schleuder
146
147
  key.armored
147
148
  end
148
149
 
150
+ def key_minimal_base64_encoded(fingerprint=self.fingerprint)
151
+ key = keys(fingerprint).first
152
+
153
+ if key.blank?
154
+ return false
155
+ end
156
+
157
+ Base64.strict_encode64(key.minimal)
158
+ end
159
+
149
160
  def check_keys
150
161
  now = Time.now
151
162
  checkdate = now + (60 * 60 * 24 * 14) # two weeks
@@ -340,16 +351,24 @@ module Schleuder
340
351
  true
341
352
  end
342
353
 
343
- def send_to_subscriptions(mail)
354
+ def send_to_subscriptions(mail, incoming_mail=nil)
344
355
  logger.debug "Sending to subscriptions."
345
356
  mail.add_internal_footer!
346
357
  self.subscriptions.each do |subscription|
347
358
  begin
348
- if subscription.delivery_enabled
349
- subscription.send_mail(mail)
350
- else
359
+
360
+ if ! subscription.delivery_enabled
351
361
  logger.info "Not sending to #{subscription.email}: delivery is disabled."
362
+ next
363
+ end
364
+
365
+ if ! self.deliver_selfsent && incoming_mail.was_validly_signed? && ( subscription == incoming_mail.signer )
366
+ logger.info "Not sending to #{subscription.email}: delivery of self sent is disabled."
367
+ next
352
368
  end
369
+
370
+ subscription.send_mail(mail)
371
+
353
372
  rescue => exc
354
373
  msg = I18n.t('errors.delivery_error',
355
374
  { email: subscription.email, error: exc.to_s })
@@ -18,9 +18,14 @@ module Schleuder
18
18
  notify_admin(string, original_message)
19
19
  end
20
20
 
21
- def notify_admin(thing, original_message=nil, subject='Error')
21
+ def notify_superadmin(message:, original_message: nil, subject: 'Error')
22
+ notify_admin(message, original_message, subject, superadmin)
23
+ end
24
+
25
+ def notify_admin(thing, original_message=nil, subject='Error', recipients=nil)
22
26
  # Minimize using other classes here, we don't know what caused the error.
23
27
  msg_parts = convert_to_msg_parts(thing, original_message)
28
+ recipients ||= adminaddresses
24
29
  Array(adminaddresses).each do |address, key|
25
30
  mail = Mail.new
26
31
  mail.from = @from
@@ -37,6 +42,8 @@ module Schleuder
37
42
  gpg_opts.merge!(encrypt: true, keys: { address => key.fingerprint })
38
43
  end
39
44
  mail.gpg gpg_opts
45
+
46
+ mail.header['List-Id'] = "<#{@list.email.gsub('@', '.')}>"
40
47
  end
41
48
  mail.deliver
42
49
  end
@@ -17,6 +17,7 @@ module Mail
17
17
  attr_accessor :original_message
18
18
  attr_accessor :list
19
19
  attr_accessor :protected_headers_subject
20
+ attr_writer :dynamic_pseudoheaders
20
21
 
21
22
  # TODO: This should be in initialize(), but I couldn't understand the
22
23
  # strange errors about wrong number of arguments when overriding
@@ -44,22 +45,19 @@ module Mail
44
45
  # might be gone (e.g. request-keywords that delete subscriptions or
45
46
  # keys).
46
47
  new.signer
47
- self.dynamic_pseudoheaders.each do |str|
48
- new.add_pseudoheader(str)
49
- end
48
+ new.dynamic_pseudoheaders = self.dynamic_pseudoheaders.dup
50
49
 
51
50
  # Store previously protected subject for later access.
52
51
  # mail-gpg pulls headers from the decrypted mime parts "up" into the main
53
52
  # headers, which reveals protected subjects.
54
53
  if self.subject != new.subject
55
54
  new.protected_headers_subject = self.subject.dup
56
-
57
- # Delete the protected headers which might leak information.
58
- if new.parts.first.content_type == "text/rfc822-headers; protected-headers=v1"
59
- new.parts.shift
60
- end
61
55
  end
62
56
 
57
+ # Delete the protected headers which might leak information.
58
+ if new.parts.first && new.parts.first.content_type == "text/rfc822-headers; protected-headers=v1"
59
+ new.parts.shift
60
+ end
63
61
 
64
62
  new
65
63
  end
@@ -207,11 +205,15 @@ module Mail
207
205
  @recipient.match(/-bounce@/).present? ||
208
206
  # Empty Return-Path
209
207
  self.return_path.to_s == '<>' ||
210
- # Auto-Submitted exists and does not equal 'no' and no cron header
211
- # present, as cron emails have the auto-submitted header.
208
+ # Auto-Submitted exists and does not equal 'no' and:
209
+ # - no cron header is present
210
+ # - no Jenkins job notification header is present
211
+ # as these emails have the auto-submitted header.
212
212
  ( self['Auto-Submitted'].present? && \
213
213
  self['Auto-Submitted'].to_s.downcase != 'no' && \
214
- !self['X-Cron-Env'].present?)
214
+ !self['X-Cron-Env'].present? && \
215
+ !self['X-Jenkins-Job'].present? && \
216
+ self.subject.to_s !~ /\A\*\*\* SECURITY information.*\*\*\*\Z/)
215
217
  end
216
218
 
217
219
  def keywords
@@ -245,11 +247,11 @@ module Mail
245
247
  # decide itself how to encode, it works. If we don't, some
246
248
  # character-sequences are not properly re-encoded.
247
249
  part.content_transfer_encoding = nil
248
- # Make the converted strings (now UTF-8) match what mime-part's headers say,
249
- # fall back to US-ASCII if none is set.
250
- # https://tools.ietf.org/html/rfc2046#section-4.1.2
251
- # -> Default charset is US-ASCII
252
- part.body = lines.compact.join.encode(part.charset||'US-ASCII')
250
+
251
+ # Set the right charset on the now parsed body
252
+ new_body = lines.compact.join
253
+ part.charset = new_body.encoding.to_s
254
+ part.body = new_body
253
255
 
254
256
  @keywords
255
257
  end
@@ -267,20 +269,17 @@ module Mail
267
269
  end
268
270
 
269
271
  def add_pseudoheader(string_or_key, value=nil)
270
- @dynamic_pseudoheaders ||= []
271
- if value.present?
272
- @dynamic_pseudoheaders << make_pseudoheader(string_or_key, value)
273
- else
274
- @dynamic_pseudoheaders << string_or_key.to_s
275
- end
272
+ dynamic_pseudoheaders << make_pseudoheader(string_or_key, value)
276
273
  end
277
274
 
278
275
  def make_pseudoheader(key, value)
279
- "#{key.to_s.camelize}: #{value.to_s}"
276
+ output = "#{key.to_s.camelize}: #{value.to_s}"
277
+ # wrap lines after 76 with 2 indents
278
+ output.gsub(/(.{1,76})( +|$)\n?/, " \\1\n").chomp.lstrip
280
279
  end
281
280
 
282
281
  def dynamic_pseudoheaders
283
- @dynamic_pseudoheaders || []
282
+ @dynamic_pseudoheaders ||= []
284
283
  end
285
284
 
286
285
  def signature_state
@@ -331,7 +330,8 @@ module Mail
331
330
  end
332
331
 
333
332
  def pseudoheaders(list)
334
- (standard_pseudoheaders(list) + dynamic_pseudoheaders).flatten.join("\n") + "\n"
333
+ separator = '------------------------------------------------------------------------------'
334
+ (standard_pseudoheaders(list) + dynamic_pseudoheaders).flatten.join("\n") + "\n" + separator + "\n"
335
335
  end
336
336
 
337
337
  def add_msgids(list, orig)
@@ -346,6 +346,14 @@ module Mail
346
346
  end
347
347
 
348
348
  def add_list_headers(list)
349
+ if list.include_autocrypt_header
350
+ # Inject whitespaces, to let Mail break the string at these points
351
+ # leading to correct wrapping.
352
+ keydata = list.key_minimal_base64_encoded.gsub(/(.{78})/, '\1 ')
353
+
354
+ self['Autocrypt'] = "addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata}"
355
+ end
356
+
349
357
  if list.include_list_headers
350
358
  self['List-Id'] = "<#{list.email.gsub('@', '.')}>"
351
359
  self['List-Owner'] = "<mailto:#{list.owner_address}> (Use list's public key)"
@@ -1,16 +1,12 @@
1
1
  module Schleuder
2
2
  module ListPlugins
3
3
  def self.attach_listkey(arguments, list, mail)
4
- filename = "#{list.fingerprint}.pgpkey"
5
- # "Mail" only really converts to multipart if the content-type is blank.
6
- mail.content_type = nil
7
- mail.add_file({
8
- filename: filename,
9
- content: list.export_key
10
- })
11
- mail.attachments[filename].content_type = 'application/pgp-keys'
12
- mail.attachments[filename].content_description = "OpenPGP public key of #{list.email}"
13
- mail.attachments[filename].content_disposition = "attachment; filename=#{filename}"
4
+ new_part = Mail::Part.new
5
+ new_part.body = list.export_key
6
+ new_part.content_type = 'application/pgp-keys'
7
+ new_part.content_description = "OpenPGP public key of #{list.email}"
8
+ new_part.content_disposition = "attachment; filename=#{list.fingerprint}.pgpkey"
9
+ mail.add_part new_part
14
10
  nil
15
11
  end
16
12
  end
@@ -104,35 +104,18 @@ module Schleuder
104
104
  # helper methods
105
105
  private
106
106
 
107
- def self.is_armored_key?(material)
108
- return false unless /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ =~ material
109
- return false unless /^-----END PGP PUBLIC KEY BLOCK-----$/ =~ material
110
-
111
- lines = material.split("\n").reject(&:empty?)
112
- # remove header
113
- lines.shift
114
- # remove tail
115
- lines.pop
116
- # verify the rest
117
- # TODO: verify length except for lasts lines?
118
- # headers according to https://tools.ietf.org/html/rfc4880#section-6.2
119
- lines.map do |line|
120
- /\A((comment|version|messageid|hash|charset):.*|[0-9a-z\/=+]+)\Z/i =~ line
121
- end.all?
122
- end
123
-
124
107
  def self.import_keys_from_attachments(list, mail)
125
108
  mail.attachments.map do |attachment|
126
109
  material = attachment.body.to_s
127
110
 
128
- list.import_key(material) if self.is_armored_key?(material)
111
+ list.import_key(material)
129
112
  end
130
113
  end
131
114
 
132
115
  def self.import_key_from_body(list, mail)
133
116
  key_material = mail.first_plaintext_part.body.to_s
134
117
 
135
- list.import_key(key_material) if self.is_armored_key?(key_material)
118
+ list.import_key(key_material)
136
119
  end
137
120
  end
138
121
  end
@@ -56,6 +56,9 @@ module Schleuder
56
56
 
57
57
  # Only continue if all recipients are still here.
58
58
  if recip_map.size < arguments.size
59
+ recip_map.keys.each do |aborted_sender|
60
+ mail.add_pseudoheader(:error, I18n.t("plugins.resend.aborted", email: aborted_sender))
61
+ end
59
62
  return
60
63
  end
61
64
 
@@ -117,22 +120,22 @@ module Schleuder
117
120
  Array(recipients).inject({}) do |hash, email|
118
121
  keys = mail.list.keys(email)
119
122
  # Exclude unusable keys.
120
- keys.select! { |key| key.usable_for?(:encrypt) }
121
- case keys.size
123
+ usable_keys = keys.select { |key| key.usable_for?(:encrypt) }
124
+ case usable_keys.size
122
125
  when 1
123
- hash[email] = keys.first
126
+ hash[email] = usable_keys.first
124
127
  when 0
125
128
  if encrypted_only
126
129
  # Don't add the email to the result to exclude it from the
127
130
  # recipients.
128
- add_keys_error(mail, email, keys.size)
131
+ add_resend_msg(mail, email, :error, 'not_resent_no_keys', usable_keys.size, keys.size)
129
132
  else
130
133
  hash[email] = ''
131
134
  end
132
135
  else
133
136
  # Always report this situation, regardless of sending or not. It's
134
137
  # bad and should be fixed.
135
- add_keys_error(mail, email, keys.size)
138
+ add_resend_msg(mail, email, :notice, 'not_resent_encrypted_no_keys', usable_keys.size, keys.size)
136
139
  if ! encrypted_only
137
140
  hash[email] = ''
138
141
  end
@@ -152,8 +155,8 @@ module Schleuder
152
155
  gpg_opts
153
156
  end
154
157
 
155
- def self.add_keys_error(mail, email, keys_size)
156
- mail.add_pseudoheader(:error, I18n.t("plugins.resend.not_resent_no_keys", email: email, num_keys: keys_size))
158
+ def self.add_resend_msg(mail, email, severity, msg, usable_keys_size, all_keys_size)
159
+ mail.add_pseudoheader(severity, I18n.t("plugins.resend.#{msg}", email: email, usable_keys: usable_keys_size, all_keys: all_keys_size))
157
160
  end
158
161
 
159
162
  def self.add_error_header(mail, recipients_map)
@@ -163,15 +166,15 @@ module Schleuder
163
166
  def self.add_resent_headers(mail, recipients_map, to_or_cc, sent_encrypted)
164
167
  if sent_encrypted
165
168
  prefix = I18n.t('plugins.resend.encrypted_to')
166
- str = recipients_map.map do |email, key|
169
+ str = "\n" + recipients_map.map do |email, key|
167
170
  "#{email} (#{key.fingerprint})"
168
- end.join(', ')
171
+ end.join(",\n")
169
172
  else
170
173
  prefix = I18n.t('plugins.resend.unencrypted_to')
171
- str = recipients_map.keys.join(', ')
174
+ str = ' ' + recipients_map.keys.join(", ")
172
175
  end
173
176
  headername = resent_header_name(to_or_cc)
174
- mail.add_pseudoheader(headername, "#{prefix} #{str}")
177
+ mail.add_pseudoheader(headername, "#{prefix}#{str}")
175
178
  end
176
179
 
177
180
  def self.resent_header_name(to_or_cc)
@@ -5,15 +5,43 @@ module Schleuder
5
5
  return error if error
6
6
 
7
7
  logger.info "Parsing incoming email."
8
+
9
+ # is it valid utf-8?
10
+ msg_scrubbed = false
11
+ unless msg.valid_encoding?
12
+ logger.warn "Converting message due to invalid characters"
13
+ detection = CharlockHolmes::EncodingDetector.detect(msg)
14
+ begin
15
+ msg = CharlockHolmes::Converter.convert(msg, detection[:encoding], 'UTF-8')
16
+ rescue ArgumentError
17
+ # it looks like even icu wasn't able to convert
18
+ # so we scrub the invalid characters to be able to
19
+ # at least parse the message somehow. Though this might
20
+ # result in data loss.
21
+ logger.warn "Scrubbing message due to invalid characters"
22
+ msg = msg.scrub
23
+ msg_scrubbed = true
24
+ end
25
+ end
26
+
8
27
  @mail = Mail.create_message_to_list(msg, recipient, list)
9
28
 
29
+ if msg_scrubbed
30
+ @mail.add_pseudoheader(:note, I18n.t("pseudoheaders.scrubbed_message"))
31
+ end
32
+
10
33
  error = run_filters('pre')
11
34
  return error if error
12
35
 
13
36
  begin
14
37
  # This decrypts, verifies, etc.
15
38
  @mail = @mail.setup
16
- rescue GPGME::Error::DecryptFailed
39
+
40
+ rescue GPGME::Error::BadPassphrase,
41
+ GPGME::Error::DecryptFailed,
42
+ GPGME::Error::NoData,
43
+ GPGME::Error::NoSecretKey
44
+
17
45
  logger.warn "Decryption of incoming message failed."
18
46
  return Errors::DecryptionFailed.new(list)
19
47
  end
@@ -46,7 +74,7 @@ module Schleuder
46
74
  # Subscriptions
47
75
  logger.debug "Creating clean copy of message"
48
76
  copy = @mail.clean_copy(list.headers_to_meta.any?)
49
- list.send_to_subscriptions(copy)
77
+ list.send_to_subscriptions(copy, @mail)
50
78
  nil
51
79
  end
52
80
 
@@ -1,3 +1,3 @@
1
1
  module Schleuder
2
- VERSION = '3.3.0'
2
+ VERSION = '3.5.3'
3
3
  end
@@ -121,7 +121,9 @@ de:
121
121
  Oder, um einen Schlüssel per HTTP von einem Server zu laden:
122
122
  X-FETCH-KEY: https://example.org/keys/mykey.asc
123
123
  resend:
124
- not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{num_keys} Schlüssel gefunden und unverschlüsseltes Senden verboten).
124
+ not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar. Unverschlüsseltes Senden verboten).
125
+ not_resent_encrypted_no_keys: Verschlüsseltes Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar).
126
+ aborted: Resending an <%{email}> abgebrochen aufgrund anderer Probleme.
125
127
  encrypted_to: Verschlüsselt an
126
128
  unencrypted_to: Unverschlüsselt an
127
129
  invalid_recipient: "Ungültige Emailadresse für resend: %{address}"
@@ -250,7 +252,9 @@ de:
250
252
  fetch_key:
251
253
  invalid_input: "Ungültige Angabe. Gültig sind: URLs, OpenPGP-Fingerabdrücke, oder Emailadressen."
252
254
  pseudoheaders:
255
+ scrubbed_message: Diese Email enthielt ungültige Zeichen, die aus Verarbeitungsgründen möglicherweise entfernt wurden.
253
256
  stripped_html_from_multialt: Diese Email enthielt einen alternativen HTML-Teil, der PGP-Daten beinhaltete. Der HTML-Teil wurde entfernt, um die Email sauberer analysieren zu können.
257
+ stripped_html_from_multialt_with_keywords: Diese Email enthielt Schlüsselwörter und einen alternativen HTML-Teil. Der HTML-Teil wurde entfernt, um zu verhindern dass diese Schlüsselwörter Aussenstehenden bekannt werden.
254
258
  signature_states:
255
259
  unknown: "Unbekannte Signatur von unbekanntem Schlüssel 0x%{fingerprint}"
256
260
  unsigned: "Unsigniert"
@@ -125,7 +125,9 @@ en:
125
125
  Or, to fetch a key keys by URL:
126
126
  X-FETCH-KEY: https://example.org/keys/mykey.asc
127
127
  resend:
128
- not_resent_no_keys: Resending to <%{email}> failed (%{num_keys} keys found and unencrypted sending disallowed).
128
+ not_resent_no_keys: Resending to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used. Unencrypted sending not allowed).
129
+ not_resent_encrypted_no_keys: Resending as encrypted email to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used).
130
+ aborted: Resending to <%{email}> aborted due to other errors.
129
131
  encrypted_to: Encrypted to
130
132
  unencrypted_to: Unencrypted to
131
133
  invalid_recipient: "Invalid email-address for resending: %{address}"
@@ -254,7 +256,9 @@ en:
254
256
  fetch_key:
255
257
  invalid_input: "Invalid input. Allowed are: URLs, OpenPGP-fingerprints, or email-addresses."
256
258
  pseudoheaders:
259
+ scrubbed_message: This message included invalid characters, which might have been removed to be able to process the message properly.
257
260
  stripped_html_from_multialt: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly.
261
+ stripped_html_from_multialt_with_keywords: This message included keywords and an alternating HTML-part. The HTML-part was removed to prevent the disclosure of these keywords to third parties.
258
262
  signature_states:
259
263
  unknown: "Unknown signature by unknown key 0x%{fingerprint}"
260
264
  unsigned: "Unsigned"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schleuder
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - schleuder dev team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-06 00:00:00.000000000 Z
11
+ date: 2020-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: gpgme
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '2.0'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 2.0.13
22
+ version: 2.0.19
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,21 +29,21 @@ dependencies:
29
29
  version: '2.0'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 2.0.13
32
+ version: 2.0.19
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: mail
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: 2.6.0
39
+ version: 2.7.1
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: 2.6.0
46
+ version: 2.7.1
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: mail-gpg
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -51,9 +51,9 @@ dependencies:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
53
  version: '0.3'
54
- - - ">="
54
+ - - "<"
55
55
  - !ruby/object:Gem::Version
56
- version: 0.3.3
56
+ version: 0.4.3
57
57
  type: :runtime
58
58
  prerelease: false
59
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -61,9 +61,9 @@ dependencies:
61
61
  - - "~>"
62
62
  - !ruby/object:Gem::Version
63
63
  version: '0.3'
64
- - - ">="
64
+ - - "<"
65
65
  - !ruby/object:Gem::Version
66
- version: 0.3.3
66
+ version: 0.4.3
67
67
  - !ruby/object:Gem::Dependency
68
68
  name: activerecord
69
69
  requirement: !ruby/object:Gem::Requirement
@@ -112,14 +112,14 @@ dependencies:
112
112
  requirements:
113
113
  - - "~>"
114
114
  - !ruby/object:Gem::Version
115
- version: '1'
115
+ version: 1.3.6
116
116
  type: :runtime
117
117
  prerelease: false
118
118
  version_requirements: !ruby/object:Gem::Requirement
119
119
  requirements:
120
120
  - - "~>"
121
121
  - !ruby/object:Gem::Version
122
- version: '1'
122
+ version: 1.3.6
123
123
  - !ruby/object:Gem::Dependency
124
124
  name: sinatra
125
125
  requirement: !ruby/object:Gem::Requirement
@@ -176,6 +176,20 @@ dependencies:
176
176
  - - "~>"
177
177
  - !ruby/object:Gem::Version
178
178
  version: '1'
179
+ - !ruby/object:Gem::Dependency
180
+ name: charlock_holmes
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - "~>"
184
+ - !ruby/object:Gem::Version
185
+ version: 0.7.6
186
+ type: :runtime
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - "~>"
191
+ - !ruby/object:Gem::Version
192
+ version: 0.7.6
179
193
  - !ruby/object:Gem::Dependency
180
194
  name: rspec
181
195
  requirement: !ruby/object:Gem::Requirement
@@ -249,7 +263,7 @@ dependencies:
249
263
  description: |-
250
264
  Schleuder is a group's email-gateway: subscribers can exchange encrypted emails among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list.
251
265
 
252
- (Please note: For some platforms there's a better way of installing Schleuder than `gem install`. See <https://schleuder.org/docs/#installation> for details.)
266
+ (Please note: For some platforms there's a better way of installing Schleuder than `gem install`. See <https://schleuder.org/schleuder/docs/server-admins.html#installation> for details.)
253
267
  email: team@schleuder.org
254
268
  executables:
255
269
  - schleuder
@@ -275,6 +289,8 @@ files:
275
289
  - db/migrate/20160501172700_fix_headers_to_meta_defaults.rb
276
290
  - db/migrate/20170713215059_add_internal_footer_to_list.rb
277
291
  - db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb
292
+ - db/migrate/20180723173900_add_deliver_selfsent_to_list.rb
293
+ - db/migrate/20190906194820_add_autocrypt_header_to_list.rb
278
294
  - db/schema.rb
279
295
  - etc/init.d/schleuder-api-daemon
280
296
  - etc/list-defaults.yml
@@ -323,6 +339,7 @@ files:
323
339
  - lib/schleuder/filters/post_decryption/60_receive_signed_only.rb
324
340
  - lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb
325
341
  - lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb
342
+ - lib/schleuder/filters/post_decryption/90_strip_html_from_alternative_if_keywords_present.rb
326
343
  - lib/schleuder/filters/pre_decryption/10_forward_bounce_to_admins.rb
327
344
  - lib/schleuder/filters/pre_decryption/20_forward_all_incoming_to_admins.rb
328
345
  - lib/schleuder/filters/pre_decryption/30_send_key.rb
@@ -390,8 +407,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
390
407
  - !ruby/object:Gem::Version
391
408
  version: '0'
392
409
  requirements: []
393
- rubyforge_project: "[none]"
394
- rubygems_version: 2.7.7
410
+ rubyforge_project:
411
+ rubygems_version: 2.7.6.2
395
412
  signing_key:
396
413
  specification_version: 4
397
414
  summary: Schleuder is a gpg-enabled mailing list manager with remailing-capabilities.