outgoing.rb 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. class SecureMailing::SMIME::Outgoing < SecureMailing::Backend::Handler
  2. def initialize(mail, security)
  3. @mail = mail
  4. @security = security
  5. end
  6. def process
  7. return if !process?
  8. @mail = workaround_mail_bit_encoding_issue(@mail)
  9. if @security[:sign][:success] && @security[:encryption][:success]
  10. processed = encrypt(signed)
  11. log('sign', 'success')
  12. log('encryption', 'success')
  13. elsif @security[:sign][:success]
  14. processed = Mail.new(signed)
  15. log('sign', 'success')
  16. elsif @security[:encryption][:success]
  17. processed = encrypt(@mail.encoded)
  18. log('encryption', 'success')
  19. end
  20. overwrite_mail(processed)
  21. end
  22. def process?
  23. return false if @security.blank?
  24. return false if @security[:type] != 'S/MIME'
  25. @security[:sign][:success] || @security[:encryption][:success]
  26. end
  27. # S/MIME signing fails because of message encoding #3147
  28. # workaround for https://github.com/mikel/mail/issues/1190
  29. def workaround_mail_bit_encoding_issue(mail)
  30. # change 7bit/8bit encoding to binary so that
  31. # base64 will be used to encode the content
  32. if mail.body.encoding.include?('bit')
  33. mail.body.encoding = :binary
  34. end
  35. # go into recursion for nested parts
  36. mail.parts&.each do |part|
  37. workaround_mail_bit_encoding_issue(part)
  38. end
  39. mail
  40. end
  41. def overwrite_mail(processed)
  42. @mail.body = nil
  43. @mail.body = processed.body.encoded
  44. @mail.content_disposition = processed.content_disposition
  45. @mail.content_transfer_encoding = processed.content_transfer_encoding
  46. @mail.content_type = processed.content_type
  47. end
  48. def signed
  49. from = @mail.from.first
  50. cert_model = SMIMECertificate.for_sender_email_address(from)
  51. raise "Unable to find ssl private key for '#{from}'" if !cert_model
  52. raise "Expired certificate for #{from} (fingerprint #{cert_model.fingerprint}) with #{cert_model.not_before_at} to #{cert_model.not_after_at}" if !@security[:sign][:allow_expired] && cert_model.expired?
  53. private_key = OpenSSL::PKey::RSA.new(cert_model.private_key, cert_model.private_key_secret)
  54. OpenSSL::PKCS7.write_smime(OpenSSL::PKCS7.sign(cert_model.parsed, private_key, @mail.encoded, [], OpenSSL::PKCS7::DETACHED))
  55. rescue => e
  56. log('sign', 'failed', e.message)
  57. raise
  58. end
  59. def encrypt(data)
  60. certificates = SMIMECertificate.for_recipipent_email_addresses!(@mail.to)
  61. expired_cert = certificates.detect(&:expired?)
  62. raise "Expired certificates for cert with #{expired_cert.not_before_at} to #{expired_cert.not_after_at}" if !@security[:encryption][:allow_expired] && expired_cert.present?
  63. Mail.new(OpenSSL::PKCS7.write_smime(OpenSSL::PKCS7.encrypt(certificates.map(&:parsed), data, cipher)))
  64. rescue => e
  65. log('encryption', 'failed', e.message)
  66. raise
  67. end
  68. def cipher
  69. @cipher ||= OpenSSL::Cipher.new('AES-128-CBC')
  70. end
  71. def log(action, status, error = nil)
  72. HttpLog.create(
  73. direction: 'out',
  74. facility: 'S/MIME',
  75. url: "#{@mail[:from]} -> #{@mail[:to]}",
  76. status: status,
  77. ip: nil,
  78. request: @security,
  79. response: { error: error },
  80. method: action,
  81. created_by_id: 1,
  82. updated_by_id: 1,
  83. )
  84. end
  85. end