outgoing.rb 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class SecureMailing::SMIME::Outgoing < SecureMailing::Backend::HandlerOutgoing
  3. def type
  4. 'S/MIME'
  5. end
  6. def signed
  7. from = mail.from.first
  8. cert_model = SMIMECertificate.find_by_email_address(from, filter: { key: 'private', usage: :signature, ignore_usable: true }).first
  9. raise "Unable to find ssl private key for '#{from}'" if !cert_model
  10. raise "Expired certificate for #{from} (fingerprint #{cert_model.fingerprint}) with #{cert_model.parsed.not_before} to #{cert_model.parsed.not_after}" if !security[:sign][:allow_expired] && !cert_model.parsed.usable?
  11. private_key = OpenSSL::PKey::RSA.new(cert_model.private_key, cert_model.private_key_secret)
  12. Mail.new(OpenSSL::PKCS7.write_smime(OpenSSL::PKCS7.sign(cert_model.parsed, private_key, mail.encoded, chain(cert_model), OpenSSL::PKCS7::DETACHED)))
  13. rescue => e
  14. log('sign', 'failed', e.message)
  15. raise
  16. end
  17. def chain(cert)
  18. lookup_issuer_hash = cert.parsed.issuer_hash
  19. result = []
  20. loop do
  21. found_cert = SMIMECertificate.find_by(subject_hash: lookup_issuer_hash)
  22. break if found_cert.blank?
  23. subject_hash = found_cert.parsed.subject_hash
  24. lookup_issuer_hash = found_cert.parsed.issuer_hash
  25. result.push(found_cert.parsed)
  26. # we've reached the root CA
  27. break if subject_hash == lookup_issuer_hash
  28. end
  29. result
  30. end
  31. def encrypt(data)
  32. unusable_cert = certificates.detect { |cert| !cert.parsed.usable? }
  33. raise "Unusable certificates for cert with #{unusable_cert.parsed.not_before} to #{unusable_cert.parsed.not_after}" if !security[:encryption][:allow_expired] && unusable_cert.present?
  34. Mail.new(OpenSSL::PKCS7.write_smime(OpenSSL::PKCS7.encrypt(certificates.map(&:parsed), data, cipher)))
  35. rescue => e
  36. log('encryption', 'failed', e.message)
  37. raise
  38. end
  39. def cipher
  40. @cipher ||= OpenSSL::Cipher.new('AES-128-CBC')
  41. end
  42. private
  43. def certificates
  44. certificates = []
  45. %w[to cc].each do |recipient|
  46. addresses = mail.send(recipient)
  47. next if !addresses
  48. certificates += SMIMECertificate.find_for_multiple_email_addresses!(addresses, filter: { key: 'public', ignore_usable: true, usage: :encryption }, blame: true)
  49. end
  50. certificates
  51. end
  52. end