NeoMutt  2024-03-23-23-gec7045
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
  • 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()

static void fix_uid ( char *  uid)
static

Decode backslash-escaped user ids (in place)

Parameters
uidString to decode

Definition at line 81 of file gnupgparse.c.

82{
83 char *s = NULL, *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') && isxdigit((unsigned char) s[2]) &&
89 isxdigit((unsigned char) s[3]))
90 {
91 *d++ = (hexval(s[2]) << 4) | hexval(s[3]);
92 s += 4;
93 }
94 else
95 {
96 *d++ = *s++;
97 }
98 }
99 *d = '\0';
100
102 {
103 int n = s - uid + 1; /* chars available in original buffer */
104
105 char *buf = mutt_mem_malloc(n + 1);
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}
static char * Charset
Cached copy of $charset.
Definition: gnupgparse.c:75
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:593
#define ICONV_T_INVALID
Error value for iconv functions.
Definition: charset.h:101
#define MUTT_ICONV_NO_FLAGS
No flags are set.
Definition: charset.h:72
static bool iconv_t_valid(const iconv_t cd)
Is the conversion descriptor valid?
Definition: charset.h:113
+ 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 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, *p = NULL;
143 int trust = 0;
145 char tstr[11] = { 0 };
146
147 *is_subkey = false;
148 if (*buf == '\0')
149 return NULL;
150
151 /* if we're given a key, merge our parsing results, else
152 * start with a fresh one to work with so that we don't
153 * mess up the real key in case we find parsing errors. */
154 struct PgpKeyInfo tmp = { 0 };
155 if (k)
156 memcpy(&tmp, k, sizeof(tmp));
157
158 mutt_debug(LL_DEBUG2, "buf = '%s'\n", buf);
159
160 const bool c_pgp_ignore_subkeys = cs_subset_bool(NeoMutt->sub, "pgp_ignore_subkeys");
161 for (p = buf; p; p = pend)
162 {
163 pend = strchr(p, ':');
164 if (pend)
165 *pend++ = 0;
166 field++;
167 if ((*p == '\0') && (field != 1) && (field != 10))
168 continue;
169
170 if (is_fpr && (field != 10))
171 continue;
172
173 switch (field)
174 {
175 case 1: /* record type */
176 {
177 mutt_debug(LL_DEBUG2, "record type: %s\n", p);
178
179 if (mutt_str_equal(p, "pub"))
180 is_pub = true;
181 else if (mutt_str_equal(p, "sub"))
182 *is_subkey = true;
183 else if (mutt_str_equal(p, "sec"))
184 ; // do nothing
185 else if (mutt_str_equal(p, "ssb"))
186 *is_subkey = true;
187 else if (mutt_str_equal(p, "uid"))
188 is_uid = true;
189 else if (mutt_str_equal(p, "fpr"))
190 is_fpr = true;
191 else
192 return NULL;
193
194 if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
195 memset(&tmp, 0, sizeof(tmp));
196
197 break;
198 }
199 case 2: /* trust info */
200 {
201 mutt_debug(LL_DEBUG2, "trust info: %s\n", p);
202
203 switch (*p)
204 { /* look only at the first letter */
205 case 'd':
207 break;
208 case 'e':
210 break;
211 case 'f':
212 trust = 3;
213 break;
214 case 'm':
215 trust = 2;
216 break;
217 case 'n':
218 trust = 1;
219 break;
220 case 'r':
222 break;
223 case 'u':
224 trust = 3;
225 break;
226 }
227
228 if (!is_uid && !(*is_subkey && c_pgp_ignore_subkeys))
229 tmp.flags |= flags;
230
231 break;
232 }
233 case 3: /* key length */
234 {
235 mutt_debug(LL_DEBUG2, "key len: %s\n", p);
236
237 if (!(*is_subkey && c_pgp_ignore_subkeys) && !mutt_str_atos_full(p, &tmp.keylen))
238 {
239 goto bail;
240 }
241 break;
242 }
243 case 4: /* pubkey algo */
244 {
245 mutt_debug(LL_DEBUG2, "pubkey algorithm: %s\n", p);
246
247 if (!(*is_subkey && c_pgp_ignore_subkeys))
248 {
249 int x = 0;
250 if (!mutt_str_atoi_full(p, &x))
251 goto bail;
252 tmp.numalg = x;
253 tmp.algorithm = pgp_pkalgbytype(x);
254 }
255 break;
256 }
257 case 5: /* 16 hex digits with the long keyid. */
258 {
259 mutt_debug(LL_DEBUG2, "key id: %s\n", p);
260
261 if (!(*is_subkey && c_pgp_ignore_subkeys))
262 mutt_str_replace(&tmp.keyid, p);
263 break;
264 }
265 case 6: /* timestamp (1998-02-28) */
266 {
267 mutt_debug(LL_DEBUG2, "time stamp: %s\n", p);
268
269 if (strchr(p, '-')) /* gpg pre-2.0.10 used format (yyyy-mm-dd) */
270 {
271 struct tm time = { 0 };
272
273 time.tm_sec = 0;
274 time.tm_min = 0;
275 time.tm_hour = 12;
276 strncpy(tstr, p, 11);
277 tstr[4] = '\0';
278 tstr[7] = '\0';
279 if (!mutt_str_atoi_full(tstr, &time.tm_year))
280 {
281 p = tstr;
282 goto bail;
283 }
284 time.tm_year -= 1900;
285 if (!mutt_str_atoi_full(tstr + 5, &time.tm_mon))
286 {
287 p = tstr + 5;
288 goto bail;
289 }
290 time.tm_mon -= 1;
291 if (!mutt_str_atoi_full(tstr + 8, &time.tm_mday))
292 {
293 p = tstr + 8;
294 goto bail;
295 }
296 tmp.gen_time = mutt_date_make_time(&time, false);
297 }
298 else /* gpg 2.0.10+ uses seconds since 1970-01-01 */
299 {
300 unsigned long long secs;
301
302 if (!mutt_str_atoull(p, &secs))
303 goto bail;
304 tmp.gen_time = (time_t) secs;
305 }
306 break;
307 }
308 case 7: /* valid for n days */
309 break;
310 case 8: /* Local id */
311 break;
312 case 9: /* ownertrust */
313 break;
314 case 10: /* name */
315 {
316 /* Empty field or no trailing colon.
317 * We allow an empty field for a pub record type because it is
318 * possible for a primary uid record to have an empty User-ID
319 * field. Without any address records, it is not possible to
320 * use the key in neomutt. */
321 if (!(pend && (*p || is_pub)))
322 break;
323
324 if (is_fpr)
325 {
326 /* don't let a subkey fpr overwrite an existing primary key fpr */
327 if (!tmp.fingerprint)
328 tmp.fingerprint = mutt_str_dup(p);
329 break;
330 }
331
332 /* ignore user IDs on subkeys */
333 if (!is_uid && (*is_subkey && c_pgp_ignore_subkeys))
334 break;
335
336 mutt_debug(LL_DEBUG2, "user ID: %s\n", NONULL(p));
337
338 uid = mutt_mem_calloc(1, sizeof(struct PgpUid));
339 fix_uid(p);
340 uid->addr = mutt_str_dup(p);
341 uid->trust = trust;
342 uid->flags |= flags;
343 uid->next = tmp.address;
344 tmp.address = uid;
345
346 if (strstr(p, "ENCR"))
348 if (strstr(p, "SIGN"))
350
351 break;
352 }
353 case 11: /* signature class */
354 break;
355 case 12: /* key capabilities */
356 mutt_debug(LL_DEBUG2, "capabilities info: %s\n", p);
357
358 while (*p)
359 {
360 switch (*p++)
361 {
362 case 'D':
363 flags |= KEYFLAG_DISABLED;
364 break;
365
366 case 'e':
367 flags |= KEYFLAG_CANENCRYPT;
368 break;
369
370 case 's':
371 flags |= KEYFLAG_CANSIGN;
372 break;
373 }
374 }
375
376 if (!is_uid && (!*is_subkey || !c_pgp_ignore_subkeys ||
377 !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) ||
378 (flags & KEYFLAG_EXPIRED))))
379 {
380 tmp.flags |= flags;
381 }
382
383 break;
384
385 default:
386 break;
387 }
388 }
389
390 /* merge temp key back into real key */
391 if (!(is_uid || is_fpr || (*is_subkey && c_pgp_ignore_subkeys)))
392 k = mutt_mem_malloc(sizeof(*k));
393 if (!k)
394 return NULL;
395 memcpy(k, &tmp, sizeof(*k));
396 /* fixup parentship of uids after merging the temp key into
397 * the real key */
398 if (tmp.address)
399 {
400 for (uid = k->address; uid; uid = uid->next)
401 uid->parent = k;
402 }
403
404 return k;
405
406bail:
407 mutt_debug(LL_DEBUG1, "invalid number: '%s'\n", p);
408 return NULL;
409}
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:48
static void fix_uid(char *uid)
Decode backslash-escaped user ids (in place)
Definition: gnupgparse.c:81
#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:241
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:709
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:329
#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_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
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: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 418 of file gnupgparse.c.

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