From b0b4e24c4fa80c10d310a229f5a0c65a57ad9393 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 7 Oct 2022 17:35:44 +0200 Subject: [PATCH] wkd: Implement --blacklist option for gpg-wks-client * tools/gpg-wks-client.c (blacklist_array, blacklist_array_len): New. (parse_arguments): Install blacklist. (read_file): New. (cmp_blacklist, add_blacklist, is_in_blacklist): New. (mirror_one_key): Check list. * tools/gpg-wks.h (opt): Remove field blacklist. -- GnuPG-bug-id: 6224 --- doc/wks.texi | 39 +++++++++- tools/gpg-wks-client.c | 157 ++++++++++++++++++++++++++++++++++++++++- tools/gpg-wks.h | 1 - 3 files changed, 193 insertions(+), 4 deletions(-) diff --git a/doc/wks.texi b/doc/wks.texi index 48e534b7d..73934fb54 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -53,6 +53,26 @@ Directory. .B gpg-wks-client .RI [ options ] .B \-\-read +.br +.B gpg-wks-client +.RI [ options ] +.B \-\-mirror +.br +.B gpg-wks-client +.RI [ options ] +.B \-\-install-key +.br +.B gpg-wks-client +.RI [ options ] +.B \-\-remove-key +.br +.B gpg-wks-client +.RI [ options ] +.B \-\-print-wkd-hash +.br +.B gpg-wks-client +.RI [ options ] +.B \-\-print-wkd-url @end ifset @mansect description @@ -101,6 +121,13 @@ fingerprint and the mailbox separated by a space. The command @option{--remove-key} removes a key from that directory, its only argument is a user-id. +The command @option{--mirror} is similar to @option{--install-key} but +takes the keys from the the LDAP server configured for Dirmngr. If no +arguments are given all keys and user ids are installed. If arguments +are given they are taken as domain names to limit the to be installed +keys. The option @option{--blacklist} may be used to further limit +the to be installed keys. + The command @option{--print-wkd-hash} prints the WKD user-id identifiers and the corresponding mailboxes from the user-ids given on the command line or via stdin (one user-id per line). @@ -175,8 +202,16 @@ easily get the return code of the process. @itemx --directory @var{dir} @opindex directory Use @var{dir} as top level directory for the commands -@option{--install-key} and @option{--remove-key}. The default is -@file{openpgpkey}. +@option{--mirror}, @option{--install-key} and @option{--remove-key}. +The default is @file{openpgpkey}. + + +@item --blacklist @var{file} +@opindex blacklist +This option is used to exclude certain mail addresses from a mirror +operation. The format of @var{file} is one mail address (just the +addrspec, e.g. "postel@@isi.edu") per line. Empty lines and lines +starting with a '#' are ignored. @item --verbose @opindex verbose diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index c9e6e5a50..a30457e75 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -144,8 +144,14 @@ static struct debug_flags_s debug_flags [] = /* Value of the option --fake-submission-addr. */ const char *fake_submission_addr; +/* An array with blacklisted addresses and its length. Use + * is_in_blacklist to check. */ +static char **blacklist_array; +static size_t blacklist_array_len; + static void wrong_args (const char *text) GPGRT_ATTR_NORETURN; +static void add_blacklist (const char *fname); static gpg_error_t proc_userid_from_stdin (gpg_error_t (*func)(const char *), const char *text); static gpg_error_t command_supported (char *userid); @@ -249,7 +255,7 @@ parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts) opt.no_autostart = 1; break; case oBlacklist: - opt.blacklist = pargs->r.ret_str; + add_blacklist (pargs->r.ret_str); break; case aSupported: @@ -477,6 +483,153 @@ main (int argc, char **argv) } + +/* Read a file FNAME into a buffer and return that malloced buffer. + * Caller must free the buffer. On error NULL is returned, on success + * the valid length of the buffer is stored at R_LENGTH. The returned + * buffer is guaranteed to be Nul terminated. */ +static char * +read_file (const char *fname, size_t *r_length) +{ + estream_t fp; + char *buf; + size_t buflen; + + if (!strcmp (fname, "-")) + { + size_t nread, bufsize = 0; + + fp = es_stdin; + es_set_binary (fp); + buf = NULL; + buflen = 0; +#define NCHUNK 32767 + do + { + bufsize += NCHUNK; + if (!buf) + buf = xmalloc (bufsize+1); + else + buf = xrealloc (buf, bufsize+1); + + nread = es_fread (buf+buflen, 1, NCHUNK, fp); + if (nread < NCHUNK && es_ferror (fp)) + { + log_error ("error reading '[stdin]': %s\n", strerror (errno)); + xfree (buf); + return NULL; + } + buflen += nread; + } + while (nread == NCHUNK); +#undef NCHUNK + } + else + { + struct stat st; + + fp = es_fopen (fname, "rb"); + if (!fp) + { + log_error ("can't open '%s': %s\n", fname, strerror (errno)); + return NULL; + } + + if (fstat (es_fileno (fp), &st)) + { + log_error ("can't stat '%s': %s\n", fname, strerror (errno)); + es_fclose (fp); + return NULL; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (es_fread (buf, buflen, 1, fp) != 1) + { + log_error ("error reading '%s': %s\n", fname, strerror (errno)); + es_fclose (fp); + xfree (buf); + return NULL; + } + es_fclose (fp); + } + buf[buflen] = 0; + if (r_length) + *r_length = buflen; + return buf; +} + + +static int +cmp_blacklist (const void *arg_a, const void *arg_b) +{ + const char *a = *(const char **)arg_a; + const char *b = *(const char **)arg_b; + return strcmp (a, b); +} + + +/* Add a blacklist to our global table. This is called during option + * parsing and thus any use of log_error will eventually stop further + * processing. */ +static void +add_blacklist (const char *fname) +{ + char *buffer; + char *p, *pend; + char **array; + size_t arraysize, arrayidx; + + buffer = read_file (fname, NULL); + if (!buffer) + return; + + /* Estimate the number of entries by counting the non-comment lines. */ + arraysize = 2; /* For the first and an extra NULL item. */ + for (p=buffer; *p; p++) + if (*p == '\n' && p[1] && p[1] != '#') + arraysize++; + + array = xcalloc (arraysize, sizeof *array); + arrayidx = 0; + + /* Loop over all lines. */ + for (p = buffer; p && *p; p = pend) + { + pend = strchr (p, '\n'); + if (pend) + *pend++ = 0; + trim_spaces (p); + if (!*p || *p == '#' ) + continue; + ascii_strlwr (p); + log_assert (arrayidx < arraysize); + array[arrayidx] = p; + arrayidx++; + } + log_assert (arrayidx < arraysize); + + qsort (array, arrayidx, sizeof *array, cmp_blacklist); + + blacklist_array = array; + blacklist_array_len = arrayidx; + gpgrt_annotate_leaked_object (buffer); + gpgrt_annotate_leaked_object (blacklist_array); +} + + +/* Return true if NAME is in a blacklist. */ +static int +is_in_blacklist (const char *name) +{ + if (!name || !blacklist_array) + return 0; + return !!bsearch (&name, blacklist_array, blacklist_array_len, + sizeof *blacklist_array, cmp_blacklist); +} + + + /* Read user ids from stdin and call FUNC for each user id. TEXT is * used for error messages. */ static gpg_error_t @@ -1765,6 +1918,8 @@ mirror_one_key (estream_t key) continue; /* No mail box or already processed. */ if (!domain_matches_mbox (domain, uid->mbox)) continue; /* We don't want this one. */ + if (is_in_blacklist (uid->mbox)) + continue; err = mirror_one_keys_userid (key, uid->mbox, uidlist, fpr); if (err) diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index 50350eddb..32aa8c328 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -44,7 +44,6 @@ struct const char *directory; const char *default_from; strlist_t extra_headers; - const char *blacklist; } opt; /* Debug values and macros. */