2010-06-09 18:53:51 +02:00
|
|
|
|
/* certcache.c - Certificate caching
|
2017-02-16 18:58:27 +01:00
|
|
|
|
* Copyright (C) 2004, 2005, 2007, 2008, 2017 g10 Code GmbH
|
2010-06-09 18:53:51 +02:00
|
|
|
|
*
|
|
|
|
|
* This file is part of DirMngr.
|
|
|
|
|
*
|
|
|
|
|
* DirMngr 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 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* DirMngr 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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2010-06-09 18:53:51 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <dirent.h>
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
#include <npth.h>
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
|
|
|
|
#include "dirmngr.h"
|
|
|
|
|
#include "misc.h"
|
2017-02-16 18:58:27 +01:00
|
|
|
|
#include "../common/ksba-io-support.h"
|
2010-06-09 18:53:51 +02:00
|
|
|
|
#include "crlfetch.h"
|
|
|
|
|
#include "certcache.h"
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
#define MAX_NONPERM_CACHED_CERTS 1000
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
|
|
|
|
/* Constants used to classify search patterns. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
enum pattern_class
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
PATTERN_UNKNOWN = 0,
|
|
|
|
|
PATTERN_EMAIL,
|
|
|
|
|
PATTERN_EMAIL_SUBSTR,
|
|
|
|
|
PATTERN_FINGERPRINT16,
|
|
|
|
|
PATTERN_FINGERPRINT20,
|
|
|
|
|
PATTERN_SHORT_KEYID,
|
|
|
|
|
PATTERN_LONG_KEYID,
|
|
|
|
|
PATTERN_SUBJECT,
|
|
|
|
|
PATTERN_SERIALNO,
|
|
|
|
|
PATTERN_SERIALNO_ISSUER,
|
|
|
|
|
PATTERN_ISSUER,
|
|
|
|
|
PATTERN_SUBSTR
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* A certificate cache item. This consists of a the KSBA cert object
|
|
|
|
|
and some meta data for easier lookup. We use a hash table to keep
|
|
|
|
|
track of all items and use the (randomly distributed) first byte of
|
|
|
|
|
the fingerprint directly as the hash which makes it pretty easy. */
|
|
|
|
|
struct cert_item_s
|
|
|
|
|
{
|
|
|
|
|
struct cert_item_s *next; /* Next item with the same hash value. */
|
|
|
|
|
ksba_cert_t cert; /* The KSBA cert object or NULL is this is
|
|
|
|
|
not a valid item. */
|
|
|
|
|
unsigned char fpr[20]; /* The fingerprint of this object. */
|
|
|
|
|
char *issuer_dn; /* The malloced issuer DN. */
|
|
|
|
|
ksba_sexp_t sn; /* The malloced serial number */
|
|
|
|
|
char *subject_dn; /* The malloced subject DN - maybe NULL. */
|
2017-02-21 12:23:20 +01:00
|
|
|
|
|
|
|
|
|
/* If this field is set the certificate has been taken from some
|
|
|
|
|
* configuration and shall not be flushed from the cache. */
|
|
|
|
|
unsigned int permanent:1;
|
|
|
|
|
|
|
|
|
|
/* If this field is set the certificate is trusted. The actual
|
|
|
|
|
* value is a (possible) combination of CERTTRUST_CLASS values. */
|
|
|
|
|
unsigned int trustclasses:4;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
};
|
|
|
|
|
typedef struct cert_item_s *cert_item_t;
|
|
|
|
|
|
|
|
|
|
/* The actual cert cache consisting of 256 slots for items indexed by
|
|
|
|
|
the first byte of the fingerprint. */
|
|
|
|
|
static cert_item_t cert_cache[256];
|
|
|
|
|
|
2015-03-19 11:14:52 +01:00
|
|
|
|
/* This is the global cache_lock variable. In general locking is not
|
2010-06-09 18:53:51 +02:00
|
|
|
|
needed but it would take extra efforts to make sure that no
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
indirect use of npth functions is done, so we simply lock it
|
|
|
|
|
always. Note: We can't use static initialization, as that is not
|
|
|
|
|
available through w32-pth. */
|
|
|
|
|
static npth_rwlock_t cert_cache_lock;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
|
|
|
|
/* Flag to track whether the cache has been initialized. */
|
|
|
|
|
static int initialization_done;
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
/* Total number of non-permanent certificates. */
|
|
|
|
|
static unsigned int total_nonperm_certificates;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
2017-09-18 22:49:05 +02:00
|
|
|
|
/* For each cert class the corresponding bit is set if at least one
|
|
|
|
|
* certificate of that class is loaded permanetly. */
|
|
|
|
|
static unsigned int any_cert_of_class;
|
|
|
|
|
|
2017-02-16 21:25:58 +01:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
/* We load some functions dynamically. Provide typedefs for tehse
|
2017-04-28 03:06:33 +02:00
|
|
|
|
* functions. */
|
2017-02-16 21:25:58 +01:00
|
|
|
|
typedef HCERTSTORE (WINAPI *CERTOPENSYSTEMSTORE)
|
|
|
|
|
(HCRYPTPROV hProv, LPCSTR szSubsystemProtocol);
|
|
|
|
|
typedef PCCERT_CONTEXT (WINAPI *CERTENUMCERTIFICATESINSTORE)
|
|
|
|
|
(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
|
|
|
|
|
typedef WINBOOL (WINAPI *CERTCLOSESTORE)
|
|
|
|
|
(HCERTSTORE hCertStore,DWORD dwFlags);
|
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
|
|
|
|
/* Helper to do the cache locking. */
|
|
|
|
|
static void
|
|
|
|
|
init_cache_lock (void)
|
|
|
|
|
{
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = npth_rwlock_init (&cert_cache_lock, NULL);
|
|
|
|
|
if (err)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_fatal (_("can't initialize certificate cache lock: %s\n"),
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
strerror (err));
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
acquire_cache_read_lock (void)
|
|
|
|
|
{
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = npth_rwlock_rdlock (&cert_cache_lock);
|
|
|
|
|
if (err)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_fatal (_("can't acquire read lock on the certificate cache: %s\n"),
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
strerror (err));
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
acquire_cache_write_lock (void)
|
|
|
|
|
{
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = npth_rwlock_wrlock (&cert_cache_lock);
|
|
|
|
|
if (err)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_fatal (_("can't acquire write lock on the certificate cache: %s\n"),
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
strerror (err));
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
release_cache_lock (void)
|
|
|
|
|
{
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = npth_rwlock_unlock (&cert_cache_lock);
|
|
|
|
|
if (err)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_fatal (_("can't release lock on the certificate cache: %s\n"),
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
strerror (err));
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return false if both serial numbers match. Can't be used for
|
|
|
|
|
sorting. */
|
|
|
|
|
static int
|
|
|
|
|
compare_serialno (ksba_sexp_t serial1, ksba_sexp_t serial2 )
|
|
|
|
|
{
|
|
|
|
|
unsigned char *a = serial1;
|
|
|
|
|
unsigned char *b = serial2;
|
|
|
|
|
return cmp_simple_canon_sexp (a, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-03-19 11:14:52 +01:00
|
|
|
|
/* Return a malloced canonical S-Expression with the serial number
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* converted from the hex string HEXSN. Return NULL on memory
|
|
|
|
|
* error. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
ksba_sexp_t
|
2010-06-09 18:53:51 +02:00
|
|
|
|
hexsn_to_sexp (const char *hexsn)
|
|
|
|
|
{
|
|
|
|
|
char *buffer, *p;
|
|
|
|
|
size_t len;
|
|
|
|
|
char numbuf[40];
|
|
|
|
|
|
|
|
|
|
len = unhexify (NULL, hexsn);
|
|
|
|
|
snprintf (numbuf, sizeof numbuf, "(%u:", (unsigned int)len);
|
|
|
|
|
buffer = xtrymalloc (strlen (numbuf) + len + 2 );
|
|
|
|
|
if (!buffer)
|
|
|
|
|
return NULL;
|
|
|
|
|
p = stpcpy (buffer, numbuf);
|
|
|
|
|
len = unhexify (p, hexsn);
|
|
|
|
|
p[len] = ')';
|
2011-02-04 12:57:53 +01:00
|
|
|
|
p[len+1] = 0;
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Compute the fingerprint of the certificate CERT and put it into
|
|
|
|
|
the 20 bytes large buffer DIGEST. Return address of this buffer. */
|
|
|
|
|
unsigned char *
|
|
|
|
|
cert_compute_fpr (ksba_cert_t cert, unsigned char *digest)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_md_hd_t md;
|
|
|
|
|
|
|
|
|
|
err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
|
|
|
|
|
if (err)
|
|
|
|
|
log_fatal ("gcry_md_open failed: %s\n", gpg_strerror (err));
|
|
|
|
|
|
|
|
|
|
err = ksba_cert_hash (cert, 0, HASH_FNC, md);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("oops: ksba_cert_hash failed: %s\n", gpg_strerror (err));
|
|
|
|
|
memset (digest, 0xff, 20); /* Use a dummy value. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gcry_md_final (md);
|
|
|
|
|
memcpy (digest, gcry_md_read (md, GCRY_MD_SHA1), 20);
|
|
|
|
|
}
|
|
|
|
|
gcry_md_close (md);
|
|
|
|
|
return digest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-02-17 16:39:48 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* Cleanup one slot. This releases all resourses but keeps the actual
|
|
|
|
|
slot in the cache marked for reuse. */
|
|
|
|
|
static void
|
|
|
|
|
clean_cache_slot (cert_item_t ci)
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_t cert;
|
|
|
|
|
|
|
|
|
|
if (!ci->cert)
|
|
|
|
|
return; /* Already cleaned. */
|
|
|
|
|
|
|
|
|
|
ksba_free (ci->sn);
|
|
|
|
|
ci->sn = NULL;
|
|
|
|
|
ksba_free (ci->issuer_dn);
|
|
|
|
|
ci->issuer_dn = NULL;
|
|
|
|
|
ksba_free (ci->subject_dn);
|
|
|
|
|
ci->subject_dn = NULL;
|
|
|
|
|
cert = ci->cert;
|
|
|
|
|
ci->cert = NULL;
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
ci->permanent = 0;
|
|
|
|
|
ci->trustclasses = 0;
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Put the certificate CERT into the cache. It is assumed that the
|
2017-02-16 11:51:57 +01:00
|
|
|
|
* cache is locked while this function is called.
|
|
|
|
|
*
|
|
|
|
|
* FROM_CONFIG indicates that CERT is a permanent certificate and
|
|
|
|
|
* should stay in the cache. IS_TRUSTED requests that the trusted
|
2017-02-20 22:19:50 +01:00
|
|
|
|
* flag is set for the certificate; a value of 1 indicates the
|
2017-02-16 11:51:57 +01:00
|
|
|
|
* cert is trusted due to GnuPG mechanisms, a value of 2 indicates
|
|
|
|
|
* that it is trusted because it has been taken from the system's
|
|
|
|
|
* store of trusted certificates. If FPR_BUFFER is not NULL the
|
|
|
|
|
* fingerprint of the certificate will be stored there. FPR_BUFFER
|
|
|
|
|
* needs to point to a buffer of at least 20 bytes. The fingerprint
|
|
|
|
|
* will be stored on success or when the function returns
|
|
|
|
|
* GPG_ERR_DUP_VALUE. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
static gpg_error_t
|
2017-02-21 12:23:20 +01:00
|
|
|
|
put_cert (ksba_cert_t cert, int permanent, unsigned int trustclass,
|
|
|
|
|
void *fpr_buffer)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned char help_fpr_buffer[20], *fpr;
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
|
|
|
|
|
fpr = fpr_buffer? fpr_buffer : &help_fpr_buffer;
|
|
|
|
|
|
|
|
|
|
/* If we already reached the caching limit, drop a couple of certs
|
2017-02-16 11:51:57 +01:00
|
|
|
|
* from the cache. Our dropping strategy is simple: We keep a
|
|
|
|
|
* static index counter and use this to start looking for
|
|
|
|
|
* certificates, then we drop 5 percent of the oldest certificates
|
|
|
|
|
* starting at that index. For a large cache this is a fair way of
|
|
|
|
|
* removing items. An LRU strategy would be better of course.
|
|
|
|
|
* Because we append new entries to the head of the list and we want
|
|
|
|
|
* to remove old ones first, we need to do this from the tail. The
|
|
|
|
|
* implementation is not very efficient but compared to the long
|
|
|
|
|
* time it takes to retrieve a certificate from an external resource
|
|
|
|
|
* it seems to be reasonable. */
|
2017-02-21 12:23:20 +01:00
|
|
|
|
if (!permanent && total_nonperm_certificates >= MAX_NONPERM_CACHED_CERTS)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
static int idx;
|
|
|
|
|
cert_item_t ci_mark;
|
|
|
|
|
int i;
|
|
|
|
|
unsigned int drop_count;
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
drop_count = MAX_NONPERM_CACHED_CERTS / 20;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (drop_count < 2)
|
|
|
|
|
drop_count = 2;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_info (_("dropping %u certificates from the cache\n"), drop_count);
|
|
|
|
|
assert (idx < 256);
|
|
|
|
|
for (i=idx; drop_count; i = ((i+1)%256))
|
|
|
|
|
{
|
|
|
|
|
ci_mark = NULL;
|
|
|
|
|
for (ci = cert_cache[i]; ci; ci = ci->next)
|
2017-02-21 12:23:20 +01:00
|
|
|
|
if (ci->cert && !ci->permanent)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
ci_mark = ci;
|
|
|
|
|
if (ci_mark)
|
|
|
|
|
{
|
|
|
|
|
clean_cache_slot (ci_mark);
|
|
|
|
|
drop_count--;
|
2017-02-21 12:23:20 +01:00
|
|
|
|
total_nonperm_certificates--;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i==idx)
|
|
|
|
|
idx++;
|
|
|
|
|
else
|
|
|
|
|
idx = i;
|
|
|
|
|
idx %= 256;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cert_compute_fpr (cert, fpr);
|
|
|
|
|
for (ci=cert_cache[*fpr]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && !memcmp (ci->fpr, fpr, 20))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return gpg_error (GPG_ERR_DUP_VALUE);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* Try to reuse an existing entry. */
|
|
|
|
|
for (ci=cert_cache[*fpr]; ci; ci = ci->next)
|
|
|
|
|
if (!ci->cert)
|
|
|
|
|
break;
|
|
|
|
|
if (!ci)
|
|
|
|
|
{ /* No: Create a new entry. */
|
|
|
|
|
ci = xtrycalloc (1, sizeof *ci);
|
|
|
|
|
if (!ci)
|
|
|
|
|
return gpg_error_from_errno (errno);
|
|
|
|
|
ci->next = cert_cache[*fpr];
|
|
|
|
|
cert_cache[*fpr] = ci;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksba_cert_ref (cert);
|
|
|
|
|
ci->cert = cert;
|
|
|
|
|
memcpy (ci->fpr, fpr, 20);
|
|
|
|
|
ci->sn = ksba_cert_get_serial (cert);
|
|
|
|
|
ci->issuer_dn = ksba_cert_get_issuer (cert, 0);
|
|
|
|
|
if (!ci->issuer_dn || !ci->sn)
|
|
|
|
|
{
|
|
|
|
|
clean_cache_slot (ci);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_CERT_OBJ);
|
|
|
|
|
}
|
|
|
|
|
ci->subject_dn = ksba_cert_get_subject (cert, 0);
|
2017-02-21 12:23:20 +01:00
|
|
|
|
ci->permanent = !!permanent;
|
|
|
|
|
ci->trustclasses = trustclass;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
2017-09-18 22:49:05 +02:00
|
|
|
|
if (permanent)
|
|
|
|
|
any_cert_of_class |= trustclass;
|
|
|
|
|
else
|
2017-02-21 12:23:20 +01:00
|
|
|
|
total_nonperm_certificates++;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Load certificates from the directory DIRNAME. All certificates
|
|
|
|
|
matching the pattern "*.crt" or "*.der" are loaded. We assume that
|
2017-02-21 12:41:43 +01:00
|
|
|
|
certificates are DER encoded and not PEM encapsulated. The cache
|
2015-11-16 12:41:46 +01:00
|
|
|
|
should be in a locked state when calling this function. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
static gpg_error_t
|
2017-02-21 12:23:20 +01:00
|
|
|
|
load_certs_from_dir (const char *dirname, unsigned int trustclass)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
DIR *dir;
|
|
|
|
|
struct dirent *ep;
|
|
|
|
|
char *p;
|
|
|
|
|
size_t n;
|
2010-07-16 15:19:45 +02:00
|
|
|
|
estream_t fp;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
ksba_reader_t reader;
|
|
|
|
|
ksba_cert_t cert;
|
|
|
|
|
char *fname = NULL;
|
|
|
|
|
|
|
|
|
|
dir = opendir (dirname);
|
|
|
|
|
if (!dir)
|
|
|
|
|
{
|
|
|
|
|
return 0; /* We do not consider this a severe error. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ( (ep=readdir (dir)) )
|
|
|
|
|
{
|
|
|
|
|
p = ep->d_name;
|
|
|
|
|
if (*p == '.' || !*p)
|
|
|
|
|
continue; /* Skip any hidden files and invalid entries. */
|
|
|
|
|
n = strlen (p);
|
|
|
|
|
if ( n < 5 || (strcmp (p+n-4,".crt") && strcmp (p+n-4,".der")))
|
|
|
|
|
continue; /* Not the desired "*.crt" or "*.der" pattern. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
xfree (fname);
|
|
|
|
|
fname = make_filename (dirname, p, NULL);
|
2010-07-16 15:19:45 +02:00
|
|
|
|
fp = es_fopen (fname, "rb");
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("can't open '%s': %s\n"),
|
2010-06-09 18:53:51 +02:00
|
|
|
|
fname, strerror (errno));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2010-08-06 15:52:01 +02:00
|
|
|
|
|
|
|
|
|
err = create_estream_ksba_reader (&reader, fp);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
2010-07-16 15:19:45 +02:00
|
|
|
|
es_fclose (fp);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = ksba_cert_new (&cert);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = ksba_cert_read_der (cert, reader);
|
|
|
|
|
ksba_reader_release (reader);
|
2010-07-16 15:19:45 +02:00
|
|
|
|
es_fclose (fp);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("can't parse certificate '%s': %s\n"),
|
2010-06-09 18:53:51 +02:00
|
|
|
|
fname, gpg_strerror (err));
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
err = put_cert (cert, 1, trustclass, NULL);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_info (_("certificate '%s' already cached\n"), fname);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
else if (!err)
|
|
|
|
|
{
|
2018-04-25 09:43:18 +02:00
|
|
|
|
if ((trustclass & CERTTRUST_CLASS_CONFIG))
|
|
|
|
|
http_register_cfg_ca (fname);
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
if (trustclass)
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_info (_("trusted certificate '%s' loaded\n"), fname);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_info (_("certificate '%s' loaded\n"), fname);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (opt.verbose)
|
|
|
|
|
{
|
|
|
|
|
p = get_fingerprint_hexstring_colon (cert);
|
|
|
|
|
log_info (_(" SHA1 fingerprint = %s\n"), p);
|
|
|
|
|
xfree (p);
|
|
|
|
|
|
|
|
|
|
cert_log_name (_(" issuer ="), cert);
|
|
|
|
|
cert_log_subject (_(" subject ="), cert);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("error loading certificate '%s': %s\n"),
|
2010-06-09 18:53:51 +02:00
|
|
|
|
fname, gpg_strerror (err));
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (fname);
|
|
|
|
|
closedir (dir);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-02-21 12:41:43 +01:00
|
|
|
|
/* Load certificates from FILE. The certificates are expected to be
|
2017-02-16 18:58:27 +01:00
|
|
|
|
* PEM encoded so that it is possible to load several certificates.
|
2017-02-21 12:41:43 +01:00
|
|
|
|
* TRUSTCLASSES is used to mark the certificates as trusted. The
|
|
|
|
|
* cache should be in a locked state when calling this function.
|
|
|
|
|
* NO_ERROR repalces an error message when FNAME was not found by an
|
|
|
|
|
* information message. */
|
2017-02-16 18:58:27 +01:00
|
|
|
|
static gpg_error_t
|
2017-02-21 12:41:43 +01:00
|
|
|
|
load_certs_from_file (const char *fname, unsigned int trustclasses,
|
|
|
|
|
int no_error)
|
2017-02-16 18:58:27 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
estream_t fp = NULL;
|
|
|
|
|
gnupg_ksba_io_t ioctx = NULL;
|
|
|
|
|
ksba_reader_t reader;
|
|
|
|
|
ksba_cert_t cert = NULL;
|
|
|
|
|
|
|
|
|
|
fp = es_fopen (fname, "rb");
|
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
2017-02-21 12:41:43 +01:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_ENONET && no_error)
|
|
|
|
|
log_info (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
|
|
|
|
|
else
|
|
|
|
|
log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err));
|
2017-02-16 18:58:27 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = gnupg_ksba_create_reader (&ioctx,
|
2017-02-21 13:57:24 +01:00
|
|
|
|
(GNUPG_KSBA_IO_AUTODETECT
|
|
|
|
|
| GNUPG_KSBA_IO_MULTIPEM),
|
2017-02-16 18:58:27 +01:00
|
|
|
|
fp, &reader);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't create reader: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Loop to read all certificates from the file. */
|
|
|
|
|
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;
|
|
|
|
|
else
|
|
|
|
|
log_error (_("can't parse certificate '%s': %s\n"),
|
|
|
|
|
fname, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 12:41:43 +01:00
|
|
|
|
err = put_cert (cert, 1, trustclasses, NULL);
|
2017-02-16 18:58:27 +01:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
|
|
|
|
|
log_info (_("certificate '%s' already cached\n"), fname);
|
|
|
|
|
else if (err)
|
|
|
|
|
log_error (_("error loading certificate '%s': %s\n"),
|
|
|
|
|
fname, gpg_strerror (err));
|
|
|
|
|
else if (opt.verbose > 1)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
log_info (_("trusted certificate '%s' loaded\n"), fname);
|
|
|
|
|
p = get_fingerprint_hexstring_colon (cert);
|
|
|
|
|
log_info (_(" SHA1 fingerprint = %s\n"), p);
|
|
|
|
|
xfree (p);
|
|
|
|
|
|
|
|
|
|
cert_log_name (_(" issuer ="), cert);
|
|
|
|
|
cert_log_subject (_(" subject ="), cert);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksba_reader_clear (reader, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
while (!gnupg_ksba_reader_eof_seen (ioctx));
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
gnupg_ksba_destroy_reader (ioctx);
|
|
|
|
|
es_fclose (fp);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2017-02-21 12:41:43 +01:00
|
|
|
|
|
2017-02-16 21:25:58 +01:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
/* Load all certificates from the Windows store named STORENAME. All
|
|
|
|
|
* certificates are considered to be system provided trusted
|
|
|
|
|
* certificates. The cache should be in a locked state when calling
|
|
|
|
|
* this function. */
|
|
|
|
|
static void
|
|
|
|
|
load_certs_from_w32_store (const char *storename)
|
|
|
|
|
{
|
|
|
|
|
static int init_done;
|
|
|
|
|
static CERTOPENSYSTEMSTORE pCertOpenSystemStore;
|
|
|
|
|
static CERTENUMCERTIFICATESINSTORE pCertEnumCertificatesInStore;
|
|
|
|
|
static CERTCLOSESTORE pCertCloseStore;
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
HCERTSTORE w32store;
|
|
|
|
|
const CERT_CONTEXT *w32cert;
|
|
|
|
|
ksba_cert_t cert = NULL;
|
|
|
|
|
unsigned int count = 0;
|
|
|
|
|
|
|
|
|
|
/* Initialize on the first use. */
|
|
|
|
|
if (!init_done)
|
|
|
|
|
{
|
|
|
|
|
static HANDLE hCrypt32;
|
|
|
|
|
|
|
|
|
|
init_done = 1;
|
|
|
|
|
|
|
|
|
|
hCrypt32 = LoadLibrary ("Crypt32.dll");
|
|
|
|
|
if (!hCrypt32)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't load Crypt32.dll: %s\n", w32_strerror (-1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pCertOpenSystemStore = (CERTOPENSYSTEMSTORE)
|
|
|
|
|
GetProcAddress (hCrypt32, "CertOpenSystemStoreA");
|
|
|
|
|
pCertEnumCertificatesInStore = (CERTENUMCERTIFICATESINSTORE)
|
|
|
|
|
GetProcAddress (hCrypt32, "CertEnumCertificatesInStore");
|
|
|
|
|
pCertCloseStore = (CERTCLOSESTORE)
|
|
|
|
|
GetProcAddress (hCrypt32, "CertCloseStore");
|
|
|
|
|
if ( !pCertOpenSystemStore
|
|
|
|
|
|| !pCertEnumCertificatesInStore
|
|
|
|
|
|| !pCertCloseStore)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't load crypt32.dll: %s\n", "missing function");
|
|
|
|
|
pCertOpenSystemStore = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pCertOpenSystemStore)
|
|
|
|
|
return; /* Not initialized. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w32store = pCertOpenSystemStore (0, storename);
|
|
|
|
|
if (!w32store)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't open certificate store '%s': %s\n",
|
|
|
|
|
storename, w32_strerror (-1));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w32cert = NULL;
|
|
|
|
|
while ((w32cert = pCertEnumCertificatesInStore (w32store, w32cert)))
|
|
|
|
|
{
|
|
|
|
|
if (w32cert->dwCertEncodingType == X509_ASN_ENCODING)
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
err = ksba_cert_new (&cert);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = ksba_cert_init_from_mem (cert,
|
|
|
|
|
w32cert->pbCertEncoded,
|
|
|
|
|
w32cert->cbCertEncoded);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("can't parse certificate '%s': %s\n"),
|
|
|
|
|
storename, gpg_strerror (err));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
err = put_cert (cert, 1, CERTTRUST_CLASS_SYSTEM, NULL);
|
2017-02-16 21:25:58 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
count++;
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
|
2017-04-03 20:34:13 +02:00
|
|
|
|
{
|
|
|
|
|
if (DBG_X509)
|
|
|
|
|
log_debug (_("certificate '%s' already cached\n"), storename);
|
|
|
|
|
}
|
2017-02-16 21:25:58 +01:00
|
|
|
|
else if (err)
|
|
|
|
|
log_error (_("error loading certificate '%s': %s\n"),
|
|
|
|
|
storename, gpg_strerror (err));
|
|
|
|
|
else if (opt.verbose > 1)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
log_info (_("trusted certificate '%s' loaded\n"), storename);
|
|
|
|
|
p = get_fingerprint_hexstring_colon (cert);
|
|
|
|
|
log_info (_(" SHA1 fingerprint = %s\n"), p);
|
|
|
|
|
xfree (p);
|
|
|
|
|
|
|
|
|
|
cert_log_name (_(" issuer ="), cert);
|
|
|
|
|
cert_log_subject (_(" subject ="), cert);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
pCertCloseStore (w32store, 0);
|
|
|
|
|
|
|
|
|
|
if (DBG_X509)
|
|
|
|
|
log_debug ("number of certs loaded from store '%s': %u\n",
|
|
|
|
|
storename, count);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|
2017-02-16 18:58:27 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Load the trusted certificates provided by the system. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
load_certs_from_system (void)
|
|
|
|
|
{
|
2017-02-16 21:25:58 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
|
|
|
|
|
load_certs_from_w32_store ("ROOT");
|
|
|
|
|
load_certs_from_w32_store ("CA");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
#else /*!HAVE_W32_SYSTEM*/
|
|
|
|
|
|
2017-02-16 18:58:27 +01:00
|
|
|
|
/* A list of certificate bundles to try. */
|
|
|
|
|
static struct {
|
|
|
|
|
const char *name;
|
|
|
|
|
} table[] = {
|
|
|
|
|
#ifdef DEFAULT_TRUST_STORE_FILE
|
|
|
|
|
{ DEFAULT_TRUST_STORE_FILE }
|
|
|
|
|
#else
|
|
|
|
|
{ "/etc/ssl/ca-bundle.pem" },
|
|
|
|
|
{ "/etc/ssl/certs/ca-certificates.crt" },
|
|
|
|
|
{ "/etc/pki/tls/cert.pem" },
|
|
|
|
|
{ "/usr/local/share/certs/ca-root-nss.crt" },
|
|
|
|
|
{ "/etc/ssl/cert.pem" }
|
|
|
|
|
#endif /*!DEFAULT_TRUST_STORE_FILE*/
|
|
|
|
|
};
|
|
|
|
|
int idx;
|
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
|
|
|
|
|
for (idx=0; idx < DIM (table); idx++)
|
|
|
|
|
if (!access (table[idx].name, F_OK))
|
|
|
|
|
{
|
|
|
|
|
/* Take the first available bundle. */
|
2017-02-21 12:41:43 +01:00
|
|
|
|
err = load_certs_from_file (table[idx].name, CERTTRUST_CLASS_SYSTEM, 0);
|
2017-02-16 18:58:27 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
2017-02-16 21:25:58 +01:00
|
|
|
|
#endif /*!HAVE_W32_SYSTEM*/
|
2017-02-16 18:58:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* Initialize the certificate cache if not yet done. */
|
|
|
|
|
void
|
2017-02-21 13:57:24 +01:00
|
|
|
|
cert_cache_init (strlist_t hkp_cacerts)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
2017-02-21 12:41:43 +01:00
|
|
|
|
char *fname;
|
2017-02-21 13:57:24 +01:00
|
|
|
|
strlist_t sl;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (initialization_done)
|
|
|
|
|
return;
|
|
|
|
|
init_cache_lock ();
|
|
|
|
|
acquire_cache_write_lock ();
|
|
|
|
|
|
2017-02-16 18:58:27 +01:00
|
|
|
|
load_certs_from_system ();
|
|
|
|
|
|
2017-02-21 12:41:43 +01:00
|
|
|
|
fname = make_filename_try (gnupg_sysconfdir (), "trusted-certs", NULL);
|
|
|
|
|
if (fname)
|
|
|
|
|
load_certs_from_dir (fname, CERTTRUST_CLASS_CONFIG);
|
|
|
|
|
xfree (fname);
|
|
|
|
|
|
|
|
|
|
fname = make_filename_try (gnupg_sysconfdir (), "extra-certs", NULL);
|
|
|
|
|
if (fname)
|
|
|
|
|
load_certs_from_dir (fname, 0);
|
|
|
|
|
xfree (fname);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
2017-02-21 12:41:43 +01:00
|
|
|
|
fname = make_filename_try (gnupg_datadir (),
|
|
|
|
|
"sks-keyservers.netCA.pem", NULL);
|
|
|
|
|
if (fname)
|
|
|
|
|
load_certs_from_file (fname, CERTTRUST_CLASS_HKPSPOOL, 1);
|
|
|
|
|
xfree (fname);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
|
2017-02-21 14:55:04 +01:00
|
|
|
|
for (sl = hkp_cacerts; sl; sl = sl->next)
|
|
|
|
|
load_certs_from_file (sl->d, CERTTRUST_CLASS_HKP, 0);
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
initialization_done = 1;
|
|
|
|
|
release_cache_lock ();
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
cert_cache_print_stats ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Deinitialize the certificate cache. With FULL set to true even the
|
|
|
|
|
unused certificate slots are released. */
|
|
|
|
|
void
|
|
|
|
|
cert_cache_deinit (int full)
|
|
|
|
|
{
|
|
|
|
|
cert_item_t ci, ci2;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (!initialization_done)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
acquire_cache_write_lock ();
|
|
|
|
|
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci->next)
|
|
|
|
|
clean_cache_slot (ci);
|
|
|
|
|
|
|
|
|
|
if (full)
|
|
|
|
|
{
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci2)
|
|
|
|
|
{
|
|
|
|
|
ci2 = ci->next;
|
|
|
|
|
xfree (ci);
|
|
|
|
|
}
|
|
|
|
|
cert_cache[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-25 09:43:18 +02:00
|
|
|
|
http_register_cfg_ca (NULL);
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
total_nonperm_certificates = 0;
|
2017-09-18 22:49:05 +02:00
|
|
|
|
any_cert_of_class = 0;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
initialization_done = 0;
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print some statistics to the log file. */
|
|
|
|
|
void
|
|
|
|
|
cert_cache_print_stats (void)
|
|
|
|
|
{
|
2017-02-21 12:23:20 +01:00
|
|
|
|
cert_item_t ci;
|
|
|
|
|
int idx;
|
|
|
|
|
unsigned int n_nonperm = 0;
|
|
|
|
|
unsigned int n_permanent = 0;
|
|
|
|
|
unsigned int n_trusted = 0;
|
|
|
|
|
unsigned int n_trustclass_system = 0;
|
|
|
|
|
unsigned int n_trustclass_config = 0;
|
|
|
|
|
unsigned int n_trustclass_hkp = 0;
|
|
|
|
|
unsigned int n_trustclass_hkpspool = 0;
|
|
|
|
|
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (idx = 0; idx < 256; idx++)
|
|
|
|
|
for (ci=cert_cache[idx]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert)
|
|
|
|
|
{
|
|
|
|
|
if (ci->permanent)
|
|
|
|
|
n_permanent++;
|
|
|
|
|
else
|
|
|
|
|
n_nonperm++;
|
|
|
|
|
if (ci->trustclasses)
|
|
|
|
|
{
|
|
|
|
|
n_trusted++;
|
|
|
|
|
if ((ci->trustclasses & CERTTRUST_CLASS_SYSTEM))
|
|
|
|
|
n_trustclass_system++;
|
|
|
|
|
if ((ci->trustclasses & CERTTRUST_CLASS_CONFIG))
|
|
|
|
|
n_trustclass_config++;
|
|
|
|
|
if ((ci->trustclasses & CERTTRUST_CLASS_HKP))
|
|
|
|
|
n_trustclass_hkp++;
|
|
|
|
|
if ((ci->trustclasses & CERTTRUST_CLASS_HKPSPOOL))
|
|
|
|
|
n_trustclass_hkpspool++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_info (_("permanently loaded certificates: %u\n"),
|
2017-02-21 12:23:20 +01:00
|
|
|
|
n_permanent);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
log_info (_(" runtime cached certificates: %u\n"),
|
2017-02-21 12:23:20 +01:00
|
|
|
|
n_nonperm);
|
|
|
|
|
log_info (_(" trusted certificates: %u (%u,%u,%u,%u)\n"),
|
|
|
|
|
n_trusted,
|
|
|
|
|
n_trustclass_system,
|
|
|
|
|
n_trustclass_config,
|
|
|
|
|
n_trustclass_hkp,
|
|
|
|
|
n_trustclass_hkpspool);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-09-18 22:49:05 +02:00
|
|
|
|
/* Return true if any cert of a class in MASK is permanently
|
|
|
|
|
* loaded. */
|
|
|
|
|
int
|
|
|
|
|
cert_cache_any_in_class (unsigned int mask)
|
|
|
|
|
{
|
|
|
|
|
return !!(any_cert_of_class & mask);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* Put CERT into the certificate cache. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
cache_cert (ksba_cert_t cert)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
acquire_cache_write_lock ();
|
|
|
|
|
err = put_cert (cert, 0, 0, NULL);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
|
|
|
|
|
log_info (_("certificate already cached\n"));
|
|
|
|
|
else if (!err)
|
|
|
|
|
log_info (_("certificate cached\n"));
|
|
|
|
|
else
|
|
|
|
|
log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Put CERT into the certificate cache and store the fingerprint of
|
|
|
|
|
the certificate into FPR_BUFFER. If the certificate is already in
|
|
|
|
|
the cache do not print a warning; just store the
|
|
|
|
|
fingerprint. FPR_BUFFER needs to be at least 20 bytes. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
cache_cert_silent (ksba_cert_t cert, void *fpr_buffer)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
acquire_cache_write_lock ();
|
|
|
|
|
err = put_cert (cert, 0, 0, fpr_buffer);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_DUP_VALUE)
|
|
|
|
|
err = 0;
|
|
|
|
|
if (err)
|
|
|
|
|
log_error (_("error caching certificate: %s\n"), gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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
|
|
|
|
|
must release a returned certificate. Note that although we are
|
|
|
|
|
using reference counting the caller should not just compare the
|
|
|
|
|
pointers to check for identical certificates. */
|
|
|
|
|
ksba_cert_t
|
|
|
|
|
get_cert_byfpr (const unsigned char *fpr)
|
|
|
|
|
{
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (ci=cert_cache[*fpr]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && !memcmp (ci->fpr, fpr, 20))
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_ref (ci->cert);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return ci->cert;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return a certificate object for the given fingerprint. STRING is
|
|
|
|
|
expected to be a SHA-1 fingerprint in standard hex notation with or
|
|
|
|
|
without colons. If no matching certificate is available in the
|
|
|
|
|
cache NULL is returned. The caller must release a returned
|
|
|
|
|
certificate. Note that although we are using reference counting
|
|
|
|
|
the caller should not just compare the pointers to check for
|
|
|
|
|
identical certificates. */
|
|
|
|
|
ksba_cert_t
|
|
|
|
|
get_cert_byhexfpr (const char *string)
|
|
|
|
|
{
|
|
|
|
|
unsigned char fpr[20];
|
|
|
|
|
const char *s;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (strchr (string, ':'))
|
|
|
|
|
{
|
|
|
|
|
for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1);)
|
|
|
|
|
{
|
|
|
|
|
if (s[2] && s[2] != ':')
|
|
|
|
|
break; /* Invalid string. */
|
|
|
|
|
fpr[i++] = xtoi_2 (s);
|
|
|
|
|
s += 2;
|
|
|
|
|
if (i!= 20 && *s == ':')
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1); s+=2 )
|
|
|
|
|
fpr[i++] = xtoi_2 (s);
|
|
|
|
|
}
|
|
|
|
|
if (i!=20 || *s)
|
|
|
|
|
{
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error (_("invalid SHA1 fingerprint string '%s'\n"), string);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return get_cert_byfpr (fpr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the certificate matching ISSUER_DN and SERIALNO. */
|
|
|
|
|
ksba_cert_t
|
|
|
|
|
get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno)
|
|
|
|
|
{
|
|
|
|
|
/* Simple and inefficient implementation. fixme! */
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn)
|
|
|
|
|
&& !compare_serialno (ci->sn, serialno))
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_ref (ci->cert);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return ci->cert;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the certificate matching ISSUER_DN. SEQ should initially be
|
|
|
|
|
set to 0 and bumped up to get the next issuer with that DN. */
|
|
|
|
|
ksba_cert_t
|
|
|
|
|
get_cert_byissuer (const char *issuer_dn, unsigned int seq)
|
|
|
|
|
{
|
|
|
|
|
/* Simple and very inefficient implementation and API. fixme! */
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn))
|
|
|
|
|
if (!seq--)
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_ref (ci->cert);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return ci->cert;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the certificate matching SUBJECT_DN. SEQ should initially be
|
|
|
|
|
set to 0 and bumped up to get the next subject with that DN. */
|
|
|
|
|
ksba_cert_t
|
|
|
|
|
get_cert_bysubject (const char *subject_dn, unsigned int seq)
|
|
|
|
|
{
|
|
|
|
|
/* Simple and very inefficient implementation and API. fixme! */
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
int i;
|
|
|
|
|
|
2011-02-23 10:51:36 +01:00
|
|
|
|
if (!subject_dn)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
{
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && ci->subject_dn
|
|
|
|
|
&& !strcmp (ci->subject_dn, subject_dn))
|
|
|
|
|
if (!seq--)
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_ref (ci->cert);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return ci->cert;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-20 22:19:50 +01:00
|
|
|
|
/* Return a value describing the class of PATTERN. The offset of
|
2010-06-09 18:53:51 +02:00
|
|
|
|
the actual string to be used for the comparison is stored at
|
|
|
|
|
R_OFFSET. The offset of the serialnumer is stored at R_SN_OFFSET. */
|
|
|
|
|
static enum pattern_class
|
|
|
|
|
classify_pattern (const char *pattern, size_t *r_offset, size_t *r_sn_offset)
|
|
|
|
|
{
|
2011-11-24 15:48:24 +01:00
|
|
|
|
enum pattern_class result;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
const char *s;
|
|
|
|
|
int hexprefix = 0;
|
|
|
|
|
int hexlength;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
*r_offset = *r_sn_offset = 0;
|
|
|
|
|
|
|
|
|
|
/* Skip leading spaces. */
|
|
|
|
|
for(s = pattern; *s && spacep (s); s++ )
|
|
|
|
|
;
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
switch (*s)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
case 0: /* Empty string is an error. */
|
|
|
|
|
result = PATTERN_UNKNOWN;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '.': /* An email address, compare from end. */
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Not implemented. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '<': /* An email address. */
|
|
|
|
|
result = PATTERN_EMAIL;
|
|
|
|
|
s++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '@': /* Part of an email address. */
|
|
|
|
|
result = PATTERN_EMAIL_SUBSTR;
|
|
|
|
|
s++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '=': /* Exact compare. */
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Does not make sense for X.509. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '*': /* Case insensitive substring search. */
|
2011-11-24 15:48:24 +01:00
|
|
|
|
result = PATTERN_SUBSTR;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
s++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '+': /* Compare individual words. */
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Not implemented. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '/': /* Subject's DN. */
|
|
|
|
|
s++;
|
|
|
|
|
if (!*s || spacep (s))
|
|
|
|
|
result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */
|
|
|
|
|
else
|
|
|
|
|
result = PATTERN_SUBJECT;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '#': /* Serial number or issuer DN. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
const char *si;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
s++;
|
|
|
|
|
if ( *s == '/')
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* An issuer's DN is indicated by "#/" */
|
|
|
|
|
s++;
|
|
|
|
|
if (!*s || spacep (s))
|
|
|
|
|
result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */
|
|
|
|
|
else
|
|
|
|
|
result = PATTERN_ISSUER;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{ /* Serialnumber + optional issuer ID. */
|
|
|
|
|
for (si=s; *si && *si != '/'; si++)
|
|
|
|
|
if (!strchr("01234567890abcdefABCDEF", *si))
|
|
|
|
|
break;
|
|
|
|
|
if (*si && *si != '/')
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Invalid digit in serial number. */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
*r_sn_offset = s - pattern;
|
|
|
|
|
if (!*si)
|
|
|
|
|
result = PATTERN_SERIALNO;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
s = si+1;
|
|
|
|
|
if (!*s || spacep (s))
|
|
|
|
|
result = PATTERN_UNKNOWN; /* No DN or prefixed
|
|
|
|
|
with a space. */
|
|
|
|
|
else
|
|
|
|
|
result = PATTERN_SERIALNO_ISSUER;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ':': /* Unified fingerprint. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
const char *se, *si;
|
|
|
|
|
int i;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
se = strchr (++s, ':');
|
|
|
|
|
if (!se)
|
|
|
|
|
result = PATTERN_UNKNOWN;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (i=0, si=s; si < se; si++, i++ )
|
|
|
|
|
if (!strchr("01234567890abcdefABCDEF", *si))
|
|
|
|
|
break;
|
|
|
|
|
if ( si < se )
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Invalid digit. */
|
|
|
|
|
else if (i == 32)
|
|
|
|
|
result = PATTERN_FINGERPRINT16;
|
|
|
|
|
else if (i == 40)
|
|
|
|
|
result = PATTERN_FINGERPRINT20;
|
|
|
|
|
else
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Invalid length for a fingerprint. */
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
}
|
2010-06-09 18:53:51 +02:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '&': /* Keygrip. */
|
|
|
|
|
result = PATTERN_UNKNOWN; /* Not implemented. */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (s[0] == '0' && s[1] == 'x')
|
|
|
|
|
{
|
|
|
|
|
hexprefix = 1;
|
|
|
|
|
s += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hexlength = strspn(s, "0123456789abcdefABCDEF");
|
|
|
|
|
|
|
|
|
|
/* Check if a hexadecimal number is terminated by EOS or blank. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (hexlength && s[hexlength] && !spacep (s+hexlength))
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
/* If the "0x" prefix is used a correct termination is required. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (hexprefix)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
2011-02-04 12:57:53 +01:00
|
|
|
|
result = PATTERN_UNKNOWN;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
break; /* switch */
|
|
|
|
|
}
|
|
|
|
|
hexlength = 0; /* Not a hex number. */
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0'))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (hexlength == 9)
|
|
|
|
|
s++;
|
|
|
|
|
result = PATTERN_SHORT_KEYID;
|
|
|
|
|
}
|
|
|
|
|
else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0'))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (hexlength == 17)
|
|
|
|
|
s++;
|
|
|
|
|
result = PATTERN_LONG_KEYID;
|
|
|
|
|
}
|
|
|
|
|
else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0'))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (hexlength == 33)
|
|
|
|
|
s++;
|
|
|
|
|
result = PATTERN_FINGERPRINT16;
|
|
|
|
|
}
|
|
|
|
|
else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0'))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (hexlength == 41)
|
|
|
|
|
s++;
|
|
|
|
|
result = PATTERN_FINGERPRINT20;
|
|
|
|
|
}
|
|
|
|
|
else if (!hexprefix)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* The fingerprints used with X.509 are often delimited by
|
|
|
|
|
colons, so we try to single this case out. */
|
|
|
|
|
result = PATTERN_UNKNOWN;
|
|
|
|
|
hexlength = strspn (s, ":0123456789abcdefABCDEF");
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
int i, c;
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
for (i=0; i < 20; i++, s += 3)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
c = hextobyte(s);
|
|
|
|
|
if (c == -1 || (i < 19 && s[2] != ':'))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (i == 20)
|
|
|
|
|
result = PATTERN_FINGERPRINT20;
|
|
|
|
|
}
|
|
|
|
|
if (result == PATTERN_UNKNOWN) /* Default to substring match. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2010-06-09 18:53:51 +02:00
|
|
|
|
result = PATTERN_SUBSTR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else /* A hex number with a prefix but with a wrong length. */
|
|
|
|
|
result = PATTERN_UNKNOWN;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (result != PATTERN_UNKNOWN)
|
|
|
|
|
*r_offset = s - pattern;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Given PATTERN, which is a string as used by GnuPG to specify a
|
|
|
|
|
certificate, return all matching certificates by calling the
|
|
|
|
|
supplied function RETFNC. */
|
|
|
|
|
gpg_error_t
|
2011-02-04 12:57:53 +01:00
|
|
|
|
get_certs_bypattern (const char *pattern,
|
2010-06-09 18:53:51 +02:00
|
|
|
|
gpg_error_t (*retfnc)(void*,ksba_cert_t),
|
|
|
|
|
void *retfnc_data)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err = GPG_ERR_BUG;
|
|
|
|
|
enum pattern_class class;
|
|
|
|
|
size_t offset, sn_offset;
|
|
|
|
|
const char *hexserialno;
|
|
|
|
|
ksba_sexp_t serialno = NULL;
|
|
|
|
|
ksba_cert_t cert = NULL;
|
|
|
|
|
unsigned int seq;
|
|
|
|
|
|
|
|
|
|
if (!pattern || !retfnc)
|
|
|
|
|
return gpg_error (GPG_ERR_INV_ARG);
|
|
|
|
|
|
|
|
|
|
class = classify_pattern (pattern, &offset, &sn_offset);
|
|
|
|
|
hexserialno = pattern + sn_offset;
|
|
|
|
|
pattern += offset;
|
|
|
|
|
switch (class)
|
|
|
|
|
{
|
2011-02-04 12:57:53 +01:00
|
|
|
|
case PATTERN_UNKNOWN:
|
2010-06-09 18:53:51 +02:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_NAME);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PATTERN_FINGERPRINT20:
|
|
|
|
|
cert = get_cert_byhexfpr (pattern);
|
|
|
|
|
err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PATTERN_SERIALNO_ISSUER:
|
|
|
|
|
serialno = hexsn_to_sexp (hexserialno);
|
|
|
|
|
if (!serialno)
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cert = get_cert_bysn (pattern, serialno);
|
|
|
|
|
err = cert? 0 : gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PATTERN_ISSUER:
|
|
|
|
|
for (seq=0,err=0; !err && (cert = get_cert_byissuer (pattern, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
err = retfnc (retfnc_data, cert);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (!err && !seq)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PATTERN_SUBJECT:
|
|
|
|
|
for (seq=0,err=0; !err && (cert = get_cert_bysubject (pattern, seq));seq++)
|
|
|
|
|
{
|
|
|
|
|
err = retfnc (retfnc_data, cert);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (!err && !seq)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
break;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
case PATTERN_EMAIL:
|
|
|
|
|
case PATTERN_EMAIL_SUBSTR:
|
|
|
|
|
case PATTERN_FINGERPRINT16:
|
|
|
|
|
case PATTERN_SHORT_KEYID:
|
|
|
|
|
case PATTERN_LONG_KEYID:
|
|
|
|
|
case PATTERN_SUBSTR:
|
|
|
|
|
case PATTERN_SERIALNO:
|
|
|
|
|
/* Not supported. */
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_NAME);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!err && cert)
|
|
|
|
|
err = retfnc (retfnc_data, cert);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
xfree (serialno);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the certificate matching ISSUER_DN and SERIALNO; if it is
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* not already in the cache, try to find it from other resources. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
ksba_cert_t
|
|
|
|
|
find_cert_bysn (ctrl_t ctrl, const char *issuer_dn, ksba_sexp_t serialno)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
ksba_cert_t cert;
|
|
|
|
|
cert_fetch_context_t context = NULL;
|
|
|
|
|
char *hexsn, *buf;
|
|
|
|
|
|
|
|
|
|
/* First check whether it has already been cached. */
|
|
|
|
|
cert = get_cert_bysn (issuer_dn, serialno);
|
|
|
|
|
if (cert)
|
|
|
|
|
return cert;
|
|
|
|
|
|
|
|
|
|
/* Ask back to the service requester to return the certificate.
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* This is because we can assume that he already used the
|
|
|
|
|
* certificate while checking for the CRL. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
hexsn = serial_hex (serialno);
|
|
|
|
|
if (!hexsn)
|
|
|
|
|
{
|
|
|
|
|
log_error ("serial_hex() failed\n");
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2017-02-16 10:19:59 +01:00
|
|
|
|
buf = strconcat ("#", hexsn, "/", issuer_dn, NULL);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!buf)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't allocate enough memory: %s\n", strerror (errno));
|
|
|
|
|
xfree (hexsn);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
xfree (hexsn);
|
2017-02-16 10:19:59 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
cert = get_cert_local (ctrl, buf);
|
|
|
|
|
xfree (buf);
|
|
|
|
|
if (cert)
|
|
|
|
|
{
|
|
|
|
|
cache_cert (cert);
|
|
|
|
|
return cert; /* Done. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("find_cert_bysn: certificate not returned by caller"
|
|
|
|
|
" - doing lookup\n");
|
|
|
|
|
|
|
|
|
|
/* Retrieve the certificate from external resources. */
|
|
|
|
|
while (!cert)
|
|
|
|
|
{
|
|
|
|
|
ksba_sexp_t sn;
|
|
|
|
|
char *issdn;
|
|
|
|
|
|
|
|
|
|
if (!context)
|
|
|
|
|
{
|
|
|
|
|
err = ca_cert_fetch (ctrl, &context, issuer_dn);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error fetching certificate by S/N: %s\n"),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
err = fetch_next_ksba_cert (context, &cert);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error fetching certificate by S/N: %s\n"),
|
|
|
|
|
gpg_strerror (err) );
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
issdn = ksba_cert_get_issuer (cert, 0);
|
|
|
|
|
if (strcmp (issuer_dn, issdn))
|
|
|
|
|
{
|
|
|
|
|
log_debug ("find_cert_bysn: Ooops: issuer DN does not match\n");
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
ksba_free (issdn);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
break;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sn = ksba_cert_get_serial (cert);
|
|
|
|
|
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
{
|
|
|
|
|
log_debug (" considering certificate (#");
|
|
|
|
|
dump_serial (sn);
|
|
|
|
|
log_printf ("/");
|
|
|
|
|
dump_string (issdn);
|
|
|
|
|
log_printf (")\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!compare_serialno (serialno, sn))
|
|
|
|
|
{
|
|
|
|
|
ksba_free (sn);
|
|
|
|
|
ksba_free (issdn);
|
|
|
|
|
cache_cert (cert);
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug (" found\n");
|
|
|
|
|
break; /* Ready. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksba_free (sn);
|
|
|
|
|
ksba_free (issdn);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end_cert_fetch (context);
|
|
|
|
|
return cert;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Return the certificate matching SUBJECT_DN and (if not NULL)
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* KEYID. If it is not already in the cache, try to find it from other
|
|
|
|
|
* resources. Note, that the external search does not work for user
|
|
|
|
|
* certificates because the LDAP lookup is on the caCertificate
|
|
|
|
|
* attribute. For our purposes this is just fine. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
ksba_cert_t
|
|
|
|
|
find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
int seq;
|
|
|
|
|
ksba_cert_t cert = NULL;
|
|
|
|
|
cert_fetch_context_t context = NULL;
|
|
|
|
|
ksba_sexp_t subj;
|
|
|
|
|
|
|
|
|
|
/* If we have certificates from an OCSP request we first try to use
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* them. This is because these certificates will really be the
|
|
|
|
|
* required ones and thus even in the case that they can't be
|
|
|
|
|
* uniquely located by the following code we can use them. This is
|
|
|
|
|
* for example required by Telesec certificates where a keyId is
|
|
|
|
|
* used but the issuer certificate comes without a subject keyId! */
|
2011-02-23 10:51:36 +01:00
|
|
|
|
if (ctrl->ocsp_certs && subject_dn)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
cert_ref_t cr;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* For efficiency reasons we won't use get_cert_bysubject here. */
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (i=0; i < 256; i++)
|
|
|
|
|
for (ci=cert_cache[i]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && ci->subject_dn
|
|
|
|
|
&& !strcmp (ci->subject_dn, subject_dn))
|
|
|
|
|
for (cr=ctrl->ocsp_certs; cr; cr = cr->next)
|
|
|
|
|
if (!memcmp (ci->fpr, cr->fpr, 20))
|
|
|
|
|
{
|
|
|
|
|
ksba_cert_ref (ci->cert);
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return ci->cert; /* We use this certificate. */
|
|
|
|
|
}
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n");
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-16 10:35:18 +01:00
|
|
|
|
/* No check whether the certificate is cached. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++)
|
|
|
|
|
{
|
|
|
|
|
if (!keyid)
|
|
|
|
|
break; /* No keyid requested, so return the first one found. */
|
|
|
|
|
if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)
|
|
|
|
|
&& !cmp_simple_canon_sexp (keyid, subj))
|
|
|
|
|
{
|
|
|
|
|
xfree (subj);
|
|
|
|
|
break; /* Found matching cert. */
|
|
|
|
|
}
|
|
|
|
|
xfree (subj);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
}
|
|
|
|
|
if (cert)
|
|
|
|
|
return cert; /* Done. */
|
|
|
|
|
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("find_cert_bysubject: certificate not in cache\n");
|
|
|
|
|
|
|
|
|
|
/* Ask back to the service requester to return the certificate.
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* This is because we can assume that he already used the
|
|
|
|
|
* certificate while checking for the CRL. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (keyid)
|
|
|
|
|
cert = get_cert_local_ski (ctrl, subject_dn, keyid);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* In contrast to get_cert_local_ski, get_cert_local uses any
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* passed pattern, so we need to make sure that an exact subject
|
|
|
|
|
* search is done. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
char *buf;
|
|
|
|
|
|
2017-02-16 10:19:59 +01:00
|
|
|
|
buf = strconcat ("/", subject_dn, NULL);
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!buf)
|
|
|
|
|
{
|
|
|
|
|
log_error ("can't allocate enough memory: %s\n", strerror (errno));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
cert = get_cert_local (ctrl, buf);
|
|
|
|
|
xfree (buf);
|
|
|
|
|
}
|
|
|
|
|
if (cert)
|
|
|
|
|
{
|
|
|
|
|
cache_cert (cert);
|
|
|
|
|
return cert; /* Done. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("find_cert_bysubject: certificate not returned by caller"
|
|
|
|
|
" - doing lookup\n");
|
|
|
|
|
|
|
|
|
|
/* Locate the certificate using external resources. */
|
|
|
|
|
while (!cert)
|
|
|
|
|
{
|
|
|
|
|
char *subjdn;
|
|
|
|
|
|
|
|
|
|
if (!context)
|
|
|
|
|
{
|
|
|
|
|
err = ca_cert_fetch (ctrl, &context, subject_dn);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error fetching certificate by subject: %s\n"),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
err = fetch_next_ksba_cert (context, &cert);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("error fetching certificate by subject: %s\n"),
|
|
|
|
|
gpg_strerror (err) );
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
subjdn = ksba_cert_get_subject (cert, 0);
|
|
|
|
|
if (strcmp (subject_dn, subjdn))
|
|
|
|
|
{
|
|
|
|
|
log_info ("find_cert_bysubject: subject DN does not match\n");
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
ksba_free (subjdn);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
continue;
|
2010-06-09 18:53:51 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
{
|
|
|
|
|
log_debug (" considering certificate (/");
|
|
|
|
|
dump_string (subjdn);
|
|
|
|
|
log_printf (")\n");
|
|
|
|
|
}
|
|
|
|
|
ksba_free (subjdn);
|
|
|
|
|
|
|
|
|
|
/* If no key ID has been provided, we return the first match. */
|
|
|
|
|
if (!keyid)
|
|
|
|
|
{
|
|
|
|
|
cache_cert (cert);
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug (" found\n");
|
|
|
|
|
break; /* Ready. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* With the key ID given we need to compare it. */
|
|
|
|
|
if (!ksba_cert_get_subj_key_id (cert, NULL, &subj))
|
|
|
|
|
{
|
|
|
|
|
if (!cmp_simple_canon_sexp (keyid, subj))
|
|
|
|
|
{
|
|
|
|
|
ksba_free (subj);
|
|
|
|
|
cache_cert (cert);
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug (" found\n");
|
|
|
|
|
break; /* Ready. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ksba_free (subj);
|
|
|
|
|
ksba_cert_release (cert);
|
|
|
|
|
cert = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end_cert_fetch (context);
|
|
|
|
|
return cert;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-02-21 12:23:20 +01:00
|
|
|
|
/* Return 0 if the certificate is a trusted certificate. Returns
|
2017-02-16 18:58:27 +01:00
|
|
|
|
* GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in
|
2017-02-21 12:23:20 +01:00
|
|
|
|
* case of systems errors. TRUSTCLASSES are the bitwise ORed
|
|
|
|
|
* CERTTRUST_CLASS values to use for the check. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
gpg_error_t
|
2017-02-21 12:23:20 +01:00
|
|
|
|
is_trusted_cert (ksba_cert_t cert, unsigned int trustclasses)
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
|
|
|
|
unsigned char fpr[20];
|
|
|
|
|
cert_item_t ci;
|
|
|
|
|
|
|
|
|
|
cert_compute_fpr (cert, fpr);
|
|
|
|
|
|
|
|
|
|
acquire_cache_read_lock ();
|
|
|
|
|
for (ci=cert_cache[*fpr]; ci; ci = ci->next)
|
|
|
|
|
if (ci->cert && !memcmp (ci->fpr, fpr, 20))
|
|
|
|
|
{
|
2017-02-21 12:23:20 +01:00
|
|
|
|
if ((ci->trustclasses & trustclasses))
|
2010-06-09 18:53:51 +02:00
|
|
|
|
{
|
2017-02-21 12:23:20 +01:00
|
|
|
|
/* The certificate is trusted in one of the given
|
|
|
|
|
* TRUSTCLASSES. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return 0; /* Yes, it is trusted. */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
release_cache_lock ();
|
|
|
|
|
return gpg_error (GPG_ERR_NOT_TRUSTED);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Given the certificate CERT locate the issuer for this certificate
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* and return it at R_CERT. Returns 0 on success or
|
|
|
|
|
* GPG_ERR_NOT_FOUND. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
gpg_error_t
|
|
|
|
|
find_issuing_cert (ctrl_t ctrl, ksba_cert_t cert, ksba_cert_t *r_cert)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
char *issuer_dn;
|
|
|
|
|
ksba_cert_t issuer_cert = NULL;
|
|
|
|
|
ksba_name_t authid;
|
|
|
|
|
ksba_sexp_t authidno;
|
|
|
|
|
ksba_sexp_t keyid;
|
|
|
|
|
|
|
|
|
|
*r_cert = NULL;
|
|
|
|
|
|
|
|
|
|
issuer_dn = ksba_cert_get_issuer (cert, 0);
|
|
|
|
|
if (!issuer_dn)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("no issuer found in certificate\n"));
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_CERT);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* First we need to check whether we can return that certificate
|
|
|
|
|
using the authorithyKeyIdentifier. */
|
|
|
|
|
err = ksba_cert_get_auth_key_id (cert, &keyid, &authid, &authidno);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_info (_("error getting authorityKeyIdentifier: %s\n"),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
const char *s = ksba_name_enum (authid, 0);
|
|
|
|
|
if (s && *authidno)
|
|
|
|
|
{
|
|
|
|
|
issuer_cert = find_cert_bysn (ctrl, s, authidno);
|
|
|
|
|
}
|
2017-02-16 10:35:18 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!issuer_cert && keyid)
|
|
|
|
|
{
|
|
|
|
|
/* Not found by issuer+s/n. Now that we have an AKI
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* keyIdentifier look for a certificate with a matching
|
|
|
|
|
* SKI. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
issuer_cert = find_cert_bysubject (ctrl, issuer_dn, keyid);
|
|
|
|
|
}
|
2017-02-16 10:35:18 +01:00
|
|
|
|
|
2010-06-09 18:53:51 +02:00
|
|
|
|
/* Print a note so that the user does not feel too helpless when
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* an issuer certificate was found and gpgsm prints BAD
|
|
|
|
|
* signature because it is not the correct one. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!issuer_cert)
|
|
|
|
|
{
|
|
|
|
|
log_info ("issuer certificate ");
|
|
|
|
|
if (keyid)
|
|
|
|
|
{
|
|
|
|
|
log_printf ("{");
|
|
|
|
|
dump_serial (keyid);
|
|
|
|
|
log_printf ("} ");
|
|
|
|
|
}
|
|
|
|
|
if (authidno)
|
|
|
|
|
{
|
|
|
|
|
log_printf ("(#");
|
|
|
|
|
dump_serial (authidno);
|
|
|
|
|
log_printf ("/");
|
|
|
|
|
dump_string (s);
|
|
|
|
|
log_printf (") ");
|
|
|
|
|
}
|
|
|
|
|
log_printf ("not found using authorityKeyIdentifier\n");
|
|
|
|
|
}
|
|
|
|
|
ksba_name_release (authid);
|
|
|
|
|
xfree (authidno);
|
|
|
|
|
xfree (keyid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this did not work, try just with the issuer's name and assume
|
2017-02-16 10:35:18 +01:00
|
|
|
|
* that there is only one such certificate. We only look into our
|
|
|
|
|
* cache then. */
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (err || !issuer_cert)
|
|
|
|
|
{
|
|
|
|
|
issuer_cert = get_cert_bysubject (issuer_dn, 0);
|
|
|
|
|
if (issuer_cert)
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
leave:
|
2010-06-09 18:53:51 +02:00
|
|
|
|
if (!err && !issuer_cert)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
xfree (issuer_dn);
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
ksba_cert_release (issuer_cert);
|
|
|
|
|
else
|
|
|
|
|
*r_cert = issuer_cert;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2017-02-17 16:39:48 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|