mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
dirmngr: Add timestamp / RFC3161 client
* dirmngr/rfc3161.c: Add rfc3161 implementation. * dirmngr/rfc3161.h: Add rfc3161 header. * dirmngr/Makefile.am: Add new file to makefile. * dirmngr/dirmngr.h: Add tsa responder url option. * dirmngr/dirmngr.c: Add tsa responder url option. * dirmngr/server.c: Add assuan call to request a timestamp.
This commit is contained in:
parent
678c819027
commit
b781feb487
@ -86,7 +86,8 @@ dirmngr_SOURCES = dirmngr.c dirmngr.h server.c crlcache.c crlfetch.c \
|
|||||||
dns-stuff.c dns-stuff.h \
|
dns-stuff.c dns-stuff.h \
|
||||||
http.c http.h http-common.c http-common.h http-ntbtls.c \
|
http.c http.h http-common.c http-common.h http-ntbtls.c \
|
||||||
ks-action.c ks-action.h ks-engine.h \
|
ks-action.c ks-action.h ks-engine.h \
|
||||||
ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c
|
ks-engine-hkp.c ks-engine-http.c ks-engine-finger.c ks-engine-kdns.c \
|
||||||
|
rfc3161.c
|
||||||
|
|
||||||
if USE_LIBDNS
|
if USE_LIBDNS
|
||||||
dirmngr_SOURCES += dns.c dns.h
|
dirmngr_SOURCES += dns.c dns.h
|
||||||
|
@ -161,6 +161,7 @@ enum cmd_and_opt_values {
|
|||||||
oListenBacklog,
|
oListenBacklog,
|
||||||
oFakeCRL,
|
oFakeCRL,
|
||||||
oCompatibilityFlags,
|
oCompatibilityFlags,
|
||||||
|
oTSAResponder,
|
||||||
aTest
|
aTest
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -308,6 +309,8 @@ static gpgrt_opt_t opts[] = {
|
|||||||
ARGPARSE_s_n (oBatch, "batch", "@"),
|
ARGPARSE_s_n (oBatch, "batch", "@"),
|
||||||
ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
|
ARGPARSE_s_s (oHTTPWrapperProgram, "http-wrapper-program", "@"),
|
||||||
|
|
||||||
|
ARGPARSE_s_s (oTSAResponder, "tsa-responder",
|
||||||
|
N_("|URL|use TSA responder at URL")),
|
||||||
|
|
||||||
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
|
ARGPARSE_group (302,N_("@\n(See the \"info\" manual for a complete listing "
|
||||||
"of all commands and options)\n")),
|
"of all commands and options)\n")),
|
||||||
@ -726,6 +729,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
|
|||||||
xfree (opt.fake_crl);
|
xfree (opt.fake_crl);
|
||||||
opt.fake_crl = NULL;
|
opt.fake_crl = NULL;
|
||||||
opt.compat_flags = 0;
|
opt.compat_flags = 0;
|
||||||
|
opt.tsa_responder = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,6 +909,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
|
|||||||
pargs->err = ARGPARSE_PRINT_WARNING;
|
pargs->err = ARGPARSE_PRINT_WARNING;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case oTSAResponder: opt.tsa_responder = pargs->r.ret_str; break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 0; /* Not handled. */
|
return 0; /* Not handled. */
|
||||||
|
@ -162,6 +162,7 @@ struct
|
|||||||
|
|
||||||
/* Compatibility flags (COMPAT_FLAG_xxxx). */
|
/* Compatibility flags (COMPAT_FLAG_xxxx). */
|
||||||
unsigned int compat_flags;
|
unsigned int compat_flags;
|
||||||
|
char *tsa_responder;
|
||||||
} opt;
|
} opt;
|
||||||
|
|
||||||
|
|
||||||
|
522
dirmngr/rfc3161.c
Normal file
522
dirmngr/rfc3161.c
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
/* rfc3161.c - X.509 Time-Stamp protocol using HTTPS transport.
|
||||||
|
* Copyright (C) 2022-2023 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of GnuPG.
|
||||||
|
*
|
||||||
|
* GnuPG is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GnuPG 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "dirmngr.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "http.h"
|
||||||
|
#include "validate.h"
|
||||||
|
#include "certcache.h"
|
||||||
|
#include "rfc3161.h"
|
||||||
|
#include "../common/tlv.h"
|
||||||
|
#include "../common/exechelp.h"
|
||||||
|
|
||||||
|
#include <ksba.h>
|
||||||
|
|
||||||
|
/* The maximum size we allow as a response from TSA. */
|
||||||
|
#define MAX_RESPONSE_SIZE 65536
|
||||||
|
|
||||||
|
/* Read from FP and return a newly allocated buffer in R_BUFFER with the
|
||||||
|
entire data read from FP. */
|
||||||
|
static gpg_error_t
|
||||||
|
read_response (estream_t fp, unsigned char **r_buffer, size_t *r_buflen)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
unsigned char *buffer;
|
||||||
|
size_t bufsize, nbytes;
|
||||||
|
|
||||||
|
*r_buffer = NULL;
|
||||||
|
*r_buflen = 0;
|
||||||
|
|
||||||
|
bufsize = 4096;
|
||||||
|
buffer = xtrymalloc (bufsize);
|
||||||
|
if (!buffer)
|
||||||
|
return gpg_error_from_errno (errno);
|
||||||
|
|
||||||
|
nbytes = 0;
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
unsigned char *tmp;
|
||||||
|
size_t nread = 0;
|
||||||
|
|
||||||
|
assert (nbytes < bufsize);
|
||||||
|
nread = es_fread (buffer+nbytes, 1, bufsize-nbytes, fp);
|
||||||
|
if (nread < bufsize-nbytes && es_ferror (fp))
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
log_error (_("error reading from responder: %s\n"),
|
||||||
|
strerror (errno));
|
||||||
|
xfree (buffer);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if ( !(nread == bufsize-nbytes && !es_feof (fp)))
|
||||||
|
{ /* Response successfully received. */
|
||||||
|
nbytes += nread;
|
||||||
|
*r_buffer = buffer;
|
||||||
|
*r_buflen = nbytes;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nbytes += nread;
|
||||||
|
|
||||||
|
/* Need to enlarge the buffer. */
|
||||||
|
if (bufsize >= MAX_RESPONSE_SIZE)
|
||||||
|
{
|
||||||
|
log_error (_("response from server too large; limit is %d bytes\n"),
|
||||||
|
MAX_RESPONSE_SIZE);
|
||||||
|
xfree (buffer);
|
||||||
|
return gpg_error (GPG_ERR_TOO_LARGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bufsize += 4096;
|
||||||
|
tmp = xtryrealloc (buffer, bufsize);
|
||||||
|
if (!tmp)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
xfree (buffer);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
buffer = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpg_error_t
|
||||||
|
tsa_parse_response (const unsigned char *buffer, size_t length,
|
||||||
|
ksba_cms_t *r_cms, unsigned char **r_signed_data,
|
||||||
|
size_t *r_signed_data_length)
|
||||||
|
{
|
||||||
|
gpg_error_t err = 0;
|
||||||
|
const char *where = "";
|
||||||
|
tlv_parser_t tlv;
|
||||||
|
gcry_md_hd_t hd;
|
||||||
|
unsigned char *tmperror;
|
||||||
|
unsigned char *error;
|
||||||
|
size_t error_length;
|
||||||
|
const unsigned char *failinfo;
|
||||||
|
size_t failinfo_length;
|
||||||
|
const unsigned char *tmp_signed;
|
||||||
|
struct tag_info info;
|
||||||
|
size_t len;
|
||||||
|
int status;
|
||||||
|
ksba_reader_t reader;
|
||||||
|
ksba_stop_reason_t stopreason;
|
||||||
|
const char *algoid;
|
||||||
|
int algo;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
tlv = tlv_parser_new (buffer, length, 0);
|
||||||
|
if (!tlv)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror();
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
where = "start";
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
if (tlv_expect_sequence(tlv))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
where = "status";
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
if (tlv_expect_sequence(tlv))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
where = "pkistatus";
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
if (tlv_expect_integer(tlv, &status))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
where = "statusString";
|
||||||
|
if (tlv_expect_sequence(tlv))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
where = "failInfo";
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
errors:
|
||||||
|
if (tlv_expect_object(tlv, CLASS_UNIVERSAL, TAG_UTF8_STRING, &tmperror, &error_length)) {
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
error = xtrymalloc(error_length + 1);
|
||||||
|
memcpy(error, tmperror, error_length);
|
||||||
|
error[error_length] = 0;
|
||||||
|
log_error("Error: %s\n", error);
|
||||||
|
xfree(error);
|
||||||
|
|
||||||
|
if (tlv_next(tlv))
|
||||||
|
goto bailout;
|
||||||
|
if (tlv_parser_level(tlv) == 3)
|
||||||
|
goto errors;
|
||||||
|
|
||||||
|
if (tlv_expect_object(tlv, CLASS_UNIVERSAL, TAG_BIT_STRING, &failinfo, &failinfo_length))
|
||||||
|
goto bailout;
|
||||||
|
return GPG_ERR_SERVER_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
*r_signed_data = buffer + tlv_parser_offset(tlv);
|
||||||
|
tmp_signed = *r_signed_data;
|
||||||
|
len = length - tlv_parser_offset(tlv);
|
||||||
|
err = tlv_parse_tag(r_signed_data, &len, &info);
|
||||||
|
if (err)
|
||||||
|
goto bailout;
|
||||||
|
*r_signed_data_length = info.length + info.nhdr;
|
||||||
|
*r_signed_data = xmalloc(*r_signed_data_length);
|
||||||
|
memcpy(*r_signed_data, tmp_signed, *r_signed_data_length);
|
||||||
|
tlv_parser_release(tlv);
|
||||||
|
|
||||||
|
ksba_reader_new(&reader);
|
||||||
|
ksba_reader_set_mem(reader, *r_signed_data, *r_signed_data_length);
|
||||||
|
|
||||||
|
ksba_cms_set_reader_writer(*r_cms, reader, NULL);
|
||||||
|
|
||||||
|
where = "parse_cert";
|
||||||
|
err = gcry_md_open(&hd, 0, 0);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
do
|
||||||
|
{
|
||||||
|
err = ksba_cms_parse(*r_cms, &stopreason);
|
||||||
|
if (stopreason == KSBA_SR_NEED_HASH
|
||||||
|
|| stopreason == KSBA_SR_BEGIN_DATA)
|
||||||
|
{
|
||||||
|
/* We are now able to enable the hash algorithms */
|
||||||
|
for (i=0; (algoid=ksba_cms_get_digest_algo_list (*r_cms, i)); i++)
|
||||||
|
{
|
||||||
|
algo = gcry_md_map_name (algoid);
|
||||||
|
if (!algo)
|
||||||
|
{
|
||||||
|
log_error ("unknown hash algorithm '%s'\n",
|
||||||
|
algoid? algoid:"?");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gcry_md_enable (hd, algo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ksba_cms_set_hash_function (*r_cms, HASH_FNC, hd);
|
||||||
|
}
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error("ksba_cms_parse failed: %s\n", gpg_strerror(err));
|
||||||
|
goto bailout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (stopreason != KSBA_SR_READY);
|
||||||
|
gcry_md_close(hd);
|
||||||
|
if (err)
|
||||||
|
goto bailout;
|
||||||
|
|
||||||
|
ksba_cms_set_reader_writer(*r_cms, NULL, NULL);
|
||||||
|
ksba_reader_release(reader);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bailout:
|
||||||
|
if (!err)
|
||||||
|
err = gpg_error (GPG_ERR_GENERAL);
|
||||||
|
log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n",
|
||||||
|
__func__, where,
|
||||||
|
tlv_parser_offset (tlv),
|
||||||
|
tlv_parser_level (tlv),
|
||||||
|
tlv_parser_lastfunc (tlv),
|
||||||
|
tlv_parser_lasterrstr (tlv),
|
||||||
|
gpg_strerror (err));
|
||||||
|
tlv_parser_release (tlv);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Construct an TSP request, send it to the TSA at URL and parse
|
||||||
|
* the response. */
|
||||||
|
static gpg_error_t
|
||||||
|
do_tsp_request (ctrl_t ctrl, const char *url, char *hashalgooid,
|
||||||
|
const void *tbshash, unsigned int tbshashlen,
|
||||||
|
ksba_cms_t *r_cms, unsigned char **r_signed_data,
|
||||||
|
size_t *r_signed_data_length)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
ksba_der_t dbld = NULL;
|
||||||
|
|
||||||
|
unsigned char *response;
|
||||||
|
size_t responselen;
|
||||||
|
http_t http;
|
||||||
|
int redirects_left = 2;
|
||||||
|
char *free_this = NULL;
|
||||||
|
unsigned char *tmpder;
|
||||||
|
size_t tmpderlen;
|
||||||
|
|
||||||
|
dbld = ksba_der_builder_new (0);
|
||||||
|
if (!dbld)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_syserror ();
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
ksba_der_add_tag (dbld, 0, KSBA_TYPE_SEQUENCE);
|
||||||
|
ksba_der_add_int (dbld, "\x01", 1, 0);
|
||||||
|
ksba_der_add_tag (dbld, 0, KSBA_TYPE_SEQUENCE);
|
||||||
|
ksba_der_add_tag ( dbld, 0, KSBA_TYPE_SEQUENCE);
|
||||||
|
ksba_der_add_oid ( dbld, hashalgooid);
|
||||||
|
ksba_der_add_end ( dbld);
|
||||||
|
ksba_der_add_val ( dbld, 0, KSBA_TYPE_OCTET_STRING, tbshash, tbshashlen);
|
||||||
|
ksba_der_add_end (dbld);
|
||||||
|
/* reqPolicy would go here. */
|
||||||
|
{
|
||||||
|
unsigned char nonce[32];
|
||||||
|
gcry_create_nonce (nonce, sizeof(nonce));
|
||||||
|
ksba_der_add_int (dbld, nonce, sizeof(nonce), 1);
|
||||||
|
};
|
||||||
|
/* Whether we're requesting the certificate */
|
||||||
|
//int true_val = 1;
|
||||||
|
//ksba_der_add_val(dbld, 0, KSBA_TYPE_BOOLEAN, &true_val, 1);
|
||||||
|
/* certReq would go here. */
|
||||||
|
/* extensions would go here. */
|
||||||
|
ksba_der_add_end (dbld);
|
||||||
|
|
||||||
|
err = ksba_der_builder_get (dbld, &tmpder, &tmpderlen);
|
||||||
|
ksba_der_builder_reset(dbld); // TODO is this needed?
|
||||||
|
if (err) {
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
once_more:
|
||||||
|
err = http_open (ctrl, &http, HTTP_REQ_POST, url, NULL, NULL,
|
||||||
|
((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
|
||||||
|
| (dirmngr_use_tor ()? HTTP_FLAG_FORCE_TOR:0)
|
||||||
|
| (opt.disable_ipv4? HTTP_FLAG_IGNORE_IPv4 : 0)
|
||||||
|
| (opt.disable_ipv6? HTTP_FLAG_IGNORE_IPv6 : 0)),
|
||||||
|
ctrl->http_proxy, NULL, NULL, NULL);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error (_("error connecting to '%s': %s\n"), url, gpg_strerror (err));
|
||||||
|
xfree (free_this);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
es_fprintf (http_get_write_ptr (http),
|
||||||
|
"Content-Type: application/timestamp-query\r\n"
|
||||||
|
"Content-Length: %lu\r\n",
|
||||||
|
(unsigned long)tmpderlen );
|
||||||
|
http_start_data (http);
|
||||||
|
if (es_fwrite (tmpder, tmpderlen, 1, http_get_write_ptr (http)) != 1)
|
||||||
|
{
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
log_error ("error sending request to '%s': %s\n", url, strerror (errno));
|
||||||
|
http_close (http, 0);
|
||||||
|
xfree (tmpder);
|
||||||
|
xfree (free_this);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
xfree (tmpder);
|
||||||
|
tmpder = NULL;
|
||||||
|
|
||||||
|
err = http_wait_response (http);
|
||||||
|
if (err || http_get_status_code (http) != 200)
|
||||||
|
{
|
||||||
|
if (err)
|
||||||
|
log_error (_("error reading HTTP response for '%s': %s\n"),
|
||||||
|
url, gpg_strerror (err));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (http_get_status_code (http))
|
||||||
|
{
|
||||||
|
case 301:
|
||||||
|
case 302:
|
||||||
|
{
|
||||||
|
const char *s = http_get_header (http, "Location", 0);
|
||||||
|
|
||||||
|
log_info (_("URL '%s' redirected to '%s' (%u)\n"),
|
||||||
|
url, s?s:"[none]", http_get_status_code (http));
|
||||||
|
if (s && *s && redirects_left-- )
|
||||||
|
{
|
||||||
|
xfree (free_this); url = NULL;
|
||||||
|
free_this = xtrystrdup (s);
|
||||||
|
if (!free_this)
|
||||||
|
err = gpg_error_from_errno (errno);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
url = free_this;
|
||||||
|
http_close (http, 0);
|
||||||
|
goto once_more;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
err = gpg_error (GPG_ERR_NO_DATA);
|
||||||
|
log_error (_("too many redirections\n"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 413: /* Payload too large */
|
||||||
|
err = gpg_error (GPG_ERR_TOO_LARGE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log_error (_("error accessing '%s': http status %u\n"),
|
||||||
|
url, http_get_status_code (http));
|
||||||
|
err = gpg_error (GPG_ERR_NO_DATA);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http_close (http, 0);
|
||||||
|
xfree (free_this);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = read_response (http_get_read_ptr (http), &response, &responselen);
|
||||||
|
http_close (http, 0);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error (_("error reading HTTP response for '%s': %s\n"),
|
||||||
|
url, gpg_strerror (err));
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tsa_parse_response (response, responselen, r_cms, r_signed_data,
|
||||||
|
r_signed_data_length);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
log_error (_("error parsing TSA response for '%s': %s\n"),
|
||||||
|
url, gpg_strerror (err));
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
leave:
|
||||||
|
xfree (response);
|
||||||
|
xfree (free_this);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send a timestamp request to the current TSA (from CTRL) and return
|
||||||
|
* the answer. HASHALGO shall be provided by the caller; we do no
|
||||||
|
* consistency checking here. */
|
||||||
|
gpg_error_t
|
||||||
|
dirmngr_get_timestamp (ctrl_t ctrl, char *hashalgoid,
|
||||||
|
const void *tbshash, unsigned int tbshashlen, ksba_cms_t *r_cms)
|
||||||
|
{
|
||||||
|
gpg_error_t err;
|
||||||
|
const char *url;
|
||||||
|
unsigned char *signed_data = NULL;
|
||||||
|
size_t signed_data_length;
|
||||||
|
gnupg_isotime_t signing_time;
|
||||||
|
gnupg_isotime_t current_time;
|
||||||
|
gnupg_isotime_t tmp_time;
|
||||||
|
|
||||||
|
int exitcode;
|
||||||
|
estream_t in;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
const char *argv[] = {
|
||||||
|
"--verify",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
ksba_cms_new(r_cms);
|
||||||
|
|
||||||
|
if (opt.disable_http)
|
||||||
|
{
|
||||||
|
log_error (_("Timestamp request not possible due to disabled HTTP\n"));
|
||||||
|
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (opt.tsa_responder && *opt.tsa_responder)
|
||||||
|
url = opt.tsa_responder;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log_info (_("no default URL for a TSA available\n"));
|
||||||
|
err = gpg_error (GPG_ERR_CONFIGURATION);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ask the TSA. */
|
||||||
|
err = do_tsp_request (ctrl, url, hashalgoid, tbshash, tbshashlen, r_cms,
|
||||||
|
&signed_data, &signed_data_length);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
/* Allow for some clock skew. */
|
||||||
|
gnupg_get_isotime (current_time);
|
||||||
|
add_seconds_to_isotime (current_time, opt.ocsp_max_clock_skew);
|
||||||
|
|
||||||
|
ksba_cms_get_signing_time (*r_cms, 0, signing_time);
|
||||||
|
if (strcmp (signing_time, current_time) > 0 )
|
||||||
|
{
|
||||||
|
log_error (_("TSA responder returned a status in the future\n"));
|
||||||
|
log_info ("used now: %s signing_time: %s\n", current_time, signing_time);
|
||||||
|
if (!err)
|
||||||
|
err = gpg_error (GPG_ERR_TIME_CONFLICT);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that THIS_UPDATE is not too far back in the past. */
|
||||||
|
gnupg_copy_time (tmp_time, signing_time);
|
||||||
|
add_seconds_to_isotime (tmp_time,
|
||||||
|
60 + opt.ocsp_max_clock_skew); //TODO configurable
|
||||||
|
|
||||||
|
if (!*tmp_time || strcmp (tmp_time, current_time) < 0 )
|
||||||
|
{
|
||||||
|
log_error (_("TSA responder returned a non-current status\n"));
|
||||||
|
log_info ("used now: %s signing_time: %s\n",
|
||||||
|
current_time, signing_time);
|
||||||
|
if (!err)
|
||||||
|
err = gpg_error (GPG_ERR_TIME_CONFLICT);
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = gnupg_spawn_process (gnupg_module_name(GNUPG_MODULE_NAME_GPGSM), argv, NULL, 0, &in, NULL, NULL, &pid);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
es_fwrite(signed_data, 1, signed_data_length, in);
|
||||||
|
es_fclose(in);
|
||||||
|
|
||||||
|
gnupg_wait_process(gnupg_module_name(GNUPG_MODULE_NAME_GPGSM), pid, 1, &exitcode);
|
||||||
|
gnupg_release_process(pid);
|
||||||
|
if (!exitcode) {
|
||||||
|
log_error("Signature verification successful\n");
|
||||||
|
} else {
|
||||||
|
log_error("Signature verification not successful\n");
|
||||||
|
err = GPG_ERR_BAD_SIGNATURE;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
|
||||||
|
leave:
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
ksba_cms_release(*r_cms);
|
||||||
|
*r_cms = NULL;
|
||||||
|
goto leave;
|
||||||
|
}
|
||||||
|
xfree(signed_data);
|
||||||
|
return err;
|
||||||
|
}
|
32
dirmngr/rfc3161.h
Normal file
32
dirmngr/rfc3161.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* rfc3161.h - X.509 Time-Stamp protocol interface
|
||||||
|
* Copyright (C) 2022 g10 Code GmbH
|
||||||
|
*
|
||||||
|
* This file is part of GnuPG.
|
||||||
|
*
|
||||||
|
* GnuPG is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* GnuPG 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GNUPG_RFC3161_H
|
||||||
|
#define GNUPG_RFC3161_H
|
||||||
|
|
||||||
|
#include <gpg-error.h>
|
||||||
|
#include "dirmngr.h"
|
||||||
|
|
||||||
|
gpg_error_t dirmngr_get_timestamp (ctrl_t ctrl, char *hashalgoid,
|
||||||
|
const void *tbshash,
|
||||||
|
unsigned int tbshashlen, ksba_cms_t *r_cms);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*GNUPG_RFC3161_H*/
|
@ -64,6 +64,7 @@
|
|||||||
#include "../common/mbox-util.h"
|
#include "../common/mbox-util.h"
|
||||||
#include "../common/zb32.h"
|
#include "../common/zb32.h"
|
||||||
#include "../common/server-help.h"
|
#include "../common/server-help.h"
|
||||||
|
#include "rfc3161.h"
|
||||||
|
|
||||||
/* To avoid DoS attacks we limit the size of a certificate to
|
/* To avoid DoS attacks we limit the size of a certificate to
|
||||||
something reasonable. The DoS was actually only an issue back when
|
something reasonable. The DoS was actually only an issue back when
|
||||||
@ -655,6 +656,29 @@ option_handler (assuan_context_t ctx, const char *key, const char *value)
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gpg_error_t
|
||||||
|
cmd_tsa (assuan_context_t ctx, char *line)
|
||||||
|
{
|
||||||
|
gpg_error_t err = 0;
|
||||||
|
unsigned char *digest;
|
||||||
|
ksba_cms_t cms;
|
||||||
|
ctrl_t ctrl = assuan_get_pointer(ctx);
|
||||||
|
gcry_md_hd_t hd;
|
||||||
|
const char *oid = "2.16.840.1.101.3.4.2.1";
|
||||||
|
gcry_md_open(&hd, gcry_md_map_name(oid), 0);
|
||||||
|
gcry_md_write(hd, line, strlen(line));
|
||||||
|
digest = gcry_md_read(hd, 0);
|
||||||
|
err = dirmngr_get_timestamp(ctrl, oid, digest, 32, &cms);
|
||||||
|
if (err)
|
||||||
|
goto leave;
|
||||||
|
gnupg_isotime_t time;
|
||||||
|
ksba_cms_get_signing_time(cms, 0, &time);
|
||||||
|
ksba_cms_release(cms);
|
||||||
|
leave:
|
||||||
|
gcry_md_close(hd);
|
||||||
|
return leave_cmd (ctx, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char hlp_dns_cert[] =
|
static const char hlp_dns_cert[] =
|
||||||
@ -3049,6 +3073,7 @@ register_commands (assuan_context_t ctx)
|
|||||||
assuan_handler_t handler;
|
assuan_handler_t handler;
|
||||||
const char * const help;
|
const char * const help;
|
||||||
} table[] = {
|
} table[] = {
|
||||||
|
{ "TSA", cmd_tsa, hlp_dns_cert },
|
||||||
{ "DNS_CERT", cmd_dns_cert, hlp_dns_cert },
|
{ "DNS_CERT", cmd_dns_cert, hlp_dns_cert },
|
||||||
{ "WKD_GET", cmd_wkd_get, hlp_wkd_get },
|
{ "WKD_GET", cmd_wkd_get, hlp_wkd_get },
|
||||||
{ "LDAPSERVER", cmd_ldapserver, hlp_ldapserver },
|
{ "LDAPSERVER", cmd_ldapserver, hlp_ldapserver },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user