Coverage Report

Created: 2025-04-22 06:17

/src/neomutt/ncrypt/crypt.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Signing/encryption multiplexor
4
 *
5
 * @authors
6
 * Copyright (C) 2016-2024 Richard Russon <rich@flatcap.org>
7
 * Copyright (C) 2019-2021 Pietro Cerutti <gahr@gahr.ch>
8
 * Copyright (C) 2023 Anna Figueiredo Gomes <navi@vlhl.dev>
9
 *
10
 * @copyright
11
 * This program is free software: you can redistribute it and/or modify it under
12
 * the terms of the GNU General Public License as published by the Free Software
13
 * Foundation, either version 2 of the License, or (at your option) any later
14
 * version.
15
 *
16
 * This program is distributed in the hope that it will be useful, but WITHOUT
17
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU General Public License along with
22
 * this program.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
/**
26
 * @page crypt_crypt Signing/encryption multiplexor
27
 *
28
 * Signing/encryption multiplexor
29
 */
30
31
#include "config.h"
32
#include <locale.h>
33
#include <stdbool.h>
34
#include <stdio.h>
35
#include <string.h>
36
#include "mutt/lib.h"
37
#include "address/lib.h"
38
#include "config/lib.h"
39
#include "email/lib.h"
40
#include "core/lib.h"
41
#include "alias/lib.h"
42
#include "gui/lib.h"
43
#include "mutt.h"
44
#include "crypt.h"
45
#include "lib.h"
46
#include "attach/lib.h"
47
#include "question/lib.h"
48
#include "send/lib.h"
49
#include "copy.h"
50
#include "cryptglue.h"
51
#include "globals.h"
52
#include "handler.h"
53
#include "mx.h"
54
#ifdef USE_AUTOCRYPT
55
#include "autocrypt/lib.h"
56
#endif
57
58
/**
59
 * crypt_current_time - Print the current time
60
 * @param state    State to use
61
 * @param app_name App name, e.g. "PGP"
62
 *
63
 * print the current time to avoid spoofing of the signature output
64
 */
65
void crypt_current_time(struct State *state, const char *app_name)
66
0
{
67
0
  char p[256] = { 0 };
68
0
  char tmp[512] = { 0 };
69
70
0
  if (!WithCrypto)
71
0
    return;
72
73
0
  const bool c_crypt_timestamp = cs_subset_bool(NeoMutt->sub, "crypt_timestamp");
74
0
  if (c_crypt_timestamp)
75
0
  {
76
0
    mutt_date_localtime_format(p, sizeof(p), _(" (current time: %c)"), mutt_date_now());
77
0
  }
78
0
  else
79
0
  {
80
0
    *p = '\0';
81
0
  }
82
83
0
  snprintf(tmp, sizeof(tmp), _("[-- %s output follows%s --]\n"), NONULL(app_name), p);
84
0
  state_attach_puts(state, tmp);
85
0
}
86
87
/**
88
 * crypt_forget_passphrase - Forget a passphrase and display a message
89
 */
90
void crypt_forget_passphrase(void)
91
0
{
92
0
  if (WithCrypto & APPLICATION_PGP)
93
0
    crypt_pgp_void_passphrase();
94
95
0
  if (WithCrypto & APPLICATION_SMIME)
96
0
    crypt_smime_void_passphrase();
97
98
0
  if (WithCrypto)
99
0
  {
100
    /* L10N: Due to the implementation details (e.g. some passwords are managed
101
       by gpg-agent) we can't know whether we forgot zero, 1, 12, ...
102
       passwords. So in English we use "Passphrases". Your language might
103
       have other means to express this. */
104
0
    mutt_message(_("Passphrases forgotten"));
105
0
  }
106
0
}
107
108
#ifndef DEBUG
109
#include <sys/resource.h>
110
/**
111
 * disable_coredumps - Prevent coredumps if neomutt were to crash
112
 */
113
static void disable_coredumps(void)
114
0
{
115
0
  struct rlimit rl = { 0, 0 };
116
0
  static bool done = false;
117
118
0
  if (!done)
119
0
  {
120
0
    setrlimit(RLIMIT_CORE, &rl);
121
0
    done = true;
122
0
  }
123
0
}
124
#endif
125
126
/**
127
 * crypt_valid_passphrase - Check that we have a usable passphrase, ask if not
128
 * @param flags Flags, see #SecurityFlags
129
 * @retval true  Success
130
 * @retval false Failed
131
 */
132
bool crypt_valid_passphrase(SecurityFlags flags)
133
0
{
134
0
  bool rc = false;
135
136
0
#ifndef DEBUG
137
0
  disable_coredumps();
138
0
#endif
139
140
0
  if (((WithCrypto & APPLICATION_PGP) != 0) && (flags & APPLICATION_PGP))
141
0
    rc = crypt_pgp_valid_passphrase();
142
143
0
  if (((WithCrypto & APPLICATION_SMIME) != 0) && (flags & APPLICATION_SMIME))
144
0
    rc = crypt_smime_valid_passphrase();
145
146
0
  return rc;
147
0
}
148
149
/**
150
 * mutt_protect - Encrypt and/or sign a message
151
 * @param e        Email
152
 * @param keylist  List of keys to encrypt to (space-separated)
153
 * @param postpone When true, signing is automatically disabled
154
 * @retval  0 Success
155
 * @retval -1 Error
156
 */
157
int mutt_protect(struct Email *e, char *keylist, bool postpone)
158
0
{
159
0
  struct Body *pbody = NULL, *tmp_pbody = NULL;
160
0
  struct Body *tmp_smime_pbody = NULL;
161
0
  struct Body *tmp_pgp_pbody = NULL;
162
0
  bool has_retainable_sig = false;
163
164
0
  if (!WithCrypto)
165
0
    return -1;
166
167
0
  SecurityFlags security = e->security;
168
0
  int sign = security & (SEC_AUTOCRYPT | SEC_SIGN);
169
0
  if (postpone)
170
0
  {
171
0
    sign = SEC_NO_FLAGS;
172
0
    security &= ~SEC_SIGN;
173
0
  }
174
175
0
  if (!(security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) && !sign)
176
0
    return 0;
177
178
0
  if (sign && !(security & SEC_AUTOCRYPT) && !crypt_valid_passphrase(security))
179
0
    return -1;
180
181
0
  if (((WithCrypto & APPLICATION_PGP) != 0) && !(security & SEC_AUTOCRYPT) &&
182
0
      ((security & PGP_INLINE) == PGP_INLINE))
183
0
  {
184
0
    if ((e->body->type != TYPE_TEXT) || !mutt_istr_equal(e->body->subtype, "plain"))
185
0
    {
186
0
      if (query_quadoption(_("Inline PGP can't be used with attachments.  Revert to PGP/MIME?"),
187
0
                           NeoMutt->sub, "pgp_mime_auto") != MUTT_YES)
188
0
      {
189
0
        mutt_error(_("Mail not sent: inline PGP can't be used with attachments"));
190
0
        return -1;
191
0
      }
192
0
    }
193
0
    else if (mutt_istr_equal("flowed", mutt_param_get(&e->body->parameter, "format")))
194
0
    {
195
0
      if ((query_quadoption(_("Inline PGP can't be used with format=flowed.  Revert to PGP/MIME?"),
196
0
                            NeoMutt->sub, "pgp_mime_auto")) != MUTT_YES)
197
0
      {
198
0
        mutt_error(_("Mail not sent: inline PGP can't be used with format=flowed"));
199
0
        return -1;
200
0
      }
201
0
    }
202
0
    else
203
0
    {
204
      /* they really want to send it inline... go for it */
205
0
      if (!isendwin())
206
0
      {
207
0
        mutt_endwin();
208
0
        puts(_("Invoking PGP..."));
209
0
      }
210
0
      pbody = crypt_pgp_traditional_encryptsign(e->body, security, keylist);
211
0
      if (pbody)
212
0
      {
213
0
        e->body = pbody;
214
0
        return 0;
215
0
      }
216
217
      /* otherwise inline won't work...ask for revert */
218
0
      if (query_quadoption(_("Message can't be sent inline.  Revert to using PGP/MIME?"),
219
0
                           NeoMutt->sub, "pgp_mime_auto") != MUTT_YES)
220
0
      {
221
0
        mutt_error(_("Mail not sent"));
222
0
        return -1;
223
0
      }
224
0
    }
225
226
    /* go ahead with PGP/MIME */
227
0
  }
228
229
0
  if (!isendwin())
230
0
    mutt_endwin();
231
232
0
  if (WithCrypto & APPLICATION_SMIME)
233
0
    tmp_smime_pbody = e->body;
234
0
  if (WithCrypto & APPLICATION_PGP)
235
0
    tmp_pgp_pbody = e->body;
236
237
#ifdef CRYPT_BACKEND_GPGME
238
  const bool c_crypt_use_pka = cs_subset_bool(NeoMutt->sub, "crypt_use_pka");
239
  if (sign && c_crypt_use_pka)
240
#else
241
0
  if (sign)
242
0
#endif
243
0
  {
244
    /* Set sender (necessary for e.g. PKA).  */
245
0
    const char *mailbox = NULL;
246
0
    struct Address *from = TAILQ_FIRST(&e->env->from);
247
0
    bool free_from = false;
248
249
0
    if (!from)
250
0
    {
251
0
      free_from = true;
252
0
      from = mutt_default_from(NeoMutt->sub);
253
0
    }
254
255
0
    mailbox = buf_string(from->mailbox);
256
0
    const struct Address *c_envelope_from_address = cs_subset_address(NeoMutt->sub, "envelope_from_address");
257
0
    if (!mailbox && c_envelope_from_address)
258
0
      mailbox = buf_string(c_envelope_from_address->mailbox);
259
260
0
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME))
261
0
      crypt_smime_set_sender(mailbox);
262
0
    else if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP))
263
0
      crypt_pgp_set_sender(mailbox);
264
265
0
    if (free_from)
266
0
      mutt_addr_free(&from);
267
0
  }
268
269
0
  const bool c_crypt_protected_headers_write = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_write");
270
0
  if (c_crypt_protected_headers_write)
271
0
  {
272
0
    const bool c_devel_security = cs_subset_bool(NeoMutt->sub, "devel_security");
273
0
    struct Envelope *protected_headers = mutt_env_new();
274
0
    mutt_env_set_subject(protected_headers, e->env->subject);
275
0
    if (c_devel_security)
276
0
    {
277
0
      mutt_addrlist_copy(&protected_headers->return_path, &e->env->return_path, false);
278
0
      mutt_addrlist_copy(&protected_headers->from, &e->env->from, false);
279
0
      mutt_addrlist_copy(&protected_headers->to, &e->env->to, false);
280
0
      mutt_addrlist_copy(&protected_headers->cc, &e->env->cc, false);
281
0
      mutt_addrlist_copy(&protected_headers->sender, &e->env->sender, false);
282
0
      mutt_addrlist_copy(&protected_headers->reply_to, &e->env->reply_to, false);
283
0
      mutt_addrlist_copy(&protected_headers->mail_followup_to,
284
0
                         &e->env->mail_followup_to, false);
285
0
      mutt_addrlist_copy(&protected_headers->x_original_to, &e->env->x_original_to, false);
286
0
      mutt_list_copy_tail(&protected_headers->references, &e->env->references);
287
0
      mutt_list_copy_tail(&protected_headers->in_reply_to, &e->env->in_reply_to);
288
0
      mutt_env_to_intl(protected_headers, NULL, NULL);
289
0
    }
290
0
    mutt_prepare_envelope(protected_headers, 0, NeoMutt->sub);
291
292
0
    mutt_env_free(&e->body->mime_headers);
293
0
    e->body->mime_headers = protected_headers;
294
0
    mutt_param_set(&e->body->parameter, "protected-headers", "v1");
295
0
  }
296
297
#ifdef USE_AUTOCRYPT
298
  /* A note about e->body->mime_headers.  If postpone or send
299
   * fails, the mime_headers is cleared out before returning to the
300
   * compose menu.  So despite the "robustness" code above and in the
301
   * gen_gossip_list function below, mime_headers will not be set when
302
   * entering mutt_protect().
303
   *
304
   * This is important to note because the user could toggle
305
   * $crypt_protected_headers_write or $autocrypt off back in the
306
   * compose menu.  We don't want mutt_rfc822_write_header() to write
307
   * stale data from one option if the other is set.
308
   */
309
  const bool c_autocrypt = cs_subset_bool(NeoMutt->sub, "autocrypt");
310
  if (c_autocrypt && !postpone && (security & SEC_AUTOCRYPT))
311
  {
312
    mutt_autocrypt_generate_gossip_list(e);
313
  }
314
#endif
315
316
0
  if (sign)
317
0
  {
318
0
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME))
319
0
    {
320
0
      tmp_pbody = crypt_smime_sign_message(e->body, &e->env->from);
321
0
      if (!tmp_pbody)
322
0
        goto bail;
323
0
      pbody = tmp_pbody;
324
0
      tmp_smime_pbody = tmp_pbody;
325
0
    }
326
327
0
    const bool c_pgp_retainable_sigs = cs_subset_bool(NeoMutt->sub, "pgp_retainable_sigs");
328
0
    if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP) &&
329
0
        (!(security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) || c_pgp_retainable_sigs))
330
0
    {
331
0
      tmp_pbody = crypt_pgp_sign_message(e->body, &e->env->from);
332
0
      if (!tmp_pbody)
333
0
        goto bail;
334
335
0
      has_retainable_sig = true;
336
0
      sign = SEC_NO_FLAGS;
337
0
      pbody = tmp_pbody;
338
0
      tmp_pgp_pbody = tmp_pbody;
339
0
    }
340
0
  }
341
342
0
  if (security & (SEC_ENCRYPT | SEC_AUTOCRYPT))
343
0
  {
344
0
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (security & APPLICATION_SMIME))
345
0
    {
346
0
      tmp_pbody = crypt_smime_build_smime_entity(tmp_smime_pbody, keylist);
347
0
      if (!tmp_pbody)
348
0
      {
349
        /* signed ? free it! */
350
0
        goto bail;
351
0
      }
352
      /* free tmp_body if messages was signed AND encrypted ... */
353
0
      if ((tmp_smime_pbody != e->body) && (tmp_smime_pbody != tmp_pbody))
354
0
      {
355
        /* detach and don't delete e->body,
356
         * which tmp_smime_pbody->parts after signing. */
357
0
        tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
358
0
        e->body->next = NULL;
359
0
        mutt_body_free(&tmp_smime_pbody);
360
0
      }
361
0
      pbody = tmp_pbody;
362
0
    }
363
364
0
    if (((WithCrypto & APPLICATION_PGP) != 0) && (security & APPLICATION_PGP))
365
0
    {
366
0
      pbody = crypt_pgp_encrypt_message(e, tmp_pgp_pbody, keylist, sign, &e->env->from);
367
0
      if (!pbody)
368
0
      {
369
        /* did we perform a retainable signature? */
370
0
        if (has_retainable_sig)
371
0
        {
372
          /* remove the outer multipart layer */
373
0
          tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody);
374
          /* get rid of the signature */
375
0
          mutt_body_free(&tmp_pgp_pbody->next);
376
0
        }
377
378
0
        goto bail;
379
0
      }
380
381
      // destroy temporary signature envelope when doing retainable signatures.
382
0
      if (has_retainable_sig)
383
0
      {
384
0
        tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody);
385
0
        mutt_body_free(&tmp_pgp_pbody->next);
386
0
      }
387
0
    }
388
0
  }
389
390
0
  if (pbody)
391
0
  {
392
0
    e->body = pbody;
393
0
    return 0;
394
0
  }
395
396
0
bail:
397
0
  mutt_env_free(&e->body->mime_headers);
398
0
  mutt_param_delete(&e->body->parameter, "protected-headers");
399
0
  return -1;
400
0
}
401
402
/**
403
 * mutt_is_multipart_signed - Is a message signed?
404
 * @param b Body of email
405
 * @retval num Message is signed, see #SecurityFlags
406
 * @retval   0 Message is not signed (#SEC_NO_FLAGS)
407
 */
408
SecurityFlags mutt_is_multipart_signed(struct Body *b)
409
0
{
410
0
  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype || !mutt_istr_equal(b->subtype, "signed"))
411
0
  {
412
0
    return SEC_NO_FLAGS;
413
0
  }
414
415
0
  char *p = mutt_param_get(&b->parameter, "protocol");
416
0
  if (!p)
417
0
    return SEC_NO_FLAGS;
418
419
0
  if (mutt_istr_equal(p, "multipart/mixed"))
420
0
    return SEC_SIGN;
421
422
0
  if (((WithCrypto & APPLICATION_PGP) != 0) && mutt_istr_equal(p, "application/pgp-signature"))
423
0
  {
424
0
    return PGP_SIGN;
425
0
  }
426
427
0
  if (((WithCrypto & APPLICATION_SMIME) != 0) &&
428
0
      (mutt_istr_equal(p, "application/x-pkcs7-signature") ||
429
0
       mutt_istr_equal(p, "application/pkcs7-signature")))
430
0
  {
431
0
    return SMIME_SIGN;
432
0
  }
433
434
0
  return SEC_NO_FLAGS;
435
0
}
436
437
/**
438
 * mutt_is_multipart_encrypted - Does the message have encrypted parts?
439
 * @param b Body of email
440
 * @retval num Message has got encrypted parts, see #SecurityFlags
441
 * @retval   0 Message hasn't got encrypted parts (#SEC_NO_FLAGS)
442
 */
443
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
444
0
{
445
0
  if ((WithCrypto & APPLICATION_PGP) == 0)
446
0
    return SEC_NO_FLAGS;
447
448
0
  char *p = NULL;
449
450
0
  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype ||
451
0
      !mutt_istr_equal(b->subtype, "encrypted") ||
452
0
      !(p = mutt_param_get(&b->parameter, "protocol")) ||
453
0
      !mutt_istr_equal(p, "application/pgp-encrypted"))
454
0
  {
455
0
    return SEC_NO_FLAGS;
456
0
  }
457
458
0
  return PGP_ENCRYPT;
459
0
}
460
461
/**
462
 * mutt_is_valid_multipart_pgp_encrypted - Is this a valid multi-part encrypted message?
463
 * @param b Body of email
464
 * @retval >0 Message is valid, with encrypted parts, e.g. #PGP_ENCRYPT
465
 * @retval  0 Message hasn't got encrypted parts
466
 */
467
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
468
0
{
469
0
  if (mutt_is_multipart_encrypted(b) == SEC_NO_FLAGS)
470
0
    return 0;
471
472
0
  b = b->parts;
473
0
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
474
0
      !mutt_istr_equal(b->subtype, "pgp-encrypted"))
475
0
  {
476
0
    return 0;
477
0
  }
478
479
0
  b = b->next;
480
0
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
481
0
      !mutt_istr_equal(b->subtype, "octet-stream"))
482
0
  {
483
0
    return 0;
484
0
  }
485
486
0
  return PGP_ENCRYPT;
487
0
}
488
489
/**
490
 * mutt_is_malformed_multipart_pgp_encrypted - Check for malformed layout
491
 * @param b Body of email
492
 * @retval num Success, see #SecurityFlags
493
 * @retval   0 Error, (#SEC_NO_FLAGS)
494
 *
495
 * This checks for the malformed layout caused by MS Exchange in
496
 * some cases:
497
 * ```
498
 *  <multipart/mixed>
499
 *     <text/plain>
500
 *     <application/pgp-encrypted> [BASE64-encoded]
501
 *     <application/octet-stream> [BASE64-encoded]
502
 * ```
503
 */
504
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
505
0
{
506
0
  if (!(WithCrypto & APPLICATION_PGP))
507
0
    return SEC_NO_FLAGS;
508
509
0
  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype || !mutt_istr_equal(b->subtype, "mixed"))
510
0
  {
511
0
    return SEC_NO_FLAGS;
512
0
  }
513
514
0
  b = b->parts;
515
0
  if (!b || (b->type != TYPE_TEXT) || !b->subtype ||
516
0
      !mutt_istr_equal(b->subtype, "plain") || (b->length != 0))
517
0
  {
518
0
    return SEC_NO_FLAGS;
519
0
  }
520
521
0
  b = b->next;
522
0
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
523
0
      !mutt_istr_equal(b->subtype, "pgp-encrypted"))
524
0
  {
525
0
    return SEC_NO_FLAGS;
526
0
  }
527
528
0
  b = b->next;
529
0
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
530
0
      !mutt_istr_equal(b->subtype, "octet-stream"))
531
0
  {
532
0
    return SEC_NO_FLAGS;
533
0
  }
534
535
0
  b = b->next;
536
0
  if (b)
537
0
    return SEC_NO_FLAGS;
538
539
0
  return PGP_ENCRYPT;
540
0
}
541
542
/**
543
 * mutt_is_application_pgp - Does the message use PGP?
544
 * @param b Body of email
545
 * @retval >0 Message uses PGP, e.g. #PGP_ENCRYPT
546
 * @retval  0 Message doesn't use PGP, (#SEC_NO_FLAGS)
547
 */
548
SecurityFlags mutt_is_application_pgp(const struct Body *b)
549
0
{
550
0
  SecurityFlags t = SEC_NO_FLAGS;
551
0
  char *p = NULL;
552
553
0
  if (b->type == TYPE_APPLICATION)
554
0
  {
555
0
    if (mutt_istr_equal(b->subtype, "pgp") || mutt_istr_equal(b->subtype, "x-pgp-message"))
556
0
    {
557
0
      p = mutt_param_get(&b->parameter, "x-action");
558
0
      if (p && (mutt_istr_equal(p, "sign") || mutt_istr_equal(p, "signclear")))
559
0
      {
560
0
        t |= PGP_SIGN;
561
0
      }
562
563
0
      p = mutt_param_get(&b->parameter, "format");
564
0
      if (p && mutt_istr_equal(p, "keys-only"))
565
0
      {
566
0
        t |= PGP_KEY;
567
0
      }
568
569
0
      if (t == SEC_NO_FLAGS)
570
0
        t |= PGP_ENCRYPT; /* not necessarily correct, but... */
571
0
    }
572
573
0
    if (mutt_istr_equal(b->subtype, "pgp-signed"))
574
0
      t |= PGP_SIGN;
575
576
0
    if (mutt_istr_equal(b->subtype, "pgp-keys"))
577
0
      t |= PGP_KEY;
578
0
  }
579
0
  else if ((b->type == TYPE_TEXT) && mutt_istr_equal("plain", b->subtype))
580
0
  {
581
0
    if (((p = mutt_param_get(&b->parameter, "x-mutt-action")) ||
582
0
         (p = mutt_param_get(&b->parameter, "x-action")) ||
583
0
         (p = mutt_param_get(&b->parameter, "action"))) &&
584
0
        mutt_istr_startswith(p, "pgp-sign"))
585
0
    {
586
0
      t |= PGP_SIGN;
587
0
    }
588
0
    else if (p && mutt_istr_startswith(p, "pgp-encrypt"))
589
0
    {
590
0
      t |= PGP_ENCRYPT;
591
0
    }
592
0
    else if (p && mutt_istr_startswith(p, "pgp-keys"))
593
0
    {
594
0
      t |= PGP_KEY;
595
0
    }
596
0
  }
597
0
  if (t)
598
0
    t |= PGP_INLINE;
599
600
0
  return t;
601
0
}
602
603
/**
604
 * mutt_is_application_smime - Does the message use S/MIME?
605
 * @param b Body of email
606
 * @retval >0 Message uses S/MIME, e.g. #SMIME_ENCRYPT
607
 * @retval  0 Message doesn't use S/MIME, (#SEC_NO_FLAGS)
608
 */
609
SecurityFlags mutt_is_application_smime(struct Body *b)
610
0
{
611
0
  if (!b)
612
0
    return SEC_NO_FLAGS;
613
614
0
  if (((b->type & TYPE_APPLICATION) == 0) || !b->subtype)
615
0
    return SEC_NO_FLAGS;
616
617
0
  char *t = NULL;
618
0
  bool complain = false;
619
  /* S/MIME MIME types don't need x- anymore, see RFC2311 */
620
0
  if (mutt_istr_equal(b->subtype, "x-pkcs7-mime") || mutt_istr_equal(b->subtype, "pkcs7-mime"))
621
0
  {
622
0
    t = mutt_param_get(&b->parameter, "smime-type");
623
0
    if (t)
624
0
    {
625
0
      if (mutt_istr_equal(t, "enveloped-data"))
626
0
        return SMIME_ENCRYPT;
627
0
      if (mutt_istr_equal(t, "signed-data"))
628
0
        return SMIME_SIGN | SMIME_OPAQUE;
629
0
      return SEC_NO_FLAGS;
630
0
    }
631
    /* Netscape 4.7 uses
632
     * Content-Description: S/MIME Encrypted Message
633
     * instead of Content-Type parameter */
634
0
    if (mutt_istr_equal(b->description, "S/MIME Encrypted Message"))
635
0
      return SMIME_ENCRYPT;
636
0
    complain = true;
637
0
  }
638
0
  else if (!mutt_istr_equal(b->subtype, "octet-stream"))
639
0
  {
640
0
    return SEC_NO_FLAGS;
641
0
  }
642
643
0
  t = mutt_param_get(&b->parameter, "name");
644
645
0
  if (!t)
646
0
    t = b->d_filename;
647
0
  if (!t)
648
0
    t = b->filename;
649
0
  if (!t)
650
0
  {
651
0
    if (complain)
652
0
    {
653
0
      mutt_message(_("S/MIME messages with no hints on content are unsupported"));
654
0
    }
655
0
    return SEC_NO_FLAGS;
656
0
  }
657
658
  /* no .p7c, .p10 support yet. */
659
660
0
  int len = mutt_str_len(t) - 4;
661
0
  if ((len > 0) && (*(t + len) == '.'))
662
0
  {
663
0
    len++;
664
0
    if (mutt_istr_equal((t + len), "p7m"))
665
0
    {
666
      /* Not sure if this is the correct thing to do, but
667
       * it's required for compatibility with Outlook */
668
0
      return SMIME_SIGN | SMIME_OPAQUE;
669
0
    }
670
0
    else if (mutt_istr_equal((t + len), "p7s"))
671
0
    {
672
0
      return SMIME_SIGN | SMIME_OPAQUE;
673
0
    }
674
0
  }
675
676
0
  return SEC_NO_FLAGS;
677
0
}
678
679
/**
680
 * crypt_query - Check out the type of encryption used
681
 * @param b Body of email
682
 * @retval num Flags, see #SecurityFlags
683
 * @retval 0   Error (#SEC_NO_FLAGS)
684
 *
685
 * Set the cached status values if there are any.
686
 */
687
SecurityFlags crypt_query(struct Body *b)
688
0
{
689
0
  if (!WithCrypto || !b)
690
0
    return SEC_NO_FLAGS;
691
692
0
  SecurityFlags rc = SEC_NO_FLAGS;
693
694
0
  if (b->type == TYPE_APPLICATION)
695
0
  {
696
0
    if (WithCrypto & APPLICATION_PGP)
697
0
      rc |= mutt_is_application_pgp(b);
698
699
0
    if (WithCrypto & APPLICATION_SMIME)
700
0
    {
701
0
      rc |= mutt_is_application_smime(b);
702
0
      if (rc && b->goodsig)
703
0
        rc |= SEC_GOODSIGN;
704
0
      if (rc && b->badsig)
705
0
        rc |= SEC_BADSIGN;
706
0
    }
707
0
  }
708
0
  else if (((WithCrypto & APPLICATION_PGP) != 0) && (b->type == TYPE_TEXT))
709
0
  {
710
0
    rc |= mutt_is_application_pgp(b);
711
0
    if (rc && b->goodsig)
712
0
      rc |= SEC_GOODSIGN;
713
0
  }
714
715
0
  if (b->type == TYPE_MULTIPART)
716
0
  {
717
0
    rc |= mutt_is_multipart_encrypted(b);
718
0
    rc |= mutt_is_multipart_signed(b);
719
0
    rc |= mutt_is_malformed_multipart_pgp_encrypted(b);
720
721
0
    if (rc && b->goodsig)
722
0
      rc |= SEC_GOODSIGN;
723
#ifdef USE_AUTOCRYPT
724
    if (rc && b->is_autocrypt)
725
      rc |= SEC_AUTOCRYPT;
726
#endif
727
0
  }
728
729
0
  if ((b->type == TYPE_MULTIPART) || (b->type == TYPE_MESSAGE))
730
0
  {
731
0
    SecurityFlags u = b->parts ? SEC_ALL_FLAGS : SEC_NO_FLAGS; /* Bits set in all parts */
732
0
    SecurityFlags w = SEC_NO_FLAGS; /* Bits set in any part  */
733
734
0
    for (b = b->parts; b; b = b->next)
735
0
    {
736
0
      const SecurityFlags v = crypt_query(b);
737
0
      u &= v;
738
0
      w |= v;
739
0
    }
740
0
    rc |= u | (w & ~SEC_GOODSIGN);
741
742
0
    if ((w & SEC_GOODSIGN) && !(u & SEC_GOODSIGN))
743
0
      rc |= SEC_PARTSIGN;
744
0
  }
745
746
0
  return rc;
747
0
}
748
749
/**
750
 * crypt_write_signed - Write the message body/part
751
 * @param b        Body to write
752
 * @param state    State to use
753
 * @param tempfile File to write to
754
 * @retval  0 Success
755
 * @retval -1 Error
756
 *
757
 * Body/part A described by state state to the given TEMPFILE.
758
 */
759
int crypt_write_signed(struct Body *b, struct State *state, const char *tempfile)
760
0
{
761
0
  if (!WithCrypto)
762
0
    return -1;
763
764
0
  FILE *fp = mutt_file_fopen(tempfile, "w");
765
0
  if (!fp)
766
0
  {
767
0
    mutt_perror("%s", tempfile);
768
0
    return -1;
769
0
  }
770
771
0
  if (!mutt_file_seek(state->fp_in, b->hdr_offset, SEEK_SET))
772
0
  {
773
0
    mutt_file_fclose(&fp);
774
0
    return -1;
775
0
  }
776
0
  size_t bytes = b->length + b->offset - b->hdr_offset;
777
0
  bool hadcr = false;
778
0
  while (bytes > 0)
779
0
  {
780
0
    const int c = fgetc(state->fp_in);
781
0
    if (c == EOF)
782
0
      break;
783
784
0
    bytes--;
785
786
0
    if (c == '\r')
787
0
    {
788
0
      hadcr = true;
789
0
    }
790
0
    else
791
0
    {
792
0
      if ((c == '\n') && !hadcr)
793
0
        fputc('\r', fp);
794
795
0
      hadcr = false;
796
0
    }
797
798
0
    fputc(c, fp);
799
0
  }
800
0
  mutt_file_fclose(&fp);
801
802
0
  return 0;
803
0
}
804
805
/**
806
 * crypt_convert_to_7bit - Convert an email to 7bit encoding
807
 * @param b Body of email to convert
808
 */
809
void crypt_convert_to_7bit(struct Body *b)
810
0
{
811
0
  if (!WithCrypto)
812
0
    return;
813
814
0
  const bool c_pgp_strict_enc = cs_subset_bool(NeoMutt->sub, "pgp_strict_enc");
815
0
  while (b)
816
0
  {
817
0
    if (b->type == TYPE_MULTIPART)
818
0
    {
819
0
      if (b->encoding != ENC_7BIT)
820
0
      {
821
0
        b->encoding = ENC_7BIT;
822
0
        crypt_convert_to_7bit(b->parts);
823
0
      }
824
0
      else if (((WithCrypto & APPLICATION_PGP) != 0) && c_pgp_strict_enc)
825
0
      {
826
0
        crypt_convert_to_7bit(b->parts);
827
0
      }
828
0
    }
829
0
    else if ((b->type == TYPE_MESSAGE) && !mutt_istr_equal(b->subtype, "delivery-status"))
830
0
    {
831
0
      if (b->encoding != ENC_7BIT)
832
0
        mutt_message_to_7bit(b, NULL, NeoMutt->sub);
833
0
    }
834
0
    else if (b->encoding == ENC_8BIT)
835
0
    {
836
0
      b->encoding = ENC_QUOTED_PRINTABLE;
837
0
    }
838
0
    else if (b->encoding == ENC_BINARY)
839
0
    {
840
0
      b->encoding = ENC_BASE64;
841
0
    }
842
0
    else if (b->content && (b->encoding != ENC_BASE64) &&
843
0
             (b->content->from || (b->content->space && c_pgp_strict_enc)))
844
0
    {
845
0
      b->encoding = ENC_QUOTED_PRINTABLE;
846
0
    }
847
0
    b = b->next;
848
0
  }
849
0
}
850
851
/**
852
 * crypt_extract_keys_from_messages - Extract keys from a message
853
 * @param m  Mailbox
854
 * @param ea Array of Emails to process
855
 *
856
 * The extracted keys will be added to the user's keyring.
857
 */
858
void crypt_extract_keys_from_messages(struct Mailbox *m, struct EmailArray *ea)
859
0
{
860
0
  if (!WithCrypto)
861
0
    return;
862
863
0
  struct Buffer *tempfile = buf_pool_get();
864
0
  buf_mktemp(tempfile);
865
0
  FILE *fp_out = mutt_file_fopen(buf_string(tempfile), "w");
866
0
  if (!fp_out)
867
0
  {
868
0
    mutt_perror("%s", buf_string(tempfile));
869
0
    goto cleanup;
870
0
  }
871
872
0
  if (WithCrypto & APPLICATION_PGP)
873
0
    OptDontHandlePgpKeys = true;
874
875
0
  struct Email **ep = NULL;
876
0
  ARRAY_FOREACH(ep, ea)
877
0
  {
878
0
    struct Email *e = *ep;
879
0
    struct Message *msg = mx_msg_open(m, e);
880
0
    if (!msg)
881
0
    {
882
0
      continue;
883
0
    }
884
0
    mutt_parse_mime_message(e, msg->fp);
885
0
    if (e->security & SEC_ENCRYPT && !crypt_valid_passphrase(e->security))
886
0
    {
887
0
      mx_msg_close(m, &msg);
888
0
      mutt_file_fclose(&fp_out);
889
0
      break;
890
0
    }
891
892
0
    if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
893
0
    {
894
0
      mutt_copy_message(fp_out, e, msg, MUTT_CM_DECODE | MUTT_CM_CHARCONV, CH_NO_FLAGS, 0);
895
0
      fflush(fp_out);
896
897
0
      mutt_endwin();
898
0
      puts(_("Trying to extract PGP keys...\n"));
899
0
      crypt_pgp_invoke_import(buf_string(tempfile));
900
0
    }
901
902
0
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
903
0
    {
904
0
      const bool encrypt = e->security & SEC_ENCRYPT;
905
0
      mutt_copy_message(fp_out, e, msg,
906
0
                        encrypt ? MUTT_CM_NOHEADER | MUTT_CM_DECODE_CRYPT | MUTT_CM_DECODE_SMIME :
907
0
                                  MUTT_CM_NO_FLAGS,
908
0
                        CH_NO_FLAGS, 0);
909
0
      fflush(fp_out);
910
911
0
      const char *mbox = NULL;
912
0
      if (!TAILQ_EMPTY(&e->env->from))
913
0
      {
914
0
        mutt_expand_aliases(&e->env->from);
915
0
        mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
916
0
      }
917
0
      else if (!TAILQ_EMPTY(&e->env->sender))
918
0
      {
919
0
        mutt_expand_aliases(&e->env->sender);
920
0
        mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
921
0
      }
922
0
      if (mbox)
923
0
      {
924
0
        mutt_endwin();
925
0
        puts(_("Trying to extract S/MIME certificates..."));
926
0
        crypt_smime_invoke_import(buf_string(tempfile), mbox);
927
0
      }
928
0
    }
929
0
    mx_msg_close(m, &msg);
930
931
0
    rewind(fp_out);
932
0
  }
933
934
0
  mutt_file_fclose(&fp_out);
935
0
  if (isendwin())
936
0
    mutt_any_key_to_continue(NULL);
937
938
0
  mutt_file_unlink(buf_string(tempfile));
939
940
0
  if (WithCrypto & APPLICATION_PGP)
941
0
    OptDontHandlePgpKeys = false;
942
943
0
cleanup:
944
0
  buf_pool_release(&tempfile);
945
0
}
946
947
/**
948
 * crypt_get_keys - Check we have all the keys we need
949
 * @param[in]  e           Email with addresses to match
950
 * @param[out] keylist     Keys needed
951
 * @param[in]  oppenc_mode If true, use opportunistic encryption
952
 * @retval  0 Success
953
 * @retval -1 Error
954
 *
955
 * Do a quick check to make sure that we can find all of the
956
 * encryption keys if the user has requested this service.
957
 * Return the list of keys in KEYLIST.
958
 * If oppenc_mode is true, only keys that can be determined without
959
 * prompting will be used.
960
 */
961
int crypt_get_keys(struct Email *e, char **keylist, bool oppenc_mode)
962
0
{
963
0
  if (!WithCrypto)
964
0
    return 0;
965
966
0
  struct AddressList addrlist = TAILQ_HEAD_INITIALIZER(addrlist);
967
0
  const char *fqdn = mutt_fqdn(true, NeoMutt->sub);
968
0
  const char *self_encrypt = NULL;
969
970
  /* Do a quick check to make sure that we can find all of the encryption
971
   * keys if the user has requested this service.  */
972
973
0
  *keylist = NULL;
974
975
#ifdef USE_AUTOCRYPT
976
  if (!oppenc_mode && (e->security & SEC_AUTOCRYPT))
977
  {
978
    if (mutt_autocrypt_ui_recommendation(e, keylist) <= AUTOCRYPT_REC_NO)
979
      return -1;
980
    return 0;
981
  }
982
#endif
983
984
0
  if (WithCrypto & APPLICATION_PGP)
985
0
    OptPgpCheckTrust = true;
986
987
0
  mutt_addrlist_copy(&addrlist, &e->env->to, false);
988
0
  mutt_addrlist_copy(&addrlist, &e->env->cc, false);
989
0
  mutt_addrlist_copy(&addrlist, &e->env->bcc, false);
990
0
  mutt_addrlist_qualify(&addrlist, fqdn);
991
0
  mutt_addrlist_dedupe(&addrlist);
992
993
0
  if (oppenc_mode || (e->security & SEC_ENCRYPT))
994
0
  {
995
0
    if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
996
0
    {
997
0
      *keylist = crypt_pgp_find_keys(&addrlist, oppenc_mode);
998
0
      if (!*keylist)
999
0
      {
1000
0
        mutt_addrlist_clear(&addrlist);
1001
0
        return -1;
1002
0
      }
1003
0
      OptPgpCheckTrust = false;
1004
0
      const bool c_pgp_self_encrypt = cs_subset_bool(NeoMutt->sub, "pgp_self_encrypt");
1005
0
      const char *const c_pgp_default_key = cs_subset_string(NeoMutt->sub, "pgp_default_key");
1006
0
      const enum QuadOption c_pgp_encrypt_self = cs_subset_quad(NeoMutt->sub, "pgp_encrypt_self");
1007
0
      if (c_pgp_self_encrypt || (c_pgp_encrypt_self == MUTT_YES))
1008
0
        self_encrypt = c_pgp_default_key;
1009
0
    }
1010
0
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
1011
0
    {
1012
0
      *keylist = crypt_smime_find_keys(&addrlist, oppenc_mode);
1013
0
      if (!*keylist)
1014
0
      {
1015
0
        mutt_addrlist_clear(&addrlist);
1016
0
        return -1;
1017
0
      }
1018
0
      const bool c_smime_self_encrypt = cs_subset_bool(NeoMutt->sub, "smime_self_encrypt");
1019
0
      const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1020
0
      const enum QuadOption c_smime_encrypt_self = cs_subset_quad(NeoMutt->sub, "smime_encrypt_self");
1021
0
      if (c_smime_self_encrypt || (c_smime_encrypt_self == MUTT_YES))
1022
0
        self_encrypt = c_smime_default_key;
1023
0
    }
1024
0
  }
1025
1026
0
  if (!oppenc_mode && self_encrypt)
1027
0
  {
1028
0
    const size_t keylist_size = mutt_str_len(*keylist);
1029
0
    MUTT_MEM_REALLOC(keylist, keylist_size + mutt_str_len(self_encrypt) + 2, char);
1030
0
    sprintf(*keylist + keylist_size, " %s", self_encrypt);
1031
0
  }
1032
1033
0
  mutt_addrlist_clear(&addrlist);
1034
1035
0
  return 0;
1036
0
}
1037
1038
/**
1039
 * crypt_opportunistic_encrypt - Can all recipients be determined
1040
 * @param e Email
1041
 *
1042
 * Check if all recipients keys can be automatically determined.
1043
 * Enable encryption if they can, otherwise disable encryption.
1044
 */
1045
void crypt_opportunistic_encrypt(struct Email *e)
1046
0
{
1047
0
  if (!WithCrypto)
1048
0
    return;
1049
1050
0
  const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
1051
0
  if (!(c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT)))
1052
0
    return;
1053
1054
0
  char *pgpkeylist = NULL;
1055
1056
0
  crypt_get_keys(e, &pgpkeylist, true);
1057
0
  if (pgpkeylist)
1058
0
  {
1059
0
    e->security |= SEC_ENCRYPT;
1060
0
    FREE(&pgpkeylist);
1061
0
  }
1062
0
  else
1063
0
  {
1064
0
    e->security &= ~SEC_ENCRYPT;
1065
0
  }
1066
0
}
1067
1068
/**
1069
 * crypt_fetch_signatures - Create an array of an emails parts
1070
 * @param[out] b_sigs Array of Body parts
1071
 * @param[in]  b      Body part to examine
1072
 * @param[out] n      Cumulative count of parts
1073
 */
1074
static void crypt_fetch_signatures(struct Body ***b_sigs, struct Body *b, int *n)
1075
0
{
1076
0
  if (!WithCrypto)
1077
0
    return;
1078
1079
0
  for (; b; b = b->next)
1080
0
  {
1081
0
    if (b->type == TYPE_MULTIPART)
1082
0
    {
1083
0
      crypt_fetch_signatures(b_sigs, b->parts, n);
1084
0
    }
1085
0
    else
1086
0
    {
1087
0
      if ((*n % 5) == 0)
1088
0
        MUTT_MEM_REALLOC(b_sigs, *n + 6, struct Body *);
1089
1090
0
      (*b_sigs)[(*n)++] = b;
1091
0
    }
1092
0
  }
1093
0
}
1094
1095
/**
1096
 * mutt_should_hide_protected_subject - Should NeoMutt hide the protected subject?
1097
 * @param e Email to test
1098
 * @retval true The subject should be protected
1099
 */
1100
bool mutt_should_hide_protected_subject(struct Email *e)
1101
0
{
1102
0
  const bool c_crypt_protected_headers_write = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_write");
1103
0
  const char *const c_crypt_protected_headers_subject =
1104
0
      cs_subset_string(NeoMutt->sub, "crypt_protected_headers_subject");
1105
0
  if (c_crypt_protected_headers_write && (e->security & (SEC_ENCRYPT | SEC_AUTOCRYPT)) &&
1106
0
      !(e->security & SEC_INLINE) && c_crypt_protected_headers_subject)
1107
0
  {
1108
0
    return true;
1109
0
  }
1110
1111
0
  return false;
1112
0
}
1113
1114
/**
1115
 * mutt_protected_headers_handler - Handler for protected headers - Implements ::handler_t - @ingroup handler_api
1116
 */
1117
int mutt_protected_headers_handler(struct Body *b_email, struct State *state)
1118
0
{
1119
0
  if (!cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_read"))
1120
0
    return 0;
1121
1122
0
  state_mark_protected_header(state);
1123
1124
0
  if (!b_email->mime_headers)
1125
0
    goto blank;
1126
1127
0
  const bool c_devel_security = cs_subset_bool(NeoMutt->sub, "devel_security");
1128
0
  const bool display = (state->flags & STATE_DISPLAY);
1129
0
  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
1130
0
  const bool c_crypt_protected_headers_weed = cs_subset_bool(NeoMutt->sub, "crypt_protected_headers_weed");
1131
0
  const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
1132
0
  const int wraplen = display ? mutt_window_wrap_cols(state->wraplen, c_wrap) : 0;
1133
0
  const CopyHeaderFlags chflags = display ? CH_DISPLAY : CH_NO_FLAGS;
1134
0
  struct Buffer *buf = buf_pool_get();
1135
0
  bool weed = (display && c_weed);
1136
0
  if (c_devel_security)
1137
0
    weed &= c_crypt_protected_headers_weed;
1138
1139
0
  if (c_devel_security)
1140
0
  {
1141
0
    if (b_email->mime_headers->date && (!display || !c_weed || !mutt_matches_ignore("date")))
1142
0
    {
1143
0
      mutt_write_one_header(state->fp_out, "Date", b_email->mime_headers->date,
1144
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1145
0
    }
1146
1147
0
    if (!weed || !mutt_matches_ignore("return-path"))
1148
0
    {
1149
0
      mutt_addrlist_write(&b_email->mime_headers->return_path, buf, display);
1150
0
      mutt_write_one_header(state->fp_out, "Return-Path", buf_string(buf),
1151
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1152
0
    }
1153
0
    if (!weed || !mutt_matches_ignore("from"))
1154
0
    {
1155
0
      buf_reset(buf);
1156
0
      mutt_addrlist_write(&b_email->mime_headers->from, buf, display);
1157
0
      mutt_write_one_header(state->fp_out, "From", buf_string(buf),
1158
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1159
0
    }
1160
0
    if (!weed || !mutt_matches_ignore("to"))
1161
0
    {
1162
0
      buf_reset(buf);
1163
0
      mutt_addrlist_write(&b_email->mime_headers->to, buf, display);
1164
0
      mutt_write_one_header(state->fp_out, "To", buf_string(buf), state->prefix,
1165
0
                            wraplen, chflags, NeoMutt->sub);
1166
0
    }
1167
0
    if (!weed || !mutt_matches_ignore("cc"))
1168
0
    {
1169
0
      buf_reset(buf);
1170
0
      mutt_addrlist_write(&b_email->mime_headers->cc, buf, display);
1171
0
      mutt_write_one_header(state->fp_out, "Cc", buf_string(buf), state->prefix,
1172
0
                            wraplen, chflags, NeoMutt->sub);
1173
0
    }
1174
0
    if (!weed || !mutt_matches_ignore("sender"))
1175
0
    {
1176
0
      buf_reset(buf);
1177
0
      mutt_addrlist_write(&b_email->mime_headers->sender, buf, display);
1178
0
      mutt_write_one_header(state->fp_out, "Sender", buf_string(buf),
1179
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1180
0
    }
1181
0
    if (!weed || !mutt_matches_ignore("reply-to"))
1182
0
    {
1183
0
      buf_reset(buf);
1184
0
      mutt_addrlist_write(&b_email->mime_headers->reply_to, buf, display);
1185
0
      mutt_write_one_header(state->fp_out, "Reply-To", buf_string(buf),
1186
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1187
0
    }
1188
0
    if (!weed || !mutt_matches_ignore("mail-followup-to"))
1189
0
    {
1190
0
      buf_reset(buf);
1191
0
      mutt_addrlist_write(&b_email->mime_headers->mail_followup_to, buf, display);
1192
0
      mutt_write_one_header(state->fp_out, "Mail-Followup-To", buf_string(buf),
1193
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1194
0
    }
1195
0
    if (!weed || !mutt_matches_ignore("x-original-to"))
1196
0
    {
1197
0
      buf_reset(buf);
1198
0
      mutt_addrlist_write(&b_email->mime_headers->x_original_to, buf, display);
1199
0
      mutt_write_one_header(state->fp_out, "X-Original-To", buf_string(buf),
1200
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1201
0
    }
1202
0
  }
1203
1204
0
  if (b_email->mime_headers->subject && (!weed || !mutt_matches_ignore("subject")))
1205
0
  {
1206
0
    mutt_write_one_header(state->fp_out, "Subject", b_email->mime_headers->subject,
1207
0
                          state->prefix, wraplen, chflags, NeoMutt->sub);
1208
0
  }
1209
1210
0
  if (c_devel_security)
1211
0
  {
1212
0
    if (b_email->mime_headers->message_id && (!weed || !mutt_matches_ignore("message-id")))
1213
0
    {
1214
0
      mutt_write_one_header(state->fp_out, "Message-ID", b_email->mime_headers->message_id,
1215
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1216
0
    }
1217
0
    if (!weed || !mutt_matches_ignore("references"))
1218
0
    {
1219
0
      buf_reset(buf);
1220
0
      mutt_list_write(&b_email->mime_headers->references, buf);
1221
0
      mutt_write_one_header(state->fp_out, "References", buf_string(buf),
1222
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1223
0
    }
1224
0
    if (!weed || !mutt_matches_ignore("in-reply-to"))
1225
0
    {
1226
0
      buf_reset(buf);
1227
0
      mutt_list_write(&b_email->mime_headers->in_reply_to, buf);
1228
0
      mutt_write_one_header(state->fp_out, "In-Reply-To", buf_string(buf),
1229
0
                            state->prefix, wraplen, chflags, NeoMutt->sub);
1230
0
    }
1231
0
  }
1232
1233
0
  buf_pool_release(&buf);
1234
1235
0
blank:
1236
0
  state_puts(state, "\n");
1237
0
  return 0;
1238
0
}
1239
1240
/**
1241
 * mutt_signed_handler - Handler for "multipart/signed" - Implements ::handler_t - @ingroup handler_api
1242
 */
1243
int mutt_signed_handler(struct Body *b_email, struct State *state)
1244
0
{
1245
0
  if (!WithCrypto)
1246
0
    return -1;
1247
1248
0
  bool inconsistent = false;
1249
0
  struct Body *top = b_email;
1250
0
  struct Body **signatures = NULL;
1251
0
  int sigcnt = 0;
1252
0
  int rc = 0;
1253
0
  struct Buffer *tempfile = NULL;
1254
1255
0
  b_email = b_email->parts;
1256
0
  SecurityFlags signed_type = mutt_is_multipart_signed(top);
1257
0
  if (signed_type == SEC_NO_FLAGS)
1258
0
  {
1259
    /* A null protocol value is already checked for in mutt_body_handler() */
1260
0
    state_printf(state, _("[-- Error: Unknown multipart/signed protocol %s --]\n\n"),
1261
0
                 mutt_param_get(&top->parameter, "protocol"));
1262
0
    return mutt_body_handler(b_email, state);
1263
0
  }
1264
1265
0
  if (!(b_email && b_email->next))
1266
0
  {
1267
0
    inconsistent = true;
1268
0
  }
1269
0
  else
1270
0
  {
1271
0
    switch (signed_type)
1272
0
    {
1273
0
      case SEC_SIGN:
1274
0
        if ((b_email->next->type != TYPE_MULTIPART) ||
1275
0
            !mutt_istr_equal(b_email->next->subtype, "mixed"))
1276
0
        {
1277
0
          inconsistent = true;
1278
0
        }
1279
0
        break;
1280
0
      case PGP_SIGN:
1281
0
        if ((b_email->next->type != TYPE_APPLICATION) ||
1282
0
            !mutt_istr_equal(b_email->next->subtype, "pgp-signature"))
1283
0
        {
1284
0
          inconsistent = true;
1285
0
        }
1286
0
        break;
1287
0
      case SMIME_SIGN:
1288
0
        if ((b_email->next->type != TYPE_APPLICATION) ||
1289
0
            (!mutt_istr_equal(b_email->next->subtype, "x-pkcs7-signature") &&
1290
0
             !mutt_istr_equal(b_email->next->subtype, "pkcs7-signature")))
1291
0
        {
1292
0
          inconsistent = true;
1293
0
        }
1294
0
        break;
1295
0
      default:
1296
0
        inconsistent = true;
1297
0
    }
1298
0
  }
1299
0
  if (inconsistent)
1300
0
  {
1301
0
    state_attach_puts(state, _("[-- Error: Missing or bad-format multipart/signed signature --]\n\n"));
1302
0
    return mutt_body_handler(b_email, state);
1303
0
  }
1304
1305
0
  if (state->flags & STATE_DISPLAY)
1306
0
  {
1307
0
    crypt_fetch_signatures(&signatures, b_email->next, &sigcnt);
1308
1309
0
    if (sigcnt != 0)
1310
0
    {
1311
0
      tempfile = buf_pool_get();
1312
0
      buf_mktemp(tempfile);
1313
0
      bool goodsig = true;
1314
0
      if (crypt_write_signed(b_email, state, buf_string(tempfile)) == 0)
1315
0
      {
1316
0
        for (int i = 0; i < sigcnt; i++)
1317
0
        {
1318
0
          if (((WithCrypto & APPLICATION_PGP) != 0) &&
1319
0
              (signatures[i]->type == TYPE_APPLICATION) &&
1320
0
              mutt_istr_equal(signatures[i]->subtype, "pgp-signature"))
1321
0
          {
1322
0
            if (crypt_pgp_verify_one(signatures[i], state, buf_string(tempfile)) != 0)
1323
0
              goodsig = false;
1324
1325
0
            continue;
1326
0
          }
1327
1328
0
          if (((WithCrypto & APPLICATION_SMIME) != 0) &&
1329
0
              (signatures[i]->type == TYPE_APPLICATION) &&
1330
0
              (mutt_istr_equal(signatures[i]->subtype, "x-pkcs7-signature") ||
1331
0
               mutt_istr_equal(signatures[i]->subtype, "pkcs7-signature")))
1332
0
          {
1333
0
            if (crypt_smime_verify_one(signatures[i], state, buf_string(tempfile)) != 0)
1334
0
              goodsig = false;
1335
1336
0
            continue;
1337
0
          }
1338
1339
0
          state_printf(state, _("[-- Warning: We can't verify %s/%s signatures --]\n\n"),
1340
0
                       BODY_TYPE(signatures[i]), signatures[i]->subtype);
1341
0
        }
1342
0
      }
1343
1344
0
      mutt_file_unlink(buf_string(tempfile));
1345
0
      buf_pool_release(&tempfile);
1346
1347
0
      top->goodsig = goodsig;
1348
0
      top->badsig = !goodsig;
1349
1350
      /* Now display the signed body */
1351
0
      state_attach_puts(state, _("[-- The following data is signed --]\n"));
1352
1353
0
      mutt_protected_headers_handler(b_email, state);
1354
1355
0
      FREE(&signatures);
1356
0
    }
1357
0
    else
1358
0
    {
1359
0
      state_attach_puts(state, _("[-- Warning: Can't find any signatures --]\n\n"));
1360
0
    }
1361
0
  }
1362
1363
0
  rc = mutt_body_handler(b_email, state);
1364
1365
0
  if ((state->flags & STATE_DISPLAY) && (sigcnt != 0))
1366
0
    state_attach_puts(state, _("[-- End of signed data --]\n"));
1367
1368
0
  return rc;
1369
0
}
1370
1371
/**
1372
 * crypt_get_fingerprint_or_id - Get the fingerprint or long key ID
1373
 * @param[in]  p       String to examine
1374
 * @param[out] pphint  Start of string to be passed to pgp_add_string_to_hints() or crypt_add_string_to_hints()
1375
 * @param[out] ppl     Start of long key ID if detected, else NULL
1376
 * @param[out] pps     Start of short key ID if detected, else NULL
1377
 * @retval ptr  Copy of fingerprint, if any, stripped of all spaces.  Must be FREE'd by caller
1378
 * @retval NULL Otherwise
1379
 *
1380
 * Obtain pointers to fingerprint or short or long key ID, if any.
1381
 *
1382
 * Upon return, at most one of return, *ppl and *pps pointers is non-NULL,
1383
 * indicating the longest fingerprint or ID found, if any.
1384
 */
1385
const char *crypt_get_fingerprint_or_id(const char *p, const char **pphint,
1386
                                        const char **ppl, const char **pps)
1387
0
{
1388
0
  const char *ps = NULL, *pl = NULL, *phint = NULL;
1389
0
  char *pfcopy = NULL, *s1 = NULL, *s2 = NULL;
1390
0
  char c;
1391
0
  int isid;
1392
0
  size_t hexdigits;
1393
1394
  /* User input may be partial name, fingerprint or short or long key ID,
1395
   * independent of `$pgp_long_ids`.
1396
   * Fingerprint without spaces is 40 hex digits (SHA-1) or 32 hex digits (MD5).
1397
   * Strip leading "0x" for key ID detection and prepare pl and ps to indicate
1398
   * if an ID was found and to simplify logic in the key loop's inner
1399
   * condition of the caller. */
1400
1401
0
  char *pf = mutt_str_skip_whitespace(p);
1402
0
  if (mutt_istr_startswith(pf, "0x"))
1403
0
    pf += 2;
1404
1405
  /* Check if a fingerprint is given, must be hex digits only, blanks
1406
   * separating groups of 4 hex digits are allowed. Also pre-check for ID. */
1407
0
  isid = 2; /* unknown */
1408
0
  hexdigits = 0;
1409
0
  s1 = pf;
1410
0
  do
1411
0
  {
1412
0
    c = *(s1++);
1413
0
    if ((('0' <= c) && (c <= '9')) || (('A' <= c) && (c <= 'F')) ||
1414
0
        (('a' <= c) && (c <= 'f')))
1415
0
    {
1416
0
      hexdigits++;
1417
0
      if (isid == 2)
1418
0
        isid = 1; /* it is an ID so far */
1419
0
    }
1420
0
    else if (c)
1421
0
    {
1422
0
      isid = 0; /* not an ID */
1423
0
      if ((c == ' ') && ((hexdigits % 4) == 0))
1424
0
        ; /* skip blank before or after 4 hex digits */
1425
0
      else
1426
0
        break; /* any other character or position */
1427
0
    }
1428
0
  } while (c);
1429
1430
  /* If at end of input, check for correct fingerprint length and copy if. */
1431
0
  pfcopy = (!c && ((hexdigits == 40) || (hexdigits == 32)) ? mutt_str_dup(pf) : NULL);
1432
1433
0
  if (pfcopy)
1434
0
  {
1435
    /* Use pfcopy to strip all spaces from fingerprint and as hint. */
1436
0
    s1 = pfcopy;
1437
0
    s2 = pfcopy;
1438
0
    do
1439
0
    {
1440
0
      *(s1++) = *(s2 = mutt_str_skip_whitespace(s2));
1441
0
    } while (*(s2++));
1442
1443
0
    phint = pfcopy;
1444
0
    ps = NULL;
1445
0
    pl = NULL;
1446
0
  }
1447
0
  else
1448
0
  {
1449
0
    phint = p;
1450
0
    ps = NULL;
1451
0
    pl = NULL;
1452
0
    if (isid == 1)
1453
0
    {
1454
0
      if (mutt_str_len(pf) == 16)
1455
0
        pl = pf; /* long key ID */
1456
0
      else if (mutt_str_len(pf) == 8)
1457
0
        ps = pf; /* short key ID */
1458
0
    }
1459
0
  }
1460
1461
0
  *pphint = phint;
1462
0
  *ppl = pl;
1463
0
  *pps = ps;
1464
0
  return pfcopy;
1465
0
}
1466
1467
/**
1468
 * crypt_is_numerical_keyid - Is this a numerical keyid
1469
 * @param s Key to test
1470
 * @retval true Keyid is numeric
1471
 *
1472
 * Check if a crypt-hook value is a key id.
1473
 */
1474
bool crypt_is_numerical_keyid(const char *s)
1475
0
{
1476
  /* or should we require the "0x"? */
1477
0
  if (mutt_strn_equal(s, "0x", 2))
1478
0
    s += 2;
1479
0
  if (strlen(s) % 8)
1480
0
    return false;
1481
0
  while (*s)
1482
0
    if (!strchr("0123456789ABCDEFabcdef", *s++))
1483
0
      return false;
1484
1485
0
  return true;
1486
0
}