mutt stable branch with some hacks
1/*
2 * Copyright (C) 1998-2000,2003 Werner Koch <werner.koch@guug.de>
3 * Copyright (C) 1999-2003 Thomas Roessler <roessler@does-not-exist.org>
4 *
5 * This program is free software; you can redistribute it
6 * and/or modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later
9 * version.
10 *
11 * This program is distributed in the hope that it will be
12 * useful, but WITHOUT ANY WARRANTY; without even the implied
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14 * PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23/*
24 * NOTE
25 *
26 * This code used to be the parser for GnuPG's output.
27 *
28 * Nowadays, we are using an external pubring lister with PGP which mimics
29 * gpg's output format.
30 *
31 */
32
33#if HAVE_CONFIG_H
34# include "config.h"
35#endif
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include <sys/types.h>
42#include <sys/stat.h>
43#include <unistd.h>
44#include <fcntl.h>
45#include <time.h>
46#include <ctype.h>
47
48#include "mutt.h"
49#include "pgp.h"
50#include "charset.h"
51
52/* for hexval */
53#include "mime.h"
54
55/****************
56 * Read the GNUPG keys. For now we read the complete keyring by
57 * calling gnupg in a special mode.
58 *
59 * The output format of gpgm is colon delimited with these fields:
60 * - record type ("pub","uid","sig","rev" etc.)
61 * - trust info
62 * - key length
63 * - pubkey algo
64 * - 16 hex digits with the long keyid.
65 * - timestamp (1998-02-28)
66 * - Local id
67 * - ownertrust
68 * - name
69 * - signature class
70 */
71
72/* decode the backslash-escaped user ids. */
73
74static char *_chs = 0;
75
76static void fix_uid (char *uid)
77{
78 char *s, *d;
79 iconv_t cd;
80
81 for (s = d = uid; *s;)
82 {
83 if (*s == '\\' && *(s+1) == 'x' && isxdigit ((unsigned char) *(s+2)) && isxdigit ((unsigned char) *(s+3)))
84 {
85 *d++ = hexval (*(s+2)) << 4 | hexval (*(s+3));
86 s += 4;
87 }
88 else
89 *d++ = *s++;
90 }
91 *d = '\0';
92
93 if (_chs && (cd = mutt_iconv_open (_chs, "utf-8", 0)) != (iconv_t)-1)
94 {
95 int n = s - uid + 1; /* chars available in original buffer */
96 char *buf;
97 ICONV_CONST char *ib;
98 char *ob;
99 size_t ibl, obl;
100
101 buf = safe_malloc (n+1);
102 ib = uid, ibl = d - uid + 1, ob = buf, obl = n;
103 iconv (cd, &ib, &ibl, &ob, &obl);
104 if (!ibl)
105 {
106 if (ob-buf < n)
107 {
108 memcpy (uid, buf, ob-buf);
109 uid[ob-buf] = '\0';
110 }
111 else if (n >= 0 && ob-buf == n && (buf[n] = 0, strlen (buf) < (size_t)n))
112 memcpy (uid, buf, n);
113 }
114 FREE (&buf);
115 iconv_close (cd);
116 }
117}
118
119static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k)
120{
121 pgp_uid_t *uid = NULL;
122 int field = 0, is_uid = 0;
123 int is_pub = 0;
124 int is_fpr = 0;
125 char *pend, *p;
126 int trust = 0;
127 int flags = 0;
128 struct pgp_keyinfo tmp;
129
130 *is_subkey = 0;
131 if (!*buf)
132 return NULL;
133
134 /* if we're given a key, merge our parsing results, else
135 * start with a fresh one to work with so that we don't
136 * mess up the real key in case we find parsing errors. */
137 if (k)
138 memcpy (&tmp, k, sizeof (tmp));
139 else
140 memset (&tmp, 0, sizeof (tmp));
141
142 dprint (2, (debugfile, "parse_pub_line: buf = `%s'\n", buf));
143
144 for (p = buf; p; p = pend)
145 {
146 if ((pend = strchr (p, ':')))
147 *pend++ = 0;
148 field++;
149 if (!*p && (field != 1) && (field != 10))
150 continue;
151
152 if (is_fpr && (field != 10))
153 continue;
154
155 switch (field)
156 {
157 case 1: /* record type */
158 {
159 dprint (2, (debugfile, "record type: %s\n", p));
160
161 if (!mutt_strcmp (p, "pub"))
162 is_pub = 1;
163 else if (!mutt_strcmp (p, "sub"))
164 *is_subkey = 1;
165 else if (!mutt_strcmp (p, "sec"))
166 ;
167 else if (!mutt_strcmp (p, "ssb"))
168 *is_subkey = 1;
169 else if (!mutt_strcmp (p, "uid"))
170 is_uid = 1;
171 else if (!mutt_strcmp (p, "fpr"))
172 is_fpr = 1;
173 else
174 return NULL;
175
176 if (!(is_uid || is_fpr || (*is_subkey && option (OPTPGPIGNORESUB))))
177 memset (&tmp, 0, sizeof (tmp));
178
179 break;
180 }
181 case 2: /* trust info */
182 {
183 dprint (2, (debugfile, "trust info: %s\n", p));
184
185 switch (*p)
186 { /* look only at the first letter */
187 case 'e':
188 flags |= KEYFLAG_EXPIRED;
189 break;
190 case 'r':
191 flags |= KEYFLAG_REVOKED;
192 break;
193 case 'd':
194 flags |= KEYFLAG_DISABLED;
195 break;
196 case 'n':
197 trust = 1;
198 break;
199 case 'm':
200 trust = 2;
201 break;
202 case 'f':
203 trust = 3;
204 break;
205 case 'u':
206 trust = 3;
207 break;
208 }
209
210 if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB)))
211 tmp.flags |= flags;
212
213 break;
214 }
215 case 3: /* key length */
216 {
217 dprint (2, (debugfile, "key len: %s\n", p));
218
219 if (!(*is_subkey && option (OPTPGPIGNORESUB)) &&
220 mutt_atos (p, &tmp.keylen) < 0)
221 goto bail;
222 break;
223 }
224 case 4: /* pubkey algo */
225 {
226 dprint (2, (debugfile, "pubkey algorithm: %s\n", p));
227
228 if (!(*is_subkey && option (OPTPGPIGNORESUB)))
229 {
230 int x = 0;
231 if (mutt_atoi (p, &x) < 0)
232 goto bail;
233 tmp.numalg = x;
234 tmp.algorithm = pgp_pkalgbytype (x);
235 }
236 break;
237 }
238 case 5: /* 16 hex digits with the long keyid. */
239 {
240 dprint (2, (debugfile, "key id: %s\n", p));
241
242 if (!(*is_subkey && option (OPTPGPIGNORESUB)))
243 mutt_str_replace (&tmp.keyid, p);
244 break;
245
246 }
247 case 6: /* timestamp (1998-02-28) */
248 {
249 dprint (2, (debugfile, "time stamp: %s\n", p));
250
251 if (strchr (p, '-')) /* gpg pre-2.0.10 used format (yyyy-mm-dd) */
252 {
253 char tstr[11];
254 struct tm time;
255
256 time.tm_sec = 0;
257 time.tm_min = 0;
258 time.tm_hour = 12;
259 strncpy (tstr, p, 11);
260 tstr[4] = '\0';
261 tstr[7] = '\0';
262 if (mutt_atoi (tstr, &time.tm_year) < 0)
263 {
264 p = tstr;
265 goto bail;
266 }
267 time.tm_year -= 1900;
268 if (mutt_atoi (tstr+5, &time.tm_mon) < 0)
269 {
270 p = tstr+5;
271 goto bail;
272 }
273 time.tm_mon -= 1;
274 if (mutt_atoi (tstr+8, &time.tm_mday) < 0)
275 {
276 p = tstr+8;
277 goto bail;
278 }
279 tmp.gen_time = mutt_mktime (&time, 0);
280 }
281 else /* gpg 2.0.10+ uses seconds since 1970-01-01 */
282 {
283 unsigned long long secs;
284
285 if (mutt_atoull (p, &secs) < 0)
286 goto bail;
287 tmp.gen_time = (time_t)secs;
288 }
289 break;
290 }
291 case 7: /* valid for n days */
292 break;
293 case 8: /* Local id */
294 break;
295 case 9: /* ownertrust */
296 break;
297 case 10: /* name */
298 {
299 /* Empty field or no trailing colon.
300 * We allow an empty field for a pub record type because it is
301 * possible for a primary uid record to have an empty User-ID
302 * field. Without any address records, it is not possible to
303 * use the key in mutt.
304 */
305 if (!(pend && (*p || is_pub)))
306 break;
307
308 if (is_fpr)
309 {
310 /* don't let a subkey fpr overwrite an existing primary key fpr */
311 if (!tmp.fingerprint)
312 tmp.fingerprint = safe_strdup (p);
313 break;
314 }
315
316 /* ignore user IDs on subkeys */
317 if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB)))
318 break;
319
320 dprint (2, (debugfile, "user ID: %s\n", NONULL (p)));
321
322 uid = safe_calloc (sizeof (pgp_uid_t), 1);
323 fix_uid (p);
324 uid->addr = safe_strdup (p);
325 uid->trust = trust;
326 uid->flags |= flags;
327 uid->next = tmp.address;
328 tmp.address = uid;
329
330 if (strstr (p, "ENCR"))
331 tmp.flags |= KEYFLAG_PREFER_ENCRYPTION;
332 if (strstr (p, "SIGN"))
333 tmp.flags |= KEYFLAG_PREFER_SIGNING;
334
335 break;
336 }
337 case 11: /* signature class */
338 break;
339 case 12: /* key capabilities */
340 dprint (2, (debugfile, "capabilities info: %s\n", p));
341
342 while (*p)
343 {
344 switch (*p++)
345 {
346 case 'D':
347 flags |= KEYFLAG_DISABLED;
348 break;
349
350 case 'e':
351 flags |= KEYFLAG_CANENCRYPT;
352 break;
353
354 case 's':
355 flags |= KEYFLAG_CANSIGN;
356 break;
357 }
358 }
359
360 if (!is_uid &&
361 (!*is_subkey || !option (OPTPGPIGNORESUB)
362 || !((flags & KEYFLAG_DISABLED)
363 || (flags & KEYFLAG_REVOKED)
364 || (flags & KEYFLAG_EXPIRED))))
365 tmp.flags |= flags;
366
367 break;
368
369 default:
370 break;
371 }
372 }
373
374 /* merge temp key back into real key */
375 if (!(is_uid || is_fpr || (*is_subkey && option (OPTPGPIGNORESUB))))
376 k = safe_malloc (sizeof (*k));
377 memcpy (k, &tmp, sizeof (*k));
378 /* fixup parentship of uids after mering the temp key into
379 * the real key */
380 if (tmp.address)
381 {
382 for (uid = k->address; uid; uid = uid->next)
383 uid->parent = k;
384 }
385
386 return k;
387
388bail:
389 dprint(5,(debugfile,"parse_pub_line: invalid number: '%s'\n", p));
390 return NULL;
391}
392
393pgp_key_t pgp_get_candidates (pgp_ring_t keyring, LIST * hints)
394{
395 FILE *fp;
396 pid_t thepid;
397 char buf[LONG_STRING];
398 pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL;
399 int is_sub;
400 int devnull;
401
402 if ((devnull = open ("/dev/null", O_RDWR)) == -1)
403 return NULL;
404
405 mutt_str_replace (&_chs, Charset);
406
407 thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull,
408 keyring, hints);
409 if (thepid == -1)
410 {
411 close (devnull);
412 return NULL;
413 }
414
415 kend = &db;
416 k = NULL;
417 while (fgets (buf, sizeof (buf) - 1, fp))
418 {
419 if (!(kk = parse_pub_line (buf, &is_sub, k)))
420 continue;
421
422 /* Only append kk to the list if it's new. */
423 if (kk != k)
424 {
425 if (k)
426 kend = &k->next;
427 *kend = k = kk;
428
429 if (is_sub)
430 {
431 pgp_uid_t **l;
432
433 k->flags |= KEYFLAG_SUBKEY;
434 k->parent = mainkey;
435 for (l = &k->address; *l; l = &(*l)->next)
436 ;
437 *l = pgp_copy_uids (mainkey->address, k);
438 }
439 else
440 mainkey = k;
441 }
442 }
443
444 if (ferror (fp))
445 mutt_perror ("fgets");
446
447 safe_fclose (&fp);
448 mutt_wait_filter (thepid);
449
450 close (devnull);
451
452 return db;
453}