/* * Copyright notice from original mutt: * Copyright (C) 1998-2000 Werner Koch * Copyright (C) 1999-2000 Thomas Roessler * * This file is part of mutt-ng, see http://www.muttng.org/. * It's licensed under the GNU General Public License, * please see the file GPL in the top level source directory. */ /* * NOTE * * This code used to be the parser for GnuPG's output. * * Nowadays, we are using an external pubring lister with PGP which mimics * gpg's output format. * */ #if HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #include "mutt.h" #include "pgp.h" #include "charset.h" /* for hexval */ #include "mime.h" #include "lib/mem.h" #include "lib/str.h" #include "lib/debug.h" /**************** * Read the GNUPG keys. For now we read the complete keyring by * calling gnupg in a special mode. * * The output format of gpgm is colon delimited with these fields: * - record type ("pub","uid","sig","rev" etc.) * - trust info * - key length * - pubkey algo * - 16 hex digits with the long keyid. * - timestamp (1998-02-28) * - Local id * - ownertrust * - name * - signature class */ /* decode the backslash-escaped user ids. */ static char *_chs = 0; static void fix_uid (char *uid) { char *s, *d; iconv_t cd; for (s = d = uid; *s;) { if (*s == '\\' && *(s + 1) == 'x' && isxdigit ((unsigned char) *(s + 2)) && isxdigit ((unsigned char) *(s + 3))) { *d++ = hexval (*(s + 2)) << 4 | hexval (*(s + 3)); s += 4; } else *d++ = *s++; } *d = '\0'; if (_chs && (cd = mutt_iconv_open (_chs, "utf-8", 0)) != (iconv_t) - 1) { int n = s - uid + 1; /* chars available in original buffer */ char *buf; ICONV_CONST char *ib; char *ob; size_t ibl, obl; buf = mem_malloc (n + 1); ib = uid, ibl = d - uid + 1, ob = buf, obl = n; iconv (cd, &ib, &ibl, &ob, &obl); if (!ibl) { if (ob - buf < n) { memcpy (uid, buf, ob - buf); uid[ob - buf] = '\0'; } else if (ob - buf == n && (buf[n] = 0, str_len (buf) < n)) memcpy (uid, buf, n); } mem_free (&buf); iconv_close (cd); } } static pgp_key_t parse_pub_line (char *buf, int *is_subkey, pgp_key_t k) { pgp_uid_t *uid = NULL; int field = 0, is_uid = 0; char *pend, *p; int trust = 0; int flags = 0; *is_subkey = 0; if (!*buf) return NULL; debug_print (2, ("buf = `%s'\n", buf)); for (p = buf; p; p = pend) { if ((pend = strchr (p, ':'))) *pend++ = 0; field++; if (field > 1 && !*p) continue; switch (field) { case 1: /* record type */ { debug_print (2, ("record type: %s\n", p)); if (!str_cmp (p, "pub")); else if (!str_cmp (p, "sub")) *is_subkey = 1; else if (!str_cmp (p, "sec")); else if (!str_cmp (p, "ssb")) *is_subkey = 1; else if (!str_cmp (p, "uid")) is_uid = 1; else return NULL; if (!(is_uid || (*is_subkey && option (OPTPGPIGNORESUB)))) k = mem_calloc (sizeof *k, 1); break; } case 2: /* trust info */ { debug_print (2, ("trust info: %s\n", p)); switch (*p) { /* look only at the first letter */ case 'e': flags |= KEYFLAG_EXPIRED; break; case 'r': flags |= KEYFLAG_REVOKED; break; case 'd': flags |= KEYFLAG_DISABLED; break; case 'n': trust = 1; break; case 'm': trust = 2; break; case 'f': trust = 3; break; case 'u': trust = 3; break; } if (!is_uid && !(*is_subkey && option (OPTPGPIGNORESUB))) k->flags |= flags; break; } case 3: /* key length */ { debug_print (2, ("key len: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB))) k->keylen = atoi (p); /* fixme: add validation checks */ break; } case 4: /* pubkey algo */ { debug_print (2, ("pubkey algorithm: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB))) { k->numalg = atoi (p); k->algorithm = pgp_pkalgbytype (atoi (p)); } break; } case 5: /* 16 hex digits with the long keyid. */ { debug_print (2, ("key id: %s\n", p)); if (!(*is_subkey && option (OPTPGPIGNORESUB))) str_replace (&k->keyid, p); break; } case 6: /* timestamp (1998-02-28) */ { char tstr[11]; struct tm time; debug_print (2, ("time stamp: %s\n", p)); if (!p) break; time.tm_sec = 0; time.tm_min = 0; time.tm_hour = 12; strncpy (tstr, p, 11); tstr[4] = '\0'; time.tm_year = atoi (tstr) - 1900; tstr[7] = '\0'; time.tm_mon = (atoi (tstr + 5)) - 1; time.tm_mday = atoi (tstr + 8); k->gen_time = mutt_mktime (&time, 0); break; } case 7: /* valid for n days */ break; case 8: /* Local id */ break; case 9: /* ownertrust */ break; case 10: /* name */ { if (!pend || !*p) break; /* empty field or no trailing colon */ /* ignore user IDs on subkeys */ if (!is_uid && (*is_subkey && option (OPTPGPIGNORESUB))) break; debug_print (2, ("user ID: %s\n", p)); uid = mem_calloc (sizeof (pgp_uid_t), 1); fix_uid (p); uid->addr = str_dup (p); uid->trust = trust; uid->flags |= flags; uid->parent = k; uid->next = k->address; k->address = uid; if (strstr (p, "ENCR")) k->flags |= KEYFLAG_PREFER_ENCRYPTION; if (strstr (p, "SIGN")) k->flags |= KEYFLAG_PREFER_SIGNING; break; } case 11: /* signature class */ break; case 12: /* key capabilities */ debug_print (2, ("capabilities info: %s\n", p)); while (*p) { switch (*p++) { case 'D': flags |= KEYFLAG_DISABLED; break; case 'e': flags |= KEYFLAG_CANENCRYPT; break; case 's': flags |= KEYFLAG_CANSIGN; break; } } if (!is_uid && (!*is_subkey || !option (OPTPGPIGNORESUB) || !((flags & KEYFLAG_DISABLED) || (flags & KEYFLAG_REVOKED) || (flags & KEYFLAG_EXPIRED)))) k->flags |= flags; break; default: break; } } return k; } pgp_key_t pgp_get_candidates (pgp_ring_t keyring, LIST * hints) { FILE *fp; pid_t thepid; char buf[LONG_STRING]; pgp_key_t db = NULL, *kend, k = NULL, kk, mainkey = NULL; int is_sub; int devnull; if ((devnull = open ("/dev/null", O_RDWR)) == -1) return NULL; str_replace (&_chs, Charset); thepid = pgp_invoke_list_keys (NULL, &fp, NULL, -1, -1, devnull, keyring, hints); if (thepid == -1) { close (devnull); return NULL; } kend = &db; k = NULL; while (fgets (buf, sizeof (buf) - 1, fp)) { if (!(kk = parse_pub_line (buf, &is_sub, k))) continue; /* Only append kk to the list if it's new. */ if (kk != k) { if (k) kend = &k->next; *kend = k = kk; if (is_sub) { pgp_uid_t **l; k->flags |= KEYFLAG_SUBKEY; k->parent = mainkey; for (l = &k->address; *l; l = &(*l)->next); *l = pgp_copy_uids (mainkey->address, k); } else mainkey = k; } } if (ferror (fp)) mutt_perror ("fgets"); fclose (fp); mutt_wait_filter (thepid); close (devnull); return db; }