smime_spec.rb 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe SecureMailing::SMIME do
  4. before do
  5. Setting.set('smime_integration', true)
  6. end
  7. let(:raw_body) { 'Some text' }
  8. let(:system_email_address) { 'smime1@example.com' }
  9. let(:customer_email_address) { 'smime2@example.com' }
  10. let(:cc_customer_email_address) { 'smime3@example.com' }
  11. let(:sender_certificate_subject) { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{sender_email_address}" }
  12. let(:recipient_certificate_subject) { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{recipient_email_address}" }
  13. let(:cc_recipient_certificate_subject) { "/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=#{cc_recipient_email_address}" }
  14. let(:expired_email_address) { 'expiredsmime1@example.com' }
  15. let(:ca_certificate_subject) { '/emailAddress=RootCA@example.com/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com' }
  16. let(:intermediate_ca_certificate_subject) { '/C=DE/ST=Berlin/O=Example Security/OU=IT Department/CN=example.com/emailAddress=IntermediateCA@example.com' }
  17. let(:content_type) { 'text/plain' }
  18. def build_mail
  19. Channel::EmailBuild.build(
  20. from: sender_email_address,
  21. to: recipient_email_address,
  22. cc: cc_recipient_email_address,
  23. body: raw_body,
  24. content_type: content_type,
  25. security: security_preferences
  26. )
  27. end
  28. describe '.outgoing' do
  29. shared_examples 'HttpLog writer' do |status|
  30. it "logs #{status}" do
  31. expect do
  32. build_mail
  33. rescue
  34. # allow failures
  35. end.to change(HttpLog, :count).by(1)
  36. expect(HttpLog.last.attributes).to include('direction' => 'out', 'status' => status)
  37. end
  38. end
  39. let(:sender_email_address) { system_email_address }
  40. let(:recipient_email_address) { customer_email_address }
  41. let(:cc_recipient_email_address) { cc_customer_email_address }
  42. context 'without security' do
  43. let(:security_preferences) do
  44. nil
  45. end
  46. it 'builds mail' do
  47. expect(build_mail.body).not_to match(SecureMailing::SMIME::Incoming::EXPRESSION_SIGNATURE)
  48. expect(build_mail.body).not_to match(SecureMailing::SMIME::Incoming::EXPRESSION_MIME)
  49. expect(build_mail.body).to eq(raw_body)
  50. end
  51. end
  52. context 'signing' do
  53. let(:security_preferences) do
  54. {
  55. type: 'S/MIME',
  56. sign: {
  57. success: true,
  58. },
  59. encryption: {
  60. success: false,
  61. },
  62. }
  63. end
  64. context 'private key present' do
  65. let!(:sender_certificate) do
  66. create(:smime_certificate, :with_private, fixture: system_email_address)
  67. end
  68. it 'builds mail' do
  69. expect(build_mail.body).to match(SecureMailing::SMIME::Incoming::EXPRESSION_SIGNATURE)
  70. end
  71. it_behaves_like 'HttpLog writer', 'success'
  72. context 'expired certificate' do
  73. let(:system_email_address) { expired_email_address }
  74. it 'raises exception' do
  75. expect { build_mail }.to raise_error RuntimeError
  76. end
  77. it_behaves_like 'HttpLog writer', 'failed'
  78. end
  79. context 'when message is 7bit or 8bit encoded' do
  80. let(:mail) do
  81. smime_mail = build_mail
  82. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  83. SecureMailing.incoming(mail)
  84. mail
  85. end
  86. context 'when Content-Type is text/plain' do
  87. let(:raw_body) { "\r\n\r\n@john.doe, now known as John Dóe has accepted your invitation to join the Administrator / htmltest project.\r\n\r\nhttp://169.254.169.254:3000/root/htmltest\r\n\r\n-- \r\nYou're receiving this email because of your account on 169.254.169.254.\r\n\r\n\r\n\r\n" }
  88. it 'verifies' do
  89. expect(mail['x-zammad-article-preferences']['security']['sign']['success']).to be true
  90. end
  91. end
  92. context 'when Content-Type is text/html' do
  93. let(:content_type) { 'text/html' }
  94. let(:raw_body) { "<div><ul><li><p>an \nexample „Text“ with ümläütß. </p></li></ul></div>" }
  95. it 'verifies' do
  96. expect(mail['x-zammad-article-preferences']['security']['sign']['success']).to be true
  97. end
  98. end
  99. end
  100. context 'when certificate chain is present' do
  101. let(:system_email_address) { 'chain@example.com' }
  102. let!(:chain) do
  103. [
  104. sender_certificate,
  105. create(:smime_certificate, fixture: 'IntermediateCA'),
  106. create(:smime_certificate, fixture: 'RootCA'),
  107. ]
  108. end
  109. let(:p7enc) do
  110. mail = Channel::EmailParser.new.parse(build_mail.to_s)
  111. OpenSSL::PKCS7.read_smime(mail[:raw])
  112. end
  113. it 'is included in the generated mail' do
  114. expect(p7enc.certificates.map(&:subject).sort).to eq(chain.map { |x| x.parsed.subject }.sort)
  115. end
  116. end
  117. end
  118. context 'no private key present' do
  119. before do
  120. create(:smime_certificate, fixture: system_email_address)
  121. end
  122. it 'raises exception' do
  123. expect { build_mail }.to raise_error RuntimeError
  124. end
  125. it_behaves_like 'HttpLog writer', 'failed'
  126. end
  127. end
  128. context 'encryption' do
  129. let(:security_preferences) do
  130. {
  131. type: 'S/MIME',
  132. sign: {
  133. success: false,
  134. },
  135. encryption: {
  136. success: true,
  137. },
  138. }
  139. end
  140. context 'public key present' do
  141. before do
  142. create(:smime_certificate, fixture: recipient_email_address)
  143. create(:smime_certificate, fixture: cc_recipient_email_address)
  144. end
  145. it 'builds mail' do
  146. mail = build_mail
  147. expect(mail['Content-Type'].value).to match(SecureMailing::SMIME::Incoming::EXPRESSION_MIME)
  148. expect(mail.body).not_to include(raw_body)
  149. end
  150. it_behaves_like 'HttpLog writer', 'success'
  151. context 'expired certificate' do
  152. let(:recipient_email_address) { expired_email_address }
  153. it 'raises exception' do
  154. expect { build_mail }.to raise_error RuntimeError
  155. end
  156. it_behaves_like 'HttpLog writer', 'failed'
  157. end
  158. end
  159. context 'no public key present' do
  160. it 'raises exception' do
  161. expect { build_mail }.to raise_error ActiveRecord::RecordNotFound
  162. end
  163. it_behaves_like 'HttpLog writer', 'failed'
  164. end
  165. end
  166. end
  167. describe '.incoming' do
  168. shared_examples 'HttpLog writer' do |status|
  169. it "logs #{status}" do
  170. expect do
  171. mail
  172. rescue
  173. # allow failures
  174. end.to change(HttpLog, :count).by(2)
  175. expect(HttpLog.last.attributes).to include('direction' => 'in', 'status' => status)
  176. end
  177. end
  178. let(:sender_email_address) { customer_email_address }
  179. let(:recipient_email_address) { system_email_address }
  180. let(:cc_recipient_email_address) { cc_customer_email_address }
  181. context 'signature verification' do
  182. let(:allow_expired) { false }
  183. let(:security_preferences) do
  184. {
  185. type: 'S/MIME',
  186. sign: {
  187. success: true,
  188. allow_expired: allow_expired,
  189. },
  190. encryption: {
  191. success: false,
  192. },
  193. }
  194. end
  195. context 'sender certificate present' do
  196. before do
  197. create(:smime_certificate, :with_private, fixture: sender_email_address)
  198. end
  199. let(:mail) do
  200. smime_mail = build_mail
  201. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  202. SecureMailing.incoming(mail)
  203. mail
  204. end
  205. it 'verifies' do
  206. expect(mail[:body]).to include(raw_body)
  207. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  208. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
  209. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  210. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  211. end
  212. it_behaves_like 'HttpLog writer', 'success'
  213. context 'expired' do
  214. # required to build mail with expired certificate
  215. let(:allow_expired) { true }
  216. let(:sender_email_address) { expired_email_address }
  217. it 'verifies with comment' do
  218. expect(mail[:body]).to include(raw_body)
  219. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  220. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to include(expired_email_address).and include('expired')
  221. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  222. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  223. end
  224. it_behaves_like 'HttpLog writer', 'success'
  225. end
  226. context 'with wrapped mime-type S/MIME signature (e.g. for Microsoft Outlook)' do
  227. before do
  228. # We need to disable the open ssl detached flag, to force the smime-type with 'signed-data'.
  229. stub_const('OpenSSL::PKCS7::DETACHED', nil)
  230. end
  231. it 'check that mail was verified' do
  232. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  233. end
  234. it 'check that signe comment exists' do
  235. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
  236. end
  237. it 'check that body was verified' do
  238. expect(mail[:body]).to include(raw_body)
  239. end
  240. end
  241. context 'sender is signer' do
  242. let(:sender_email_address) { system_email_address }
  243. let(:mail) do
  244. smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer.eml').read
  245. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  246. SecureMailing.incoming(mail)
  247. mail
  248. end
  249. it 'verifies' do
  250. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  251. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
  252. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  253. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  254. end
  255. end
  256. context 'sender is signer, with upcased sender address' do
  257. let(:sender_email_address) { system_email_address }
  258. let(:mail) do
  259. smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer.eml').read.sub('smime1@example.com', 'SMIME1@example.com')
  260. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  261. SecureMailing.incoming(mail)
  262. mail
  263. end
  264. it 'verifies' do
  265. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  266. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
  267. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  268. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  269. end
  270. end
  271. context 'sender is not signer' do
  272. before do
  273. create(:smime_certificate, :with_private, fixture: system_email_address)
  274. end
  275. let(:mail) do
  276. smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_not_signer.eml').read
  277. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  278. SecureMailing.incoming(mail)
  279. mail
  280. end
  281. it 'verifies with comment' do
  282. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  283. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('This message was not signed by its sender.')
  284. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  285. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  286. end
  287. end
  288. context 'sender certificate with another sender name signed by trusted CA (#4457)' do
  289. before do
  290. create(:smime_certificate, fixture: 'SenderCA')
  291. end
  292. let(:mail) do
  293. smime_mail = Rails.root.join('spec/fixtures/files/smime/sender_is_signer_with_ca.eml').read
  294. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  295. SecureMailing.incoming(mail)
  296. mail
  297. end
  298. it 'verifies' do
  299. expect(mail[:from]).to include('Zammad Helpdesk <smime-sender-ca@example.com>')
  300. expect(mail[:body]).to include(raw_body)
  301. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  302. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('/emailAddress=SenderCA@example.com/C=DE/ST=Berlin/L=Berlin/O=Example Security/OU=IT Department/CN=example.com')
  303. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  304. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  305. end
  306. end
  307. end
  308. context 'no sender certificate' do
  309. let!(:sender_certificate) { create(:smime_certificate, :with_private, fixture: sender_email_address) }
  310. let(:mail) do
  311. smime_mail = build_mail
  312. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  313. sender_certificate.destroy!
  314. SecureMailing.incoming(mail)
  315. mail
  316. end
  317. it 'fails' do
  318. expect(mail[:body]).to include(raw_body)
  319. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  320. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  321. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  322. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  323. end
  324. context 'when the mail is not containing the certificate chain but Intermediate CA is present on incoming mails (#5357)' do
  325. let(:ca_fixture) { 'IntermediateCA' }
  326. context 'when present in the SMIME store' do
  327. let(:mail) do
  328. smime_mail = build_mail
  329. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  330. sender_certificate.destroy!
  331. create(:smime_certificate, fixture: ca_fixture)
  332. SecureMailing.incoming(mail)
  333. mail
  334. end
  335. it 'does verify' do
  336. expect(mail[:body]).to include(raw_body)
  337. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  338. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
  339. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  340. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  341. end
  342. end
  343. context 'when present in the SSL store' do
  344. let(:mail) do
  345. smime_mail = build_mail
  346. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  347. sender_certificate.destroy!
  348. create(:ssl_certificate, fixture: ca_fixture)
  349. SecureMailing.incoming(mail)
  350. mail
  351. end
  352. it 'does verify' do
  353. expect(mail[:body]).to include(raw_body)
  354. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  355. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
  356. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  357. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  358. end
  359. end
  360. end
  361. context 'public key present in signature' do
  362. let(:not_related_fixture) { 'smime3@example.com' }
  363. let!(:not_related_certificate) { create(:smime_certificate, fixture: not_related_fixture) }
  364. context 'not related certificate present' do
  365. it 'fails' do
  366. expect(mail[:body]).to include(raw_body)
  367. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  368. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  369. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  370. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  371. end
  372. it_behaves_like 'HttpLog writer', 'failed'
  373. context 'CA' do
  374. let(:not_related_fixture) { 'ExpiredCA' }
  375. it 'fails' do
  376. expect(mail[:body]).to include(raw_body)
  377. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  378. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  379. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  380. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  381. end
  382. it_behaves_like 'HttpLog writer', 'failed'
  383. end
  384. end
  385. context 'usage not prevented' do
  386. before do
  387. # remove OpenSSL::PKCS7::NOINTERN
  388. stub_const('SecureMailing::SMIME::Incoming::OPENSSL_PKCS7_VERIFY_FLAGS', OpenSSL::PKCS7::NOVERIFY)
  389. end
  390. it 'does not perform verification' do
  391. expect(mail[:body]).to include(raw_body)
  392. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  393. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  394. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  395. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  396. end
  397. end
  398. end
  399. context 'intermediate CA present' do
  400. before do
  401. create(:smime_certificate, fixture: ca_fixture)
  402. end
  403. let(:ca_fixture) { 'IntermediateCA' }
  404. it 'verifies' do
  405. expect(mail[:body]).to include(raw_body)
  406. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  407. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(intermediate_ca_certificate_subject)
  408. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  409. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  410. end
  411. it_behaves_like 'HttpLog writer', 'success'
  412. context 'expired' do
  413. let(:ca_fixture) { 'ExpiredIntermediateCA' }
  414. it 'fails' do
  415. expect(mail[:body]).to include(raw_body)
  416. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  417. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  418. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  419. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  420. end
  421. it_behaves_like 'HttpLog writer', 'failed'
  422. context 'allowed' do
  423. let(:allow_expired) { true }
  424. # ATTENTION: expired CA is a special case where `allow_expired` does not count
  425. it 'fails' do
  426. expect(mail[:body]).to include(raw_body)
  427. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  428. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  429. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  430. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  431. end
  432. it_behaves_like 'HttpLog writer', 'failed'
  433. end
  434. end
  435. end
  436. context 'certificate chain' do
  437. let(:sender_email_address) { 'chain@example.com' }
  438. let(:ca_subject_chain) { ca_chain.reverse.map { |cert| cert.parsed.subject }.join(', ') }
  439. context 'incomplete certificate chain present' do
  440. before do
  441. create(:smime_certificate, fixture: 'RootCA')
  442. end
  443. it 'fails' do
  444. expect(mail[:body]).to include(raw_body)
  445. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  446. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq('The certificate for verification could not be found.')
  447. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  448. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  449. end
  450. end
  451. context 'certificate chain only partly present' do
  452. let(:ca_certificate_subject) { subject_chain }
  453. let!(:ca_chain) do
  454. create_list(:smime_certificate, 1, fixture: 'IntermediateCA')
  455. end
  456. it 'verifies' do
  457. expect(mail[:body]).to include(raw_body)
  458. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  459. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(ca_subject_chain)
  460. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  461. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  462. end
  463. end
  464. context 'complete certificate chain present' do
  465. let!(:ca_chain) do
  466. create_list(:smime_certificate, 1, fixture: 'ChainCA')
  467. end
  468. it 'verifies' do
  469. allow(Rails.logger).to receive(:warn)
  470. expect(mail[:body]).to include(raw_body)
  471. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  472. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(ca_subject_chain)
  473. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  474. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to be_nil
  475. expect(Rails.logger).not_to have_received(:warn).with(%r{#{Regexp.escape(ca_certificate_subject)}})
  476. end
  477. end
  478. end
  479. end
  480. end
  481. context 'decryption' do
  482. let(:allow_expired) { false }
  483. let(:security_preferences) do
  484. {
  485. type: 'S/MIME',
  486. sign: {
  487. success: false,
  488. },
  489. encryption: {
  490. success: true,
  491. allow_expired: allow_expired,
  492. },
  493. }
  494. end
  495. let!(:sender_certificate) { create(:smime_certificate, :with_private, fixture: sender_email_address) }
  496. let!(:recipient_certificate) { create(:smime_certificate, :with_private, fixture: recipient_email_address) }
  497. let!(:cc_recipient_certificate) { create(:smime_certificate, :with_private, fixture: cc_recipient_email_address) }
  498. context 'private key present' do
  499. let(:mail) do
  500. smime_mail = build_mail
  501. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  502. SecureMailing.incoming(mail)
  503. mail
  504. end
  505. it 'decrypts' do
  506. expect(mail[:body]).to include(raw_body)
  507. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  508. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
  509. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
  510. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq(recipient_certificate_subject)
  511. end
  512. it_behaves_like 'HttpLog writer', 'success'
  513. context 'expired allowed' do
  514. let(:allow_expired) { true }
  515. let(:system_email_address) { expired_email_address }
  516. it 'decrypts with comment' do
  517. expect(mail[:body]).to include(raw_body)
  518. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  519. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
  520. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
  521. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to include(expired_email_address).and include('expired')
  522. end
  523. it_behaves_like 'HttpLog writer', 'success'
  524. end
  525. end
  526. context 'no private key present' do
  527. let(:mail) do
  528. smime_mail = build_mail
  529. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  530. sender_certificate.destroy!
  531. recipient_certificate.destroy!
  532. cc_recipient_certificate.destroy!
  533. SecureMailing.incoming(mail)
  534. mail
  535. end
  536. it 'fails' do
  537. expect(mail[:body]).to include('no visible content')
  538. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be false
  539. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to be_nil
  540. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be false
  541. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq('The private key for decryption could not be found.')
  542. end
  543. it_behaves_like 'HttpLog writer', 'failed'
  544. end
  545. end
  546. context 'with signature verification and decryption' do
  547. let!(:sender_certificate) { create(:smime_certificate, :with_private, fixture: sender_email_address) }
  548. let!(:recipient_certificate) { create(:smime_certificate, :with_private, fixture: recipient_email_address) }
  549. let!(:cc_recipient_certificate) { create(:smime_certificate, :with_private, fixture: cc_recipient_email_address) }
  550. let(:security_preferences) do
  551. {
  552. type: 'S/MIME',
  553. sign: {
  554. success: true,
  555. },
  556. encryption: {
  557. success: true,
  558. },
  559. }
  560. end
  561. let(:mail) do
  562. smime_mail = build_mail
  563. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  564. SecureMailing.incoming(mail)
  565. mail
  566. end
  567. context 'with wrapped mime-type S/MIME signature (e.g. for Microsoft Outlook)' do
  568. before do
  569. # We need to disable the open ssl detached flag, to force the smime-type with 'signed-data'.
  570. stub_const('OpenSSL::PKCS7::DETACHED', nil)
  571. end
  572. it 'check that mail was decrypted' do
  573. expect(mail['x-zammad-article-preferences'][:security][:encryption][:success]).to be true
  574. end
  575. it 'check that encryption comment exists' do
  576. expect(mail['x-zammad-article-preferences'][:security][:encryption][:comment]).to eq(recipient_certificate_subject)
  577. end
  578. it 'check that mail was verified' do
  579. expect(mail['x-zammad-article-preferences'][:security][:sign][:success]).to be true
  580. end
  581. it 'check that signe comment exists' do
  582. expect(mail['x-zammad-article-preferences'][:security][:sign][:comment]).to eq(sender_certificate_subject)
  583. end
  584. it 'check that body was encrypted and verified' do
  585. expect(mail[:body]).to include(raw_body)
  586. end
  587. end
  588. end
  589. end
  590. describe '.retry' do
  591. let(:sender_email_address) { customer_email_address }
  592. let(:recipient_email_address) { system_email_address }
  593. let(:cc_recipient_email_address) { system_email_address }
  594. let(:security_preferences) do
  595. {
  596. type: 'S/MIME',
  597. sign: {
  598. success: true,
  599. },
  600. encryption: {
  601. success: true,
  602. },
  603. }
  604. end
  605. let(:mail) do
  606. sender_certificate = create(:smime_certificate, :with_private, fixture: sender_email_address)
  607. recipient_certificate = create(:smime_certificate, :with_private, fixture: system_email_address)
  608. smime_mail = Channel::EmailBuild.build(
  609. from: sender_email_address,
  610. to: recipient_email_address,
  611. body: raw_body,
  612. content_type: 'text/plain',
  613. security: security_preferences,
  614. attachments: [
  615. {
  616. content_type: 'text/plain',
  617. content: 'blub',
  618. filename: 'test-file1.txt',
  619. },
  620. ],
  621. )
  622. mail = Channel::EmailParser.new.parse(smime_mail.to_s)
  623. sender_certificate.destroy
  624. recipient_certificate.destroy
  625. mail
  626. end
  627. let!(:article) do
  628. _ticket, article, _user, _mail = Channel::EmailParser.new.process({}, mail['raw'])
  629. article
  630. end
  631. context 'private key added' do
  632. before do
  633. create(:smime_certificate, :with_private, fixture: recipient_email_address)
  634. create(:smime_certificate, fixture: sender_email_address)
  635. end
  636. it 'succeeds' do
  637. SecureMailing.retry(article)
  638. expect(article.preferences[:security][:sign][:success]).to be true
  639. expect(article.preferences[:security][:sign][:comment]).to eq(sender_certificate_subject)
  640. expect(article.preferences[:security][:encryption][:success]).to be true
  641. expect(article.preferences[:security][:encryption][:comment]).to eq(recipient_certificate_subject)
  642. expect(article.body).to include(raw_body)
  643. expect(article.attachments.count).to eq(1)
  644. expect(article.attachments.first.filename).to eq('test-file1.txt')
  645. end
  646. context 'S/MIME activated' do
  647. before do
  648. Setting.set('smime_integration', false)
  649. end
  650. it 'succeeds' do
  651. Setting.set('smime_integration', true)
  652. SecureMailing.retry(article)
  653. expect(article.preferences[:security][:sign][:success]).to be true
  654. expect(article.preferences[:security][:sign][:comment]).to eq(sender_certificate_subject)
  655. expect(article.preferences[:security][:encryption][:success]).to be true
  656. expect(article.preferences[:security][:encryption][:comment]).to eq(recipient_certificate_subject)
  657. expect(article.body).to include(raw_body)
  658. expect(article.attachments.count).to eq(1)
  659. expect(article.attachments.first.filename).to eq('test-file1.txt')
  660. end
  661. end
  662. end
  663. end
  664. end