2016-06-29 12:00:22 +02:00
|
|
|
|
/* call-dirmngr.c - Interact with the Dirmngr.
|
2022-10-06 18:38:29 +02:00
|
|
|
|
* Copyright (C) 2016, 2022 g10 Code GmbH
|
2017-06-19 12:37:52 +02:00
|
|
|
|
* Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
|
2016-06-29 12:00:22 +02:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
2017-06-19 12:37:52 +02:00
|
|
|
|
* 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.
|
2016-06-29 12:00:22 +02:00
|
|
|
|
*
|
2017-06-19 12:37:52 +02:00
|
|
|
|
* This file is distributed in the hope that it will be useful,
|
2016-06-29 12:00:22 +02:00
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2017-06-19 12:37:52 +02:00
|
|
|
|
* GNU Lesser General Public License for more details.
|
2016-06-29 12:00:22 +02:00
|
|
|
|
*
|
2017-06-19 12:37:52 +02:00
|
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2016-06-29 12:00:22 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#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>
|
2017-03-07 12:21:23 +01:00
|
|
|
|
#include "../common/util.h"
|
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/asshelp.h"
|
|
|
|
|
#include "../common/mbox-util.h"
|
2016-06-29 12:00:22 +02:00
|
|
|
|
#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,
|
2023-08-29 13:18:13 +02:00
|
|
|
|
opt.autostart?ASSHELP_FLAG_AUTOSTART:0,
|
|
|
|
|
opt.verbose, opt.debug_ipc,
|
2016-06-29 12:00:22 +02:00
|
|
|
|
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;
|
2023-01-19 10:52:43 +01:00
|
|
|
|
const char *s, *s2;
|
|
|
|
|
const char *warn = NULL;
|
|
|
|
|
int is_note = 0;
|
2016-06-29 12:00:22 +02:00
|
|
|
|
|
|
|
|
|
(void)parm;
|
|
|
|
|
|
2023-01-19 10:52:43 +01:00
|
|
|
|
/* Note: The code below is mostly duplicated from g10/call-dirmngr.c */
|
|
|
|
|
if ((s = has_leading_keyword (line, "WARNING"))
|
|
|
|
|
|| (is_note = !!(s = has_leading_keyword (line, "NOTE"))))
|
|
|
|
|
{
|
|
|
|
|
if ((s2 = has_leading_keyword (s, "wkd_cached_result")))
|
|
|
|
|
{
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
warn = _("WKD uses a cached result");
|
|
|
|
|
}
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "tor_not_running")))
|
|
|
|
|
warn = _("Tor is not running");
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "tor_config_problem")))
|
|
|
|
|
warn = _("Tor is not properly configured");
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "dns_config_problem")))
|
|
|
|
|
warn = _("DNS is not properly configured");
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "http_redirect")))
|
|
|
|
|
warn = _("unacceptable HTTP redirect from server");
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "http_redirect_cleanup")))
|
|
|
|
|
warn = _("unacceptable HTTP redirect from server was cleaned up");
|
|
|
|
|
else if ((s2 = has_leading_keyword (s, "tls_cert_error")))
|
|
|
|
|
warn = _("server uses an invalid certificate");
|
|
|
|
|
else
|
|
|
|
|
warn = NULL;
|
|
|
|
|
|
|
|
|
|
if (warn)
|
|
|
|
|
{
|
|
|
|
|
if (is_note)
|
|
|
|
|
log_info (_("Note: %s\n"), warn);
|
|
|
|
|
else
|
|
|
|
|
log_info (_("WARNING: %s\n"), warn);
|
|
|
|
|
if (s2 && opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
while (*s2 && !spacep (s2))
|
|
|
|
|
s2++;
|
|
|
|
|
while (*s2 && spacep (s2))
|
|
|
|
|
s2++;
|
|
|
|
|
if (*s2)
|
|
|
|
|
log_info ("(%s)\n", s2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-29 12:00:22 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2016-09-02 16:54:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2016-12-08 16:57:21 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
2022-10-06 18:38:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send the KS_GET command to the dirmngr. The caller provides CB
|
2024-05-13 00:09:23 +02:00
|
|
|
|
* which is called for each key. The callback is called with a stream
|
2022-10-06 18:38:29 +02:00
|
|
|
|
* 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;
|
|
|
|
|
}
|