NeoMutt  2025-12-11-1039-g550ac6
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
gnupgparse.c File Reference

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

#include "config.h"
#include <fcntl.h>
#include <iconv.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.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 "module_data.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).
static struct PgpKeyInfoparse_pub_line (char *buf, bool *is_subkey, struct PgpKeyInfo *k)
 Parse the 'pub' line from the pgp output.
struct PgpKeyInfopgp_get_candidates (enum PgpRing keyring, struct ListHead *hints)
 Find PGP keys matching a list of hints.

Detailed Description

Parse the output of CLI PGP program.

Authors
  • Pietro Cerutti
  • Richard Russon
  • Ian Zimmerman

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()

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{
82 char *s = NULL;
83 char *d = NULL;
84 iconv_t cd = ICONV_T_INVALID;
85
86 for (s = uid, d = uid; *s;)
87 {
88 if ((s[0] == '\\') && (s[1] == 'x') && mutt_isxdigit(s[2]) && mutt_isxdigit(s[3]))
89 {
90 *d++ = (hexval(s[2]) << 4) | hexval(s[3]);
91 s += 4;
92 }
93 else
94 {
95 *d++ = *s++;
96 }
97 }
98 *d = '\0';
99
100 if (mod_data->charset &&
101 iconv_t_valid(cd = mutt_ch_iconv_open(mod_data->charset, "utf-8", MUTT_ICONV_NONE)))
102 {
103 int n = s - uid + 1; /* chars available in original buffer */
104
105 char *buf = MUTT_MEM_MALLOC(n + 1, char);
106 const char *ib = uid;
107 size_t ibl = d - uid + 1;
108 char *ob = buf;
109 size_t obl = n;
110 iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
111 if (ibl == 0)
112 {
113 if (ob - buf < n)
114 {
115 memcpy(uid, buf, ob - buf);
116 uid[ob - buf] = '\0';
117 }
118 else if ((n >= 0) && ((ob - buf) == n) && (buf[n] = 0, (strlen(buf) < (size_t) n)))
119 {
120 memcpy(uid, buf, n);
121 }
122 }
123 FREE(&buf);
124 }
125}
bool mutt_isxdigit(int arg)
Wrapper for isxdigit(3).
Definition ctype.c:111
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_MALLOC(n, type)
Definition memory.h:53
#define hexval(ch)
Convert hexadecimal character to its integer value.
Definition mime.h:81
@ MODULE_ID_NCRYPT
ModuleNcrypt, Ncrypt
Definition module_api.h:82
iconv_t mutt_ch_iconv_open(const char *tocode, const char *fromcode, uint8_t flags)
Set up iconv for conversions.
Definition charset.c:581
#define MUTT_ICONV_NONE
No flags are set.
Definition charset.h:66
#define ICONV_T_INVALID
Error value for iconv functions.
Definition charset.h:111
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition charset.h:123
void * neomutt_get_module_data(struct NeoMutt *n, enum ModuleId id)
Get the private data for a Module.
Definition neomutt.c:666
Ncrypt private Module data.
Definition module_data.h:39
char * charset
gnupgparse charset
Definition module_data.h:50
Container for Accounts, Notifications.
Definition neomutt.h:41
Here is the call graph for this function:
Here is the caller graph for this function:

◆ parse_pub_line()

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 135 of file gnupgparse.c.

136{
137 struct PgpUid *uid = NULL;
138 int field = 0;
139 bool is_uid = false;
140 bool is_pub = false;
141 bool is_fpr = false;
142 char *pend = NULL;
143 char *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 default:
228 break;
229 }
230
231 if (!is_uid && !(*is_subkey && c_pgp_ignore_subkeys))
232 tmp.flags |= flags;
233
234 break;
235 }
236 case 3: /* key length */
237 {
238 mutt_debug(LL_DEBUG2, "key len: %s\n", p);
239
240 if (!(*is_subkey && c_pgp_ignore_subkeys) && !mutt_str_atos_full(p, &tmp.keylen))
241 {
242 goto bail;
243 }
244 break;
245 }
246 case 4: /* pubkey algo */
247 {
248 mutt_debug(LL_DEBUG2, "pubkey algorithm: %s\n", p);
249
250 if (!(*is_subkey && c_pgp_ignore_subkeys))
251 {
252 int x = 0;
253 if (!mutt_str_atoi_full(p, &x))
254 goto bail;
255 tmp.numalg = x;
256 tmp.algorithm = pgp_pkalgbytype(x);
257 }
258 break;
259 }
260 case 5: /* 16 hex digits with the long keyid. */
261 {
262 mutt_debug(LL_DEBUG2, "key id: %s\n", p);
263
264 if (!(*is_subkey && c_pgp_ignore_subkeys))
265 mutt_str_replace(&tmp.keyid, p);
266 break;
267 }
268 case 6: /* timestamp (1998-02-28) */
269 {
270 mutt_debug(LL_DEBUG2, "time stamp: %s\n", p);
271
272 if (strchr(p, '-')) /* gpg pre-2.0.10 used format (yyyy-mm-dd) */
273 {
274 struct tm time = { 0 };
275
276 time.tm_sec = 0;
277 time.tm_min = 0;
278 time.tm_hour = 12;
279 strncpy(tstr, p, 11);
280 tstr[4] = '\0';
281 tstr[7] = '\0';
282 if (!mutt_str_atoi_full(tstr, &time.tm_year))
283 {
284 p = tstr;
285 goto bail;
286 }
287 time.tm_year -= 1900;
288 if (!mutt_str_atoi_full(tstr + 5, &time.tm_mon))
289 {
290 p = tstr + 5;
291 goto bail;
292 }
293 time.tm_mon -= 1;
294 if (!mutt_str_atoi_full(tstr + 8, &time.tm_mday))
295 {
296 p = tstr + 8;
297 goto bail;
298 }
299 tmp.gen_time = mutt_date_make_time(&time, false);
300 }
301 else /* gpg 2.0.10+ uses seconds since 1970-01-01 */
302 {
303 unsigned long long secs = 0;
304
305 if (!mutt_str_atoull(p, &secs))
306 goto bail;
307 tmp.gen_time = (time_t) secs;
308 }
309 break;
310 }
311 case 7: /* valid for n days */
312 break;
313 case 8: /* Local id */
314 break;
315 case 9: /* ownertrust */
316 break;
317 case 10: /* name */
318 {
319 /* Empty field or no trailing colon.
320 * We allow an empty field for a pub record type because it is
321 * possible for a primary uid record to have an empty User-ID
322 * field. Without any address records, it is not possible to
323 * use the key in neomutt. */
324 if (!(pend && (*p || is_pub)))
325 break;
326
327 if (is_fpr)
328 {
329 /* don't let a subkey fpr overwrite an existing primary key fpr */
330 if (!tmp.fingerprint)
331 tmp.fingerprint = mutt_str_dup(p);
332 break;
333 }
334
335 /* ignore user IDs on subkeys */
336 if (!is_uid && (*is_subkey && c_pgp_ignore_subkeys))
337 break;
338
339 mutt_debug(LL_DEBUG2, "user ID: %s\n", NONULL(p));
340
341 uid = MUTT_MEM_CALLOC(1, struct PgpUid);
342 fix_uid(p);
343 uid->addr = mutt_str_dup(p);
344 uid->trust = trust;
345 uid->flags |= flags;
346 uid->next = tmp.address;
347 tmp.address = uid;
348
349 if (strstr(p, "ENCR"))
351 if (strstr(p, "SIGN"))
353
354 break;
355 }
356 case 11: /* signature class */
357 break;
358 case 12: /* key capabilities */
359 mutt_debug(LL_DEBUG2, "capabilities info: %s\n", p);
360
361 while (*p)
362 {
363 switch (*p++)
364 {
365 case 'D':
366 flags |= KEYFLAG_DISABLED;
367 break;
368
369 case 'e':
370 flags |= KEYFLAG_CANENCRYPT;
371 break;
372
373 case 's':
374 flags |= KEYFLAG_CANSIGN;
375 break;
376
377 default:
378 break;
379 }
380 }
381
382 if (!is_uid && (!*is_subkey || !c_pgp_ignore_subkeys ||
383 !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) ||
384 (flags & KEYFLAG_EXPIRED))))
385 {
386 tmp.flags |= flags;
387 }
388
389 break;
390
391 default:
392 break;
393 }
394 }
395
396 /* merge temp key back into real key */
397 if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
398 k = MUTT_MEM_MALLOC(1, struct PgpKeyInfo);
399 if (!k)
400 return NULL;
401 memcpy(k, &tmp, sizeof(*k));
402 /* fixup parentship of uids after merging the temp key into
403 * the real key */
404 if (tmp.address)
405 {
406 for (uid = k->address; uid; uid = uid->next)
407 uid->parent = k;
408 }
409
410 return k;
411
412bail:
413 mutt_debug(LL_DEBUG1, "invalid number: '%s'\n", p);
414 return NULL;
415}
const char * mutt_str_atoull(const char *str, unsigned long long *dst)
Convert ASCII string to an unsigned long long.
Definition atoi.c:295
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition helpers.c:47
static void fix_uid(char *uid)
Decode backslash-escaped user ids (in place).
Definition gnupgparse.c:79
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
@ LL_DEBUG1
Log at debug level 1.
Definition logging2.h:45
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
time_t mutt_date_make_time(struct tm *t, bool local)
Convert struct tm to time_t.
Definition date.c:243
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition string.c:257
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition string.c:666
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition string.c:284
uint16_t KeyFlags
Definition lib.h:159
@ KEYFLAG_REVOKED
Key is revoked.
Definition lib.h:152
@ KEYFLAG_PREFER_SIGNING
Key's owner prefers signing.
Definition lib.h:157
@ KEYFLAG_NONE
No flags are set.
Definition lib.h:146
@ KEYFLAG_PREFER_ENCRYPTION
Key's owner prefers encryption.
Definition lib.h:156
@ KEYFLAG_CANSIGN
Key is suitable for signing.
Definition lib.h:147
@ KEYFLAG_EXPIRED
Key is expired.
Definition lib.h:151
@ KEYFLAG_CANENCRYPT
Key is suitable for encryption.
Definition lib.h:148
@ KEYFLAG_DISABLED
Key is marked disabled.
Definition lib.h:153
const char * pgp_pkalgbytype(unsigned char type)
Get the name of the algorithm from its ID.
Definition pgplib.c:42
#define NONULL(x)
Definition string2.h:44
struct ConfigSubset * sub
Inherited config items.
Definition neomutt.h:49
Information about a PGP key.
Definition pgplib.h:49
char * keyid
Key ID.
Definition pgplib.h:50
KeyFlags flags
Key flags.
Definition pgplib.h:53
struct PgpUid * address
User IDs.
Definition pgplib.h:52
char * fingerprint
Key fingerprint.
Definition pgplib.h:51
short keylen
Key length.
Definition pgplib.h:54
time_t gen_time
Key generation time.
Definition pgplib.h:55
const char * algorithm
Algorithm name.
Definition pgplib.h:57
int numalg
Algorithm number.
Definition pgplib.h:56
PGP User ID.
Definition pgplib.h:36
short trust
Trust level.
Definition pgplib.h:38
struct PgpKeyInfo * parent
Parent key.
Definition pgplib.h:40
int flags
Flags for this UID.
Definition pgplib.h:39
char * addr
Email address.
Definition pgplib.h:37
struct PgpUid * next
Linked list.
Definition pgplib.h:41
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 424 of file gnupgparse.c.

425{
427 FILE *fp = NULL;
428 pid_t pid;
429 char buf[1024] = { 0 };
430 struct PgpKeyInfo *db = NULL;
431 struct PgpKeyInfo **kend = NULL;
432 struct PgpKeyInfo *k = NULL;
433 struct PgpKeyInfo *kk = NULL;
434 struct PgpKeyInfo *mainkey = NULL;
435 bool is_sub = false;
436
437 int fd_null = open("/dev/null", O_RDWR);
438 if (fd_null == -1)
439 return NULL;
440
441 mutt_str_replace(&mod_data->charset, cc_charset());
442
443 pid = pgp_invoke_list_keys(NULL, &fp, NULL, -1, -1, fd_null, keyring, hints);
444 if (pid == -1)
445 {
446 close(fd_null);
447 return NULL;
448 }
449
450 kend = &db;
451 k = NULL;
452 while (fgets(buf, sizeof(buf) - 1, fp))
453 {
454 kk = parse_pub_line(buf, &is_sub, k);
455 if (!kk)
456 continue;
457
458 /* Only append kk to the list if it's new. */
459 if (kk != k)
460 {
461 if (k)
462 kend = &k->next;
463 *kend = kk;
464 k = kk;
465
466 if (is_sub)
467 {
468 struct PgpUid **l = NULL;
469
470 k->flags |= KEYFLAG_SUBKEY;
471 k->parent = mainkey;
472 for (l = &k->address; *l; l = &(*l)->next)
473 ; // do nothing
474
475 *l = pgp_copy_uids(mainkey->address, k);
476 }
477 else
478 {
479 mainkey = k;
480 }
481 }
482 }
483
484 if (ferror(fp))
485 mutt_perror("fgets");
486
487 mutt_file_fclose(&fp);
488 filter_wait(pid);
489
490 close(fd_null);
491
492 return db;
493}
const char * cc_charset(void)
Get the cached value of $charset.
#define mutt_file_fclose(FP)
Definition file.h:144
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:135
#define mutt_perror(...)
Definition logging2.h:95
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition filter.c:231
@ KEYFLAG_SUBKEY
Key is a subkey.
Definition lib.h:154
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:421
struct PgpUid * pgp_copy_uids(struct PgpUid *up, struct PgpKeyInfo *parent)
Copy a list of PGP UIDs.
Definition pgplib.c:128
struct PgpKeyInfo * next
Linked list.
Definition pgplib.h:59
struct PgpKeyInfo * parent
Parent key.
Definition pgplib.h:58
Here is the call graph for this function:
Here is the caller graph for this function: