From 8aaca58e1ee45cd948fa2596769079b5dd98715d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Mon, 10 Jun 2013 13:33:00 +0200 Subject: [PATCH] trust: Add support for parsing the ca-certificates.conf file This is the conf file used by update-ca-certificates. We use this to add a new token which reads certificates in the same way as update-ca-certificates. https://bugs.freedesktop.org/show_bug.cgi?id=65601 --- common/test.c | 2 + configure.ac | 21 +- doc/manual/p11-kit-devel.xml | 7 + doc/manual/p11-kit-trust.xml | 6 + trust/Makefile.am | 8 + trust/loader.c | 412 +++++++++++++++++++++ trust/loader.h | 44 +++ trust/module.c | 29 +- trust/tests/files/ca-certificates.conf | 7 + trust/tests/frob-token.c | 3 +- .../input/anchors/{cacert3.der => cacert3.crt} | Bin trust/tests/input/unrecognized-file.txt | 1 + trust/tests/test-module.c | 35 +- trust/tests/test-token.c | 291 ++++++++++----- trust/token.c | 154 +------- trust/token.h | 6 +- 16 files changed, 784 insertions(+), 242 deletions(-) create mode 100644 trust/loader.c create mode 100644 trust/loader.h create mode 100644 trust/tests/files/ca-certificates.conf rename trust/tests/input/anchors/{cacert3.der => cacert3.crt} (100%) create mode 100644 trust/tests/input/unrecognized-file.txt diff --git a/common/test.c b/common/test.c index 8866e48..122e78a 100644 --- a/common/test.c +++ b/common/test.c @@ -121,6 +121,8 @@ p11_test_fail (const char *filename, printf ("# in %s() at %s:%d\n", function, filename, line); free (output); + + longjmp (gl.jump, 1); } static void diff --git a/configure.ac b/configure.ac index aa3dfa1..ae9387d 100644 --- a/configure.ac +++ b/configure.ac @@ -254,6 +254,25 @@ fi AC_DEFINE_UNQUOTED(TRUST_PATHS, ["$with_trust_paths"], [The trust module input paths]) AC_SUBST(with_trust_paths) +AC_ARG_ENABLE([ca-certificates], + AS_HELP_STRING([--enable-ca-certificates], + [Enable building ca-certificates.conf module]) +) + +AC_MSG_CHECKING([if debian ca-certificates.conf module is enabled]) +AS_IF([test "$enable_trust_module" != "yes"], [ + AS_IF([test "$enable_ca_certificates" = "yes"], [ + AC_MSG_ERROR([--enable-trust-module is needed in order to build the trust ca-certificates.conf module]) + ]) +]) + +AS_IF([test "$enable_ca_certificates" != "yes"], + [enable_ca_certificates="no"], + [AC_DEFINE_UNQUOTED(WITH_CA_CERTIFICATES, 1, [Build with ca-certificates.conf support])] +) + +AC_MSG_RESULT([$enable_ca_certificates]) + # -------------------------------------------------------------------- # GTK Doc @@ -488,5 +507,5 @@ AC_MSG_NOTICE([build options: Build trust module: $enable_trust_module Trust module paths: $trust_status - + Trust from ca-certificates.conf: $enable_ca_certificates ]) diff --git a/doc/manual/p11-kit-devel.xml b/doc/manual/p11-kit-devel.xml index 873aff1..b76ed2c 100644 --- a/doc/manual/p11-kit-devel.xml +++ b/doc/manual/p11-kit-devel.xml @@ -185,6 +185,13 @@ $ make install library. + + Make the trust module read the Debian style + /etc/ca-certificates.conf file and related directories in the + same way that update-ca-certificates does. See the + update-ca-certificates manual page for more info. + + Enables building of the documentation and command line manual. The documentation is built in the doc/html/ directory of diff --git a/doc/manual/p11-kit-trust.xml b/doc/manual/p11-kit-trust.xml index 999bfef..995f661 100644 --- a/doc/manual/p11-kit-trust.xml +++ b/doc/manual/p11-kit-trust.xml @@ -64,6 +64,12 @@ $ pkg-config --variable p11_trust_paths p11-kit-1 The first input path becomes the first PKCS#11 token of the trust module, and has the highest priority when callers search for trust policy information. + + Additionally if the trust module has been built with the + --enable-ca-certificates option, then the + /etc/ca-certificates.conf file and related directories + are read in the same way as update-ca-certificates + would act.
diff --git a/trust/Makefile.am b/trust/Makefile.am index 4a3430c..24d4680 100644 --- a/trust/Makefile.am +++ b/trust/Makefile.am @@ -16,6 +16,7 @@ AM_CPPFLAGS = \ MODULE_SRCS = \ builder.c builder.h \ index.c index.h \ + loader.c loader.h \ parser.c parser.h \ persist.c persist.h \ module.c module.h \ @@ -31,6 +32,8 @@ module_LTLIBRARIES = \ p11-kit-trust.la p11_kit_trust_la_CFLAGS = \ + -DCASYSTEM=\"$(datadir)/ca-certificates\" \ + -DCALOCAL=\"$(prefix)/local/share/ca-certificates\" \ $(LIBTASN1_CFLAGS) p11_kit_trust_la_LIBADD = \ @@ -51,6 +54,11 @@ p11_kit_trust_la_SOURCES = $(MODULE_SRCS) noinst_LTLIBRARIES = \ libtrust-testable.la +libtrust_testable_la_CFLAGS = \ + -DCASYSTEM=\"$(abs_srcdir)/tests/input\" \ + -DCALOCAL=\"$(abs_srcdir)/tests/files/local/ca-certificates\" \ + $(NULL) + libtrust_testable_la_LDFLAGS = \ -no-undefined diff --git a/trust/loader.c b/trust/loader.c new file mode 100644 index 0000000..3aafd3e --- /dev/null +++ b/trust/loader.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2013 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter + */ + +#include "config.h" + +#include "compat.h" +#define P11_DEBUG_FLAG P11_DEBUG_TRUST +#include "debug.h" +#include "message.h" +#include "parser.h" +#include "path.h" +#include "loader.h" +#include "token.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +static int +load_maybe_file (p11_token *token, + const char *filename, + int flags) +{ + int ret; + + ret = p11_parse_file (p11_token_parser (token), filename, flags); + + switch (ret) { + case P11_PARSE_SUCCESS: + p11_debug ("loaded: %s", filename); + return 1; + case P11_PARSE_UNRECOGNIZED: + p11_debug ("skipped: %s", filename); + return 0; + default: + p11_debug ("failed to parse: %s", filename); + return 0; + } +} + +static int +load_expect_file (p11_token *token, + const char *filename, + int flags) +{ + int ret; + + ret = p11_parse_file (p11_token_parser (token), filename, flags); + + switch (ret) { + case P11_PARSE_SUCCESS: + p11_debug ("loaded: %s", filename); + return 1; + case P11_PARSE_UNRECOGNIZED: + p11_message ("unrecognized contents: %s", filename); + return 0; + default: + p11_message ("failed to parse: %s", filename); + return 0; + } +} + +static int +load_directory (p11_token *token, + const char *directory, + int flags) +{ + struct dirent *dp; + struct stat sb; + char *path; + int total = 0; + int ret; + DIR *dir; + + dir = opendir (directory); + if (!dir) { + p11_message ("couldn't list directory: %s: %s", + directory, strerror (errno)); + return 0; + } + + /* We're within a global mutex, so readdir is safe */ + while ((dp = readdir (dir)) != NULL) { + path = p11_path_build (directory, dp->d_name, NULL); + return_val_if_fail (path != NULL, -1); + + if (stat (path, &sb) < 0) { + p11_message ("couldn't stat path: %s", path); + + } else if (!S_ISDIR (sb.st_mode)) { + ret = load_maybe_file (token, path, flags); + return_val_if_fail (ret >= 0, ret); + total += ret; + } + + free (path); + } + + closedir (dir); + return total; +} + +static int +load_subdirectory (p11_token *token, + const char *directory, + const char *subdir, + int flags) +{ + struct stat sb; + char *path; + int ret = 0; + + if (asprintf (&path, "%s/%s", directory, subdir) < 0) + return_val_if_reached (-1); + + if (stat (path, &sb) >= 0 && S_ISDIR (sb.st_mode)) + ret = load_directory (token, path, flags); + + free (path); + return ret; +} + +int +p11_loader_standard (p11_token *token) +{ + const char *path; + struct stat sb; + int total; + int ret; + + path = p11_token_get_path (token); + + if (stat (path, &sb) < 0) { + if (errno == ENOENT) { + p11_message ("trust certificate path does not exist: %s", + path); + } else { + p11_message ("cannot access trust certificate path: %s: %s", + path, strerror (errno)); + } + + return 0; + } + + if (S_ISDIR (sb.st_mode)) { + total = 0; + + ret = load_subdirectory (token, path, "anchors", P11_PARSE_FLAG_ANCHOR); + return_val_if_fail (ret >= 0, ret); + total += ret; + + ret = load_subdirectory (token, path, "blacklist", P11_PARSE_FLAG_BLACKLIST); + return_val_if_fail (ret >= 0, ret); + total += ret; + + ret = load_directory (token, path, P11_PARSE_FLAG_NONE); + return_val_if_fail (ret >= 0, ret); + total += ret; + + return total; + } else { + return load_maybe_file (token, path, P11_PARSE_FLAG_ANCHOR); + } +} + +static int +load_cc_directory (p11_token *token, + const char *directory, + bool deep, + int flags) +{ + struct dirent *dp; + struct stat sb; + char *path; + int total = 0; + size_t len; + int ret; + DIR *dir; + + /* First we load all the modules */ + dir = opendir (directory); + if (!dir) { + if (errno != ENOENT) { + p11_message ("couldn't list directory: %s: %s", + directory, strerror (errno)); + } + return 0; + } + + /* We're within a global mutex, so readdir is safe */ + while ((dp = readdir (dir)) != NULL) { + path = p11_path_build (directory, dp->d_name, NULL); + return_val_if_fail (path != NULL, -1); + + ret = 0; + + if (stat (path, &sb) < 0) { + p11_message ("couldn't stat path: %s", path); + + } else if (S_ISDIR (sb.st_mode)) { + if (deep && strcmp (dp->d_name, ".") != 0 && + strcmp (dp->d_name, "..") != 0) { + ret = load_cc_directory (token, path, deep, flags); + } + + } else { + /* Only load files that end in .pem or .crt */ + len = strlen (path); + if (len > 4 && + (strcmp (path + (len - 4), ".crt") == 0 || + strcmp (path + (len - 4), ".pem") == 0)) { + ret = load_expect_file (token, path, flags); + } else { + p11_debug ("skipping file without .crt or .pem extension: %s", path); + } + } + + return_val_if_fail (ret >= 0, ret); + total += ret; + free (path); + } + + closedir (dir); + return total; +} + +static int +load_ca_certificates_conf (p11_token *token, + const char *data, + size_t size, + const char *base) +{ + const char *line; + const char *end; + const char *at; + size_t remaining; + char *relative; + struct stat sb; + char *path; + int total; + int flags; + int ret; + + total = 0; + at = data; + remaining = size; + + while (remaining > 0) { + line = at; + end = memchr (at, '\n', remaining); + if (end == NULL) { + end = at + remaining; + remaining = 0; + at = end; + } else { + assert ((end - at) + 1 <= remaining); + remaining -= (end - at) + 1; + at = end + 1; + } + + /* If the line starts with a # then skip */ + if (line == end || line[0] == '#') + continue; + + /* Strip whitespace from line */ + while (line != end && isspace (line[0])) + ++line; + while (line != end && isspace (*(end - 1))) + --end; + + /* If the line is empty skip */ + if (line == end) + continue; + + /* If starts with an exclam, then blacklist */ + if (line[0] == '!') { + flags = P11_PARSE_FLAG_BLACKLIST; + line++; + } else { + flags = P11_PARSE_FLAG_ANCHOR; + } + + /* Build a path out of this */ + relative = strndup (line, end - line); + path = p11_path_build (base, relative, NULL); + free (relative); + + p11_debug ("%s %s", flags & P11_PARSE_FLAG_ANCHOR ? "anchor" : "blacklist", path); + + /* Warn about files that do not exist */ + if (stat (path, &sb) < 0) { + p11_message ("couldn't stat path: %s: %s", path, strerror (errno)); + + } else { + ret = load_expect_file (token, path, flags); + return_val_if_fail (ret >= 0, ret); + total += ret; + } + + free (path); + } + + return total; +} + +static int +load_ca_certificates_file (p11_token *token, + const char *path, + const char *base) +{ + p11_mmap *map; + void *data; + size_t size; + int ret; + + map = p11_mmap_open (path, &data, &size); + if (map == NULL) { + p11_message ("couldn't open and map file: %s: %s", path, strerror (errno)); + return 0; + } + + p11_debug ("parsing %s file in updates-ca-certificates style", path); + + ret = load_ca_certificates_conf (token, data, size, base); + p11_mmap_close (map); + + return ret; +} + +static int +load_ca_certificates_or_dir (p11_token *token) +{ + const char *ca_cert_conf; + struct stat sb; + int ret; + + /* + * If this is a debian style update-ca-cetrificates ca-certificates.conf, + * then parse according to the rules that update-ca-certificates uses. + */ + ca_cert_conf = p11_token_get_path (token); + + if (stat (ca_cert_conf, &sb) >= 0) { + + /* If the file exists, read and restrict to contents */ + ret = load_ca_certificates_file (token, ca_cert_conf, CASYSTEM); + + } else { + if (errno != ENOENT) { + p11_message ("cannot access trust path: %s: %s", + ca_cert_conf, strerror (errno)); + } + + /* Otherwise load all the certificates in /usr/share/ca-certificates as anchors */ + ret = load_cc_directory (token, CASYSTEM, true, P11_PARSE_FLAG_ANCHOR); + } + + return_val_if_fail (ret >= 0, ret); + return ret; +} + +int +p11_loader_ca_certificates (p11_token *token) +{ + int custom; + int count; + + count = load_ca_certificates_or_dir (token); + return_val_if_fail (count >= 0, count); + + custom = load_cc_directory (token, CALOCAL, false, P11_PARSE_FLAG_ANCHOR); + return_val_if_fail (custom >= 0, custom); + + return count + custom; +} diff --git a/trust/loader.h b/trust/loader.h new file mode 100644 index 0000000..0364f05 --- /dev/null +++ b/trust/loader.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2013 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the + * above copyright notice, this list of conditions and + * the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * The names of contributors to this software may not be + * used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * Author: Stef Walter + */ + +#ifndef P11_LOADER_H_ +#define P11_LOADER_H_ + +#include "token.h" + +int p11_loader_standard (p11_token *token); + +int p11_loader_ca_certificates (p11_token *token); + +#endif /* P11_LOADER_H_ */ diff --git a/trust/module.c b/trust/module.c index abfabae..0238805 100644 --- a/trust/module.c +++ b/trust/module.c @@ -43,6 +43,7 @@ #include "debug.h" #include "dict.h" #include "library.h" +#include "loader.h" #include "message.h" #include "module.h" #include "parser.h" @@ -70,7 +71,14 @@ static struct _Shared { p11_dict *sessions; p11_array *tokens; char *paths; -} gl = { 0, NULL, NULL, NULL }; + char *ca_certificates; +} gl = { 0, NULL, NULL, NULL, +#ifdef WITH_CA_CERTIFICATES + SYSCONFDIR "/ca-certificates.conf" +#else + NULL +#endif +}; /* Used during FindObjects */ typedef struct _FindObjects { @@ -234,7 +242,7 @@ create_tokens_inlock (p11_array *tokens, return_val_if_fail (base != NULL, false); } - token = p11_token_new (slot, path, label); + token = p11_token_new (slot, path, label, p11_loader_standard); return_val_if_fail (token != NULL, false); if (!p11_array_push (tokens, token)) @@ -246,6 +254,16 @@ create_tokens_inlock (p11_array *tokens, } free (alloc); + + if (gl.ca_certificates && gl.ca_certificates[0] != '\0') { + token = p11_token_new (slot, gl.ca_certificates, + "System Trust", p11_loader_ca_certificates); + return_val_if_fail (token != NULL, false); + + if (!p11_array_push (tokens, token)) + return_val_if_reached (false); + } + return true; } @@ -265,6 +283,10 @@ parse_argument (char *arg, free (gl.paths); gl.paths = value ? strdup (value) : NULL; + } else if (strcmp (arg, "ca-certificates") == 0) { + free (gl.ca_certificates); + gl.ca_certificates = value ? strdup (value) : NULL; + } else { p11_message ("unrecognized module argument: %s", arg); } @@ -295,6 +317,9 @@ sys_C_Finalize (CK_VOID_PTR reserved) free (gl.paths); gl.paths = NULL; + free (gl.ca_certificates); + gl.ca_certificates = NULL; + p11_dict_free (gl.sessions); gl.sessions = NULL; diff --git a/trust/tests/files/ca-certificates.conf b/trust/tests/files/ca-certificates.conf new file mode 100644 index 0000000..aacccc8 --- /dev/null +++ b/trust/tests/files/ca-certificates.conf @@ -0,0 +1,7 @@ + +# This is a comment + + +!distrusted.pem +unrecognized-file.txt +anchors/cacert3.crt \ No newline at end of file diff --git a/trust/tests/frob-token.c b/trust/tests/frob-token.c index 5d57ec1..cce1c24 100644 --- a/trust/tests/frob-token.c +++ b/trust/tests/frob-token.c @@ -37,6 +37,7 @@ #include +#include "loader.h" #include "token.h" int @@ -52,7 +53,7 @@ main (int argc, return 2; } - token = p11_token_new (1, argv[1], "Label"); + token = p11_token_new (1, argv[1], "Label", p11_loader_standard); count = p11_token_load (token); printf ("%d files loaded\n", count); diff --git a/trust/tests/input/anchors/cacert3.der b/trust/tests/input/anchors/cacert3.crt similarity index 100% rename from trust/tests/input/anchors/cacert3.der rename to trust/tests/input/anchors/cacert3.crt diff --git a/trust/tests/input/unrecognized-file.txt b/trust/tests/input/unrecognized-file.txt new file mode 100644 index 0000000..4d5bac3 --- /dev/null +++ b/trust/tests/input/unrecognized-file.txt @@ -0,0 +1 @@ +# This file is not recognized by the parser \ No newline at end of file diff --git a/trust/tests/test-module.c b/trust/tests/test-module.c index bf28124..2e6fecb 100644 --- a/trust/tests/test-module.c +++ b/trust/tests/test-module.c @@ -85,7 +85,7 @@ setup (void *unused) paths = SRCDIR "/input" P11_PATH_SEP \ SRCDIR "/files/self-signed-with-ku.der" P11_PATH_SEP \ SRCDIR "/files/thawte.pem"; - if (asprintf (&arguments, "paths='%s'", paths) < 0) + if (asprintf (&arguments, "paths='%s' ca-certificates=''", paths) < 0) assert (false && "not reached"); args.pReserved = arguments; args.flags = CKF_OS_LOCKING_OK; @@ -95,10 +95,10 @@ setup (void *unused) free (arguments); - count = NUM_SLOTS; + count = NUM_SLOTS + 5; rv = test.module->C_GetSlotList (CK_TRUE, test.slots, &count); assert (rv == CKR_OK); - assert (count == NUM_SLOTS); + assert_num_eq (count, NUM_SLOTS); } static void @@ -987,6 +987,32 @@ test_login_logout (void) assert (rv == CKR_USER_NOT_LOGGED_IN); } +static void +test_ca_certificates (void) +{ + CK_C_INITIALIZE_ARGS args; + CK_ULONG count; + CK_RV rv; + + memset (&test, 0, sizeof (test)); + + /* This is the entry point of the trust module, linked to this test */ + rv = C_GetFunctionList (&test.module); + assert (rv == CKR_OK); + + memset (&args, 0, sizeof (args)); + args.pReserved = "paths='' ca-certificates='" SRCDIR "/files/ca-certificates.conf'"; + args.flags = CKF_OS_LOCKING_OK; + + rv = test.module->C_Initialize (&args); + assert (rv == CKR_OK); + + count = 3; + rv = test.module->C_GetSlotList (CK_TRUE, test.slots, &count); + assert (rv == CKR_OK); + assert_num_eq (count, 1); +} + int main (int argc, char *argv[]) @@ -1020,5 +1046,8 @@ main (int argc, p11_test (test_find_serial_der_mismatch, "/module/find_serial_der_mismatch"); p11_test (test_login_logout, "/module/login_logout"); + p11_fixture (NULL, NULL); + p11_test (test_ca_certificates, "/module/ca-certificates"); + return p11_test_run (argc, argv); } diff --git a/trust/tests/test-token.c b/trust/tests/test-token.c index 6f5ccdb..e4e5a14 100644 --- a/trust/tests/test-token.c +++ b/trust/tests/test-token.c @@ -42,10 +42,137 @@ #include "attrs.h" #include "debug.h" +#include "loader.h" #include "pkcs11x.h" #include "message.h" #include "token.h" +static CK_OBJECT_CLASS builtin = CKO_NSS_BUILTIN_ROOT_LIST; +static CK_OBJECT_CLASS certificate = CKO_CERTIFICATE; +static CK_BBOOL vtrue = CK_TRUE; +static CK_BBOOL vfalse = CK_FALSE; + +/* + * Builtin trust anchor flag for NSS + */ + +static CK_ATTRIBUTE builtin_root_list[] = { + { CKA_CLASS, &builtin, sizeof (builtin) }, + { CKA_TOKEN, &vtrue, sizeof (vtrue) }, + { CKA_PRIVATE, &vfalse, sizeof (vfalse) }, + { CKA_MODIFIABLE, &vfalse, sizeof (vfalse) }, + { CKA_LABEL, "Trust Anchor Roots", 18 }, + { CKA_INVALID }, +}; + +/* + * blacklist comes from the input/distrust.pem file. It is not in the blacklist + * directory, but is an OpenSSL trusted certificate file, and is marked + * in the blacklist style for OpenSSL. + */ + +static CK_ATTRIBUTE blacklist[] = { + { CKA_CLASS, &certificate, sizeof (certificate) }, + { CKA_LABEL, "Red Hat Is the CA", 17 }, + { CKA_SERIAL_NUMBER, "\x02\x01\x01", 3 }, + { CKA_TRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_X_DISTRUSTED, &vtrue, sizeof (vtrue) }, + { CKA_INVALID }, +}; + +/* + * blacklist2 comes from the input/blacklist/self-server.der file. It is + * explicitly put on the blacklist, even though it containts no trust + * policy information. + */ + +static const unsigned char self_server_subject[] = { + 0x30, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, + 0x01, 0x19, 0x16, 0x03, 0x43, 0x4f, 0x4d, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a, 0x09, 0x92, 0x26, + 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x07, 0x45, 0x58, 0x41, 0x4d, 0x50, 0x4c, 0x45, + 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, +}; + +static CK_ATTRIBUTE blacklist2[] = { + { CKA_CLASS, &certificate, sizeof (certificate) }, + { CKA_SUBJECT, (void *)self_server_subject, sizeof (self_server_subject) }, + { CKA_TRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_X_DISTRUSTED, &vtrue, sizeof (vtrue) }, + { CKA_INVALID }, +}; + +/* + * anchor comes from the input/anchors/cacert3.der file. It is + * explicitly marked as an anchor, even though it containts no trust + * policy information. + */ + +static CK_ATTRIBUTE anchor[] = { + { CKA_CLASS, &certificate, sizeof (certificate) }, + { CKA_SUBJECT, (void *)test_cacert3_ca_subject, sizeof (test_cacert3_ca_subject) }, + { CKA_TRUSTED, &vtrue, sizeof (vtrue) }, + { CKA_X_DISTRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_INVALID }, +}; + +static const unsigned char cacert_root_subject[] = { + 0x30, 0x79, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x52, 0x6f, 0x6f, + 0x74, 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, + 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, + 0x41, 0x20, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, + 0x40, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, +}; + +/* + * notrust comes from the input/cacert-ca.der file. It contains no + * trust information, and is not explicitly marked as an anchor, so + * it's neither trusted or distrusted. + */ + +static CK_ATTRIBUTE notrust[] = { + { CKA_CLASS, &certificate, sizeof (certificate) }, + { CKA_SUBJECT, (void *)cacert_root_subject, sizeof (cacert_root_subject) }, + { CKA_TRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_X_DISTRUSTED, &vfalse, sizeof (vfalse) }, + { CKA_INVALID }, +}; + +static int +check_expected_msg (const char *file, + int line, + const char *function, + p11_token *token, + CK_ATTRIBUTE **expected) +{ + CK_OBJECT_HANDLE handle; + CK_ATTRIBUTE *object; + int i; + + /* The other objects */ + for (i = 0; expected[i]; i++) { + handle = p11_index_find (p11_token_index (token), expected[i], 2); + if (handle == 0UL) { + p11_test_fail (file, line, function, + "expected not found: %s", + p11_attrs_to_string (expected[i], -1)); + } + + object = p11_index_lookup (p11_token_index (token), handle); + assert (object != NULL); + + test_check_attrs_msg (file, line, function, expected[i], object); + } + + return i; +} + +#define check_expected(token, expected) \ + check_expected_msg (__FILE__, __LINE__, __FUNCTION__, token, expected) + struct { p11_token *token; } test; @@ -53,7 +180,7 @@ struct { static void setup (void *path) { - test.token = p11_token_new (333, path, "Label"); + test.token = p11_token_new (333, path, "Label", p11_loader_standard); assert_ptr_not_null (test.token); } @@ -81,86 +208,6 @@ test_token_load (void *path) static void test_token_flags (void *path) { - CK_OBJECT_CLASS certificate = CKO_CERTIFICATE; - CK_BBOOL falsev = CK_FALSE; - CK_BBOOL truev = CK_TRUE; - - /* - * blacklist comes from the input/distrust.pem file. It is not in the blacklist - * directory, but is an OpenSSL trusted certificate file, and is marked - * in the blacklist style for OpenSSL. - */ - - CK_ATTRIBUTE blacklist[] = { - { CKA_CLASS, &certificate, sizeof (certificate) }, - { CKA_LABEL, "Red Hat Is the CA", 17 }, - { CKA_SERIAL_NUMBER, "\x02\x01\x01", 3 }, - { CKA_TRUSTED, &falsev, sizeof (falsev) }, - { CKA_X_DISTRUSTED, &truev, sizeof (truev) }, - { CKA_INVALID }, - }; - - /* - * blacklist2 comes from the input/blacklist/self-server.der file. It is - * explicitly put on the blacklist, even though it containts no trust - * policy information. - */ - - const unsigned char self_server_subject[] = { - 0x30, 0x4b, 0x31, 0x13, 0x30, 0x11, 0x06, 0x0a, 0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, - 0x01, 0x19, 0x16, 0x03, 0x43, 0x4f, 0x4d, 0x31, 0x17, 0x30, 0x15, 0x06, 0x0a, 0x09, 0x92, 0x26, - 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19, 0x16, 0x07, 0x45, 0x58, 0x41, 0x4d, 0x50, 0x4c, 0x45, - 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x12, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, - }; - - CK_ATTRIBUTE blacklist2[] = { - { CKA_CLASS, &certificate, sizeof (certificate) }, - { CKA_SUBJECT, (void *)self_server_subject, sizeof (self_server_subject) }, - { CKA_TRUSTED, &falsev, sizeof (falsev) }, - { CKA_X_DISTRUSTED, &truev, sizeof (truev) }, - { CKA_INVALID }, - }; - - /* - * anchor comes from the input/anchors/cacert3.der file. It is - * explicitly marked as an anchor, even though it containts no trust - * policy information. - */ - - CK_ATTRIBUTE anchor[] = { - { CKA_CLASS, &certificate, sizeof (certificate) }, - { CKA_SUBJECT, (void *)test_cacert3_ca_subject, sizeof (test_cacert3_ca_subject) }, - { CKA_TRUSTED, &truev, sizeof (truev) }, - { CKA_X_DISTRUSTED, &falsev, sizeof (falsev) }, - { CKA_INVALID }, - }; - - const unsigned char cacert_root_subject[] = { - 0x30, 0x79, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x52, 0x6f, 0x6f, - 0x74, 0x20, 0x43, 0x41, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x15, 0x68, - 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, - 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x22, 0x30, 0x20, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x19, 0x43, - 0x41, 0x20, 0x43, 0x65, 0x72, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x41, - 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, - 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x40, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x6f, 0x72, 0x67, - }; - - /* - * notrust comes from the input/cacert-ca.der file. It contains no - * trust information, and is not explicitly marked as an anchor, so - * it's neither trusted or distrusted. - */ - - CK_ATTRIBUTE notrust[] = { - { CKA_CLASS, &certificate, sizeof (certificate) }, - { CKA_SUBJECT, (void *)cacert_root_subject, sizeof (cacert_root_subject) }, - { CKA_TRUSTED, &falsev, sizeof (falsev) }, - { CKA_X_DISTRUSTED, &falsev, sizeof (falsev) }, - { CKA_INVALID }, - }; - CK_ATTRIBUTE *expected[] = { anchor, blacklist, @@ -169,23 +216,10 @@ test_token_flags (void *path) NULL, }; - CK_OBJECT_HANDLE handle; - CK_ATTRIBUTE *object; - int i; - if (p11_token_load (test.token) < 0) assert_not_reached (); - /* The other objects */ - for (i = 0; expected[i]; i++) { - handle = p11_index_find (p11_token_index (test.token), expected[i], 2); - assert (handle != 0); - - object = p11_index_lookup (p11_token_index (test.token), handle); - assert_ptr_not_null (object); - - test_check_attrs (expected[i], object); - } + check_expected (test.token, expected); } static void @@ -206,6 +240,66 @@ test_token_slot (void *path) assert_num_eq (333, p11_token_get_slot (test.token)); } +static void +test_ca_certificates_file (void) +{ + CK_ATTRIBUTE *expected[] = { + builtin_root_list, + anchor, + blacklist, + NULL, + }; + + p11_token *token; + int found; + int ret; + + token = p11_token_new (333, SRCDIR "/files/ca-certificates.conf", + "Test Trust", p11_loader_ca_certificates); + assert_ptr_not_null (token); + + p11_message_quiet (); + + ret = p11_token_load (token); + + p11_message_loud (); + + found = check_expected (token, expected); + assert_num_eq (ret, found); + + p11_token_free (token); +} + +static void +test_ca_certificates_not_exist (void) +{ + CK_ATTRIBUTE *expected[] = { + builtin_root_list, + anchor, + blacklist, + NULL, + }; + + p11_token *token; + int found; + int ret; + + token = p11_token_new (333, "/non-existant/blah", + "Test Trust", p11_loader_ca_certificates); + assert_ptr_not_null (token); + + p11_message_quiet (); + + ret = p11_token_load (token); + + p11_message_loud (); + + found = check_expected (token, expected); + assert_num_eq (ret, found); + + p11_token_free (token); +} + int main (int argc, char *argv[]) @@ -213,10 +307,13 @@ main (int argc, p11_fixture (setup, teardown); p11_testx (test_token_load, SRCDIR "/input", "/token/load"); p11_testx (test_token_flags, SRCDIR "/input", "/token/flags"); - - p11_fixture (setup, teardown); p11_testx (test_token_path, "/wheee", "/token/path"); p11_testx (test_token_label, "/wheee", "/token/label"); p11_testx (test_token_slot, "/unneeded", "/token/slot"); + + p11_fixture (NULL, NULL); + p11_test (test_ca_certificates_file, "/token/ca-certificates/file"); + p11_test (test_ca_certificates_not_exist, "/token/ca-certificates/not-exist"); + return p11_test_run (argc, argv); } diff --git a/trust/token.c b/trust/token.c index f48f66b..6c83824 100644 --- a/trust/token.c +++ b/trust/token.c @@ -34,26 +34,17 @@ #include "config.h" -#include "asn1.h" #include "attrs.h" #include "builder.h" #include "compat.h" #define P11_DEBUG_FLAG P11_DEBUG_TRUST #include "debug.h" -#include "errno.h" -#include "message.h" -#include "module.h" #include "parser.h" -#include "path.h" #include "pkcs11.h" #include "pkcs11x.h" #include "token.h" -#include -#include - -#include -#include +#include #include #include @@ -65,134 +56,10 @@ struct _p11_token { char *label; CK_SLOT_ID slot; int loaded; + int (*loader) (p11_token *); }; static int -loader_load_file (p11_token *token, - const char *filename, - struct stat *sb, - int flags) -{ - int ret; - - ret = p11_parse_file (token->parser, filename, flags); - - switch (ret) { - case P11_PARSE_SUCCESS: - p11_debug ("loaded: %s", filename); - return 1; - case P11_PARSE_UNRECOGNIZED: - p11_debug ("skipped: %s", filename); - return 0; - default: - p11_debug ("failed to parse: %s", filename); - return 0; - } -} - -static int -loader_load_directory (p11_token *token, - const char *directory, - int flags) -{ - struct dirent *dp; - struct stat sb; - char *path; - int total = 0; - int ret; - DIR *dir; - - /* First we load all the modules */ - dir = opendir (directory); - if (!dir) { - p11_message ("couldn't list directory: %s: %s", - directory, strerror (errno)); - return 0; - } - - /* We're within a global mutex, so readdir is safe */ - while ((dp = readdir (dir)) != NULL) { - path = p11_path_build (directory, dp->d_name, NULL); - return_val_if_fail (path != NULL, -1); - - if (stat (path, &sb) < 0) { - p11_message ("couldn't stat path: %s", path); - - } else if (!S_ISDIR (sb.st_mode)) { - ret = loader_load_file (token, path, &sb, flags); - return_val_if_fail (ret >= 0, ret); - total += ret; - } - - free (path); - } - - closedir (dir); - return total; -} - -static int -loader_load_subdirectory (p11_token *token, - const char *directory, - const char *subdir, - int flags) -{ - struct stat sb; - char *path; - int ret = 0; - - if (asprintf (&path, "%s/%s", directory, subdir) < 0) - return_val_if_reached (-1); - - if (stat (path, &sb) >= 0 && S_ISDIR (sb.st_mode)) - ret = loader_load_directory (token, path, flags); - - free (path); - return ret; -} - -static int -loader_load_path (p11_token *token, - const char *path) -{ - struct stat sb; - int total; - int ret; - - if (stat (path, &sb) < 0) { - if (errno == ENOENT) { - p11_message ("trust certificate path does not exist: %s", - path); - } else { - p11_message ("cannot access trust certificate path: %s: %s", - path, strerror (errno)); - } - - return 0; - } - - if (S_ISDIR (sb.st_mode)) { - total = 0; - - ret = loader_load_subdirectory (token, path, "anchors", P11_PARSE_FLAG_ANCHOR); - return_val_if_fail (ret >= 0, ret); - total += ret; - - ret = loader_load_subdirectory (token, path, "blacklist", P11_PARSE_FLAG_BLACKLIST); - return_val_if_fail (ret >= 0, ret); - total += ret; - - ret = loader_load_directory (token, path, P11_PARSE_FLAG_NONE); - return_val_if_fail (ret >= 0, ret); - total += ret; - - return total; - } else { - return loader_load_file (token, path, &sb, P11_PARSE_FLAG_ANCHOR); - } -} - -static int load_builtin_objects (p11_token *token) { CK_OBJECT_CLASS builtin = CKO_NSS_BUILTIN_ROOT_LIST; @@ -223,13 +90,16 @@ p11_token_load (p11_token *token) int builtins; int count; + return_val_if_fail (token != NULL, -1); + if (token->loaded) return 0; token->loaded = 1; builtins = load_builtin_objects (token); - count = loader_load_path (token, token->path); + assert (token->loader != NULL); + count = (token->loader) (token); return_val_if_fail (count >= 0, count); return count + builtins; @@ -252,12 +122,14 @@ p11_token_free (p11_token *token) p11_token * p11_token_new (CK_SLOT_ID slot, const char *path, - const char *label) + const char *label, + int (*loader) (p11_token *)) { p11_token *token; return_val_if_fail (path != NULL, NULL); return_val_if_fail (label != NULL, NULL); + return_val_if_fail (loader != NULL, NULL); token = calloc (1, sizeof (p11_token)); return_val_if_fail (token != NULL, NULL); @@ -282,6 +154,7 @@ p11_token_new (CK_SLOT_ID slot, token->slot = slot; token->loaded = 0; + token->loader = loader; p11_debug ("token: %s: %s", token->label, token->path); return token; @@ -314,3 +187,10 @@ p11_token_index (p11_token *token) return_val_if_fail (token != NULL, NULL); return token->index; } + +p11_parser * +p11_token_parser (p11_token *token) +{ + return_val_if_fail (token != NULL, NULL); + return token->parser; +} diff --git a/trust/token.h b/trust/token.h index d7375e7..0eade8e 100644 --- a/trust/token.h +++ b/trust/token.h @@ -37,13 +37,15 @@ #include "dict.h" #include "index.h" +#include "parser.h" #include "pkcs11.h" typedef struct _p11_token p11_token; p11_token * p11_token_new (CK_SLOT_ID slot, const char *path, - const char *label); + const char *label, + int (*loader) (p11_token *)); void p11_token_free (p11_token *token); @@ -57,4 +59,6 @@ const char * p11_token_get_label (p11_token *token); CK_SLOT_ID p11_token_get_slot (p11_token *token); +p11_parser * p11_token_parser (p11_token *token); + #endif /* P11_TOKEN_H_ */ -- 1.8.2.1