summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dirmngr/certcache.c90
-rw-r--r--dirmngr/certcache.h14
-rw-r--r--dirmngr/dirmngr.h4
-rw-r--r--dirmngr/server.c104
-rw-r--r--dirmngr/validate.c12
5 files changed, 198 insertions, 26 deletions
diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c
index cd026c2d5..ff86f61b7 100644
--- a/dirmngr/certcache.c
+++ b/dirmngr/certcache.c
@@ -225,6 +225,7 @@ cert_compute_fpr (ksba_cert_t cert, unsigned char *digest)
}
+
/* Cleanup one slot. This releases all resourses but keeps the actual
slot in the cache marked for reuse. */
static void
@@ -1669,3 +1670,92 @@ find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert)
return err;
}
+
+
+
+/* Read a list of certificates in PEM format from stream FP and store
+ * them on success at R_CERTLIST. On error NULL is stored at R_CERT
+ * list and an error code returned. Note that even on success an
+ * empty list of certificates can be returned (i.e. NULL stored at
+ * R_CERTLIST) iff the input stream has no certificates. */
+gpg_error_t
+read_certlist_from_stream (certlist_t *r_certlist, estream_t fp)
+{
+ gpg_error_t err;
+ gnupg_ksba_io_t ioctx = NULL;
+ ksba_reader_t reader;
+ ksba_cert_t cert = NULL;
+ certlist_t certlist = NULL;
+ certlist_t cl, *cltail;
+
+ *r_certlist = NULL;
+
+ err = gnupg_ksba_create_reader (&ioctx,
+ (GNUPG_KSBA_IO_PEM | GNUPG_KSBA_IO_MULTIPEM),
+ fp, &reader);
+ if (err)
+ goto leave;
+
+ /* Loop to read all certificates from the stream. */
+ cltail = &certlist;
+ do
+ {
+ ksba_cert_release (cert);
+ cert = NULL;
+ err = ksba_cert_new (&cert);
+ if (!err)
+ err = ksba_cert_read_der (cert, reader);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_EOF)
+ err = 0;
+ goto leave;
+ }
+
+ /* Append the certificate to the list. We also store the
+ * fingerprint and check whether we have a cached certificate;
+ * in that case the cached certificate is put into the list to
+ * take advantage of a validation result which might be stored
+ * in the cached certificate. */
+ cl = xtrycalloc (1, sizeof *cl);
+ if (!cl)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ cert_compute_fpr (cert, cl->fpr);
+ cl->cert = get_cert_byfpr (cl->fpr);
+ if (!cl->cert)
+ {
+ cl->cert = cert;
+ cert = NULL;
+ }
+ *cltail = cl;
+ cltail = &cl->next;
+ ksba_reader_clear (reader, NULL, NULL);
+ }
+ while (!gnupg_ksba_reader_eof_seen (ioctx));
+
+ leave:
+ ksba_cert_release (cert);
+ gnupg_ksba_destroy_reader (ioctx);
+ if (err)
+ release_certlist (certlist);
+ else
+ *r_certlist = certlist;
+
+ return err;
+}
+
+
+/* Release the certificate list CL. */
+void
+release_certlist (certlist_t cl)
+{
+ while (cl)
+ {
+ certlist_t next = cl->next;
+ ksba_cert_release (cl->cert);
+ cl = next;
+ }
+}
diff --git a/dirmngr/certcache.h b/dirmngr/certcache.h
index ac93ee699..1f8670673 100644
--- a/dirmngr/certcache.h
+++ b/dirmngr/certcache.h
@@ -46,7 +46,6 @@ gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer);
* provided certificates are considered trusted. */
gpg_error_t is_trusted_cert (ksba_cert_t cert, int with_systrust);
-
/* Return a certificate object for the given fingerprint. FPR is
expected to be a 20 byte binary SHA-1 fingerprint. If no matching
certificate is available in the cache NULL is returned. The caller
@@ -100,5 +99,18 @@ gpg_error_t find_issuing_cert (ctrl_t ctrl,
+/* A simple list of certificates. */
+struct certlist_s
+{
+ struct certlist_s *next;
+ ksba_cert_t cert;
+ unsigned char fpr[20]; /* of the certificate. */
+};
+typedef struct certlist_s *certlist_t;
+
+gpg_error_t read_certlist_from_stream (certlist_t *r_certlist, estream_t fp);
+void release_certlist (certlist_t cl);
+
+
#endif /*CERTCACHE_H*/
diff --git a/dirmngr/dirmngr.h b/dirmngr/dirmngr.h
index 3724c007e..19d2303ca 100644
--- a/dirmngr/dirmngr.h
+++ b/dirmngr/dirmngr.h
@@ -155,7 +155,8 @@ struct
#define DBG_NETWORK (opt.debug & DBG_NETWORK_VALUE)
#define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE)
-/* A simple list of certificate references. */
+/* A simple list of certificate references. FIXME: Better use
+ certlist_t also for references (Store NULL at .cert) */
struct cert_ref_s
{
struct cert_ref_s *next;
@@ -163,6 +164,7 @@ struct cert_ref_s
};
typedef struct cert_ref_s *cert_ref_t;
+
/* Forward references; access only through server.c. */
struct server_local_s;
diff --git a/dirmngr/server.c b/dirmngr/server.c
index bc373f5b0..05ef439a1 100644
--- a/dirmngr/server.c
+++ b/dirmngr/server.c
@@ -60,6 +60,10 @@
Dirmngr was a system service and not a user service. */
#define MAX_CERT_LENGTH (16*1024)
+/* The limit for the CERTLIST inquiry. We allow for up to 20
+ * certificates but also take PEM encoding into account. */
+#define MAX_CERTLIST_LENGTH ((MAX_CERT_LENGTH * 20 * 4)/3)
+
/* The same goes for OpenPGP keyblocks, but here we need to allow for
much longer blocks; a 200k keyblock is not too unusual for keys
with a lot of signatures (e.g. 0x5b0358a2). 9C31503C6D866396 even
@@ -1729,7 +1733,7 @@ cmd_cachecert (assuan_context_t ctx, char *line)
static const char hlp_validate[] =
- "VALIDATE\n"
+ "VALIDATE [--systrust] [--tls]\n"
"\n"
"Validate a certificate using the certificate validation function\n"
"used internally by dirmngr. This command is only useful for\n"
@@ -1739,20 +1743,38 @@ static const char hlp_validate[] =
" INQUIRE TARGETCERT\n"
"\n"
"and the caller is expected to return the certificate for the\n"
- "request as a binary blob.";
+ "request as a binary blob. The option --tls modifies this by asking\n"
+ "for list of certificates with\n"
+ "\n"
+ " INQUIRE CERTLIST\n"
+ "\n"
+ "Here the first certificate is the target certificate, the remaining\n"
+ "certificates are suggested intermediary certificates. All certifciates\n"
+ "need to be PEM encoded.\n"
+ "\n"
+ "The option --systrust changes the behaviour to include the system\n"
+ "provided root certificates as trust anchors.";
static gpg_error_t
cmd_validate (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err;
ksba_cert_t cert = NULL;
+ certlist_t certlist = NULL;
unsigned char *value = NULL;
size_t valuelen;
+ int systrust_mode, tls_mode;
- (void)line;
+ systrust_mode = has_option (line, "--systrust");
+ tls_mode = has_option (line, "--tls");
+ line = skip_options (line);
- err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
- &value, &valuelen, MAX_CERT_LENGTH);
+ if (tls_mode)
+ err = assuan_inquire (ctrl->server_local->assuan_ctx, "CERTLIST",
+ &value, &valuelen, MAX_CERTLIST_LENGTH);
+ else
+ err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT",
+ &value, &valuelen, MAX_CERT_LENGTH);
if (err)
{
log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
@@ -1761,6 +1783,27 @@ cmd_validate (assuan_context_t ctx, char *line)
if (!valuelen) /* No data returned; return a comprehensible error. */
err = gpg_error (GPG_ERR_MISSING_CERT);
+ else if (tls_mode)
+ {
+ estream_t fp;
+
+ fp = es_fopenmem_init (0, "rb", value, valuelen);
+ if (!fp)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = read_certlist_from_stream (&certlist, fp);
+ es_fclose (fp);
+ if (!err && !certlist)
+ err = gpg_error (GPG_ERR_MISSING_CERT);
+ if (!err)
+ {
+ /* Extraxt the first certificate from the list. */
+ cert = certlist->cert;
+ ksba_cert_ref (cert);
+ }
+ }
+ }
else
{
err = ksba_cert_new (&cert);
@@ -1771,26 +1814,47 @@ cmd_validate (assuan_context_t ctx, char *line)
if(err)
goto leave;
- /* If we have this certificate already in our cache, use the cached
- * version for validation because this will take care of any cached
- * results. */
- {
- unsigned char fpr[20];
- ksba_cert_t tmpcert;
+ if (!tls_mode)
+ {
+ /* If we have this certificate already in our cache, use the
+ * cached version for validation because this will take care of
+ * any cached results. We don't need to do this in tls mode
+ * because this has already been done for certificate in a
+ * certlist_t. */
+ unsigned char fpr[20];
+ ksba_cert_t tmpcert;
+
+ cert_compute_fpr (cert, fpr);
+ tmpcert = get_cert_byfpr (fpr);
+ if (tmpcert)
+ {
+ ksba_cert_release (cert);
+ cert = tmpcert;
+ }
+ }
+
+ /* Quick hack to make verification work by inserting the supplied
+ * certs into the cache. */
+ if (tls_mode && certlist)
+ {
+ certlist_t cl;
+
+ for (cl = certlist->next; cl; cl = cl->next)
+ cache_cert (cl->cert);
+ }
- cert_compute_fpr (cert, fpr);
- tmpcert = get_cert_byfpr (fpr);
- if (tmpcert)
- {
- ksba_cert_release (cert);
- cert = tmpcert;
- }
- }
- err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CERT, NULL);
+ err = validate_cert_chain
+ (ctrl, cert, NULL,
+ tls_mode && systrust_mode ? VALIDATE_MODE_TLS_SYSTRUST :
+ tls_mode ? VALIDATE_MODE_TLS :
+ /**/ systrust_mode ? VALIDATE_MODE_CERT_SYSTRUST :
+ /**/ VALIDATE_MODE_CERT,
+ NULL);
leave:
ksba_cert_release (cert);
+ release_certlist (certlist);
return leave_cmd (ctx, err);
}
diff --git a/dirmngr/validate.c b/dirmngr/validate.c
index 5081ae0f7..8fb2df2c3 100644
--- a/dirmngr/validate.c
+++ b/dirmngr/validate.c
@@ -233,8 +233,8 @@ check_revocations (ctrl_t ctrl, chain_item_t chain)
int any_crl_too_old = 0;
chain_item_t ci;
- assert (ctrl->check_revocations_nest_level >= 0);
- assert (chain);
+ log_assert (ctrl->check_revocations_nest_level >= 0);
+ log_assert (chain);
if (ctrl->check_revocations_nest_level > 10)
{
@@ -551,7 +551,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
if (err)
goto leave; /* No. */
- err = is_trusted_cert (subject_cert, 0);
+ err = is_trusted_cert (subject_cert,
+ (mode == VALIDATE_MODE_CERT_SYSTRUST
+ || mode == VALIDATE_MODE_TLS_SYSTRUST));
if (!err)
; /* Yes we trust this cert. */
else if (gpg_err_code (err) == GPG_ERR_NOT_TRUSTED)
@@ -772,7 +774,9 @@ validate_cert_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
* our validity results to avoid double work. Far worse a
* catch-22 may happen for an improper setup hierarchy and we
* need a way to break up such a deadlock. */
- err = check_revocations (ctrl, chain);
+ if (mode != VALIDATE_MODE_TLS_SYSTRUST)
+ err = check_revocations (ctrl, chain);
+#warning fix the above
}
if (!err && opt.verbose)