NeoMutt  2024-10-02-7-gd3e66a
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
gnupgparse.c
Go to the documentation of this file.
1
36#include "config.h"
37#include <ctype.h>
38#include <fcntl.h>
39#include <iconv.h>
40#include <stdbool.h>
41#include <stdio.h>
42#include <string.h>
43#include <sys/types.h>
44#include <time.h>
45#include <unistd.h>
46#include "mutt/lib.h"
47#include "config/lib.h"
48#include "email/lib.h"
49#include "core/lib.h"
50#include "gnupgparse.h"
51#include "lib.h"
52#include "pgpinvoke.h"
53#include "pgpkey.h"
54#ifdef CRYPT_BACKEND_CLASSIC_PGP
55#include "pgplib.h"
56#endif
57
58/****************
59 * Read the GNUPG keys. For now we read the complete keyring by
60 * calling gnupg in a special mode.
61 *
62 * The output format of gpgm is colon delimited with these fields:
63 * - record type ("pub","uid","sig","rev" etc.)
64 * - trust info
65 * - key length
66 * - pubkey algo
67 * - 16 hex digits with the long keyid
68 * - timestamp (1998-02-28)
69 * - Local id
70 * - ownertrust
71 * - name
72 * - signature class
73 */
74
76static char *Charset = NULL;
77
82static void fix_uid(char *uid)
83{
84 char *s = NULL, *d = NULL;
85 iconv_t cd = ICONV_T_INVALID;
86
87 for (s = uid, d = uid; *s;)
88 {
89 if ((s[0] == '\\') && (s[1] == 'x') && isxdigit((unsigned char) s[2]) &&
90 isxdigit((unsigned char) s[3]))
91 {
92 *d++ = (hexval(s[2]) << 4) | hexval(s[3]);
93 s += 4;
94 }
95 else
96 {
97 *d++ = *s++;
98 }
99 }
100 *d = '\0';
101
103 {
104 int n = s - uid + 1; /* chars available in original buffer */
105
106 char *buf = mutt_mem_malloc(n + 1);
107 const char *ib = uid;
108 size_t ibl = d - uid + 1;
109 char *ob = buf;
110 size_t obl = n;
111 iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
112 if (ibl == 0)
113 {
114 if (ob - buf < n)
115 {
116 memcpy(uid, buf, ob - buf);
117 uid[ob - buf] = '\0';
118 }
119 else if ((n >= 0) && ((ob - buf) == n) && (buf[n] = 0, (strlen(buf) < (size_t) n)))
120 {
121 memcpy(uid, buf, n);
122 }
123 }
124 FREE(&buf);
125 }
126}
127
136static struct PgpKeyInfo *parse_pub_line(char *buf, bool *is_subkey, struct PgpKeyInfo *k)
137{
138 struct PgpUid *uid = NULL;
139 int field = 0;
140 bool is_uid = false;
141 bool is_pub = false;
142 bool is_fpr = false;
143 char *pend = NULL, *p = NULL;
144 int trust = 0;
146 char tstr[11] = { 0 };
147
148 *is_subkey = false;
149 if (*buf == '\0')
150 return NULL;
151
152 /* if we're given a key, merge our parsing results, else
153 * start with a fresh one to work with so that we don't
154 * mess up the real key in case we find parsing errors. */
155 struct PgpKeyInfo tmp = { 0 };
156 if (k)
157 memcpy(&tmp, k, sizeof(tmp));
158
159 mutt_debug(LL_DEBUG2, "buf = '%s'\n", buf);
160
161 const bool c_pgp_ignore_subkeys = cs_subset_bool(NeoMutt->sub, "pgp_ignore_subkeys");
162 for (p = buf; p; p = pend)
163 {
164 pend = strchr(p, ':');
165 if (pend)
166 *pend++ = 0;
167 field++;
168 if ((*p == '\0') && (field != 1) && (field != 10))
169 continue;
170
171 if (is_fpr && (field != 10))
172 continue;
173
174 switch (field)
175 {
176 case 1: /* record type */
177 {
178 mutt_debug(LL_DEBUG2, "record type: %s\n", p);
179
180 if (mutt_str_equal(p, "pub"))
181 is_pub = true;
182 else if (mutt_str_equal(p, "sub"))
183 *is_subkey = true;
184 else if (mutt_str_equal(p, "sec"))
185 ; // do nothing
186 else if (mutt_str_equal(p, "ssb"))
187 *is_subkey = true;
188 else if (mutt_str_equal(p, "uid"))
189 is_uid = true;
190 else if (mutt_str_equal(p, "fpr"))
191 is_fpr = true;
192 else
193 return NULL;
194
195 if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
196 memset(&tmp, 0, sizeof(tmp));
197
198 break;
199 }
200 case 2: /* trust info */
201 {
202 mutt_debug(LL_DEBUG2, "trust info: %s\n", p);
203
204 switch (*p)
205 { /* look only at the first letter */
206 case 'd':
208 break;
209 case 'e':
211 break;
212 case 'f':
213 trust = 3;
214 break;
215 case 'm':
216 trust = 2;
217 break;
218 case 'n':
219 trust = 1;
220 break;
221 case 'r':
223 break;
224 case 'u':
225 trust = 3;
226 break;
227 }
228
229 if (!is_uid && !(*is_subkey && c_pgp_ignore_subkeys))
230 tmp.flags |= flags;
231
232 break;
233 }
234 case 3: /* key length */
235 {
236 mutt_debug(LL_DEBUG2, "key len: %s\n", p);
237
238 if (!(*is_subkey && c_pgp_ignore_subkeys) && !mutt_str_atos_full(p, &tmp.keylen))
239 {
240 goto bail;
241 }
242 break;
243 }
244 case 4: /* pubkey algo */
245 {
246 mutt_debug(LL_DEBUG2, "pubkey algorithm: %s\n", p);
247
248 if (!(*is_subkey && c_pgp_ignore_subkeys))
249 {
250 int x = 0;
251 if (!mutt_str_atoi_full(p, &x))
252 goto bail;
253 tmp.numalg = x;
254 tmp.algorithm = pgp_pkalgbytype(x);
255 }
256 break;
257 }
258 case 5: /* 16 hex digits with the long keyid. */
259 {
260 mutt_debug(LL_DEBUG2, "key id: %s\n", p);
261
262 if (!(*is_subkey && c_pgp_ignore_subkeys))
263 mutt_str_replace(&tmp.keyid, p);
264 break;
265 }
266 case 6: /* timestamp (1998-02-28) */
267 {
268 mutt_debug(LL_DEBUG2, "time stamp: %s\n", p);
269
270 if (strchr(p, '-')) /* gpg pre-2.0.10 used format (yyyy-mm-dd) */
271 {
272 struct tm time = { 0 };
273
274 time.tm_sec = 0;
275 time.tm_min = 0;
276 time.tm_hour = 12;
277 strncpy(tstr, p, 11);
278 tstr[4] = '\0';
279 tstr[7] = '\0';
280 if (!mutt_str_atoi_full(tstr, &time.tm_year))
281 {
282 p = tstr;
283 goto bail;
284 }
285 time.tm_year -= 1900;
286 if (!mutt_str_atoi_full(tstr + 5, &time.tm_mon))
287 {
288 p = tstr + 5;
289 goto bail;
290 }
291 time.tm_mon -= 1;
292 if (!mutt_str_atoi_full(tstr + 8, &time.tm_mday))
293 {
294 p = tstr + 8;
295 goto bail;
296 }
297 tmp.gen_time = mutt_date_make_time(&time, false);
298 }
299 else /* gpg 2.0.10+ uses seconds since 1970-01-01 */
300 {
301 unsigned long long secs;
302
303 if (!mutt_str_atoull(p, &secs))
304 goto bail;
305 tmp.gen_time = (time_t) secs;
306 }
307 break;
308 }
309 case 7: /* valid for n days */
310 break;
311 case 8: /* Local id */
312 break;
313 case 9: /* ownertrust */
314 break;
315 case 10: /* name */
316 {
317 /* Empty field or no trailing colon.
318 * We allow an empty field for a pub record type because it is
319 * possible for a primary uid record to have an empty User-ID
320 * field. Without any address records, it is not possible to
321 * use the key in neomutt. */
322 if (!(pend && (*p || is_pub)))
323 break;
324
325 if (is_fpr)
326 {
327 /* don't let a subkey fpr overwrite an existing primary key fpr */
328 if (!tmp.fingerprint)
329 tmp.fingerprint = mutt_str_dup(p);
330 break;
331 }
332
333 /* ignore user IDs on subkeys */
334 if (!is_uid && (*is_subkey && c_pgp_ignore_subkeys))
335 break;
336
337 mutt_debug(LL_DEBUG2, "user ID: %s\n", NONULL(p));
338
339 uid = mutt_mem_calloc(1, sizeof(struct PgpUid));
340 fix_uid(p);
341 uid->addr = mutt_str_dup(p);
342 uid->trust = trust;
343 uid->flags |= flags;
344 uid->next = tmp.address;
345 tmp.address = uid;
346
347 if (strstr(p, "ENCR"))
349 if (strstr(p, "SIGN"))
351
352 break;
353 }
354 case 11: /* signature class */
355 break;
356 case 12: /* key capabilities */
357 mutt_debug(LL_DEBUG2, "capabilities info: %s\n", p);
358
359 while (*p)
360 {
361 switch (*p++)
362 {
363 case 'D':
364 flags |= KEYFLAG_DISABLED;
365 break;
366
367 case 'e':
368 flags |= KEYFLAG_CANENCRYPT;
369 break;
370
371 case 's':
372 flags |= KEYFLAG_CANSIGN;
373 break;
374 }
375 }
376
377 if (!is_uid && (!*is_subkey || !c_pgp_ignore_subkeys ||
378 !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) ||
379 (flags & KEYFLAG_EXPIRED))))
380 {
381 tmp.flags |= flags;
382 }
383
384 break;
385
386 default:
387 break;
388 }
389 }
390
391 /* merge temp key back into real key */
392 if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
393 k = mutt_mem_malloc(sizeof(*k));
394 if (!k)
395 return NULL;
396 memcpy(k, &tmp, sizeof(*k));
397 /* fixup parentship of uids after merging the temp key into
398 * the real key */
399 if (tmp.address)
400 {
401 for (uid = k->address; uid; uid = uid->next)
402 uid->parent = k;
403 }
404
405 return k;
406
407bail:
408 mutt_debug(LL_DEBUG1, "invalid number: '%s'\n", p);
409 return NULL;
410}
411
419struct PgpKeyInfo *pgp_get_candidates(enum PgpRing keyring, struct ListHead *hints)
420{
421 FILE *fp = NULL;
422 pid_t pid;
423 char buf[1024] = { 0 };
424 struct PgpKeyInfo *db = NULL, **kend = NULL, *k = NULL, *kk = NULL, *mainkey = NULL;
425 bool is_sub = false;
426
427 int fd_null = open("/dev/null", O_RDWR);
428 if (fd_null == -1)
429 return NULL;
430
432
433 pid = pgp_invoke_list_keys(NULL, &fp, NULL, -1, -1, fd_null, keyring, hints);
434 if (pid == -1)
435 {
436 close(fd_null);
437 return NULL;
438 }
439
440 kend = &db;
441 k = NULL;
442 while (fgets(buf, sizeof(buf) - 1, fp))
443 {
444 kk = parse_pub_line(buf, &is_sub, k);
445 if (!kk)
446 continue;
447
448 /* Only append kk to the list if it's new. */
449 if (kk != k)
450 {
451 if (k)
452 kend = &k->next;
453 *kend = kk;
454 k = kk;
455
456 if (is_sub)
457 {
458 struct PgpUid **l = NULL;
459
460 k->flags |= KEYFLAG_SUBKEY;
461 k->parent = mainkey;
462 for (l = &k->address; *l; l = &(*l)->next)
463 ; // do nothing
464
465 *l = pgp_copy_uids(mainkey->address, k);
466 }
467 else
468 {
469 mainkey = k;
470 }
471 }
472 }
473
474 if (ferror(fp))
475 mutt_perror("fgets");
476
477 mutt_file_fclose(&fp);
478 filter_wait(pid);
479
480 close(fd_null);
481
482 return db;
483}
const char * mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition: atoi.c:292
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:47
Convenience wrapper for the config headers.
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:116
Convenience wrapper for the core headers.
Structs that make up an email.
#define mutt_file_fclose(FP)
Definition: file.h:138
static void fix_uid(char *uid)
Decode backslash-escaped user ids (in place)
Definition: gnupgparse.c:82
static char * Charset
Cached copy of $charset.
Definition: gnupgparse.c:76
static struct PgpKeyInfo * parse_pub_line(char *buf, bool *is_subkey, struct PgpKeyInfo *k)
Parse the 'pub' line from the pgp output.
Definition: gnupgparse.c:136
struct PgpKeyInfo * pgp_get_candidates(enum PgpRing keyring, struct ListHead *hints)
Find PGP keys matching a list of hints.
Definition: gnupgparse.c:419
Parse the output of CLI PGP programinclude "pgpkey.h".
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:51
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:91
#define FREE(x)
Definition: memory.h:45
#define hexval(ch)
Definition: mime.h:80
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition: charset.c:594
#define ICONV_T_INVALID
Error value for iconv functions.
Definition: charset.h:102
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:73
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition: charset.h:114
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t
Definition: date.c:242
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:220
Convenience wrapper for the library headers.
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:280
#define KEYFLAG_EXPIRED
Key is expired.
Definition: lib.h:131
uint16_t KeyFlags
Flags describing PGP/SMIME keys, e.g. KEYFLAG_CANSIGN.
Definition: lib.h:125
#define KEYFLAG_CANENCRYPT
Key is suitable for encryption.
Definition: lib.h:128
#define KEYFLAG_SUBKEY
Key is a subkey.
Definition: lib.h:134
#define KEYFLAG_NO_FLAGS
No flags are set.
Definition: lib.h:126
#define KEYFLAG_PREFER_SIGNING
Key's owner prefers signing.
Definition: lib.h:137
#define KEYFLAG_DISABLED
Key is marked disabled.
Definition: lib.h:133
#define KEYFLAG_REVOKED
Key is revoked.
Definition: lib.h:132
#define KEYFLAG_PREFER_ENCRYPTION
Key's owner prefers encryption.
Definition: lib.h:136
#define KEYFLAG_CANSIGN
Key is suitable for signing.
Definition: lib.h:127
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:489
Wrapper around calls to external PGP program.
PGP key management routines.
PgpRing
PGP ring type.
Definition: pgpkey.h:38
const char * pgp_pkalgbytype(unsigned char type)
Get the name of the algorithm from its ID.
Definition: pgplib.c:42
struct PgpUid * pgp_copy_uids(struct PgpUid *up, struct PgpKeyInfo *parent)
Copy a list of PGP UIDs.
Definition: pgplib.c:127
Misc PGP helper routines.
Key value store.
#define NONULL(x)
Definition: string2.h:37
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
Information about a PGP key.
Definition: pgplib.h:47
char * keyid
Definition: pgplib.h:48
KeyFlags flags
Definition: pgplib.h:51
struct PgpUid * address
Definition: pgplib.h:50
char * fingerprint
Definition: pgplib.h:49
short keylen
Definition: pgplib.h:52
time_t gen_time
Definition: pgplib.h:53
const char * algorithm
Definition: pgplib.h:55
int numalg
Definition: pgplib.h:54
PGP User ID.
Definition: pgplib.h:35
short trust
Definition: pgplib.h:37
struct PgpKeyInfo * parent
Parent key.
Definition: pgplib.h:39
int flags
Definition: pgplib.h:38
char * addr
Definition: pgplib.h:36
struct PgpUid * next
Linked list.
Definition: pgplib.h:40