Coverage Report

Created: 2025-03-11 06:49

/src/neomutt/ncrypt/smime.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * SMIME helper routines
4
 *
5
 * @authors
6
 * Copyright (C) 2017-2024 Richard Russon <rich@flatcap.org>
7
 * Copyright (C) 2019-2021 Pietro Cerutti <gahr@gahr.ch>
8
 * Copyright (C) 2020 Lars Haalck <lars.haalck@uni-muenster.de>
9
 * Copyright (C) 2023 Anna Figueiredo Gomes <navi@vlhl.dev>
10
 * Copyright (C) 2024 Alejandro Colomar <alx@kernel.org>
11
 * Copyright (C) 2023-2024 Tóth János <gomba007@gmail.com>
12
 *
13
 * @copyright
14
 * This program is free software: you can redistribute it and/or modify it under
15
 * the terms of the GNU General Public License as published by the Free Software
16
 * Foundation, either version 2 of the License, or (at your option) any later
17
 * version.
18
 *
19
 * This program is distributed in the hope that it will be useful, but WITHOUT
20
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
21
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
22
 * details.
23
 *
24
 * You should have received a copy of the GNU General Public License along with
25
 * this program.  If not, see <http://www.gnu.org/licenses/>.
26
 */
27
28
/**
29
 * @page crypt_smime SMIME helper routines
30
 *
31
 * SMIME helper routines
32
 */
33
34
#include "config.h"
35
#include <limits.h>
36
#include <stdbool.h>
37
#include <stdio.h>
38
#include <string.h>
39
#include <sys/types.h>
40
#include <unistd.h>
41
#include "private.h"
42
#include "mutt/lib.h"
43
#include "address/lib.h"
44
#include "config/lib.h"
45
#include "email/lib.h"
46
#include "core/lib.h"
47
#include "alias/lib.h"
48
#include "gui/lib.h"
49
#include "mutt.h"
50
#include "lib.h"
51
#include "editor/lib.h"
52
#include "expando/lib.h"
53
#include "history/lib.h"
54
#include "question/lib.h"
55
#include "send/lib.h"
56
#include "copy.h"
57
#include "crypt.h"
58
#include "cryptglue.h"
59
#include "expando_smime.h"
60
#include "handler.h"
61
#include "mutt_logging.h"
62
#ifdef CRYPT_BACKEND_CLASSIC_SMIME
63
#include "smime.h"
64
#endif
65
66
/// Cached Smime Passphrase
67
static char SmimePass[256];
68
/// Unix time when #SmimePass expires
69
static time_t SmimeExpTime = 0; /* when does the cached passphrase expire? */
70
71
/// Smime key to use
72
static struct Buffer SmimeKeyToUse = { 0 };
73
/// Smime certificate to use
74
static struct Buffer SmimeCertToUse = { 0 };
75
/// Smime intermediate certificate to use
76
static struct Buffer SmimeIntermediateToUse = { 0 };
77
78
/**
79
 * smime_init - Initialise smime globals
80
 */
81
void smime_init(void)
82
0
{
83
0
  buf_alloc(&SmimeKeyToUse, 256);
84
0
  buf_alloc(&SmimeCertToUse, 256);
85
0
  buf_alloc(&SmimeIntermediateToUse, 256);
86
0
}
87
88
/**
89
 * smime_cleanup - Clean up smime globals
90
 */
91
void smime_cleanup(void)
92
0
{
93
0
  buf_dealloc(&SmimeKeyToUse);
94
0
  buf_dealloc(&SmimeCertToUse);
95
0
  buf_dealloc(&SmimeIntermediateToUse);
96
0
}
97
98
/**
99
 * smime_key_free - Free a list of SMIME keys
100
 * @param[out] keylist List of keys to free
101
 */
102
static void smime_key_free(struct SmimeKey **keylist)
103
0
{
104
0
  if (!keylist)
105
0
    return;
106
107
0
  struct SmimeKey *key = NULL;
108
109
0
  while (*keylist)
110
0
  {
111
0
    key = *keylist;
112
0
    *keylist = (*keylist)->next;
113
114
0
    FREE(&key->email);
115
0
    FREE(&key->hash);
116
0
    FREE(&key->label);
117
0
    FREE(&key->issuer);
118
0
    FREE(&key);
119
0
  }
120
0
}
121
122
/**
123
 * smime_copy_key - Copy an SMIME key
124
 * @param key Key to copy
125
 * @retval ptr Newly allocated SMIME key
126
 */
127
static struct SmimeKey *smime_copy_key(struct SmimeKey *key)
128
0
{
129
0
  if (!key)
130
0
    return NULL;
131
132
0
  struct SmimeKey *copy = NULL;
133
134
0
  copy = MUTT_MEM_CALLOC(1, struct SmimeKey);
135
0
  copy->email = mutt_str_dup(key->email);
136
0
  copy->hash = mutt_str_dup(key->hash);
137
0
  copy->label = mutt_str_dup(key->label);
138
0
  copy->issuer = mutt_str_dup(key->issuer);
139
0
  copy->trust = key->trust;
140
0
  copy->flags = key->flags;
141
142
0
  return copy;
143
0
}
144
145
/**
146
 * smime_class_void_passphrase - Forget the cached passphrase - Implements CryptModuleSpecs::void_passphrase() - @ingroup crypto_void_passphrase
147
 */
148
void smime_class_void_passphrase(void)
149
0
{
150
0
  memset(SmimePass, 0, sizeof(SmimePass));
151
0
  SmimeExpTime = 0;
152
0
}
153
154
/**
155
 * smime_class_valid_passphrase - Ensure we have a valid passphrase - Implements CryptModuleSpecs::valid_passphrase() - @ingroup crypto_valid_passphrase
156
 */
157
bool smime_class_valid_passphrase(void)
158
0
{
159
0
  const time_t now = mutt_date_now();
160
0
  if (now < SmimeExpTime)
161
0
  {
162
    /* Use cached copy.  */
163
0
    return true;
164
0
  }
165
166
0
  smime_class_void_passphrase();
167
168
0
  struct Buffer *buf = buf_pool_get();
169
0
  const int rc = mw_get_field(_("Enter S/MIME passphrase:"), buf,
170
0
                              MUTT_COMP_PASS | MUTT_COMP_UNBUFFERED, HC_OTHER, NULL, NULL);
171
0
  mutt_str_copy(SmimePass, buf_string(buf), sizeof(SmimePass));
172
0
  buf_pool_release(&buf);
173
174
0
  if (rc == 0)
175
0
  {
176
0
    const short c_smime_timeout = cs_subset_number(NeoMutt->sub, "smime_timeout");
177
0
    SmimeExpTime = mutt_date_add_timeout(now, c_smime_timeout);
178
0
    return true;
179
0
  }
180
0
  else
181
0
  {
182
0
    SmimeExpTime = 0;
183
0
  }
184
185
0
  return false;
186
0
}
187
188
/**
189
 * smime_command - Format an SMIME command string
190
 * @param buf    Buffer for the result
191
 * @param cctx   Data to pass to the formatter
192
 * @param exp    Expando to use
193
 */
194
static void smime_command(struct Buffer *buf, struct SmimeCommandContext *cctx,
195
                          const struct Expando *exp)
196
0
{
197
0
  expando_render(exp, SmimeCommandRenderCallbacks, cctx, MUTT_FORMAT_NO_FLAGS,
198
0
                 buf->dsize, buf);
199
0
  mutt_debug(LL_DEBUG2, "%s\n", buf_string(buf));
200
0
}
201
202
/**
203
 * smime_invoke - Run an SMIME command
204
 * @param[out] fp_smime_in       stdin  for the command, or NULL (OPTIONAL)
205
 * @param[out] fp_smime_out      stdout for the command, or NULL (OPTIONAL)
206
 * @param[out] fp_smime_err      stderr for the command, or NULL (OPTIONAL)
207
 * @param[in]  fp_smime_infd     stdin  for the command, or -1 (OPTIONAL)
208
 * @param[in]  fp_smime_outfd    stdout for the command, or -1 (OPTIONAL)
209
 * @param[in]  fp_smime_errfd    stderr for the command, or -1 (OPTIONAL)
210
 * @param[in]  fname         Filename to pass to the command
211
 * @param[in]  sig_fname     Signature filename to pass to the command
212
 * @param[in]  cryptalg      Encryption algorithm
213
 * @param[in]  digestalg     Hashing algorithm
214
 * @param[in]  key           SMIME key
215
 * @param[in]  certificates  Public certificates
216
 * @param[in]  intermediates Intermediate certificates
217
 * @param[in]  exp           Expando format string
218
 * @retval num PID of the created process
219
 * @retval -1  Error creating pipes or forking
220
 *
221
 * @note `fp_smime_in` has priority over `fp_smime_infd`.
222
 *       Likewise `fp_smime_out` and `fp_smime_err`.
223
 */
224
static pid_t smime_invoke(FILE **fp_smime_in, FILE **fp_smime_out, FILE **fp_smime_err,
225
                          int fp_smime_infd, int fp_smime_outfd, int fp_smime_errfd,
226
                          const char *fname, const char *sig_fname, const char *cryptalg,
227
                          const char *digestalg, const char *key, const char *certificates,
228
                          const char *intermediates, const struct Expando *exp)
229
0
{
230
0
  struct SmimeCommandContext cctx = { 0 };
231
232
0
  if (!exp)
233
0
    return (pid_t) -1;
234
235
0
  cctx.fname = fname;
236
0
  cctx.sig_fname = sig_fname;
237
0
  cctx.key = key;
238
0
  cctx.cryptalg = cryptalg;
239
0
  cctx.digestalg = digestalg;
240
0
  cctx.certificates = certificates;
241
0
  cctx.intermediates = intermediates;
242
243
0
  struct Buffer *cmd = buf_pool_get();
244
0
  smime_command(cmd, &cctx, exp);
245
246
0
  pid_t pid = filter_create_fd(buf_string(cmd), fp_smime_in, fp_smime_out,
247
0
                               fp_smime_err, fp_smime_infd, fp_smime_outfd,
248
0
                               fp_smime_errfd, NeoMutt->env);
249
0
  buf_pool_release(&cmd);
250
0
  return pid;
251
0
}
252
253
/**
254
 * smime_parse_key - Parse an SMIME key block
255
 * @param buf String to parse
256
 * @retval ptr  SMIME key
257
 * @retval NULL Error
258
 */
259
static struct SmimeKey *smime_parse_key(char *buf)
260
0
{
261
0
  char *pend = NULL, *p = NULL;
262
0
  int field = 0;
263
264
0
  struct SmimeKey *key = MUTT_MEM_CALLOC(1, struct SmimeKey);
265
266
0
  for (p = buf; p; p = pend)
267
0
  {
268
    /* Some users manually maintain their .index file, and use a tab
269
     * as a delimiter, which the old parsing code (using fscanf)
270
     * happened to allow.  smime_keys uses a space, so search for both.  */
271
0
    if ((pend = strchr(p, ' ')) || (pend = strchr(p, '\t')) || (pend = strchr(p, '\n')))
272
0
      *pend++ = 0;
273
274
    /* For backward compatibility, don't count consecutive delimiters
275
     * as an empty field.  */
276
0
    if (*p == '\0')
277
0
      continue;
278
279
0
    field++;
280
281
0
    switch (field)
282
0
    {
283
0
      case 1: /* mailbox */
284
0
        key->email = mutt_str_dup(p);
285
0
        break;
286
0
      case 2: /* hash */
287
0
        key->hash = mutt_str_dup(p);
288
0
        break;
289
0
      case 3: /* label */
290
0
        key->label = mutt_str_dup(p);
291
0
        break;
292
0
      case 4: /* issuer */
293
0
        key->issuer = mutt_str_dup(p);
294
0
        break;
295
0
      case 5: /* trust */
296
0
        key->trust = *p;
297
0
        break;
298
0
      case 6: /* purpose */
299
0
        while (*p)
300
0
        {
301
0
          switch (*p++)
302
0
          {
303
0
            case 'e':
304
0
              key->flags |= KEYFLAG_CANENCRYPT;
305
0
              break;
306
307
0
            case 's':
308
0
              key->flags |= KEYFLAG_CANSIGN;
309
0
              break;
310
0
          }
311
0
        }
312
0
        break;
313
0
    }
314
0
  }
315
316
  /* Old index files could be missing issuer, trust, and purpose,
317
   * but anything less than that is an error. */
318
0
  if (field < 3)
319
0
  {
320
0
    smime_key_free(&key);
321
0
    return NULL;
322
0
  }
323
324
0
  if (field < 4)
325
0
    key->issuer = mutt_str_dup("?");
326
327
0
  if (field < 5)
328
0
    key->trust = 't';
329
330
0
  if (field < 6)
331
0
    key->flags = (KEYFLAG_CANENCRYPT | KEYFLAG_CANSIGN);
332
333
0
  return key;
334
0
}
335
336
/**
337
 * smime_get_candidates - Find keys matching a string
338
 * @param search           String to match
339
 * @param only_public_key  If true, only get the public keys
340
 * @retval ptr Matching key
341
 */
342
static struct SmimeKey *smime_get_candidates(const char *search, bool only_public_key)
343
0
{
344
0
  char buf[1024] = { 0 };
345
0
  struct SmimeKey *key = NULL, *results = NULL;
346
0
  struct SmimeKey **results_end = &results;
347
348
0
  struct Buffer *index_file = buf_pool_get();
349
0
  const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
350
0
  const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
351
0
  buf_printf(index_file, "%s/.index",
352
0
             only_public_key ? NONULL(c_smime_certificates) : NONULL(c_smime_keys));
353
354
0
  FILE *fp = mutt_file_fopen(buf_string(index_file), "r");
355
0
  if (!fp)
356
0
  {
357
0
    mutt_perror("%s", buf_string(index_file));
358
0
    buf_pool_release(&index_file);
359
0
    return NULL;
360
0
  }
361
0
  buf_pool_release(&index_file);
362
363
0
  while (fgets(buf, sizeof(buf), fp))
364
0
  {
365
0
    if (((*search == '\0')) || mutt_istr_find(buf, search))
366
0
    {
367
0
      key = smime_parse_key(buf);
368
0
      if (key)
369
0
      {
370
0
        *results_end = key;
371
0
        results_end = &key->next;
372
0
      }
373
0
    }
374
0
  }
375
376
0
  mutt_file_fclose(&fp);
377
378
0
  return results;
379
0
}
380
381
/**
382
 * smime_get_key_by_hash - Find a key by its hash
383
 * @param hash             Hash to find
384
 * @param only_public_key  If true, only get the public keys
385
 * @retval ptr Matching key
386
 *
387
 * Returns the first matching key record, without prompting or checking of
388
 * abilities or trust.
389
 */
390
static struct SmimeKey *smime_get_key_by_hash(const char *hash, bool only_public_key)
391
0
{
392
0
  struct SmimeKey *match = NULL;
393
0
  struct SmimeKey *results = smime_get_candidates(hash, only_public_key);
394
0
  for (struct SmimeKey *result = results; result; result = result->next)
395
0
  {
396
0
    if (mutt_istr_equal(hash, result->hash))
397
0
    {
398
0
      match = smime_copy_key(result);
399
0
      break;
400
0
    }
401
0
  }
402
403
0
  smime_key_free(&results);
404
405
0
  return match;
406
0
}
407
408
/**
409
 * smime_get_key_by_addr - Find an SIME key by address
410
 * @param mailbox          Email address to match
411
 * @param abilities        Abilities to match, see #KeyFlags
412
 * @param only_public_key  If true, only get the public keys
413
 * @param oppenc_mode      If true, use opportunistic encryption
414
 * @retval ptr Matching key
415
 */
416
static struct SmimeKey *smime_get_key_by_addr(const char *mailbox, KeyFlags abilities,
417
                                              bool only_public_key, bool oppenc_mode)
418
0
{
419
0
  if (!mailbox)
420
0
    return NULL;
421
422
0
  struct SmimeKey *results = NULL, *result = NULL;
423
0
  struct SmimeKey *matches = NULL;
424
0
  struct SmimeKey **matches_end = &matches;
425
0
  struct SmimeKey *match = NULL;
426
0
  struct SmimeKey *trusted_match = NULL;
427
0
  struct SmimeKey *valid_match = NULL;
428
0
  struct SmimeKey *return_key = NULL;
429
0
  bool multi_trusted_matches = false;
430
431
0
  results = smime_get_candidates(mailbox, only_public_key);
432
0
  for (result = results; result; result = result->next)
433
0
  {
434
0
    if (abilities && !(result->flags & abilities))
435
0
    {
436
0
      continue;
437
0
    }
438
439
0
    if (mutt_istr_equal(mailbox, result->email))
440
0
    {
441
0
      match = smime_copy_key(result);
442
0
      *matches_end = match;
443
0
      matches_end = &match->next;
444
445
0
      if (match->trust == 't')
446
0
      {
447
0
        if (trusted_match && !mutt_istr_equal(match->hash, trusted_match->hash))
448
0
        {
449
0
          multi_trusted_matches = true;
450
0
        }
451
0
        trusted_match = match;
452
0
      }
453
0
      else if ((match->trust == 'u') || (match->trust == 'v'))
454
0
      {
455
0
        valid_match = match;
456
0
      }
457
0
    }
458
0
  }
459
460
0
  smime_key_free(&results);
461
462
0
  if (matches)
463
0
  {
464
0
    if (oppenc_mode || !isatty(STDIN_FILENO))
465
0
    {
466
0
      const bool c_crypt_opportunistic_encrypt_strong_keys =
467
0
          cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt_strong_keys");
468
0
      if (trusted_match)
469
0
        return_key = smime_copy_key(trusted_match);
470
0
      else if (valid_match && !c_crypt_opportunistic_encrypt_strong_keys)
471
0
        return_key = smime_copy_key(valid_match);
472
0
      else
473
0
        return_key = NULL;
474
0
    }
475
0
    else if (trusted_match && !multi_trusted_matches)
476
0
    {
477
0
      return_key = smime_copy_key(trusted_match);
478
0
    }
479
0
    else
480
0
    {
481
0
      return_key = smime_copy_key(dlg_smime(matches, mailbox));
482
0
    }
483
484
0
    smime_key_free(&matches);
485
0
  }
486
487
0
  return return_key;
488
0
}
489
490
/**
491
 * smime_get_key_by_str - Find an SMIME key by string
492
 * @param str              String to match
493
 * @param abilities        Abilities to match, see #KeyFlags
494
 * @param only_public_key  If true, only get the public keys
495
 * @retval ptr Matching key
496
 */
497
static struct SmimeKey *smime_get_key_by_str(const char *str, KeyFlags abilities, bool only_public_key)
498
0
{
499
0
  if (!str)
500
0
    return NULL;
501
502
0
  struct SmimeKey *results = NULL, *result = NULL;
503
0
  struct SmimeKey *matches = NULL;
504
0
  struct SmimeKey **matches_end = &matches;
505
0
  struct SmimeKey *match = NULL;
506
0
  struct SmimeKey *return_key = NULL;
507
508
0
  results = smime_get_candidates(str, only_public_key);
509
0
  for (result = results; result; result = result->next)
510
0
  {
511
0
    if (abilities && !(result->flags & abilities))
512
0
    {
513
0
      continue;
514
0
    }
515
516
0
    if (mutt_istr_equal(str, result->hash) ||
517
0
        mutt_istr_find(result->email, str) || mutt_istr_find(result->label, str))
518
0
    {
519
0
      match = smime_copy_key(result);
520
0
      *matches_end = match;
521
0
      matches_end = &match->next;
522
0
    }
523
0
  }
524
525
0
  smime_key_free(&results);
526
527
0
  if (matches)
528
0
  {
529
0
    return_key = smime_copy_key(dlg_smime(matches, str));
530
0
    smime_key_free(&matches);
531
0
  }
532
533
0
  return return_key;
534
0
}
535
536
/**
537
 * smime_ask_for_key - Ask the user to select a key
538
 * @param prompt           Prompt to show the user
539
 * @param abilities        Abilities to match, see #KeyFlags
540
 * @param only_public_key  If true, only get the public keys
541
 * @retval ptr Selected SMIME key
542
 */
543
static struct SmimeKey *smime_ask_for_key(const char *prompt, KeyFlags abilities, bool only_public_key)
544
0
{
545
0
  if (!prompt)
546
0
    return NULL;
547
548
0
  struct SmimeKey *key = NULL;
549
0
  struct Buffer *resp = buf_pool_get();
550
551
0
  mutt_clear_error();
552
553
0
  while (true)
554
0
  {
555
0
    buf_reset(resp);
556
0
    if (mw_get_field(prompt, resp, MUTT_COMP_NO_FLAGS, HC_OTHER, NULL, NULL) != 0)
557
0
    {
558
0
      goto done;
559
0
    }
560
561
0
    key = smime_get_key_by_str(buf_string(resp), abilities, only_public_key);
562
0
    if (key)
563
0
      goto done;
564
565
0
    mutt_error(_("No matching keys found for \"%s\""), buf_string(resp));
566
0
  }
567
568
0
done:
569
0
  buf_pool_release(&resp);
570
0
  return key;
571
0
}
572
573
/**
574
 * getkeys - Get the keys for a mailbox
575
 * @param mailbox Email address
576
 *
577
 * This sets the '*ToUse' variables for an upcoming decryption, where the
578
 * required key is different from `$smime_default_key`.
579
 */
580
static void getkeys(const char *mailbox)
581
0
{
582
0
  const char *k = NULL;
583
584
0
  struct SmimeKey *key = smime_get_key_by_addr(mailbox, KEYFLAG_CANENCRYPT, false, false);
585
586
0
  if (!key)
587
0
  {
588
0
    struct Buffer *prompt = buf_pool_get();
589
0
    buf_printf(prompt, _("Enter keyID for %s: "), mailbox);
590
0
    key = smime_ask_for_key(buf_string(prompt), KEYFLAG_CANENCRYPT, false);
591
0
    buf_pool_release(&prompt);
592
0
  }
593
594
0
  const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
595
0
  size_t smime_keys_len = mutt_str_len(c_smime_keys);
596
597
0
  const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
598
0
  k = key ? key->hash : NONULL(c_smime_default_key);
599
600
  /* if the key is different from last time */
601
0
  if ((buf_len(&SmimeKeyToUse) <= smime_keys_len) ||
602
0
      !mutt_istr_equal(k, SmimeKeyToUse.data + smime_keys_len + 1))
603
0
  {
604
0
    smime_class_void_passphrase();
605
0
    buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), k);
606
0
    const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
607
0
    buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), k);
608
0
  }
609
610
0
  smime_key_free(&key);
611
0
}
612
613
/**
614
 * smime_class_getkeys - Get the S/MIME keys required to encrypt this email - Implements CryptModuleSpecs::smime_getkeys() - @ingroup crypto_smime_getkeys
615
 */
616
void smime_class_getkeys(struct Envelope *env)
617
0
{
618
0
  const bool c_smime_decrypt_use_default_key = cs_subset_bool(NeoMutt->sub, "smime_decrypt_use_default_key");
619
0
  const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
620
0
  if (c_smime_decrypt_use_default_key && c_smime_default_key)
621
0
  {
622
0
    const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
623
0
    buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), c_smime_default_key);
624
0
    const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
625
0
    buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), c_smime_default_key);
626
0
    return;
627
0
  }
628
629
0
  struct Address *a = NULL;
630
0
  TAILQ_FOREACH(a, &env->to, entries)
631
0
  {
632
0
    if (mutt_addr_is_user(a))
633
0
    {
634
0
      getkeys(buf_string(a->mailbox));
635
0
      return;
636
0
    }
637
0
  }
638
639
0
  TAILQ_FOREACH(a, &env->cc, entries)
640
0
  {
641
0
    if (mutt_addr_is_user(a))
642
0
    {
643
0
      getkeys(buf_string(a->mailbox));
644
0
      return;
645
0
    }
646
0
  }
647
648
0
  struct Address *f = mutt_default_from(NeoMutt->sub);
649
0
  getkeys(buf_string(f->mailbox));
650
0
  mutt_addr_free(&f);
651
0
}
652
653
/**
654
 * smime_class_find_keys - Find the keyids of the recipients of a message - Implements CryptModuleSpecs::find_keys() - @ingroup crypto_find_keys
655
 */
656
char *smime_class_find_keys(const struct AddressList *al, bool oppenc_mode)
657
0
{
658
0
  struct SmimeKey *key = NULL;
659
0
  char *keyid = NULL, *keylist = NULL;
660
0
  size_t keylist_size = 0;
661
0
  size_t keylist_used = 0;
662
663
0
  struct Address *a = NULL;
664
0
  TAILQ_FOREACH(a, al, entries)
665
0
  {
666
0
    key = smime_get_key_by_addr(buf_string(a->mailbox), KEYFLAG_CANENCRYPT, true, oppenc_mode);
667
0
    if (!key && !oppenc_mode && isatty(STDIN_FILENO))
668
0
    {
669
0
      struct Buffer *prompt = buf_pool_get();
670
0
      buf_printf(prompt, _("Enter keyID for %s: "), buf_string(a->mailbox));
671
0
      key = smime_ask_for_key(buf_string(prompt), KEYFLAG_CANENCRYPT, true);
672
0
      buf_pool_release(&prompt);
673
0
    }
674
0
    if (!key)
675
0
    {
676
0
      if (!oppenc_mode)
677
0
        mutt_message(_("No (valid) certificate found for %s"), buf_string(a->mailbox));
678
0
      FREE(&keylist);
679
0
      return NULL;
680
0
    }
681
682
0
    keyid = key->hash;
683
0
    keylist_size += mutt_str_len(keyid) + 2;
684
0
    MUTT_MEM_REALLOC(&keylist, keylist_size, char);
685
0
    sprintf(keylist + keylist_used, "%s%s", keylist_used ? " " : "", keyid);
686
0
    keylist_used = mutt_str_len(keylist);
687
688
0
    smime_key_free(&key);
689
0
  }
690
0
  return keylist;
691
0
}
692
693
/**
694
 * smime_handle_cert_email - Process an email containing certificates
695
 * @param[in]  certificate Email with certificates
696
 * @param[in]  mailbox     Email address
697
 * @param[in]  copy        If true, save the certificates to buffer
698
 * @param[out] buffer      Buffer allocated to hold certificates
699
 * @param[out] num         Number of certificates in buffer
700
 * @retval  0 Success
701
 * @retval -1 Error
702
 * @retval -2 Error
703
 */
704
static int smime_handle_cert_email(const char *certificate, const char *mailbox,
705
                                   bool copy, char ***buffer, int *num)
706
0
{
707
0
  char email[256] = { 0 };
708
0
  int rc = -1, count = 0;
709
0
  pid_t pid;
710
711
0
  FILE *fp_err = mutt_file_mkstemp();
712
0
  if (!fp_err)
713
0
  {
714
0
    mutt_perror(_("Can't create temporary file"));
715
0
    return 1;
716
0
  }
717
718
0
  FILE *fp_out = mutt_file_mkstemp();
719
0
  if (!fp_out)
720
0
  {
721
0
    mutt_file_fclose(&fp_err);
722
0
    mutt_perror(_("Can't create temporary file"));
723
0
    return 1;
724
0
  }
725
726
0
  const struct Expando *c_smime_get_cert_email_command =
727
0
      cs_subset_expando(NeoMutt->sub, "smime_get_cert_email_command");
728
0
  pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), certificate,
729
0
                     NULL, NULL, NULL, NULL, NULL, NULL, c_smime_get_cert_email_command);
730
0
  if (pid == -1)
731
0
  {
732
0
    mutt_message(_("Error: unable to create OpenSSL subprocess"));
733
0
    mutt_file_fclose(&fp_err);
734
0
    mutt_file_fclose(&fp_out);
735
0
    return 1;
736
0
  }
737
738
0
  filter_wait(pid);
739
740
0
  fflush(fp_out);
741
0
  rewind(fp_out);
742
0
  fflush(fp_err);
743
0
  rewind(fp_err);
744
745
0
  while ((fgets(email, sizeof(email), fp_out)))
746
0
  {
747
0
    size_t len = mutt_str_len(email);
748
0
    if (len && (email[len - 1] == '\n'))
749
0
      email[len - 1] = '\0';
750
0
    if (mutt_istr_startswith(email, mailbox))
751
0
      rc = 1;
752
753
0
    rc = (rc < 0) ? 0 : rc;
754
0
    count++;
755
0
  }
756
757
0
  if (rc == -1)
758
0
  {
759
0
    mutt_endwin();
760
0
    mutt_file_copy_stream(fp_err, stdout);
761
0
    mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
762
0
    rc = 1;
763
0
  }
764
0
  else if (rc == 0)
765
0
  {
766
0
    rc = 1;
767
0
  }
768
0
  else
769
0
  {
770
0
    rc = 0;
771
0
  }
772
773
0
  if (copy && buffer && num)
774
0
  {
775
0
    (*num) = count;
776
0
    *buffer = MUTT_MEM_CALLOC(count, char *);
777
0
    count = 0;
778
779
0
    rewind(fp_out);
780
0
    while ((fgets(email, sizeof(email), fp_out)))
781
0
    {
782
0
      size_t len = mutt_str_len(email);
783
0
      if (len && (email[len - 1] == '\n'))
784
0
        email[len - 1] = '\0';
785
0
      (*buffer)[count] = MUTT_MEM_CALLOC(mutt_str_len(email) + 1, char);
786
0
      strncpy((*buffer)[count], email, mutt_str_len(email));
787
0
      count++;
788
0
    }
789
0
  }
790
0
  else if (copy)
791
0
  {
792
0
    rc = 2;
793
0
  }
794
795
0
  mutt_file_fclose(&fp_out);
796
0
  mutt_file_fclose(&fp_err);
797
798
0
  return rc;
799
0
}
800
801
/**
802
 * smime_extract_certificate - Extract an SMIME certificate from a file
803
 * @param infile File to read
804
 * @retval ptr Filename of temporary file containing certificate
805
 */
806
static char *smime_extract_certificate(const char *infile)
807
0
{
808
0
  FILE *fp_err = NULL;
809
0
  FILE *fp_out = NULL;
810
0
  FILE *fp_cert = NULL;
811
0
  char *rc = NULL;
812
0
  pid_t pid;
813
0
  int empty;
814
815
0
  struct Buffer *pk7out = buf_pool_get();
816
0
  struct Buffer *certfile = buf_pool_get();
817
818
0
  fp_err = mutt_file_mkstemp();
819
0
  if (!fp_err)
820
0
  {
821
0
    mutt_perror(_("Can't create temporary file"));
822
0
    goto cleanup;
823
0
  }
824
825
0
  buf_mktemp(pk7out);
826
0
  fp_out = mutt_file_fopen(buf_string(pk7out), "w+");
827
0
  if (!fp_out)
828
0
  {
829
0
    mutt_perror("%s", buf_string(pk7out));
830
0
    goto cleanup;
831
0
  }
832
833
  /* Step 1: Convert the signature to a PKCS#7 structure, as we can't
834
   * extract the full set of certificates directly. */
835
0
  const struct Expando *c_smime_pk7out_command = cs_subset_expando(NeoMutt->sub, "smime_pk7out_command");
836
0
  pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_out), fileno(fp_err), infile,
837
0
                     NULL, NULL, NULL, NULL, NULL, NULL, c_smime_pk7out_command);
838
0
  if (pid == -1)
839
0
  {
840
0
    mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
841
0
    goto cleanup;
842
0
  }
843
844
0
  filter_wait(pid);
845
846
0
  fflush(fp_out);
847
0
  rewind(fp_out);
848
0
  fflush(fp_err);
849
0
  rewind(fp_err);
850
0
  empty = (fgetc(fp_out) == EOF);
851
0
  if (empty)
852
0
  {
853
0
    mutt_perror("%s", buf_string(pk7out));
854
0
    mutt_file_copy_stream(fp_err, stdout);
855
0
    goto cleanup;
856
0
  }
857
0
  mutt_file_fclose(&fp_out);
858
859
0
  buf_mktemp(certfile);
860
0
  fp_cert = mutt_file_fopen(buf_string(certfile), "w+");
861
0
  if (!fp_cert)
862
0
  {
863
0
    mutt_perror("%s", buf_string(certfile));
864
0
    mutt_file_unlink(buf_string(pk7out));
865
0
    goto cleanup;
866
0
  }
867
868
  // Step 2: Extract the certificates from a PKCS#7 structure.
869
0
  const struct Expando *c_smime_get_cert_command = cs_subset_expando(NeoMutt->sub, "smime_get_cert_command");
870
0
  pid = smime_invoke(NULL, NULL, NULL, -1, fileno(fp_cert), fileno(fp_err),
871
0
                     buf_string(pk7out), NULL, NULL, NULL, NULL, NULL, NULL,
872
0
                     c_smime_get_cert_command);
873
0
  if (pid == -1)
874
0
  {
875
0
    mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
876
0
    mutt_file_unlink(buf_string(pk7out));
877
0
    goto cleanup;
878
0
  }
879
880
0
  filter_wait(pid);
881
882
0
  mutt_file_unlink(buf_string(pk7out));
883
884
0
  fflush(fp_cert);
885
0
  rewind(fp_cert);
886
0
  fflush(fp_err);
887
0
  rewind(fp_err);
888
0
  empty = (fgetc(fp_cert) == EOF);
889
0
  if (empty)
890
0
  {
891
0
    mutt_file_copy_stream(fp_err, stdout);
892
0
    goto cleanup;
893
0
  }
894
895
0
  mutt_file_fclose(&fp_cert);
896
897
0
  rc = buf_strdup(certfile);
898
899
0
cleanup:
900
0
  mutt_file_fclose(&fp_err);
901
0
  if (fp_out)
902
0
  {
903
0
    mutt_file_fclose(&fp_out);
904
0
    mutt_file_unlink(buf_string(pk7out));
905
0
  }
906
0
  if (fp_cert)
907
0
  {
908
0
    mutt_file_fclose(&fp_cert);
909
0
    mutt_file_unlink(buf_string(certfile));
910
0
  }
911
0
  buf_pool_release(&pk7out);
912
0
  buf_pool_release(&certfile);
913
0
  return rc;
914
0
}
915
916
/**
917
 * smime_extract_signer_certificate - Extract the signer's certificate
918
 * @param infile File to read
919
 * @retval ptr Name of temporary file containing certificate
920
 */
921
static char *smime_extract_signer_certificate(const char *infile)
922
0
{
923
0
  char *cert = NULL;
924
0
  struct Buffer *certfile = NULL;
925
0
  pid_t pid;
926
0
  int empty;
927
928
0
  FILE *fp_err = mutt_file_mkstemp();
929
0
  if (!fp_err)
930
0
  {
931
0
    mutt_perror(_("Can't create temporary file"));
932
0
    return NULL;
933
0
  }
934
935
0
  certfile = buf_pool_get();
936
0
  buf_mktemp(certfile);
937
0
  FILE *fp_out = mutt_file_fopen(buf_string(certfile), "w+");
938
0
  if (!fp_out)
939
0
  {
940
0
    mutt_file_fclose(&fp_err);
941
0
    mutt_perror("%s", buf_string(certfile));
942
0
    goto cleanup;
943
0
  }
944
945
  /* Extract signer's certificate
946
   */
947
0
  const struct Expando *c_smime_get_signer_cert_command =
948
0
      cs_subset_expando(NeoMutt->sub, "smime_get_signer_cert_command");
949
0
  pid = smime_invoke(NULL, NULL, NULL, -1, -1, fileno(fp_err), infile, NULL, NULL, NULL,
950
0
                     NULL, buf_string(certfile), NULL, c_smime_get_signer_cert_command);
951
0
  if (pid == -1)
952
0
  {
953
0
    mutt_any_key_to_continue(_("Error: unable to create OpenSSL subprocess"));
954
0
    goto cleanup;
955
0
  }
956
957
0
  filter_wait(pid);
958
959
0
  fflush(fp_out);
960
0
  rewind(fp_out);
961
0
  fflush(fp_err);
962
0
  rewind(fp_err);
963
0
  empty = (fgetc(fp_out) == EOF);
964
0
  if (empty)
965
0
  {
966
0
    mutt_endwin();
967
0
    mutt_file_copy_stream(fp_err, stdout);
968
0
    mutt_any_key_to_continue(NULL);
969
0
    goto cleanup;
970
0
  }
971
972
0
  mutt_file_fclose(&fp_out);
973
0
  cert = buf_strdup(certfile);
974
975
0
cleanup:
976
0
  mutt_file_fclose(&fp_err);
977
0
  if (fp_out)
978
0
  {
979
0
    mutt_file_fclose(&fp_out);
980
0
    mutt_file_unlink(buf_string(certfile));
981
0
  }
982
0
  buf_pool_release(&certfile);
983
0
  return cert;
984
0
}
985
986
/**
987
 * smime_class_invoke_import - Add a certificate and update index file (externally) - Implements CryptModuleSpecs::smime_invoke_import() - @ingroup crypto_smime_invoke_import
988
 */
989
void smime_class_invoke_import(const char *infile, const char *mailbox)
990
0
{
991
0
  char *certfile = NULL;
992
0
  struct Buffer *buf = NULL;
993
994
0
  FILE *fp_out = NULL;
995
0
  FILE *fp_err = mutt_file_mkstemp();
996
0
  if (!fp_err)
997
0
  {
998
0
    mutt_perror(_("Can't create temporary file"));
999
0
    goto done;
1000
0
  }
1001
1002
0
  fp_out = mutt_file_mkstemp();
1003
0
  if (!fp_out)
1004
0
  {
1005
0
    mutt_perror(_("Can't create temporary file"));
1006
0
    goto done;
1007
0
  }
1008
1009
0
  buf = buf_pool_get();
1010
0
  const bool c_smime_ask_cert_label = cs_subset_bool(NeoMutt->sub, "smime_ask_cert_label");
1011
0
  if (c_smime_ask_cert_label)
1012
0
  {
1013
0
    if ((mw_get_field(_("Label for certificate: "), buf, MUTT_COMP_NO_FLAGS,
1014
0
                      HC_OTHER, NULL, NULL) != 0) ||
1015
0
        buf_is_empty(buf))
1016
0
    {
1017
0
      goto done;
1018
0
    }
1019
0
  }
1020
1021
0
  mutt_endwin();
1022
0
  certfile = smime_extract_certificate(infile);
1023
0
  if (certfile)
1024
0
  {
1025
0
    mutt_endwin();
1026
1027
0
    const struct Expando *c_smime_import_cert_command =
1028
0
        cs_subset_expando(NeoMutt->sub, "smime_import_cert_command");
1029
0
    FILE *fp_smime_in = NULL;
1030
0
    pid_t pid = smime_invoke(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1031
0
                             fileno(fp_err), certfile, NULL, NULL, NULL, NULL,
1032
0
                             NULL, NULL, c_smime_import_cert_command);
1033
0
    if (pid == -1)
1034
0
    {
1035
0
      mutt_message(_("Error: unable to create OpenSSL subprocess"));
1036
0
      goto done;
1037
0
    }
1038
0
    fputs(buf_string(buf), fp_smime_in);
1039
0
    fputc('\n', fp_smime_in);
1040
0
    mutt_file_fclose(&fp_smime_in);
1041
1042
0
    filter_wait(pid);
1043
1044
0
    mutt_file_unlink(certfile);
1045
0
    FREE(&certfile);
1046
0
  }
1047
1048
0
  fflush(fp_out);
1049
0
  rewind(fp_out);
1050
0
  fflush(fp_err);
1051
0
  rewind(fp_err);
1052
1053
0
  mutt_file_copy_stream(fp_out, stdout);
1054
0
  mutt_file_copy_stream(fp_err, stdout);
1055
1056
0
done:
1057
0
  mutt_file_fclose(&fp_out);
1058
0
  mutt_file_fclose(&fp_err);
1059
0
  buf_pool_release(&buf);
1060
0
}
1061
1062
/**
1063
 * smime_class_verify_sender - Does the sender match the certificate? - Implements CryptModuleSpecs::smime_verify_sender() - @ingroup crypto_smime_verify_sender
1064
 */
1065
int smime_class_verify_sender(struct Email *e, struct Message *msg)
1066
0
{
1067
0
  const char *mbox = NULL, *certfile = NULL;
1068
0
  int rc = 1;
1069
1070
0
  struct Buffer *tempfname = buf_pool_get();
1071
0
  buf_mktemp(tempfname);
1072
0
  FILE *fp_out = mutt_file_fopen(buf_string(tempfname), "w");
1073
0
  if (!fp_out)
1074
0
  {
1075
0
    mutt_perror("%s", buf_string(tempfname));
1076
0
    goto cleanup;
1077
0
  }
1078
1079
0
  const bool encrypt = e->security & SEC_ENCRYPT;
1080
0
  mutt_copy_message(fp_out, e, msg,
1081
0
                    encrypt ? (MUTT_CM_DECODE_CRYPT & MUTT_CM_DECODE_SMIME) : MUTT_CM_NO_FLAGS,
1082
0
                    encrypt ? (CH_MIME | CH_WEED | CH_NONEWLINE) : CH_NO_FLAGS, 0);
1083
1084
0
  fflush(fp_out);
1085
0
  mutt_file_fclose(&fp_out);
1086
1087
0
  if (!TAILQ_EMPTY(&e->env->from))
1088
0
  {
1089
0
    mutt_expand_aliases(&e->env->from);
1090
0
    mbox = buf_string(TAILQ_FIRST(&e->env->from)->mailbox);
1091
0
  }
1092
0
  else if (!TAILQ_EMPTY(&e->env->sender))
1093
0
  {
1094
0
    mutt_expand_aliases(&e->env->sender);
1095
0
    mbox = buf_string(TAILQ_FIRST(&e->env->sender)->mailbox);
1096
0
  }
1097
1098
0
  if (mbox)
1099
0
  {
1100
0
    certfile = smime_extract_signer_certificate(buf_string(tempfname));
1101
0
    if (certfile)
1102
0
    {
1103
0
      mutt_file_unlink(buf_string(tempfname));
1104
0
      if (smime_handle_cert_email(certfile, mbox, false, NULL, NULL))
1105
0
      {
1106
0
        if (isendwin())
1107
0
          mutt_any_key_to_continue(NULL);
1108
0
      }
1109
0
      else
1110
0
      {
1111
0
        rc = 0;
1112
0
      }
1113
0
      mutt_file_unlink(certfile);
1114
0
      FREE(&certfile);
1115
0
    }
1116
0
    else
1117
0
    {
1118
0
      mutt_any_key_to_continue(_("no certfile"));
1119
0
    }
1120
0
  }
1121
0
  else
1122
0
  {
1123
0
    mutt_any_key_to_continue(_("no mbox"));
1124
0
  }
1125
1126
0
  mutt_file_unlink(buf_string(tempfname));
1127
1128
0
cleanup:
1129
0
  buf_pool_release(&tempfname);
1130
0
  return rc;
1131
0
}
1132
1133
/**
1134
 * smime_invoke_encrypt - Use SMIME to encrypt a file
1135
 * @param[out] fp_smime_in    stdin  for the command, or NULL (OPTIONAL)
1136
 * @param[out] fp_smime_out   stdout for the command, or NULL (OPTIONAL)
1137
 * @param[out] fp_smime_err   stderr for the command, or NULL (OPTIONAL)
1138
 * @param[in]  fp_smime_infd  stdin  for the command, or -1 (OPTIONAL)
1139
 * @param[in]  fp_smime_outfd stdout for the command, or -1 (OPTIONAL)
1140
 * @param[in]  fp_smime_errfd stderr for the command, or -1 (OPTIONAL)
1141
 * @param[in]  fname      Filename to pass to the command
1142
 * @param[in]  uids       List of IDs/fingerprints, space separated
1143
 * @retval num PID of the created process
1144
 * @retval -1  Error creating pipes or forking
1145
 *
1146
 * @note `fp_smime_in` has priority over `fp_smime_infd`.
1147
 *       Likewise `fp_smime_out` and `fp_smime_err`.
1148
 */
1149
static pid_t smime_invoke_encrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1150
                                  FILE **fp_smime_err, int fp_smime_infd,
1151
                                  int fp_smime_outfd, int fp_smime_errfd,
1152
                                  const char *fname, const char *uids)
1153
0
{
1154
0
  const char *const c_smime_encrypt_with = cs_subset_string(NeoMutt->sub, "smime_encrypt_with");
1155
0
  const struct Expando *c_smime_encrypt_command = cs_subset_expando(NeoMutt->sub, "smime_encrypt_command");
1156
0
  return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1157
0
                      fp_smime_outfd, fp_smime_errfd, fname, NULL, c_smime_encrypt_with,
1158
0
                      NULL, NULL, uids, NULL, c_smime_encrypt_command);
1159
0
}
1160
1161
/**
1162
 * smime_invoke_sign - Use SMIME to sign a file
1163
 * @param[out] fp_smime_in    stdin  for the command, or NULL (OPTIONAL)
1164
 * @param[out] fp_smime_out   stdout for the command, or NULL (OPTIONAL)
1165
 * @param[out] fp_smime_err   stderr for the command, or NULL (OPTIONAL)
1166
 * @param[in]  fp_smime_infd  stdin  for the command, or -1 (OPTIONAL)
1167
 * @param[in]  fp_smime_outfd stdout for the command, or -1 (OPTIONAL)
1168
 * @param[in]  fp_smime_errfd stderr for the command, or -1 (OPTIONAL)
1169
 * @param[in]  fname      Filename to pass to the command
1170
 * @retval num PID of the created process
1171
 * @retval -1  Error creating pipes or forking
1172
 *
1173
 * @note `fp_smime_in` has priority over `fp_smime_infd`.
1174
 *       Likewise `fp_smime_out` and `fp_smime_err`.
1175
 */
1176
static pid_t smime_invoke_sign(FILE **fp_smime_in, FILE **fp_smime_out,
1177
                               FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1178
                               int fp_smime_errfd, const char *fname)
1179
0
{
1180
0
  const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1181
0
  const struct Expando *c_smime_sign_command = cs_subset_expando(NeoMutt->sub, "smime_sign_command");
1182
0
  return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1183
0
                      fp_smime_errfd, fname, NULL, NULL, c_smime_sign_digest_alg,
1184
0
                      buf_string(&SmimeKeyToUse), buf_string(&SmimeCertToUse),
1185
0
                      buf_string(&SmimeIntermediateToUse), c_smime_sign_command);
1186
0
}
1187
1188
/**
1189
 * smime_class_build_smime_entity - Encrypt the email body to all recipients - Implements CryptModuleSpecs::smime_build_smime_entity() - @ingroup crypto_smime_build_smime_entity
1190
 */
1191
struct Body *smime_class_build_smime_entity(struct Body *b, char *certlist)
1192
0
{
1193
0
  char buf[1024] = { 0 };
1194
0
  char certfile[PATH_MAX] = { 0 };
1195
0
  char *cert_end = NULL;
1196
0
  FILE *fp_smime_in = NULL, *fp_smime_err = NULL, *fp_out = NULL, *fp_tmp = NULL;
1197
0
  struct Body *b_enc = NULL;
1198
0
  bool err = false;
1199
0
  int empty, off;
1200
0
  pid_t pid;
1201
1202
0
  struct Buffer *tempfile = buf_pool_get();
1203
0
  struct Buffer *smime_infile = buf_pool_get();
1204
1205
0
  buf_mktemp(tempfile);
1206
0
  fp_out = mutt_file_fopen(buf_string(tempfile), "w+");
1207
0
  if (!fp_out)
1208
0
  {
1209
0
    mutt_perror("%s", buf_string(tempfile));
1210
0
    goto cleanup;
1211
0
  }
1212
1213
0
  fp_smime_err = mutt_file_mkstemp();
1214
0
  if (!fp_smime_err)
1215
0
  {
1216
0
    mutt_perror(_("Can't create temporary file"));
1217
0
    goto cleanup;
1218
0
  }
1219
1220
0
  buf_mktemp(smime_infile);
1221
0
  fp_tmp = mutt_file_fopen(buf_string(smime_infile), "w+");
1222
0
  if (!fp_tmp)
1223
0
  {
1224
0
    mutt_perror("%s", buf_string(smime_infile));
1225
0
    goto cleanup;
1226
0
  }
1227
1228
0
  *certfile = '\0';
1229
0
  for (char *cert_start = certlist; cert_start; cert_start = cert_end)
1230
0
  {
1231
0
    cert_end = strchr(cert_start, ' ');
1232
0
    if (cert_end)
1233
0
      *cert_end = '\0';
1234
0
    if (*cert_start)
1235
0
    {
1236
0
      off = mutt_str_len(certfile);
1237
0
      const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1238
0
      snprintf(certfile + off, sizeof(certfile) - off, "%s%s/%s",
1239
0
               (off != 0) ? " " : "", NONULL(c_smime_certificates), cert_start);
1240
0
    }
1241
0
    if (cert_end)
1242
0
      *cert_end++ = ' ';
1243
0
  }
1244
1245
  /* write a MIME entity */
1246
0
  mutt_write_mime_header(b, fp_tmp, NeoMutt->sub);
1247
0
  fputc('\n', fp_tmp);
1248
0
  mutt_write_mime_body(b, fp_tmp, NeoMutt->sub);
1249
0
  mutt_file_fclose(&fp_tmp);
1250
1251
0
  pid = smime_invoke_encrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_out),
1252
0
                             fileno(fp_smime_err), buf_string(smime_infile), certfile);
1253
0
  if (pid == -1)
1254
0
  {
1255
0
    mutt_file_unlink(buf_string(smime_infile));
1256
0
    goto cleanup;
1257
0
  }
1258
1259
0
  mutt_file_fclose(&fp_smime_in);
1260
1261
0
  filter_wait(pid);
1262
0
  mutt_file_unlink(buf_string(smime_infile));
1263
1264
0
  fflush(fp_out);
1265
0
  rewind(fp_out);
1266
0
  empty = (fgetc(fp_out) == EOF);
1267
0
  mutt_file_fclose(&fp_out);
1268
1269
0
  fflush(fp_smime_err);
1270
0
  rewind(fp_smime_err);
1271
0
  while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1272
0
  {
1273
0
    err = true;
1274
0
    fputs(buf, stdout);
1275
0
  }
1276
0
  mutt_file_fclose(&fp_smime_err);
1277
1278
  /* pause if there is any error output from SMIME */
1279
0
  if (err)
1280
0
    mutt_any_key_to_continue(NULL);
1281
1282
0
  if (empty)
1283
0
  {
1284
    /* fatal error while trying to encrypt message */
1285
0
    if (!err)
1286
0
      mutt_any_key_to_continue(_("No output from OpenSSL..."));
1287
0
    mutt_file_unlink(buf_string(tempfile));
1288
0
    goto cleanup;
1289
0
  }
1290
1291
0
  b_enc = mutt_body_new();
1292
0
  b_enc->type = TYPE_APPLICATION;
1293
0
  b_enc->subtype = mutt_str_dup("pkcs7-mime");
1294
0
  mutt_param_set(&b_enc->parameter, "name", "smime.p7m");
1295
0
  mutt_param_set(&b_enc->parameter, "smime-type", "enveloped-data");
1296
0
  b_enc->encoding = ENC_BASE64; /* The output of OpenSSL SHOULD be binary */
1297
0
  b_enc->use_disp = true;
1298
0
  b_enc->disposition = DISP_ATTACH;
1299
0
  b_enc->d_filename = mutt_str_dup("smime.p7m");
1300
0
  b_enc->filename = buf_strdup(tempfile);
1301
0
  b_enc->unlink = true; /* delete after sending the message */
1302
0
  b_enc->parts = NULL;
1303
0
  b_enc->next = NULL;
1304
1305
0
cleanup:
1306
0
  if (fp_out)
1307
0
  {
1308
0
    mutt_file_fclose(&fp_out);
1309
0
    mutt_file_unlink(buf_string(tempfile));
1310
0
  }
1311
0
  mutt_file_fclose(&fp_smime_err);
1312
0
  if (fp_tmp)
1313
0
  {
1314
0
    mutt_file_fclose(&fp_tmp);
1315
0
    mutt_file_unlink(buf_string(smime_infile));
1316
0
  }
1317
0
  buf_pool_release(&tempfile);
1318
0
  buf_pool_release(&smime_infile);
1319
1320
0
  return b_enc;
1321
0
}
1322
1323
/**
1324
 * openssl_md_to_smime_micalg - Change the algorithm names
1325
 * @param md OpenSSL message digest name
1326
 * @retval ptr SMIME Message Integrity Check algorithm
1327
 *
1328
 * The openssl -md doesn't want hyphens:
1329
 *   md5, sha1,  sha224,  sha256,  sha384,  sha512
1330
 * However, the micalg does:
1331
 *   md5, sha-1, sha-224, sha-256, sha-384, sha-512
1332
 *
1333
 * @note The caller should free the returned string
1334
 */
1335
static char *openssl_md_to_smime_micalg(const char *md)
1336
0
{
1337
0
  if (!md)
1338
0
    return NULL;
1339
1340
0
  char *micalg = NULL;
1341
0
  if (mutt_istr_startswith(md, "sha"))
1342
0
  {
1343
0
    mutt_str_asprintf(&micalg, "sha-%s", md + 3);
1344
0
  }
1345
0
  else
1346
0
  {
1347
0
    micalg = mutt_str_dup(md);
1348
0
  }
1349
1350
0
  return micalg;
1351
0
}
1352
1353
/**
1354
 * smime_class_sign_message - Cryptographically sign the Body of a message - Implements CryptModuleSpecs::sign_message() - @ingroup crypto_sign_message
1355
 */
1356
struct Body *smime_class_sign_message(struct Body *b, const struct AddressList *from)
1357
0
{
1358
0
  struct Body *b_sign = NULL;
1359
0
  struct Body *rc = NULL;
1360
0
  char buf[1024] = { 0 };
1361
0
  struct Buffer *filetosign = NULL, *signedfile = NULL;
1362
0
  FILE *fp_smime_in = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL, *fp_sign = NULL;
1363
0
  bool err = false;
1364
0
  int empty = 0;
1365
0
  pid_t pid;
1366
0
  const char *intermediates = NULL;
1367
1368
0
  const char *const c_smime_sign_as = cs_subset_string(NeoMutt->sub, "smime_sign_as");
1369
0
  const char *const c_smime_default_key = cs_subset_string(NeoMutt->sub, "smime_default_key");
1370
0
  const char *signas = c_smime_sign_as ? c_smime_sign_as : c_smime_default_key;
1371
0
  if (!signas || (*signas == '\0'))
1372
0
  {
1373
0
    mutt_error(_("Can't sign: No key specified. Use Sign As."));
1374
0
    return NULL;
1375
0
  }
1376
1377
0
  crypt_convert_to_7bit(b); /* Signed data _must_ be in 7-bit format. */
1378
1379
0
  filetosign = buf_pool_get();
1380
0
  signedfile = buf_pool_get();
1381
1382
0
  buf_mktemp(filetosign);
1383
0
  fp_sign = mutt_file_fopen(buf_string(filetosign), "w+");
1384
0
  if (!fp_sign)
1385
0
  {
1386
0
    mutt_perror("%s", buf_string(filetosign));
1387
0
    goto cleanup;
1388
0
  }
1389
1390
0
  buf_mktemp(signedfile);
1391
0
  fp_smime_out = mutt_file_fopen(buf_string(signedfile), "w+");
1392
0
  if (!fp_smime_out)
1393
0
  {
1394
0
    mutt_perror("%s", buf_string(signedfile));
1395
0
    goto cleanup;
1396
0
  }
1397
1398
0
  mutt_write_mime_header(b, fp_sign, NeoMutt->sub);
1399
0
  fputc('\n', fp_sign);
1400
0
  mutt_write_mime_body(b, fp_sign, NeoMutt->sub);
1401
0
  mutt_file_fclose(&fp_sign);
1402
1403
0
  const char *const c_smime_keys = cs_subset_path(NeoMutt->sub, "smime_keys");
1404
0
  const char *const c_smime_certificates = cs_subset_path(NeoMutt->sub, "smime_certificates");
1405
0
  buf_printf(&SmimeKeyToUse, "%s/%s", NONULL(c_smime_keys), signas);
1406
0
  buf_printf(&SmimeCertToUse, "%s/%s", NONULL(c_smime_certificates), signas);
1407
1408
0
  struct SmimeKey *signas_key = smime_get_key_by_hash(signas, 1);
1409
0
  if (!signas_key || mutt_str_equal("?", signas_key->issuer))
1410
0
    intermediates = signas; /* so openssl won't complain in any case */
1411
0
  else
1412
0
    intermediates = signas_key->issuer;
1413
1414
0
  buf_printf(&SmimeIntermediateToUse, "%s/%s", NONULL(c_smime_certificates), intermediates);
1415
1416
0
  smime_key_free(&signas_key);
1417
1418
0
  pid = smime_invoke_sign(&fp_smime_in, NULL, &fp_smime_err, -1,
1419
0
                          fileno(fp_smime_out), -1, buf_string(filetosign));
1420
0
  if (pid == -1)
1421
0
  {
1422
0
    mutt_perror(_("Can't open OpenSSL subprocess"));
1423
0
    mutt_file_unlink(buf_string(filetosign));
1424
0
    goto cleanup;
1425
0
  }
1426
0
  fputs(SmimePass, fp_smime_in);
1427
0
  fputc('\n', fp_smime_in);
1428
0
  mutt_file_fclose(&fp_smime_in);
1429
1430
0
  filter_wait(pid);
1431
1432
  /* check for errors from OpenSSL */
1433
0
  err = false;
1434
0
  fflush(fp_smime_err);
1435
0
  rewind(fp_smime_err);
1436
0
  while (fgets(buf, sizeof(buf) - 1, fp_smime_err))
1437
0
  {
1438
0
    err = true;
1439
0
    fputs(buf, stdout);
1440
0
  }
1441
0
  mutt_file_fclose(&fp_smime_err);
1442
1443
0
  fflush(fp_smime_out);
1444
0
  rewind(fp_smime_out);
1445
0
  empty = (fgetc(fp_smime_out) == EOF);
1446
0
  mutt_file_fclose(&fp_smime_out);
1447
1448
0
  mutt_file_unlink(buf_string(filetosign));
1449
1450
0
  if (err)
1451
0
    mutt_any_key_to_continue(NULL);
1452
1453
0
  if (empty)
1454
0
  {
1455
0
    mutt_any_key_to_continue(_("No output from OpenSSL..."));
1456
0
    mutt_file_unlink(buf_string(signedfile));
1457
0
    goto cleanup; /* fatal error while signing */
1458
0
  }
1459
1460
0
  b_sign = mutt_body_new();
1461
0
  b_sign->type = TYPE_MULTIPART;
1462
0
  b_sign->subtype = mutt_str_dup("signed");
1463
0
  b_sign->encoding = ENC_7BIT;
1464
0
  b_sign->use_disp = false;
1465
0
  b_sign->disposition = DISP_INLINE;
1466
1467
0
  mutt_generate_boundary(&b_sign->parameter);
1468
1469
0
  const char *const c_smime_sign_digest_alg = cs_subset_string(NeoMutt->sub, "smime_sign_digest_alg");
1470
0
  char *micalg = openssl_md_to_smime_micalg(c_smime_sign_digest_alg);
1471
0
  mutt_param_set(&b_sign->parameter, "micalg", micalg);
1472
0
  FREE(&micalg);
1473
1474
0
  mutt_param_set(&b_sign->parameter, "protocol", "application/pkcs7-signature");
1475
1476
0
  b_sign->parts = b;
1477
0
  rc = b_sign;
1478
1479
0
  b_sign->parts->next = mutt_body_new();
1480
0
  b_sign = b_sign->parts->next;
1481
0
  b_sign->type = TYPE_APPLICATION;
1482
0
  b_sign->subtype = mutt_str_dup("pkcs7-signature");
1483
0
  b_sign->filename = buf_strdup(signedfile);
1484
0
  b_sign->d_filename = mutt_str_dup("smime.p7s");
1485
0
  b_sign->use_disp = true;
1486
0
  b_sign->disposition = DISP_ATTACH;
1487
0
  b_sign->encoding = ENC_BASE64;
1488
0
  b_sign->unlink = true; /* ok to remove this file after sending. */
1489
1490
0
cleanup:
1491
0
  if (fp_sign)
1492
0
  {
1493
0
    mutt_file_fclose(&fp_sign);
1494
0
    mutt_file_unlink(buf_string(filetosign));
1495
0
  }
1496
0
  if (fp_smime_out)
1497
0
  {
1498
0
    mutt_file_fclose(&fp_smime_out);
1499
0
    mutt_file_unlink(buf_string(signedfile));
1500
0
  }
1501
0
  buf_pool_release(&filetosign);
1502
0
  buf_pool_release(&signedfile);
1503
0
  return rc;
1504
0
}
1505
1506
/**
1507
 * smime_invoke_verify - Use SMIME to verify a file
1508
 * @param[out] fp_smime_in    stdin  for the command, or NULL (OPTIONAL)
1509
 * @param[out] fp_smime_out   stdout for the command, or NULL (OPTIONAL)
1510
 * @param[out] fp_smime_err   stderr for the command, or NULL (OPTIONAL)
1511
 * @param[in]  fp_smime_infd  stdin  for the command, or -1 (OPTIONAL)
1512
 * @param[in]  fp_smime_outfd stdout for the command, or -1 (OPTIONAL)
1513
 * @param[in]  fp_smime_errfd stderr for the command, or -1 (OPTIONAL)
1514
 * @param[in]  fname      Filename to pass to the command
1515
 * @param[in]  sig_fname  Signature filename to pass to the command
1516
 * @param[in]  opaque     If true, use `$smime_verify_opaque_command` else `$smime_verify_command`
1517
 * @retval num PID of the created process
1518
 * @retval -1  Error creating pipes or forking
1519
 *
1520
 * @note `fp_smime_in` has priority over `fp_smime_infd`.
1521
 *       Likewise `fp_smime_out` and `fp_smime_err`.
1522
 */
1523
static pid_t smime_invoke_verify(FILE **fp_smime_in, FILE **fp_smime_out,
1524
                                 FILE **fp_smime_err, int fp_smime_infd,
1525
                                 int fp_smime_outfd, int fp_smime_errfd,
1526
                                 const char *fname, const char *sig_fname, int opaque)
1527
0
{
1528
0
  const struct Expando *c_smime_verify_opaque_command =
1529
0
      cs_subset_expando(NeoMutt->sub, "smime_verify_opaque_command");
1530
0
  const struct Expando *c_smime_verify_command = cs_subset_expando(NeoMutt->sub, "smime_verify_command");
1531
0
  return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd, fp_smime_outfd,
1532
0
                      fp_smime_errfd, fname, sig_fname, NULL, NULL, NULL, NULL, NULL,
1533
0
                      (opaque ? c_smime_verify_opaque_command : c_smime_verify_command));
1534
0
}
1535
1536
/**
1537
 * smime_invoke_decrypt - Use SMIME to decrypt a file
1538
 * @param[out] fp_smime_in    stdin  for the command, or NULL (OPTIONAL)
1539
 * @param[out] fp_smime_out   stdout for the command, or NULL (OPTIONAL)
1540
 * @param[out] fp_smime_err   stderr for the command, or NULL (OPTIONAL)
1541
 * @param[in]  fp_smime_infd  stdin  for the command, or -1 (OPTIONAL)
1542
 * @param[in]  fp_smime_outfd stdout for the command, or -1 (OPTIONAL)
1543
 * @param[in]  fp_smime_errfd stderr for the command, or -1 (OPTIONAL)
1544
 * @param[in]  fname      Filename to pass to the command
1545
 * @retval num PID of the created process
1546
 * @retval -1  Error creating pipes or forking
1547
 *
1548
 * @note `fp_smime_in` has priority over `fp_smime_infd`.
1549
 *       Likewise `fp_smime_out` and `fp_smime_err`.
1550
 */
1551
static pid_t smime_invoke_decrypt(FILE **fp_smime_in, FILE **fp_smime_out,
1552
                                  FILE **fp_smime_err, int fp_smime_infd, int fp_smime_outfd,
1553
                                  int fp_smime_errfd, const char *fname)
1554
0
{
1555
0
  const struct Expando *c_smime_decrypt_command = cs_subset_expando(NeoMutt->sub, "smime_decrypt_command");
1556
0
  return smime_invoke(fp_smime_in, fp_smime_out, fp_smime_err, fp_smime_infd,
1557
0
                      fp_smime_outfd, fp_smime_errfd, fname, NULL, NULL, NULL,
1558
0
                      buf_string(&SmimeKeyToUse), buf_string(&SmimeCertToUse),
1559
0
                      NULL, c_smime_decrypt_command);
1560
0
}
1561
1562
/**
1563
 * smime_class_verify_one - Check a signed MIME part against a signature - Implements CryptModuleSpecs::verify_one() - @ingroup crypto_verify_one
1564
 */
1565
int smime_class_verify_one(struct Body *b, struct State *state, const char *tempfile)
1566
0
{
1567
0
  FILE *fp = NULL, *fp_smime_out = NULL, *fp_smime_err = NULL;
1568
0
  pid_t pid;
1569
0
  int badsig = -1;
1570
1571
0
  LOFF_T tmpoffset = 0;
1572
0
  size_t tmplength = 0;
1573
0
  int orig_type = b->type;
1574
1575
0
  struct Buffer *signedfile = buf_pool_get();
1576
1577
0
  buf_printf(signedfile, "%s.sig", tempfile);
1578
1579
  /* decode to a tempfile, saving the original destination */
1580
0
  fp = state->fp_out;
1581
0
  state->fp_out = mutt_file_fopen(buf_string(signedfile), "w");
1582
0
  if (!state->fp_out)
1583
0
  {
1584
0
    mutt_perror("%s", buf_string(signedfile));
1585
0
    goto cleanup;
1586
0
  }
1587
  /* decoding the attachment changes the size and offset, so save a copy
1588
   * of the "real" values now, and restore them after processing */
1589
0
  tmplength = b->length;
1590
0
  tmpoffset = b->offset;
1591
1592
  /* if we are decoding binary bodies, we don't want to prefix each
1593
   * line with the prefix or else the data will get corrupted.  */
1594
0
  const char *save_prefix = state->prefix;
1595
0
  state->prefix = NULL;
1596
1597
0
  mutt_decode_attachment(b, state);
1598
1599
0
  b->length = ftello(state->fp_out);
1600
0
  b->offset = 0;
1601
0
  mutt_file_fclose(&state->fp_out);
1602
1603
  /* restore final destination and substitute the tempfile for input */
1604
0
  state->fp_out = fp;
1605
0
  fp = state->fp_in;
1606
0
  state->fp_in = mutt_file_fopen(buf_string(signedfile), "r");
1607
1608
  /* restore the prefix */
1609
0
  state->prefix = save_prefix;
1610
1611
0
  b->type = orig_type;
1612
1613
0
  fp_smime_err = mutt_file_mkstemp();
1614
0
  if (!fp_smime_err)
1615
0
  {
1616
0
    mutt_perror(_("Can't create temporary file"));
1617
0
    goto cleanup;
1618
0
  }
1619
1620
0
  crypt_current_time(state, "OpenSSL");
1621
1622
0
  pid = smime_invoke_verify(NULL, &fp_smime_out, NULL, -1, -1, fileno(fp_smime_err),
1623
0
                            tempfile, buf_string(signedfile), 0);
1624
0
  if (pid != -1)
1625
0
  {
1626
0
    fflush(fp_smime_out);
1627
0
    mutt_file_fclose(&fp_smime_out);
1628
1629
0
    if (filter_wait(pid))
1630
0
    {
1631
0
      badsig = -1;
1632
0
    }
1633
0
    else
1634
0
    {
1635
0
      char *line = NULL;
1636
0
      size_t linelen;
1637
1638
0
      fflush(fp_smime_err);
1639
0
      rewind(fp_smime_err);
1640
1641
0
      line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1642
0
      if (linelen && mutt_istr_equal(line, "verification successful"))
1643
0
        badsig = 0;
1644
1645
0
      FREE(&line);
1646
0
    }
1647
0
  }
1648
1649
0
  fflush(fp_smime_err);
1650
0
  rewind(fp_smime_err);
1651
0
  mutt_file_copy_stream(fp_smime_err, state->fp_out);
1652
0
  mutt_file_fclose(&fp_smime_err);
1653
1654
0
  state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1655
1656
0
  mutt_file_unlink(buf_string(signedfile));
1657
1658
0
  b->length = tmplength;
1659
0
  b->offset = tmpoffset;
1660
1661
  /* restore the original source stream */
1662
0
  mutt_file_fclose(&state->fp_in);
1663
0
  state->fp_in = fp;
1664
1665
0
cleanup:
1666
0
  buf_pool_release(&signedfile);
1667
0
  return badsig;
1668
0
}
1669
1670
/**
1671
 * smime_handle_entity - Handle type application/pkcs7-mime
1672
 * @param b           Body to handle
1673
 * @param state       State to use
1674
 * @param fp_out_file File for the result
1675
 * @retval ptr Body for parsed MIME part
1676
 *
1677
 * This can either be a signed or an encrypted message.
1678
 */
1679
static struct Body *smime_handle_entity(struct Body *b, struct State *state, FILE *fp_out_file)
1680
0
{
1681
0
  struct Buffer *tmpfname = buf_pool_get();
1682
0
  FILE *fp_smime_out = NULL, *fp_smime_in = NULL, *fp_smime_err = NULL;
1683
0
  FILE *fp_tmp = NULL, *fp_out = NULL;
1684
0
  struct Body *p = NULL;
1685
0
  pid_t pid = -1;
1686
0
  SecurityFlags type = mutt_is_application_smime(b);
1687
1688
0
  if (!(type & APPLICATION_SMIME))
1689
0
    return NULL;
1690
1691
  /* Because of the mutt_body_handler() we avoid the buffer pool. */
1692
0
  fp_smime_out = mutt_file_mkstemp();
1693
0
  if (!fp_smime_out)
1694
0
  {
1695
0
    mutt_perror(_("Can't create temporary file"));
1696
0
    goto cleanup;
1697
0
  }
1698
1699
0
  fp_smime_err = mutt_file_mkstemp();
1700
0
  if (!fp_smime_err)
1701
0
  {
1702
0
    mutt_perror(_("Can't create temporary file"));
1703
0
    goto cleanup;
1704
0
  }
1705
1706
0
  buf_mktemp(tmpfname);
1707
0
  fp_tmp = mutt_file_fopen(buf_string(tmpfname), "w+");
1708
0
  if (!fp_tmp)
1709
0
  {
1710
0
    mutt_perror("%s", buf_string(tmpfname));
1711
0
    goto cleanup;
1712
0
  }
1713
1714
0
  if (!mutt_file_seek(state->fp_in, b->offset, SEEK_SET))
1715
0
  {
1716
0
    goto cleanup;
1717
0
  }
1718
1719
0
  mutt_file_copy_bytes(state->fp_in, fp_tmp, b->length);
1720
1721
0
  fflush(fp_tmp);
1722
0
  mutt_file_fclose(&fp_tmp);
1723
1724
0
  if ((type & SEC_ENCRYPT) &&
1725
0
      ((pid = smime_invoke_decrypt(&fp_smime_in, NULL, NULL, -1, fileno(fp_smime_out),
1726
0
                                   fileno(fp_smime_err), buf_string(tmpfname))) == -1))
1727
0
  {
1728
0
    mutt_file_unlink(buf_string(tmpfname));
1729
0
    if (state->flags & STATE_DISPLAY)
1730
0
    {
1731
0
      state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1732
0
    }
1733
0
    goto cleanup;
1734
0
  }
1735
0
  else if ((type & SEC_SIGNOPAQUE) &&
1736
0
           ((pid = smime_invoke_verify(&fp_smime_in, NULL, NULL, -1,
1737
0
                                       fileno(fp_smime_out), fileno(fp_smime_err), NULL,
1738
0
                                       buf_string(tmpfname), SEC_SIGNOPAQUE)) == -1))
1739
0
  {
1740
0
    mutt_file_unlink(buf_string(tmpfname));
1741
0
    if (state->flags & STATE_DISPLAY)
1742
0
    {
1743
0
      state_attach_puts(state, _("[-- Error: unable to create OpenSSL subprocess --]\n"));
1744
0
    }
1745
0
    goto cleanup;
1746
0
  }
1747
1748
0
  if (type & SEC_ENCRYPT)
1749
0
  {
1750
0
    if (!smime_class_valid_passphrase())
1751
0
      smime_class_void_passphrase();
1752
0
    fputs(SmimePass, fp_smime_in);
1753
0
    fputc('\n', fp_smime_in);
1754
0
  }
1755
1756
0
  mutt_file_fclose(&fp_smime_in);
1757
1758
0
  filter_wait(pid);
1759
0
  mutt_file_unlink(buf_string(tmpfname));
1760
1761
0
  if (state->flags & STATE_DISPLAY)
1762
0
  {
1763
0
    fflush(fp_smime_err);
1764
0
    rewind(fp_smime_err);
1765
1766
0
    const int c = fgetc(fp_smime_err);
1767
0
    if (c != EOF)
1768
0
    {
1769
0
      ungetc(c, fp_smime_err);
1770
1771
0
      crypt_current_time(state, "OpenSSL");
1772
0
      mutt_file_copy_stream(fp_smime_err, state->fp_out);
1773
0
      state_attach_puts(state, _("[-- End of OpenSSL output --]\n\n"));
1774
0
    }
1775
1776
0
    if (type & SEC_ENCRYPT)
1777
0
    {
1778
0
      state_attach_puts(state, _("[-- The following data is S/MIME encrypted --]\n"));
1779
0
    }
1780
0
    else
1781
0
    {
1782
0
      state_attach_puts(state, _("[-- The following data is S/MIME signed --]\n"));
1783
0
    }
1784
0
  }
1785
1786
0
  fflush(fp_smime_out);
1787
0
  rewind(fp_smime_out);
1788
1789
0
  if (type & SEC_ENCRYPT)
1790
0
  {
1791
    /* void the passphrase, even if that wasn't the problem */
1792
0
    if (fgetc(fp_smime_out) == EOF)
1793
0
    {
1794
0
      mutt_error(_("Decryption failed"));
1795
0
      smime_class_void_passphrase();
1796
0
    }
1797
0
    rewind(fp_smime_out);
1798
0
  }
1799
1800
0
  if (fp_out_file)
1801
0
  {
1802
0
    fp_out = fp_out_file;
1803
0
  }
1804
0
  else
1805
0
  {
1806
0
    fp_out = mutt_file_mkstemp();
1807
0
    if (!fp_out)
1808
0
    {
1809
0
      mutt_perror(_("Can't create temporary file"));
1810
0
      goto cleanup;
1811
0
    }
1812
0
  }
1813
0
  char buf[8192] = { 0 };
1814
0
  while (fgets(buf, sizeof(buf) - 1, fp_smime_out))
1815
0
  {
1816
0
    const size_t len = mutt_str_len(buf);
1817
0
    if ((len > 1) && (buf[len - 2] == '\r'))
1818
0
    {
1819
0
      buf[len - 2] = '\n';
1820
0
      buf[len - 1] = '\0';
1821
0
    }
1822
0
    fputs(buf, fp_out);
1823
0
  }
1824
0
  fflush(fp_out);
1825
0
  rewind(fp_out);
1826
1827
0
  const long size = mutt_file_get_size_fp(fp_out);
1828
0
  if (size == 0)
1829
0
  {
1830
0
    goto cleanup;
1831
0
  }
1832
0
  p = mutt_read_mime_header(fp_out, 0);
1833
0
  if (p)
1834
0
  {
1835
0
    p->length = size - p->offset;
1836
1837
0
    mutt_parse_part(fp_out, p);
1838
1839
0
    if (state->flags & STATE_DISPLAY)
1840
0
      mutt_protected_headers_handler(p, state);
1841
1842
    /* Store any protected headers in the parent so they can be
1843
     * accessed for index updates after the handler recursion is done.
1844
     * This is done before the handler to prevent a nested encrypted
1845
     * handler from freeing the headers. */
1846
0
    mutt_env_free(&b->mime_headers);
1847
0
    b->mime_headers = p->mime_headers;
1848
0
    p->mime_headers = NULL;
1849
1850
0
    if (state->fp_out)
1851
0
    {
1852
0
      rewind(fp_out);
1853
0
      FILE *fp_tmp_buffer = state->fp_in;
1854
0
      state->fp_in = fp_out;
1855
0
      mutt_body_handler(p, state);
1856
0
      state->fp_in = fp_tmp_buffer;
1857
0
    }
1858
1859
    /* Embedded multipart signed protected headers override the
1860
     * encrypted headers.  We need to do this after the handler so
1861
     * they can be printed in the pager. */
1862
0
    if (!(type & SMIME_SIGN) && mutt_is_multipart_signed(p) && p->parts &&
1863
0
        p->parts->mime_headers)
1864
0
    {
1865
0
      mutt_env_free(&b->mime_headers);
1866
0
      b->mime_headers = p->parts->mime_headers;
1867
0
      p->parts->mime_headers = NULL;
1868
0
    }
1869
0
  }
1870
0
  mutt_file_fclose(&fp_smime_out);
1871
1872
0
  if (!fp_out_file)
1873
0
  {
1874
0
    mutt_file_fclose(&fp_out);
1875
0
    mutt_file_unlink(buf_string(tmpfname));
1876
0
  }
1877
0
  fp_out = NULL;
1878
1879
0
  if (state->flags & STATE_DISPLAY)
1880
0
  {
1881
0
    if (type & SEC_ENCRYPT)
1882
0
      state_attach_puts(state, _("[-- End of S/MIME encrypted data --]\n"));
1883
0
    else
1884
0
      state_attach_puts(state, _("[-- End of S/MIME signed data --]\n"));
1885
0
  }
1886
1887
0
  if (type & SEC_SIGNOPAQUE)
1888
0
  {
1889
0
    char *line = NULL;
1890
0
    size_t linelen;
1891
1892
0
    rewind(fp_smime_err);
1893
1894
0
    line = mutt_file_read_line(line, &linelen, fp_smime_err, NULL, MUTT_RL_NO_FLAGS);
1895
0
    if (linelen && mutt_istr_equal(line, "verification successful"))
1896
0
      b->goodsig = true;
1897
0
    FREE(&line);
1898
0
  }
1899
0
  else if (p)
1900
0
  {
1901
0
    b->goodsig = p->goodsig;
1902
0
    b->badsig = p->badsig;
1903
0
  }
1904
1905
0
cleanup:
1906
0
  mutt_file_fclose(&fp_smime_out);
1907
0
  mutt_file_fclose(&fp_smime_err);
1908
0
  mutt_file_fclose(&fp_tmp);
1909
0
  mutt_file_fclose(&fp_out);
1910
0
  buf_pool_release(&tmpfname);
1911
0
  return p;
1912
0
}
1913
1914
/**
1915
 * smime_class_decrypt_mime - Decrypt an encrypted MIME part - Implements CryptModuleSpecs::decrypt_mime() - @ingroup crypto_decrypt_mime
1916
 */
1917
int smime_class_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **b_dec)
1918
0
{
1919
0
  struct State state = { 0 };
1920
0
  LOFF_T tmpoffset = b->offset;
1921
0
  size_t tmplength = b->length;
1922
0
  int rc = -1;
1923
1924
0
  if (!mutt_is_application_smime(b))
1925
0
    return -1;
1926
1927
0
  if (b->parts)
1928
0
    return -1;
1929
1930
0
  state.fp_in = fp_in;
1931
0
  if (!mutt_file_seek(state.fp_in, b->offset, SEEK_SET))
1932
0
  {
1933
0
    return -1;
1934
0
  }
1935
1936
0
  FILE *fp_tmp = mutt_file_mkstemp();
1937
0
  if (!fp_tmp)
1938
0
  {
1939
0
    mutt_perror(_("Can't create temporary file"));
1940
0
    return -1;
1941
0
  }
1942
1943
0
  state.fp_out = fp_tmp;
1944
0
  mutt_decode_attachment(b, &state);
1945
0
  fflush(fp_tmp);
1946
0
  b->length = ftello(state.fp_out);
1947
0
  b->offset = 0;
1948
0
  rewind(fp_tmp);
1949
0
  state.fp_in = fp_tmp;
1950
0
  state.fp_out = 0;
1951
1952
0
  *fp_out = mutt_file_mkstemp();
1953
0
  if (!*fp_out)
1954
0
  {
1955
0
    mutt_perror(_("Can't create temporary file"));
1956
0
    goto bail;
1957
0
  }
1958
1959
0
  *b_dec = smime_handle_entity(b, &state, *fp_out);
1960
0
  if (!*b_dec)
1961
0
    goto bail;
1962
1963
0
  (*b_dec)->goodsig = b->goodsig;
1964
0
  (*b_dec)->badsig = b->badsig;
1965
0
  rc = 0;
1966
1967
0
bail:
1968
0
  b->length = tmplength;
1969
0
  b->offset = tmpoffset;
1970
0
  mutt_file_fclose(&fp_tmp);
1971
0
  if (*fp_out)
1972
0
    rewind(*fp_out);
1973
1974
0
  return rc;
1975
0
}
1976
1977
/**
1978
 * smime_class_application_handler - Manage the MIME type "application/pgp" or "application/smime" - Implements CryptModuleSpecs::application_handler() - @ingroup crypto_application_handler
1979
 */
1980
int smime_class_application_handler(struct Body *b, struct State *state)
1981
0
{
1982
0
  int rc = -1;
1983
1984
  /* clear out any mime headers before the handler, so they can't be spoofed. */
1985
0
  mutt_env_free(&b->mime_headers);
1986
1987
0
  struct Body *tattach = smime_handle_entity(b, state, NULL);
1988
0
  if (tattach)
1989
0
  {
1990
0
    rc = 0;
1991
0
    mutt_body_free(&tattach);
1992
0
  }
1993
0
  return rc;
1994
0
}
1995
1996
/**
1997
 * smime_class_send_menu - Ask the user whether to sign and/or encrypt the email - Implements CryptModuleSpecs::send_menu() - @ingroup crypto_send_menu
1998
 */
1999
SecurityFlags smime_class_send_menu(struct Email *e)
2000
0
{
2001
0
  struct SmimeKey *key = NULL;
2002
0
  const char *prompt = NULL;
2003
0
  const char *letters = NULL;
2004
0
  const char *choices = NULL;
2005
0
  int choice;
2006
2007
0
  if (!(WithCrypto & APPLICATION_SMIME))
2008
0
    return e->security;
2009
2010
0
  e->security |= APPLICATION_SMIME;
2011
2012
  /* Opportunistic encrypt is controlling encryption.
2013
   * NOTE: "Signing" and "Clearing" only adjust the sign bit, so we have different
2014
   *       letter choices for those.  */
2015
0
  const bool c_crypt_opportunistic_encrypt = cs_subset_bool(NeoMutt->sub, "crypt_opportunistic_encrypt");
2016
0
  if (c_crypt_opportunistic_encrypt && (e->security & SEC_OPPENCRYPT))
2017
0
  {
2018
    /* L10N: S/MIME options (opportunistic encryption is on) */
2019
0
    prompt = _("S/MIME (s)ign, encrypt (w)ith, sign (a)s, (c)lear, or (o)ppenc mode off?");
2020
    /* L10N: S/MIME options (opportunistic encryption is on) */
2021
0
    letters = _("swaco");
2022
0
    choices = "SwaCo";
2023
0
  }
2024
0
  else if (c_crypt_opportunistic_encrypt)
2025
0
  {
2026
    /* Opportunistic encryption option is set, but is toggled off
2027
     * for this message.  */
2028
    /* L10N: S/MIME options (opportunistic encryption is off) */
2029
0
    prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, (c)lear, or (o)ppenc mode?");
2030
    /* L10N: S/MIME options (opportunistic encryption is off) */
2031
0
    letters = _("eswabco");
2032
0
    choices = "eswabcO";
2033
0
  }
2034
0
  else
2035
0
  {
2036
    /* Opportunistic encryption is unset */
2037
    /* L10N: S/MIME options */
2038
0
    prompt = _("S/MIME (e)ncrypt, (s)ign, encrypt (w)ith, sign (a)s, (b)oth, or (c)lear?");
2039
    /* L10N: S/MIME options */
2040
0
    letters = _("eswabc");
2041
0
    choices = "eswabc";
2042
0
  }
2043
2044
0
  choice = mw_multi_choice(prompt, letters);
2045
0
  if (choice > 0)
2046
0
  {
2047
0
    switch (choices[choice - 1])
2048
0
    {
2049
0
      case 'a': /* sign (a)s */
2050
0
        key = smime_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN, false);
2051
0
        if (key)
2052
0
        {
2053
0
          cs_subset_str_string_set(NeoMutt->sub, "smime_sign_as", key->hash, NULL);
2054
0
          smime_key_free(&key);
2055
2056
0
          e->security |= SEC_SIGN;
2057
2058
          /* probably need a different passphrase */
2059
0
          crypt_smime_void_passphrase();
2060
0
        }
2061
2062
0
        break;
2063
2064
0
      case 'b': /* (b)oth */
2065
0
        e->security |= (SEC_ENCRYPT | SEC_SIGN);
2066
0
        break;
2067
2068
0
      case 'c': /* (c)lear */
2069
0
        e->security &= ~(SEC_ENCRYPT | SEC_SIGN);
2070
0
        break;
2071
2072
0
      case 'C':
2073
0
        e->security &= ~SEC_SIGN;
2074
0
        break;
2075
2076
0
      case 'e': /* (e)ncrypt */
2077
0
        e->security |= SEC_ENCRYPT;
2078
0
        e->security &= ~SEC_SIGN;
2079
0
        break;
2080
2081
0
      case 'O': /* oppenc mode on */
2082
0
        e->security |= SEC_OPPENCRYPT;
2083
0
        crypt_opportunistic_encrypt(e);
2084
0
        break;
2085
2086
0
      case 'o': /* oppenc mode off */
2087
0
        e->security &= ~SEC_OPPENCRYPT;
2088
0
        break;
2089
2090
0
      case 'S': /* (s)ign in oppenc mode */
2091
0
        e->security |= SEC_SIGN;
2092
0
        break;
2093
2094
0
      case 's': /* (s)ign */
2095
0
        e->security &= ~SEC_ENCRYPT;
2096
0
        e->security |= SEC_SIGN;
2097
0
        break;
2098
2099
0
      case 'w': /* encrypt (w)ith */
2100
0
      {
2101
0
        e->security |= SEC_ENCRYPT;
2102
0
        do
2103
0
        {
2104
0
          struct Buffer *errmsg = buf_pool_get();
2105
0
          int rc = CSR_SUCCESS;
2106
0
          switch (mw_multi_choice(_("Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?"),
2107
                                  // L10N: Options for: Choose algorithm family: (1) DES, (2) RC2, (3) AES, or (c)lear?
2108
0
                                  _("123c")))
2109
0
          {
2110
0
            case 1:
2111
0
              switch (choice = mw_multi_choice(_("(1) DES, (2) Triple-DES?"),
2112
                                               // L10N: Options for: (1) DES, (2) Triple-DES
2113
0
                                               _("12")))
2114
0
              {
2115
0
                case 1:
2116
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2117
0
                                                "des", errmsg);
2118
0
                  break;
2119
0
                case 2:
2120
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2121
0
                                                "des3", errmsg);
2122
0
                  break;
2123
0
              }
2124
0
              break;
2125
2126
0
            case 2:
2127
0
              switch (choice = mw_multi_choice(_("(1) RC2-40, (2) RC2-64, (3) RC2-128?"),
2128
                                               // L10N: Options for: (1) RC2-40, (2) RC2-64, (3) RC2-128
2129
0
                                               _("123")))
2130
0
              {
2131
0
                case 1:
2132
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2133
0
                                                "rc2-40", errmsg);
2134
0
                  break;
2135
0
                case 2:
2136
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2137
0
                                                "rc2-64", errmsg);
2138
0
                  break;
2139
0
                case 3:
2140
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2141
0
                                                "rc2-128", errmsg);
2142
0
                  break;
2143
0
              }
2144
0
              break;
2145
2146
0
            case 3:
2147
0
              switch (choice = mw_multi_choice(_("(1) AES128, (2) AES192, (3) AES256?"),
2148
                                               // L10N: Options for: (1) AES128, (2) AES192, (3) AES256
2149
0
                                               _("123")))
2150
0
              {
2151
0
                case 1:
2152
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2153
0
                                                "aes128", errmsg);
2154
0
                  break;
2155
0
                case 2:
2156
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2157
0
                                                "aes192", errmsg);
2158
0
                  break;
2159
0
                case 3:
2160
0
                  rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with",
2161
0
                                                "aes256", errmsg);
2162
0
                  break;
2163
0
              }
2164
0
              break;
2165
2166
0
            case 4:
2167
0
              rc = cs_subset_str_string_set(NeoMutt->sub, "smime_encrypt_with", NULL, errmsg);
2168
              /* (c)lear */
2169
0
              FALLTHROUGH;
2170
2171
0
            case -1: /* Ctrl-G or Enter */
2172
0
              choice = 0;
2173
0
              break;
2174
0
          }
2175
2176
0
          if ((CSR_RESULT(rc) != CSR_SUCCESS) && !buf_is_empty(errmsg))
2177
0
            mutt_error("%s", buf_string(errmsg));
2178
2179
0
          buf_pool_release(&errmsg);
2180
0
        } while (choice == -1);
2181
0
        break;
2182
0
      }
2183
0
    }
2184
0
  }
2185
2186
0
  return e->security;
2187
0
}