diff options
Diffstat (limited to '')
-rw-r--r-- | tools/call-dirmngr.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/tools/call-dirmngr.c b/tools/call-dirmngr.c new file mode 100644 index 0000000..4eef9b2 --- /dev/null +++ b/tools/call-dirmngr.c @@ -0,0 +1,381 @@ +/* call-dirmngr.c - Interact with the Dirmngr. + * Copyright (C) 2016, 2022 g10 Code GmbH + * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik + * + * This file is part of GnuPG. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This file 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#include <assuan.h> +#include "../common/util.h" +#include "../common/i18n.h" +#include "../common/asshelp.h" +#include "../common/mbox-util.h" +#include "./call-dirmngr.h" + +static struct +{ + int verbose; + int debug_ipc; + int autostart; +} opt; + + + +void +set_dirmngr_options (int verbose, int debug_ipc, int autostart) +{ + opt.verbose = verbose; + opt.debug_ipc = debug_ipc; + opt.autostart = autostart; +} + + +/* Connect to the Dirmngr and return an assuan context. */ +static gpg_error_t +connect_dirmngr (assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx; + + *r_ctx = NULL; + err = start_new_dirmngr (&ctx, + GPG_ERR_SOURCE_DEFAULT, + NULL, + opt.autostart, opt.verbose, opt.debug_ipc, + NULL, NULL); + if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_DIRMNGR) + { + static int shown; + + if (!shown) + { + shown = 1; + log_info (_("no dirmngr running in this session\n")); + } + } + + if (err) + assuan_release (ctx); + else + { + *r_ctx = ctx; + } + + return err; +} + + + + +/* Parameter structure used with the WKD_GET command. */ +struct wkd_get_parm_s +{ + estream_t memfp; +}; + + +/* Data callback for the WKD_GET command. */ +static gpg_error_t +wkd_get_data_cb (void *opaque, const void *data, size_t datalen) +{ + struct wkd_get_parm_s *parm = opaque; + gpg_error_t err = 0; + size_t nwritten; + + if (!data) + return 0; /* Ignore END commands. */ + if (!parm->memfp) + return 0; /* Data is not required. */ + + if (es_write (parm->memfp, data, datalen, &nwritten)) + err = gpg_error_from_syserror (); + + return err; +} + + +/* Status callback for the WKD_GET command. */ +static gpg_error_t +wkd_get_status_cb (void *opaque, const char *line) +{ + struct wkd_get_parm_s *parm = opaque; + gpg_error_t err = 0; + + (void)line; + (void)parm; + + return err; +} + + +/* Ask the dirmngr for the submission address of a WKD server for the + * mail address ADDRSPEC. On success the submission address is stored + * at R_ADDRSPEC. */ +gpg_error_t +wkd_get_submission_address (const char *addrspec, char **r_addrspec) +{ + gpg_error_t err; + assuan_context_t ctx; + struct wkd_get_parm_s parm; + char *line = NULL; + void *vp; + char *buffer = NULL; + char *p; + + memset (&parm, 0, sizeof parm); + *r_addrspec = NULL; + + err = connect_dirmngr (&ctx); + if (err) + return err; + + line = es_bsprintf ("WKD_GET --submission-address -- %s", addrspec); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + parm.memfp = es_fopenmem (0, "rwb"); + if (!parm.memfp) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, + NULL, NULL, wkd_get_status_cb, &parm); + if (err) + goto leave; + + es_fputc (0, parm.memfp); + if (es_fclose_snatch (parm.memfp, &vp, NULL)) + { + err = gpg_error_from_syserror (); + goto leave; + } + buffer = vp; + parm.memfp = NULL; + p = strchr (buffer, '\n'); + if (p) + *p = 0; + trim_spaces (buffer); + if (!is_valid_mailbox (buffer)) + { + err = gpg_error (GPG_ERR_INV_USER_ID); + goto leave; + } + *r_addrspec = xtrystrdup (buffer); + if (!*r_addrspec) + err = gpg_error_from_syserror (); + + leave: + es_free (buffer); + es_fclose (parm.memfp); + xfree (line); + assuan_release (ctx); + return err; +} + + +/* Ask the dirmngr for the policy flags and return them as an estream + * memory stream. If no policy flags are set, NULL is stored at + * R_BUFFER. */ +gpg_error_t +wkd_get_policy_flags (const char *addrspec, estream_t *r_buffer) +{ + gpg_error_t err; + assuan_context_t ctx; + struct wkd_get_parm_s parm; + char *line = NULL; + char *buffer = NULL; + + memset (&parm, 0, sizeof parm); + *r_buffer = NULL; + + err = connect_dirmngr (&ctx); + if (err) + return err; + + line = es_bsprintf ("WKD_GET --policy-flags -- %s", addrspec); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + parm.memfp = es_fopenmem (0, "rwb"); + if (!parm.memfp) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, + NULL, NULL, wkd_get_status_cb, &parm); + if (err) + goto leave; + + es_rewind (parm.memfp); + *r_buffer = parm.memfp; + parm.memfp = 0; + + leave: + es_free (buffer); + es_fclose (parm.memfp); + xfree (line); + assuan_release (ctx); + return err; +} + + +/* Ask the dirmngr for the key for ADDRSPEC. On success a stream with + * the key is stored at R_KEY. */ +gpg_error_t +wkd_get_key (const char *addrspec, estream_t *r_key) +{ + gpg_error_t err; + assuan_context_t ctx; + struct wkd_get_parm_s parm; + char *line = NULL; + + memset (&parm, 0, sizeof parm); + *r_key = NULL; + + err = connect_dirmngr (&ctx); + if (err) + return err; + + line = es_bsprintf ("WKD_GET -- %s", addrspec); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + parm.memfp = es_fopenmem (0, "rwb"); + if (!parm.memfp) + { + err = gpg_error_from_syserror (); + goto leave; + } + err = assuan_transact (ctx, line, wkd_get_data_cb, &parm, + NULL, NULL, wkd_get_status_cb, &parm); + if (err) + goto leave; + + es_rewind (parm.memfp); + *r_key = parm.memfp; + parm.memfp = NULL; + + leave: + es_fclose (parm.memfp); + xfree (line); + assuan_release (ctx); + return err; +} + + +/* Send the KS_GET command to the dirmngr. The caller provides CB + * which is called for each key. The callback is called wit a stream + * conveying a single key and several other informational parameters. + * DOMAIN restricts the returned keys to this domain. */ +gpg_error_t +wkd_dirmngr_ks_get (const char *domain, gpg_error_t cb (estream_t key)) +{ + gpg_error_t err; + assuan_context_t ctx; + struct wkd_get_parm_s parm; + char *line = NULL; + int any = 0; + + memset (&parm, 0, sizeof parm); + + err = connect_dirmngr (&ctx); + if (err) + return err; + + line = es_bsprintf ("KS_GET --ldap --first %s", domain? domain:""); + if (!line) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (strlen (line) + 2 >= ASSUAN_LINELENGTH) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + + parm.memfp = es_fopenmem (0, "rwb"); + if (!parm.memfp) + { + err = gpg_error_from_syserror (); + goto leave; + } + + for (;;) + { + err = assuan_transact (ctx, any? "KS_GET --next" : line, + wkd_get_data_cb, &parm, + NULL, NULL, wkd_get_status_cb, &parm); + if (err) + { + if (gpg_err_code (err) == GPG_ERR_NO_DATA + && gpg_err_source (err) == GPG_ERR_SOURCE_DIRMNGR) + err = any? 0 : gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + any = 1; + + es_rewind (parm.memfp); + err = cb (parm.memfp); + if (err) + break; + es_ftruncate (parm.memfp, 0); + } + + + leave: + es_fclose (parm.memfp); + xfree (line); + assuan_release (ctx); + return err; +} |