NeoMutt  2023-11-03-107-g582dc1
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 <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)
 
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.
 

Variables

static char * Charset = NULL
 Cached copy of $charset.
 

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

81{
82 char *s = NULL, *d = NULL;
83 iconv_t cd = ICONV_T_INVALID;
84
85 for (s = uid, d = uid; *s;)
86 {
87 if ((s[0] == '\\') && (s[1] == 'x') && isxdigit((unsigned char) s[2]) &&
88 isxdigit((unsigned char) 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
101 {
102 int n = s - uid + 1; /* chars available in original buffer */
103
104 char *buf = mutt_mem_malloc(n + 1);
105 const char *ib = uid;
106 size_t ibl = d - uid + 1;
107 char *ob = buf;
108 size_t obl = n;
109 iconv(cd, (ICONV_CONST char **) &ib, &ibl, &ob, &obl);
110 if (ibl == 0)
111 {
112 if (ob - buf < n)
113 {
114 memcpy(uid, buf, ob - buf);
115 uid[ob - buf] = '\0';
116 }
117 else if ((n >= 0) && ((ob - buf) == n) && (buf[n] = 0, (strlen(buf) < (size_t) n)))
118 {
119 memcpy(uid, buf, n);
120 }
121 }
122 FREE(&buf);
123 }
124}
static char * Charset
Cached copy of $charset.
Definition: gnupgparse.c:74
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#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:589
#define ICONV_T_INVALID
Error value for iconv functions.
Definition: charset.h:100
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:71
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition: charset.h:112
+ 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 134 of file gnupgparse.c.

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

418{
419 FILE *fp = NULL;
420 pid_t pid;
421 char buf[1024] = { 0 };
422 struct PgpKeyInfo *db = NULL, **kend = NULL, *k = NULL, *kk = NULL, *mainkey = NULL;
423 bool is_sub = false;
424
425 int fd_null = open("/dev/null", O_RDWR);
426 if (fd_null == -1)
427 return NULL;
428
430
431 pid = pgp_invoke_list_keys(NULL, &fp, NULL, -1, -1, fd_null, keyring, hints);
432 if (pid == -1)
433 {
434 close(fd_null);
435 return NULL;
436 }
437
438 kend = &db;
439 k = NULL;
440 while (fgets(buf, sizeof(buf) - 1, fp))
441 {
442 kk = parse_pub_line(buf, &is_sub, k);
443 if (!kk)
444 continue;
445
446 /* Only append kk to the list if it's new. */
447 if (kk != k)
448 {
449 if (k)
450 kend = &k->next;
451 *kend = kk;
452 k = kk;
453
454 if (is_sub)
455 {
456 struct PgpUid **l = NULL;
457
458 k->flags |= KEYFLAG_SUBKEY;
459 k->parent = mainkey;
460 for (l = &k->address; *l; l = &(*l)->next)
461 ; // do nothing
462
463 *l = pgp_copy_uids(mainkey->address, k);
464 }
465 else
466 {
467 mainkey = k;
468 }
469 }
470 }
471
472 if (ferror(fp))
473 mutt_perror("fgets");
474
475 mutt_file_fclose(&fp);
476 filter_wait(pid);
477
478 close(fd_null);
479
480 return db;
481}
const char * cc_charset(void)
Get the cached value of $charset.
Definition: config_cache.c:115
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
int filter_wait(pid_t pid)
Wait for the exit of a process and return its status.
Definition: filter.c:218
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:134
#define mutt_perror(...)
Definition: logging2.h:93
#define KEYFLAG_SUBKEY
Key is a subkey.
Definition: lib.h:135
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:539
struct PgpUid * pgp_copy_uids(struct PgpUid *up, struct PgpKeyInfo *parent)
Copy a list of PGP UIDs.
Definition: pgplib.c:126
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ Charset

char* Charset = NULL
static

Cached copy of $charset.

Definition at line 74 of file gnupgparse.c.