Coverage Report

Created: 2023-09-25 07:17

/src/neomutt/ncrypt/pgpinvoke.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Wrapper around calls to external PGP program
4
 *
5
 * @authors
6
 * Copyright (C) 1997-2003 Thomas Roessler <roessler@does-not-exist.org>
7
 * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
8
 *
9
 * @copyright
10
 * This program is free software: you can redistribute it and/or modify it under
11
 * the terms of the GNU General Public License as published by the Free Software
12
 * Foundation, either version 2 of the License, or (at your option) any later
13
 * version.
14
 *
15
 * This program is distributed in the hope that it will be useful, but WITHOUT
16
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18
 * details.
19
 *
20
 * You should have received a copy of the GNU General Public License along with
21
 * this program.  If not, see <http://www.gnu.org/licenses/>.
22
 */
23
24
/**
25
 * @page crypt_pgpinvoke Wrapper around calls to external PGP program
26
 *
27
 * This file contains the new pgp invocation code.
28
 *
29
 * @note This is almost entirely format based.
30
 */
31
32
#include "config.h"
33
#include <fcntl.h>
34
#include <stdbool.h>
35
#include <stdint.h>
36
#include <stdio.h>
37
#include <unistd.h>
38
#include "mutt/lib.h"
39
#include "address/lib.h"
40
#include "config/lib.h"
41
#include "core/lib.h"
42
#include "gui/lib.h"
43
#include "pgpinvoke.h"
44
#include "lib.h"
45
#include "format_flags.h"
46
#include "globals.h" // IWYU pragma: keep
47
#include "mutt_logging.h"
48
#include "muttlib.h"
49
#include "pgpkey.h"
50
#include "protos.h"
51
#ifdef CRYPT_BACKEND_CLASSIC_PGP
52
#include "pgp.h"
53
#endif
54
55
/**
56
 * struct PgpCommandContext - Data for a PGP command
57
 *
58
 * The actual command line formatter.
59
 */
60
struct PgpCommandContext
61
{
62
  bool need_passphrase;  ///< %p
63
  const char *fname;     ///< %f
64
  const char *sig_fname; ///< %s
65
  const char *signas;    ///< %a
66
  const char *ids;       ///< %r
67
};
68
69
/**
70
 * pgp_command_format_str - Format a PGP command string - Implements ::format_t - @ingroup expando_api
71
 *
72
 * | Expando | Description
73
 * | :------ | :----------------------------------------------------------------
74
 * | \%a     | Value of `$pgp_sign_as` if set, otherwise `$pgp_default_key`
75
 * | \%f     | File containing a message
76
 * | \%p     | Expands to PGPPASSFD=0 when a pass phrase is needed, to an empty string otherwise
77
 * | \%r     | One or more key IDs (or fingerprints if available)
78
 * | \%s     | File containing the signature part of a multipart/signed attachment when verifying it
79
 */
80
static const char *pgp_command_format_str(char *buf, size_t buflen, size_t col, int cols,
81
                                          char op, const char *src, const char *prec,
82
                                          const char *if_str, const char *else_str,
83
                                          intptr_t data, MuttFormatFlags flags)
84
0
{
85
0
  char fmt[128] = { 0 };
86
0
  struct PgpCommandContext *cctx = (struct PgpCommandContext *) data;
87
0
  bool optional = (flags & MUTT_FORMAT_OPTIONAL);
88
89
0
  switch (op)
90
0
  {
91
0
    case 'a':
92
0
    {
93
0
      if (!optional)
94
0
      {
95
0
        snprintf(fmt, sizeof(fmt), "%%%ss", prec);
96
0
        snprintf(buf, buflen, fmt, NONULL(cctx->signas));
97
0
      }
98
0
      else if (!cctx->signas)
99
0
      {
100
0
        optional = false;
101
0
      }
102
0
      break;
103
0
    }
104
0
    case 'f':
105
0
    {
106
0
      if (!optional)
107
0
      {
108
0
        snprintf(fmt, sizeof(fmt), "%%%ss", prec);
109
0
        snprintf(buf, buflen, fmt, NONULL(cctx->fname));
110
0
      }
111
0
      else if (!cctx->fname)
112
0
      {
113
0
        optional = false;
114
0
      }
115
0
      break;
116
0
    }
117
0
    case 'p':
118
0
    {
119
0
      if (!optional)
120
0
      {
121
0
        snprintf(fmt, sizeof(fmt), "%%%ss", prec);
122
0
        snprintf(buf, buflen, fmt, cctx->need_passphrase ? "PGPPASSFD=0" : "");
123
0
      }
124
0
      else if (!cctx->need_passphrase || pgp_use_gpg_agent())
125
0
      {
126
0
        optional = false;
127
0
      }
128
0
      break;
129
0
    }
130
0
    case 'r':
131
0
    {
132
0
      if (!optional)
133
0
      {
134
0
        snprintf(fmt, sizeof(fmt), "%%%ss", prec);
135
0
        snprintf(buf, buflen, fmt, NONULL(cctx->ids));
136
0
      }
137
0
      else if (!cctx->ids)
138
0
      {
139
0
        optional = false;
140
0
      }
141
0
      break;
142
0
    }
143
0
    case 's':
144
0
    {
145
0
      if (!optional)
146
0
      {
147
0
        snprintf(fmt, sizeof(fmt), "%%%ss", prec);
148
0
        snprintf(buf, buflen, fmt, NONULL(cctx->sig_fname));
149
0
      }
150
0
      else if (!cctx->sig_fname)
151
0
      {
152
0
        optional = false;
153
0
      }
154
0
      break;
155
0
    }
156
0
    default:
157
0
    {
158
0
      *buf = '\0';
159
0
      break;
160
0
    }
161
0
  }
162
163
0
  if (optional)
164
0
  {
165
0
    mutt_expando_format(buf, buflen, col, cols, if_str, pgp_command_format_str,
166
0
                        data, MUTT_FORMAT_NO_FLAGS);
167
0
  }
168
0
  else if (flags & MUTT_FORMAT_OPTIONAL)
169
0
  {
170
0
    mutt_expando_format(buf, buflen, col, cols, else_str,
171
0
                        pgp_command_format_str, data, MUTT_FORMAT_NO_FLAGS);
172
0
  }
173
174
  /* We return the format string, unchanged */
175
0
  return src;
176
0
}
177
178
/**
179
 * mutt_pgp_command - Prepare a PGP Command
180
 * @param buf    Buffer for the result
181
 * @param buflen Length of buffer
182
 * @param cctx   Data to pass to the formatter
183
 * @param fmt    printf-like formatting string
184
 *
185
 * @sa pgp_command_format_str()
186
 */
187
static void mutt_pgp_command(char *buf, size_t buflen,
188
                             struct PgpCommandContext *cctx, const char *fmt)
189
0
{
190
0
  mutt_expando_format(buf, buflen, 0, buflen, NONULL(fmt), pgp_command_format_str,
191
0
                      (intptr_t) cctx, MUTT_FORMAT_NO_FLAGS);
192
0
  mutt_debug(LL_DEBUG2, "%s\n", buf);
193
0
}
194
195
/**
196
 * pgp_invoke - Run a PGP command
197
 * @param[out] fp_pgp_in       stdin  for the command, or NULL (OPTIONAL)
198
 * @param[out] fp_pgp_out      stdout for the command, or NULL (OPTIONAL)
199
 * @param[out] fp_pgp_err      stderr for the command, or NULL (OPTIONAL)
200
 * @param[in]  fd_pgp_in       stdin  for the command, or -1 (OPTIONAL)
201
 * @param[in]  fd_pgp_out      stdout for the command, or -1 (OPTIONAL)
202
 * @param[in]  fd_pgp_err      stderr for the command, or -1 (OPTIONAL)
203
 * @param[in]  need_passphrase Is a passphrase needed?
204
 * @param[in]  fname           Filename to pass to the command
205
 * @param[in]  sig_fname       Signature filename to pass to the command
206
 * @param[in]  ids             List of IDs/fingerprints, space separated
207
 * @param[in]  format          printf-like format string
208
 * @retval num PID of the created process
209
 * @retval -1  Error creating pipes or forking
210
 *
211
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
212
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
213
 */
214
static pid_t pgp_invoke(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
215
                        int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
216
                        bool need_passphrase, const char *fname,
217
                        const char *sig_fname, const char *ids, const char *format)
218
0
{
219
0
  struct PgpCommandContext cctx = { 0 };
220
0
  char cmd[STR_COMMAND] = { 0 };
221
222
0
  if (!format || (*format == '\0'))
223
0
    return (pid_t) -1;
224
225
0
  cctx.need_passphrase = need_passphrase;
226
0
  cctx.fname = fname;
227
0
  cctx.sig_fname = sig_fname;
228
0
  const char *const c_pgp_sign_as = cs_subset_string(NeoMutt->sub, "pgp_sign_as");
229
0
  const char *const c_pgp_default_key = cs_subset_string(NeoMutt->sub, "pgp_default_key");
230
0
  if (c_pgp_sign_as)
231
0
    cctx.signas = c_pgp_sign_as;
232
0
  else
233
0
    cctx.signas = c_pgp_default_key;
234
0
  cctx.ids = ids;
235
236
0
  mutt_pgp_command(cmd, sizeof(cmd), &cctx, format);
237
238
0
  return filter_create_fd(cmd, fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in,
239
0
                          fd_pgp_out, fd_pgp_err, EnvList);
240
0
}
241
242
/*
243
 * The exported interface.
244
 *
245
 * This is historic and may be removed at some point.
246
 */
247
248
/**
249
 * pgp_invoke_decode - Use PGP to decode a message
250
 * @param[out] fp_pgp_in       stdin  for the command, or NULL (OPTIONAL)
251
 * @param[out] fp_pgp_out      stdout for the command, or NULL (OPTIONAL)
252
 * @param[out] fp_pgp_err      stderr for the command, or NULL (OPTIONAL)
253
 * @param[in]  fd_pgp_in       stdin  for the command, or -1 (OPTIONAL)
254
 * @param[in]  fd_pgp_out      stdout for the command, or -1 (OPTIONAL)
255
 * @param[in]  fd_pgp_err      stderr for the command, or -1 (OPTIONAL)
256
 * @param[in]  fname           Filename to pass to the command
257
 * @param[in]  need_passphrase Is a passphrase needed?
258
 * @retval num PID of the created process
259
 * @retval -1  Error creating pipes or forking
260
 *
261
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
262
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
263
 */
264
pid_t pgp_invoke_decode(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
265
                        int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
266
                        const char *fname, bool need_passphrase)
267
0
{
268
0
  const char *const c_pgp_decode_command = cs_subset_string(NeoMutt->sub, "pgp_decode_command");
269
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out, fd_pgp_err,
270
0
                    need_passphrase, fname, NULL, NULL, c_pgp_decode_command);
271
0
}
272
273
/**
274
 * pgp_invoke_verify - Use PGP to verify a message
275
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
276
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
277
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
278
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
279
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
280
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
281
 * @param[in]  fname      Filename to pass to the command
282
 * @param[in]  sig_fname  Signature filename to pass to the command
283
 * @retval num PID of the created process
284
 * @retval -1  Error creating pipes or forking
285
 *
286
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
287
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
288
 */
289
pid_t pgp_invoke_verify(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
290
                        int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
291
                        const char *fname, const char *sig_fname)
292
0
{
293
0
  const char *const c_pgp_verify_command = cs_subset_string(NeoMutt->sub, "pgp_verify_command");
294
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
295
0
                    fd_pgp_err, false, fname, sig_fname, NULL, c_pgp_verify_command);
296
0
}
297
298
/**
299
 * pgp_invoke_decrypt - Use PGP to decrypt a file
300
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
301
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
302
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
303
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
304
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
305
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
306
 * @param[in]  fname      Filename to pass to the command
307
 * @retval num PID of the created process
308
 * @retval -1  Error creating pipes or forking
309
 *
310
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
311
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
312
 */
313
pid_t pgp_invoke_decrypt(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
314
                         int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, const char *fname)
315
0
{
316
0
  const char *const c_pgp_decrypt_command = cs_subset_string(NeoMutt->sub, "pgp_decrypt_command");
317
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
318
0
                    fd_pgp_err, true, fname, NULL, NULL, c_pgp_decrypt_command);
319
0
}
320
321
/**
322
 * pgp_invoke_sign - Use PGP to sign a file
323
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
324
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
325
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
326
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
327
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
328
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
329
 * @param[in]  fname      Filename to pass to the command
330
 * @retval num PID of the created process
331
 * @retval -1  Error creating pipes or forking
332
 *
333
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
334
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
335
 */
336
pid_t pgp_invoke_sign(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
337
                      int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, const char *fname)
338
0
{
339
0
  const char *const c_pgp_sign_command = cs_subset_string(NeoMutt->sub, "pgp_sign_command");
340
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
341
0
                    fd_pgp_err, true, fname, NULL, NULL, c_pgp_sign_command);
342
0
}
343
344
/**
345
 * pgp_invoke_encrypt - Use PGP to encrypt a file
346
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
347
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
348
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
349
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
350
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
351
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
352
 * @param[in]  fname      Filename to pass to the command
353
 * @param[in]  uids       List of IDs/fingerprints, space separated
354
 * @param[in]  sign       If true, also sign the file
355
 * @retval num PID of the created process
356
 * @retval -1  Error creating pipes or forking
357
 *
358
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
359
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
360
 */
361
pid_t pgp_invoke_encrypt(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
362
                         int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
363
                         const char *fname, const char *uids, bool sign)
364
0
{
365
0
  if (sign)
366
0
  {
367
0
    const char *const c_pgp_encrypt_sign_command = cs_subset_string(NeoMutt->sub, "pgp_encrypt_sign_command");
368
0
    return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
369
0
                      fd_pgp_err, true, fname, NULL, uids, c_pgp_encrypt_sign_command);
370
0
  }
371
0
  else
372
0
  {
373
0
    const char *const c_pgp_encrypt_only_command = cs_subset_string(NeoMutt->sub, "pgp_encrypt_only_command");
374
0
    return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
375
0
                      fd_pgp_err, false, fname, NULL, uids, c_pgp_encrypt_only_command);
376
0
  }
377
0
}
378
379
/**
380
 * pgp_invoke_traditional - Use PGP to create in inline-signed message
381
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
382
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
383
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
384
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
385
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
386
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
387
 * @param[in]  fname      Filename to pass to the command
388
 * @param[in]  uids       List of IDs/fingerprints, space separated
389
 * @param[in]  flags      Flags, see #SecurityFlags
390
 * @retval num PID of the created process
391
 * @retval -1  Error creating pipes or forking
392
 *
393
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
394
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
395
 */
396
pid_t pgp_invoke_traditional(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
397
                             int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
398
                             const char *fname, const char *uids, SecurityFlags flags)
399
0
{
400
0
  if (flags & SEC_ENCRYPT)
401
0
  {
402
0
    const char *const c_pgp_encrypt_only_command = cs_subset_string(NeoMutt->sub, "pgp_encrypt_only_command");
403
0
    const char *const c_pgp_encrypt_sign_command = cs_subset_string(NeoMutt->sub, "pgp_encrypt_sign_command");
404
0
    return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
405
0
                      fd_pgp_err, (flags & SEC_SIGN), fname, NULL, uids,
406
0
                      (flags & SEC_SIGN) ? c_pgp_encrypt_sign_command : c_pgp_encrypt_only_command);
407
0
  }
408
0
  else
409
0
  {
410
0
    const char *const c_pgp_clear_sign_command = cs_subset_string(NeoMutt->sub, "pgp_clear_sign_command");
411
0
    return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
412
0
                      fd_pgp_err, true, fname, NULL, NULL, c_pgp_clear_sign_command);
413
0
  }
414
0
}
415
416
/**
417
 * pgp_class_invoke_import - Implements CryptModuleSpecs::pgp_invoke_import() - @ingroup crypto_pgp_invoke_import
418
 */
419
void pgp_class_invoke_import(const char *fname)
420
0
{
421
0
  char cmd[STR_COMMAND] = { 0 };
422
0
  struct PgpCommandContext cctx = { 0 };
423
424
0
  struct Buffer *buf_fname = buf_pool_get();
425
426
0
  buf_quote_filename(buf_fname, fname, true);
427
0
  cctx.fname = buf_string(buf_fname);
428
0
  const char *const c_pgp_sign_as = cs_subset_string(NeoMutt->sub, "pgp_sign_as");
429
0
  const char *const c_pgp_default_key = cs_subset_string(NeoMutt->sub, "pgp_default_key");
430
0
  if (c_pgp_sign_as)
431
0
    cctx.signas = c_pgp_sign_as;
432
0
  else
433
0
    cctx.signas = c_pgp_default_key;
434
435
0
  const char *const c_pgp_import_command = cs_subset_string(NeoMutt->sub, "pgp_import_command");
436
0
  mutt_pgp_command(cmd, sizeof(cmd), &cctx, c_pgp_import_command);
437
0
  if (mutt_system(cmd) != 0)
438
0
    mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd);
439
440
0
  buf_pool_release(&buf_fname);
441
0
}
442
443
/**
444
 * pgp_class_invoke_getkeys - Implements CryptModuleSpecs::pgp_invoke_getkeys() - @ingroup crypto_pgp_invoke_getkeys
445
 */
446
void pgp_class_invoke_getkeys(struct Address *addr)
447
0
{
448
0
  char cmd[STR_COMMAND] = { 0 };
449
450
0
  struct Buffer *personal = NULL;
451
452
0
  struct PgpCommandContext cctx = { 0 };
453
454
0
  const char *const c_pgp_get_keys_command = cs_subset_string(NeoMutt->sub, "pgp_get_keys_command");
455
0
  if (!c_pgp_get_keys_command)
456
0
    return;
457
458
0
  struct Buffer *buf = buf_pool_get();
459
0
  personal = addr->personal;
460
0
  addr->personal = NULL;
461
462
0
  struct Buffer *tmp = buf_pool_get();
463
0
  mutt_addr_to_local(addr);
464
0
  mutt_addr_write(tmp, addr, false);
465
0
  buf_quote_filename(buf, buf_string(tmp), true);
466
0
  buf_pool_release(&tmp);
467
468
0
  addr->personal = personal;
469
470
0
  cctx.ids = buf_string(buf);
471
472
0
  mutt_pgp_command(cmd, sizeof(cmd), &cctx, c_pgp_get_keys_command);
473
474
0
  int fd_null = open("/dev/null", O_RDWR);
475
476
0
  if (!isendwin())
477
0
    mutt_message(_("Fetching PGP key..."));
478
479
0
  if (mutt_system(cmd) != 0)
480
0
    mutt_debug(LL_DEBUG1, "Error running \"%s\"\n", cmd);
481
482
0
  if (!isendwin())
483
0
    mutt_clear_error();
484
485
0
  if (fd_null >= 0)
486
0
    close(fd_null);
487
488
0
  buf_pool_release(&buf);
489
0
}
490
491
/**
492
 * pgp_invoke_export - Use PGP to export a key from the user's keyring
493
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
494
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
495
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
496
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
497
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
498
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
499
 * @param[in]  uids       List of IDs/fingerprints, space separated
500
 * @retval num PID of the created process
501
 * @retval -1  Error creating pipes or forking
502
 *
503
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
504
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
505
 */
506
pid_t pgp_invoke_export(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
507
                        int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, const char *uids)
508
0
{
509
0
  const char *const c_pgp_export_command = cs_subset_string(NeoMutt->sub, "pgp_export_command");
510
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
511
0
                    fd_pgp_err, false, NULL, NULL, uids, c_pgp_export_command);
512
0
}
513
514
/**
515
 * pgp_invoke_verify_key - Use PGP to verify a key
516
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
517
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
518
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
519
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
520
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
521
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
522
 * @param[in]  uids       List of IDs/fingerprints, space separated
523
 * @retval num PID of the created process
524
 * @retval -1  Error creating pipes or forking
525
 *
526
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
527
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
528
 */
529
pid_t pgp_invoke_verify_key(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
530
                            int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, const char *uids)
531
0
{
532
0
  const char *const c_pgp_verify_key_command = cs_subset_string(NeoMutt->sub, "pgp_verify_key_command");
533
0
  return pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in, fd_pgp_out,
534
0
                    fd_pgp_err, false, NULL, NULL, uids, c_pgp_verify_key_command);
535
0
}
536
537
/**
538
 * pgp_invoke_list_keys - Find matching PGP Keys
539
 * @param[out] fp_pgp_in  stdin  for the command, or NULL (OPTIONAL)
540
 * @param[out] fp_pgp_out stdout for the command, or NULL (OPTIONAL)
541
 * @param[out] fp_pgp_err stderr for the command, or NULL (OPTIONAL)
542
 * @param[in]  fd_pgp_in  stdin  for the command, or -1 (OPTIONAL)
543
 * @param[in]  fd_pgp_out stdout for the command, or -1 (OPTIONAL)
544
 * @param[in]  fd_pgp_err stderr for the command, or -1 (OPTIONAL)
545
 * @param[in]  keyring    Keyring type, e.g. #PGP_SECRING
546
 * @param[in]  hints      Match keys to these strings
547
 * @retval num PID of the created process
548
 * @retval -1  Error creating pipes or forking
549
 *
550
 * @note `fp_pgp_in` has priority over `fd_pgp_in`.
551
 *       Likewise `fp_pgp_out` and `fp_pgp_err`.
552
 */
553
pid_t pgp_invoke_list_keys(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err,
554
                           int fd_pgp_in, int fd_pgp_out, int fd_pgp_err,
555
                           enum PgpRing keyring, struct ListHead *hints)
556
0
{
557
0
  struct Buffer *uids = buf_pool_get();
558
0
  struct Buffer *quoted = buf_pool_get();
559
560
0
  struct ListNode *np = NULL;
561
0
  STAILQ_FOREACH(np, hints, entries)
562
0
  {
563
0
    buf_quote_filename(quoted, (char *) np->data, true);
564
0
    buf_addstr(uids, buf_string(quoted));
565
0
    if (STAILQ_NEXT(np, entries))
566
0
      buf_addch(uids, ' ');
567
0
  }
568
569
0
  const char *const c_pgp_list_pubring_command = cs_subset_string(NeoMutt->sub, "pgp_list_pubring_command");
570
0
  const char *const c_pgp_list_secring_command = cs_subset_string(NeoMutt->sub, "pgp_list_secring_command");
571
0
  pid_t rc = pgp_invoke(fp_pgp_in, fp_pgp_out, fp_pgp_err, fd_pgp_in,
572
0
                        fd_pgp_out, fd_pgp_err, 0, NULL, NULL, buf_string(uids),
573
0
                        (keyring == PGP_SECRING) ? c_pgp_list_secring_command :
574
0
                                                   c_pgp_list_pubring_command);
575
576
0
  buf_pool_release(&uids);
577
0
  buf_pool_release(&quoted);
578
0
  return rc;
579
0
}