NeoMutt  2021-02-05-666-ge300cd
Teaching an old dog new tricks
DOXYGEN
gnupgparse.c File Reference

Parse the output of CLI PGP program. More...

#include "config.h"
#include <ctype.h>
#include <fcntl.h>
#include <iconv.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "mutt/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "gnupgparse.h"
#include "lib.h"
#include "pgpinvoke.h"
#include "pgpkey.h"
#include "pgplib.h"
+ Include dependency graph for gnupgparse.c:

Go to the source code of this file.

Functions

static void fix_uid (char *uid)
 Decode backslash-escaped user ids (in place) More...
 
static struct PgpKeyInfoparse_pub_line (char *buf, bool *is_subkey, struct PgpKeyInfo *k)
 Parse the 'pub' line from the pgp output. More...
 
struct PgpKeyInfopgp_get_candidates (enum PgpRing keyring, struct ListHead *hints)
 Find PGP keys matching a list of hints. More...
 

Variables

static char * chs = NULL
 

Detailed Description

Parse the output of CLI PGP program.

Authors
  • Werner Koch
  • Thomas Roessler

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Definition in file gnupgparse.c.

Function Documentation

◆ fix_uid()

static void fix_uid ( char *  uid)
static

Decode backslash-escaped user ids (in place)

Parameters
uidString to decode

Definition at line 79 of file gnupgparse.c.

80 {
81  char *s = NULL, *d = NULL;
82  iconv_t cd;
83 
84  for (s = uid, d = uid; *s;)
85  {
86  if ((s[0] == '\\') && (s[1] == 'x') && isxdigit((unsigned char) s[2]) &&
87  isxdigit((unsigned char) s[3]))
88  {
89  *d++ = (hexval(s[2]) << 4) | hexval(s[3]);
90  s += 4;
91  }
92  else
93  *d++ = *s++;
94  }
95  *d = '\0';
96 
97  if (chs && ((cd = mutt_ch_iconv_open(chs, "utf-8", MUTT_ICONV_NO_FLAGS)) != (iconv_t) -1))
98  {
99  int n = s - uid + 1; /* chars available in original buffer */
100 
101  char *buf = mutt_mem_malloc(n + 1);
102  const char *ib = uid;
103  size_t ibl = d - uid + 1;
104  char *ob = buf;
105  size_t obl = n;
106  iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
107  if (ibl == 0)
108  {
109  if (ob - buf < n)
110  {
111  memcpy(uid, buf, ob - buf);
112  uid[ob - buf] = '\0';
113  }
114  else if ((n >= 0) && ((ob - buf) == n) && (buf[n] = 0, (strlen(buf) < (size_t) n)))
115  memcpy(uid, buf, n);
116  }
117  FREE(&buf);
118  iconv_close(cd);
119  }
120 }
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:569
static char * chs
Definition: gnupgparse.c:73
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define hexval(ch)
Definition: mime.h:80
#define FREE(x)
Definition: memory.h:40
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ parse_pub_line()

static struct PgpKeyInfo* parse_pub_line ( char *  buf,
bool *  is_subkey,
struct PgpKeyInfo k 
)
static

Parse the 'pub' line from the pgp output.

Parameters
[in]bufBuffer containing string to parse
[out]is_subkeyIs this a subkey of another key?
[in]kKey to from which to merge info (optional)
Return values
ptrPgpKeyInfo containing the (merged) results
NULLError

Definition at line 130 of file gnupgparse.c.

131 {
132  struct PgpUid *uid = NULL;
133  int field = 0;
134  bool is_uid = false;
135  bool is_pub = false;
136  bool is_fpr = false;
137  char *pend = NULL, *p = NULL;
138  int trust = 0;
140  struct PgpKeyInfo tmp;
141  char tstr[11];
142 
143  *is_subkey = false;
144  if (*buf == '\0')
145  return NULL;
146 
147  /* if we're given a key, merge our parsing results, else
148  * start with a fresh one to work with so that we don't
149  * mess up the real key in case we find parsing errors. */
150  if (k)
151  memcpy(&tmp, k, sizeof(tmp));
152  else
153  memset(&tmp, 0, sizeof(tmp));
154 
155  mutt_debug(LL_DEBUG2, "buf = '%s'\n", buf);
156 
157  const bool c_pgp_ignore_subkeys =
158  cs_subset_bool(NeoMutt->sub, "pgp_ignore_subkeys");
159  for (p = buf; p; p = pend)
160  {
161  pend = strchr(p, ':');
162  if (pend)
163  *pend++ = 0;
164  field++;
165  if ((*p == '\0') && (field != 1) && (field != 10))
166  continue;
167 
168  if (is_fpr && (field != 10))
169  continue;
170 
171  switch (field)
172  {
173  case 1: /* record type */
174  {
175  mutt_debug(LL_DEBUG2, "record type: %s\n", p);
176 
177  if (mutt_str_equal(p, "pub"))
178  is_pub = true;
179  else if (mutt_str_equal(p, "sub"))
180  *is_subkey = true;
181  else if (mutt_str_equal(p, "sec"))
182  ; // do nothing
183  else if (mutt_str_equal(p, "ssb"))
184  *is_subkey = true;
185  else if (mutt_str_equal(p, "uid"))
186  is_uid = true;
187  else if (mutt_str_equal(p, "fpr"))
188  is_fpr = true;
189  else
190  return NULL;
191 
192  if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
193  memset(&tmp, 0, sizeof(tmp));
194 
195  break;
196  }
197  case 2: /* trust info */
198  {
199  mutt_debug(LL_DEBUG2, "trust info: %s\n", p);
200 
201  switch (*p)
202  { /* look only at the first letter */
203  case 'd':
204  flags |= KEYFLAG_DISABLED;
205  break;
206  case 'e':
207  flags |= KEYFLAG_EXPIRED;
208  break;
209  case 'f':
210  trust = 3;
211  break;
212  case 'm':
213  trust = 2;
214  break;
215  case 'n':
216  trust = 1;
217  break;
218  case 'r':
219  flags |= KEYFLAG_REVOKED;
220  break;
221  case 'u':
222  trust = 3;
223  break;
224  }
225 
226  if (!is_uid && !(*is_subkey && c_pgp_ignore_subkeys))
227  tmp.flags |= flags;
228 
229  break;
230  }
231  case 3: /* key length */
232  {
233  mutt_debug(LL_DEBUG2, "key len: %s\n", p);
234 
235  if (!(*is_subkey && c_pgp_ignore_subkeys) && (mutt_str_atos(p, &tmp.keylen) < 0))
236  {
237  goto bail;
238  }
239  break;
240  }
241  case 4: /* pubkey algo */
242  {
243  mutt_debug(LL_DEBUG2, "pubkey algorithm: %s\n", p);
244 
245  if (!(*is_subkey && c_pgp_ignore_subkeys))
246  {
247  int x = 0;
248  if (mutt_str_atoi(p, &x) < 0)
249  goto bail;
250  tmp.numalg = x;
251  tmp.algorithm = pgp_pkalgbytype(x);
252  }
253  break;
254  }
255  case 5: /* 16 hex digits with the long keyid. */
256  {
257  mutt_debug(LL_DEBUG2, "key id: %s\n", p);
258 
259  if (!(*is_subkey && c_pgp_ignore_subkeys))
260  mutt_str_replace(&tmp.keyid, p);
261  break;
262  }
263  case 6: /* timestamp (1998-02-28) */
264  {
265  mutt_debug(LL_DEBUG2, "time stamp: %s\n", p);
266 
267  if (strchr(p, '-')) /* gpg pre-2.0.10 used format (yyyy-mm-dd) */
268  {
269  struct tm time;
270 
271  time.tm_sec = 0;
272  time.tm_min = 0;
273  time.tm_hour = 12;
274  strncpy(tstr, p, 11);
275  tstr[4] = '\0';
276  tstr[7] = '\0';
277  if (mutt_str_atoi(tstr, &time.tm_year) < 0)
278  {
279  p = tstr;
280  goto bail;
281  }
282  time.tm_year -= 1900;
283  if (mutt_str_atoi(tstr + 5, &time.tm_mon) < 0)
284  {
285  p = tstr + 5;
286  goto bail;
287  }
288  time.tm_mon -= 1;
289  if (mutt_str_atoi(tstr + 8, &time.tm_mday) < 0)
290  {
291  p = tstr + 8;
292  goto bail;
293  }
294  tmp.gen_time = mutt_date_make_time(&time, false);
295  }
296  else /* gpg 2.0.10+ uses seconds since 1970-01-01 */
297  {
298  unsigned long long secs;
299 
300  if (mutt_str_atoull(p, &secs) < 0)
301  goto bail;
302  tmp.gen_time = (time_t) secs;
303  }
304  break;
305  }
306  case 7: /* valid for n days */
307  break;
308  case 8: /* Local id */
309  break;
310  case 9: /* ownertrust */
311  break;
312  case 10: /* name */
313  {
314  /* Empty field or no trailing colon.
315  * We allow an empty field for a pub record type because it is
316  * possible for a primary uid record to have an empty User-ID
317  * field. Without any address records, it is not possible to
318  * use the key in neomutt. */
319  if (!(pend && (*p || is_pub)))
320  break;
321 
322  if (is_fpr)
323  {
324  /* don't let a subkey fpr overwrite an existing primary key fpr */
325  if (!tmp.fingerprint)
326  tmp.fingerprint = mutt_str_dup(p);
327  break;
328  }
329 
330  /* ignore user IDs on subkeys */
331  if (!is_uid && (*is_subkey && c_pgp_ignore_subkeys))
332  break;
333 
334  mutt_debug(LL_DEBUG2, "user ID: %s\n", NONULL(p));
335 
336  uid = mutt_mem_calloc(1, sizeof(struct PgpUid));
337  fix_uid(p);
338  uid->addr = mutt_str_dup(p);
339  uid->trust = trust;
340  uid->flags |= flags;
341  uid->next = tmp.address;
342  tmp.address = uid;
343 
344  if (strstr(p, "ENCR"))
346  if (strstr(p, "SIGN"))
347  tmp.flags |= KEYFLAG_PREFER_SIGNING;
348 
349  break;
350  }
351  case 11: /* signature class */
352  break;
353  case 12: /* key capabilities */
354  mutt_debug(LL_DEBUG2, "capabilities info: %s\n", p);
355 
356  while (*p)
357  {
358  switch (*p++)
359  {
360  case 'D':
361  flags |= KEYFLAG_DISABLED;
362  break;
363 
364  case 'e':
365  flags |= KEYFLAG_CANENCRYPT;
366  break;
367 
368  case 's':
369  flags |= KEYFLAG_CANSIGN;
370  break;
371  }
372  }
373 
374  if (!is_uid && (!*is_subkey || !c_pgp_ignore_subkeys ||
375  !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) ||
376  (flags & KEYFLAG_EXPIRED))))
377  {
378  tmp.flags |= flags;
379  }
380 
381  break;
382 
383  default:
384  break;
385  }
386  }
387 
388  /* merge temp key back into real key */
389  if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
390  k = mutt_mem_malloc(sizeof(*k));
391  memcpy(k, &tmp, sizeof(*k));
392  /* fixup parentship of uids after merging the temp key into
393  * the real key */
394  if (tmp.address)
395  {
396  for (uid = k->address; uid; uid = uid->next)
397  uid->parent = k;
398  }
399 
400  return k;
401 
402 bail:
403  mutt_debug(LL_DEBUG1, "invalid number: '%s'\n", p);
404  return NULL;
405 }
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:904
#define KEYFLAG_PREFER_ENCRYPTION
Key&#39;s owner prefers encryption.
Definition: lib.h:133
#define NONULL(x)
Definition: string2.h:37
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int mutt_str_atoi(const char *str, int *dst)
Convert ASCII string to an integer.
Definition: string.c:252
const char * pgp_pkalgbytype(unsigned char type)
Get the name of the algorithm from its ID.
Definition: pgplib.c:41
struct PgpUid * next
Definition: pgplib.h:40
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:370
PGP User ID.
Definition: pgplib.h:34
KeyFlags flags
Definition: pgplib.h:51
static void fix_uid(char *uid)
Decode backslash-escaped user ids (in place)
Definition: gnupgparse.c:79
int mutt_str_atos(const char *str, short *dst)
Convert ASCII string to a short.
Definition: string.c:222
Container for Accounts, Notifications.
Definition: neomutt.h:36
#define KEYFLAG_CANENCRYPT
Key is suitable for encryption.
Definition: lib.h:125
#define KEYFLAG_PREFER_SIGNING
Key&#39;s owner prefers signing.
Definition: lib.h:134
char * addr
Definition: pgplib.h:36
Information about a PGP key.
Definition: pgplib.h:46
Log at debug level 2.
Definition: logging.h:41
short trust
Definition: pgplib.h:37
#define KEYFLAG_EXPIRED
Key is expired.
Definition: lib.h:128
#define KEYFLAG_NO_FLAGS
No flags are set.
Definition: lib.h:123
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
uint16_t KeyFlags
Flags describing PGP/SMIME keys, e.g. KEYFLAG_CANSIGN.
Definition: lib.h:122
struct PgpUid * address
Definition: pgplib.h:50
#define KEYFLAG_DISABLED
Key is marked disabled.
Definition: lib.h:130
int mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: string.c:343
struct PgpKeyInfo * parent
Definition: pgplib.h:39
#define KEYFLAG_CANSIGN
Key is suitable for signing.
Definition: lib.h:124
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
Log at debug level 1.
Definition: logging.h:40
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:229
#define KEYFLAG_REVOKED
Key is revoked.
Definition: lib.h:129
int flags
Definition: pgplib.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pgp_get_candidates()

struct PgpKeyInfo* pgp_get_candidates ( enum PgpRing  keyring,
struct ListHead *  hints 
)

Find PGP keys matching a list of hints.

Parameters
keyringPGP Keyring
hintsList of strings to match
Return values
ptrKey list
NULLError

Definition at line 414 of file gnupgparse.c.

415 {
416  FILE *fp = NULL;
417  pid_t pid;
418  char buf[1024];
419  struct PgpKeyInfo *db = NULL, **kend = NULL, *k = NULL, *kk = NULL, *mainkey = NULL;
420  bool is_sub = false;
421 
422  int fd_null = open("/dev/null", O_RDWR);
423  if (fd_null == -1)
424  return NULL;
425 
426  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
427  mutt_str_replace(&chs, c_charset);
428 
429  pid = pgp_invoke_list_keys(NULL, &fp, NULL, -1, -1, fd_null, keyring, hints);
430  if (pid == -1)
431  {
432  close(fd_null);
433  return NULL;
434  }
435 
436  kend = &db;
437  k = NULL;
438  while (fgets(buf, sizeof(buf) - 1, fp))
439  {
440  kk = parse_pub_line(buf, &is_sub, k);
441  if (!kk)
442  continue;
443 
444  /* Only append kk to the list if it's new. */
445  if (kk != k)
446  {
447  if (k)
448  kend = &k->next;
449  *kend = kk;
450  k = kk;
451 
452  if (is_sub)
453  {
454  struct PgpUid **l = NULL;
455 
456  k->flags |= KEYFLAG_SUBKEY;
457  k->parent = mainkey;
458  for (l = &k->address; *l; l = &(*l)->next)
459  ; // do nothing
460 
461  *l = pgp_copy_uids(mainkey->address, k);
462  }
463  else
464  mainkey = k;
465  }
466  }
467 
468  if (ferror(fp))
469  mutt_perror("fgets");
470 
471  mutt_file_fclose(&fp);
472  filter_wait(pid);
473 
474  close(fd_null);
475 
476  return db;
477 }
struct PgpUid * next
Definition: pgplib.h:40
#define KEYFLAG_SUBKEY
Key is a subkey.
Definition: lib.h:131
PGP User ID.
Definition: pgplib.h:34
#define mutt_perror(...)
Definition: logging.h:89
Container for Accounts, Notifications.
Definition: neomutt.h:36
Information about a PGP key.
Definition: pgplib.h:46
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:153
static char * chs
Definition: gnupgparse.c:73
struct PgpUid * pgp_copy_uids(struct PgpUid *up, struct PgpKeyInfo *parent)
Copy a list of PGP UIDs.
Definition: pgplib.c:126
pid_t pgp_invoke_list_keys(FILE **fp_pgp_in, FILE **fp_pgp_out, FILE **fp_pgp_err, int fd_pgp_in, int fd_pgp_out, int fd_pgp_err, enum PgpRing keyring, struct ListHead *hints)
Find matching PGP Keys.
Definition: pgpinvoke.c:556
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
static struct PgpKeyInfo * parse_pub_line(char *buf, bool *is_subkey, struct PgpKeyInfo *k)
Parse the &#39;pub&#39; line from the pgp output.
Definition: gnupgparse.c:130
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:446
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:217
int flags
Definition: pgplib.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ chs

char* chs = NULL
static

Definition at line 73 of file gnupgparse.c.