... | ... |
@@ -19,7 +19,7 @@ AlwaysBreakBeforeMultilineStrings: false |
19 | 19 |
AlwaysBreakTemplateDeclarations: MultiLine |
20 | 20 |
BinPackArguments: true |
21 | 21 |
BinPackParameters: true |
22 |
-BraceWrapping: |
|
22 |
+BraceWrapping: |
|
23 | 23 |
AfterClass: true |
24 | 24 |
AfterControlStatement: false |
25 | 25 |
AfterEnum: false |
... | ... |
@@ -55,12 +55,12 @@ DerivePointerAlignment: true |
55 | 55 |
DisableFormat: false |
56 | 56 |
ExperimentalAutoDetectBinPacking: false |
57 | 57 |
FixNamespaceComments: true |
58 |
-ForEachMacros: |
|
58 |
+ForEachMacros: |
|
59 | 59 |
- foreach |
60 | 60 |
- Q_FOREACH |
61 | 61 |
- BOOST_FOREACH |
62 | 62 |
IncludeBlocks: Preserve |
63 |
-IncludeCategories: |
|
63 |
+IncludeCategories: |
|
64 | 64 |
- Regex: '^"(llvm|llvm-c|clang|clang-c)/' |
65 | 65 |
Priority: 2 |
66 | 66 |
- Regex: '^(<|"(gtest|gmock|isl|json)/)' |
... | ... |
@@ -111,6 +111,9 @@ SpacesInCStyleCastParentheses: false |
111 | 111 |
SpacesInParentheses: false |
112 | 112 |
SpacesInSquareBrackets: false |
113 | 113 |
Standard: Cpp11 |
114 |
+StatementMacros: |
|
115 |
+ - Q_UNUSED |
|
116 |
+ - QT_REQUIRE_VERSION |
|
114 | 117 |
TabWidth: 8 |
115 | 118 |
UseTab: Never |
116 | 119 |
... |
... | ... |
@@ -27,12 +27,36 @@ clamsubmit_SOURCES = \ |
27 | 27 |
$(top_srcdir)/shared/getopt.h \ |
28 | 28 |
$(top_srcdir)/shared/misc.c \ |
29 | 29 |
$(top_srcdir)/shared/misc.h \ |
30 |
+ $(top_srcdir)/shared/cert_util.c \ |
|
31 |
+ $(top_srcdir)/shared/cert_util.h \ |
|
32 |
+ $(top_srcdir)/shared/cert_util_internal.h \ |
|
30 | 33 |
clamsubmit.c |
31 | 34 |
|
35 |
+if MACOS |
|
36 |
+ clamsubmit_SOURCES += \ |
|
37 |
+ $(top_srcdir)/shared/mac/cert_util_mac.m \ |
|
38 |
+ $(top_srcdir)/shared/cert_util.h |
|
39 |
+endif |
|
40 |
+if WINDOWS |
|
41 |
+ clamsubmit_SOURCES += \ |
|
42 |
+ $(top_srcdir)/shared/win/cert_util_win.c \ |
|
43 |
+ $(top_srcdir)/shared/cert_util.h |
|
44 |
+endif |
|
45 |
+if LINUX |
|
46 |
+ clamsubmit_SOURCES += \ |
|
47 |
+ $(top_srcdir)/shared/linux/cert_util_linux.c \ |
|
48 |
+ $(top_srcdir)/shared/cert_util.h |
|
49 |
+endif |
|
50 |
+ |
|
51 |
+ |
|
32 | 52 |
AM_CFLAGS=@WERR_CFLAGS@ @CLAMSUBMIT_CFLAGS@ |
33 | 53 |
DEFS = @DEFS@ -DCL_NOTHREADS |
34 | 54 |
AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav @SSL_CPPFLAGS@ @JSON_CPPFLAGS@ @PCRE_CPPFLAGS@ |
35 | 55 |
LIBS = $(top_builddir)/libclamav/libclamav.la @CLAMSUBMIT_LIBS@ @THREAD_LIBS@ @JSON_LIBS@ |
36 | 56 |
|
57 |
+if MACOS |
|
58 |
+AM_LDFLAGS = -framework CoreFoundation -framework Security |
|
59 |
+endif |
|
60 |
+ |
|
37 | 61 |
AM_INSTALLCHECK_STD_OPTIONS_EXEMPT=clamsubmit$(EXEEXT) |
38 | 62 |
CLEANFILES=*.gcda *.gcno |
... | ... |
@@ -16,6 +16,9 @@ |
16 | 16 |
#include "libclamav/others.h" |
17 | 17 |
#include "shared/misc.h" |
18 | 18 |
#include "shared/getopt.h" |
19 |
+#if defined(C_DARWIN) || defined(_WIN32) |
|
20 |
+#include "shared/cert_util.h" |
|
21 |
+#endif |
|
19 | 22 |
|
20 | 23 |
#define OPTS "e:p:n:N:V:H:h?v?d" |
21 | 24 |
|
... | ... |
@@ -141,96 +144,6 @@ const char *presigned_get_string(json_object *ps_json_obj, char *key) |
141 | 141 |
return json_str; |
142 | 142 |
} |
143 | 143 |
|
144 |
-#ifdef _WIN32 |
|
145 |
-CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr) |
|
146 |
-{ |
|
147 |
- CURLcode status = CURLE_BAD_FUNCTION_ARGUMENT; |
|
148 |
- |
|
149 |
- uint32_t numCertificatesFound = 0; |
|
150 |
- |
|
151 |
- HCERTSTORE hStore = NULL; |
|
152 |
- PCCERT_CONTEXT pWinCertContext = NULL; |
|
153 |
- X509 *x509 = NULL; |
|
154 |
- X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx); |
|
155 |
- |
|
156 |
- hStore = CertOpenSystemStoreA(NULL, "ROOT"); |
|
157 |
- |
|
158 |
- if (NULL == hStore) { |
|
159 |
- fprintf(stderr, "ERROR: Failed to open system certificate store.\n"); |
|
160 |
- goto done; |
|
161 |
- } |
|
162 |
- |
|
163 |
- while (NULL != (pWinCertContext = CertEnumCertificatesInStore(hStore, pWinCertContext))) { |
|
164 |
- int addCertResult = 0; |
|
165 |
- const unsigned char *encoded_cert = pWinCertContext->pbCertEncoded; |
|
166 |
- |
|
167 |
- x509 = NULL; |
|
168 |
- x509 = d2i_X509(NULL, &encoded_cert, pWinCertContext->cbCertEncoded); |
|
169 |
- if (NULL == x509) { |
|
170 |
- fprintf(stderr, "ERROR: Failed to convert system certificate to x509.\n"); |
|
171 |
- continue; |
|
172 |
- } |
|
173 |
- |
|
174 |
- addCertResult = X509_STORE_add_cert(store, x509); |
|
175 |
- if (1 != addCertResult) { |
|
176 |
- fprintf(stderr, "ERROR: Failed to add x509 certificate to openssl certificate store.\n"); |
|
177 |
- continue; |
|
178 |
- } |
|
179 |
- |
|
180 |
- if (g_debug) { |
|
181 |
- char *issuer = NULL; |
|
182 |
- size_t issuerLen = 0; |
|
183 |
- issuerLen = CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); |
|
184 |
- |
|
185 |
- issuer = cli_malloc(issuerLen); |
|
186 |
- if (NULL == issuer) { |
|
187 |
- fprintf(stderr, "ERROR: Failed to allocate memory for certificate name.\n"); |
|
188 |
- status = CURLE_OUT_OF_MEMORY; |
|
189 |
- goto done; |
|
190 |
- } |
|
191 |
- |
|
192 |
- if (0 == CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuer, issuerLen)) { |
|
193 |
- fprintf(stderr, "ERROR: Failed to get friendly display name for certificate.\n"); |
|
194 |
- } else { |
|
195 |
- fprintf(stdout, "Certificate loaded from Windows certificate store: %s\n", issuer); |
|
196 |
- } |
|
197 |
- |
|
198 |
- free(issuer); |
|
199 |
- } |
|
200 |
- |
|
201 |
- numCertificatesFound++; |
|
202 |
- X509_free(x509); |
|
203 |
- } |
|
204 |
- |
|
205 |
- DWORD lastError = GetLastError(); |
|
206 |
- switch (lastError) { |
|
207 |
- case E_INVALIDARG: |
|
208 |
- fprintf(stderr, "ERROR: The handle in the hCertStore parameter is not the same as that in the certificate context pointed to by pPrevCertContext.\n"); |
|
209 |
- break; |
|
210 |
- case CRYPT_E_NOT_FOUND: |
|
211 |
- case ERROR_NO_MORE_FILES: |
|
212 |
- if (0 == numCertificatesFound) { |
|
213 |
- fprintf(stderr, "ERROR: No certificates were found.\n"); |
|
214 |
- } |
|
215 |
- break; |
|
216 |
- default: |
|
217 |
- fprintf(stderr, "ERROR: Unexpected error code from CertEnumCertificatesInStore()\n"); |
|
218 |
- } |
|
219 |
- |
|
220 |
-done: |
|
221 |
- |
|
222 |
- if (NULL != pWinCertContext) { |
|
223 |
- CertFreeCertificateContext(pWinCertContext); |
|
224 |
- } |
|
225 |
- if (NULL != hStore) { |
|
226 |
- CertCloseStore(hStore, 0); |
|
227 |
- } |
|
228 |
- |
|
229 |
- status = CURLE_OK; |
|
230 |
- return status; |
|
231 |
-} |
|
232 |
-#endif |
|
233 |
- |
|
234 | 144 |
int main(int argc, char *argv[]) |
235 | 145 |
{ |
236 | 146 |
int status = 1; |
... | ... |
@@ -327,7 +240,7 @@ int main(int argc, char *argv[]) |
327 | 327 |
fprintf(stderr, "ERROR: Failed to set HTTP version to 1.1 (to prevent 2.0 responses which we don't yet parse properly)!\n"); |
328 | 328 |
} |
329 | 329 |
|
330 |
-#ifdef _WIN32 |
|
330 |
+#if defined(C_DARWIN) || defined(_WIN32) |
|
331 | 331 |
if (CURLE_OK != curl_easy_setopt(clam_curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function)) { |
332 | 332 |
fprintf(stderr, "ERROR: Failed to set SSL CTX function!\n"); |
333 | 333 |
} |
... | ... |
@@ -23,6 +23,8 @@ AC_PREREQ([2.59]) |
23 | 23 |
dnl For a release change [devel] to the real version [0.xy] |
24 | 24 |
dnl also change VERSION below |
25 | 25 |
AC_INIT([ClamAV], [0.102.0-devel], [https://bugzilla.clamav.net/], [clamav], [https://www.clamav.net/]) |
26 |
+dnl put configure auxiliary into config |
|
27 |
+AC_CONFIG_AUX_DIR([config]) |
|
26 | 28 |
|
27 | 29 |
dnl put configure auxiliary into config |
28 | 30 |
AC_CONFIG_AUX_DIR([config]) |
... | ... |
@@ -155,6 +157,28 @@ else |
155 | 155 |
mspack_msg="External, $LIBMSPACK_CFLAGS $LIBMSPACK_LIBS" |
156 | 156 |
fi |
157 | 157 |
|
158 |
+# Detect the target system |
|
159 |
+build_linux=no |
|
160 |
+build_windows=no |
|
161 |
+build_mac=no |
|
162 |
+ |
|
163 |
+case "${host_os}" in |
|
164 |
+ cygwin*|mingw*) |
|
165 |
+ build_windows=yes |
|
166 |
+ ;; |
|
167 |
+ darwin*) |
|
168 |
+ AC_PROG_OBJC |
|
169 |
+ build_mac=yes |
|
170 |
+ ;; |
|
171 |
+ *) |
|
172 |
+ build_linux=yes |
|
173 |
+ ;; |
|
174 |
+esac |
|
175 |
+ |
|
176 |
+AM_CONDITIONAL([LINUX], [test "$build_linux" = "yes"]) |
|
177 |
+AM_CONDITIONAL([WINDOWS], [test "$build_windows" = "yes"]) |
|
178 |
+AM_CONDITIONAL([MACOS], [test "$build_mac" = "yes"]) |
|
179 |
+ |
|
158 | 180 |
AC_CONFIG_FILES([ |
159 | 181 |
clamscan/Makefile |
160 | 182 |
database/Makefile |
... | ... |
@@ -20,6 +20,10 @@ libfreshclam_la_LIBADD = $(top_builddir)/libclamav/libclamav.la @FRESHCLAM_LIBS@ |
20 | 20 |
libfreshclam_la_DEPENDENCIES = @LTDLDEPS@ |
21 | 21 |
libfreshclam_la_LDFLAGS = @CURL_LDFLAGS@ @SSL_LDFLAGS@ @TH_SAFE@ @JSON_LDFLAGS@ @ICONV_LDFLAGS@ $(XML_LIBS) -version-info @LIBFRESHCLAM_VERSION@ -no-undefined |
22 | 22 |
|
23 |
+if MACOS |
|
24 |
+libfreshclam_la_LDFLAGS += -framework CoreFoundation -framework Security |
|
25 |
+endif |
|
26 |
+ |
|
23 | 27 |
if VERSIONSCRIPT |
24 | 28 |
libfreshclam_la_LDFLAGS += -Wl,@VERSIONSCRIPTFLAG@,@top_srcdir@/libfreshclam/libfreshclam.map |
25 | 29 |
endif |
... | ... |
@@ -41,12 +45,31 @@ libfreshclam_la_SOURCES = \ |
41 | 41 |
$(top_srcdir)/shared/cdiff.h \ |
42 | 42 |
$(top_srcdir)/shared/tar.c \ |
43 | 43 |
$(top_srcdir)/shared/tar.h \ |
44 |
+ $(top_srcdir)/shared/cert_util.c \ |
|
45 |
+ $(top_srcdir)/shared/cert_util.h \ |
|
46 |
+ $(top_srcdir)/shared/cert_util_internal.h \ |
|
44 | 47 |
libfreshclam.c \ |
45 | 48 |
libfreshclam_internal.c \ |
46 | 49 |
libfreshclam_internal.h \ |
47 | 50 |
dns.c \ |
48 | 51 |
dns.h |
49 | 52 |
|
53 |
+if MACOS |
|
54 |
+ libfreshclam_la_SOURCES += \ |
|
55 |
+ $(top_srcdir)/shared/mac/cert_util_mac.m \ |
|
56 |
+ $(top_srcdir)/shared/cert_util.h |
|
57 |
+endif |
|
58 |
+if WINDOWS |
|
59 |
+ libfreshclam_la_SOURCES += \ |
|
60 |
+ $(top_srcdir)/shared/win/cert_util_win.c \ |
|
61 |
+ $(top_srcdir)/shared/cert_util.h |
|
62 |
+endif |
|
63 |
+if LINUX |
|
64 |
+ libfreshclam_la_SOURCES += \ |
|
65 |
+ $(top_srcdir)/shared/linux/cert_util_linux.c \ |
|
66 |
+ $(top_srcdir)/shared/cert_util.h |
|
67 |
+endif |
|
68 |
+ |
|
50 | 69 |
lib_LTLIBRARIES = libfreshclam.la |
51 | 70 |
|
52 | 71 |
|
... | ... |
@@ -65,8 +65,8 @@ |
65 | 65 |
#include <zlib.h> |
66 | 66 |
#include <math.h> |
67 | 67 |
|
68 |
-#ifdef _WIN32 |
|
69 |
-#include <wincrypt.h> |
|
68 |
+#if defined(C_DARWIN) || defined(_WIN32) |
|
69 |
+#include <cert_util.h> |
|
70 | 70 |
#endif |
71 | 71 |
|
72 | 72 |
#include <curl/curl.h> |
... | ... |
@@ -82,6 +82,7 @@ |
82 | 82 |
#include "shared/cdiff.h" |
83 | 83 |
#include "shared/tar.h" |
84 | 84 |
#include "shared/clamdcom.h" |
85 |
+#include "shared/cert_util.h" |
|
85 | 86 |
|
86 | 87 |
#include "libclamav/clamav.h" |
87 | 88 |
#include "libclamav/others.h" |
... | ... |
@@ -136,97 +137,6 @@ static int textrecordfield(const char *database) |
136 | 136 |
return 0; |
137 | 137 |
} |
138 | 138 |
|
139 |
-#ifdef _WIN32 |
|
140 |
-CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr) |
|
141 |
-{ |
|
142 |
- CURLcode status = CURLE_BAD_FUNCTION_ARGUMENT; |
|
143 |
- |
|
144 |
- uint32_t numCertificatesFound = 0; |
|
145 |
- DWORD lastError; |
|
146 |
- |
|
147 |
- HCERTSTORE hStore = NULL; |
|
148 |
- PCCERT_CONTEXT pWinCertContext = NULL; |
|
149 |
- X509 *x509 = NULL; |
|
150 |
- X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx); |
|
151 |
- |
|
152 |
- hStore = CertOpenSystemStoreA(NULL, "ROOT"); |
|
153 |
- |
|
154 |
- if (NULL == hStore) { |
|
155 |
- logg("!Failed to open system certificate store.\n"); |
|
156 |
- goto done; |
|
157 |
- } |
|
158 |
- |
|
159 |
- while (NULL != (pWinCertContext = CertEnumCertificatesInStore(hStore, pWinCertContext))) { |
|
160 |
- int addCertResult = 0; |
|
161 |
- const unsigned char *encoded_cert = pWinCertContext->pbCertEncoded; |
|
162 |
- |
|
163 |
- x509 = NULL; |
|
164 |
- x509 = d2i_X509(NULL, &encoded_cert, pWinCertContext->cbCertEncoded); |
|
165 |
- if (NULL == x509) { |
|
166 |
- logg("!Failed to convert system certificate to x509.\n"); |
|
167 |
- continue; |
|
168 |
- } |
|
169 |
- |
|
170 |
- addCertResult = X509_STORE_add_cert(store, x509); |
|
171 |
- if (1 != addCertResult) { |
|
172 |
- logg("!Failed to add x509 certificate to openssl certificate store.\n"); |
|
173 |
- continue; |
|
174 |
- } |
|
175 |
- |
|
176 |
- if (logg_verbose) { |
|
177 |
- char *issuer = NULL; |
|
178 |
- size_t issuerLen = 0; |
|
179 |
- issuerLen = CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); |
|
180 |
- |
|
181 |
- issuer = cli_malloc(issuerLen); |
|
182 |
- if (NULL == issuer) { |
|
183 |
- logg("!Failed to allocate memory for certificate name.\n"); |
|
184 |
- status = CURLE_OUT_OF_MEMORY; |
|
185 |
- goto done; |
|
186 |
- } |
|
187 |
- |
|
188 |
- if (0 == CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuer, issuerLen)) { |
|
189 |
- logg("!Failed to get friendly display name for certificate.\n"); |
|
190 |
- } else { |
|
191 |
- logg("Certificate loaded from Windows certificate store: %s\n", issuer); |
|
192 |
- } |
|
193 |
- |
|
194 |
- free(issuer); |
|
195 |
- } |
|
196 |
- |
|
197 |
- numCertificatesFound++; |
|
198 |
- X509_free(x509); |
|
199 |
- } |
|
200 |
- |
|
201 |
- lastError = GetLastError(); |
|
202 |
- switch (lastError) { |
|
203 |
- case E_INVALIDARG: |
|
204 |
- logg("!The handle in the hCertStore parameter is not the same as that in the certificate context pointed to by pPrevCertContext.\n"); |
|
205 |
- break; |
|
206 |
- case CRYPT_E_NOT_FOUND: |
|
207 |
- case ERROR_NO_MORE_FILES: |
|
208 |
- if (0 == numCertificatesFound) { |
|
209 |
- logg("!No certificates were found.\n"); |
|
210 |
- } |
|
211 |
- break; |
|
212 |
- default: |
|
213 |
- logg("!Unexpected error code from CertEnumCertificatesInStore()\n"); |
|
214 |
- } |
|
215 |
- |
|
216 |
-done: |
|
217 |
- |
|
218 |
- if (NULL != pWinCertContext) { |
|
219 |
- CertFreeCertificateContext(pWinCertContext); |
|
220 |
- } |
|
221 |
- if (NULL != hStore) { |
|
222 |
- CertCloseStore(hStore, 0); |
|
223 |
- } |
|
224 |
- |
|
225 |
- status = CURLE_OK; |
|
226 |
- return status; |
|
227 |
-} |
|
228 |
-#endif |
|
229 |
- |
|
230 | 139 |
#if LIBCURL_VERSION_NUM >= 0x073d00 |
231 | 140 |
/* In libcurl 7.61.0, support was added for extracting the time in plain |
232 | 141 |
microseconds. Older libcurl versions are stuck in using 'double' for this |
... | ... |
@@ -497,7 +407,7 @@ static fc_error_t create_curl_handle( |
497 | 497 |
} |
498 | 498 |
} |
499 | 499 |
|
500 |
-#ifdef _WIN32 |
|
500 |
+#if defined(C_DARWIN) || defined(_WIN32) |
|
501 | 501 |
if (CURLE_OK != curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, *sslctx_function)) { |
502 | 502 |
logg("!create_curl_handle: Failed to set SSL CTX function!\n"); |
503 | 503 |
} |
504 | 504 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,546 @@ |
0 |
+/* |
|
1 |
+ * OpenSSL certificate caching. |
|
2 |
+ * |
|
3 |
+ * Copyright (C) 2016-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
|
4 |
+ * |
|
5 |
+ * Authors: Russ Kubik |
|
6 |
+ * |
|
7 |
+ * This program is free software; you can redistribute it and/or modify |
|
8 |
+ * it under the terms of the GNU General Public License version 2 as |
|
9 |
+ * published by the Free Software Foundation. |
|
10 |
+ * |
|
11 |
+ * This program is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License |
|
17 |
+ * along with this program; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
19 |
+ * MA 02110-1301, USA. |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#include <openssl/x509.h> |
|
23 |
+#include <openssl/pem.h> |
|
24 |
+#include <openssl/err.h> |
|
25 |
+#include <string.h> |
|
26 |
+#include <pthread.h> |
|
27 |
+ |
|
28 |
+#include "cert_util.h" |
|
29 |
+#include "cert_util_internal.h" |
|
30 |
+ |
|
31 |
+#include "output.h" |
|
32 |
+ |
|
33 |
+static cert_store_t _cert_store = { |
|
34 |
+ .mutex = PTHREAD_MUTEX_INITIALIZER}; |
|
35 |
+ |
|
36 |
+static cl_error_t _x509_to_pem(X509 *cert, |
|
37 |
+ char **data, |
|
38 |
+ int *len) |
|
39 |
+{ |
|
40 |
+ cl_error_t ret = CL_EFORMAT; |
|
41 |
+ |
|
42 |
+ BIO *out = NULL; |
|
43 |
+ long pem_len = 0; |
|
44 |
+ char *pem_data = NULL; |
|
45 |
+ |
|
46 |
+ if (cert == NULL || data == NULL || len == NULL) { |
|
47 |
+ mprintf("!_x509_to_pem: Invalid argument\n"); |
|
48 |
+ goto done; |
|
49 |
+ } |
|
50 |
+ |
|
51 |
+ /* Output the certs to a new BIO using the PEM format */ |
|
52 |
+ out = BIO_new(BIO_s_mem()); |
|
53 |
+ if (!out) { |
|
54 |
+ mprintf("!BIO_new failed\n"); |
|
55 |
+ goto done; |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ PEM_write_bio_X509(out, cert); |
|
59 |
+ |
|
60 |
+ (void)BIO_flush(out); |
|
61 |
+ |
|
62 |
+ /* Convert the BIO to char* */ |
|
63 |
+ pem_len = BIO_get_mem_data(out, &pem_data); |
|
64 |
+ if (pem_len <= 0 || !pem_data) { |
|
65 |
+ mprintf("!BIO_new: BIO_get_mem_data failed\n"); |
|
66 |
+ BIO_free_all(out); |
|
67 |
+ goto done; |
|
68 |
+ } |
|
69 |
+ |
|
70 |
+ *data = calloc(1, pem_len + 1); |
|
71 |
+ if (!*data) { |
|
72 |
+ mprintf("!BIO_new: malloc failed\n"); |
|
73 |
+ BIO_free_all(out); |
|
74 |
+ goto done; |
|
75 |
+ } |
|
76 |
+ memcpy(*data, pem_data, pem_len); |
|
77 |
+ (*data)[pem_len] = '\0'; |
|
78 |
+ |
|
79 |
+ *len = (int)pem_len; |
|
80 |
+ |
|
81 |
+ BIO_free_all(out); |
|
82 |
+ |
|
83 |
+ ret = CL_SUCCESS; |
|
84 |
+ |
|
85 |
+done: |
|
86 |
+ return (0); |
|
87 |
+} |
|
88 |
+ |
|
89 |
+/** |
|
90 |
+ * @brief This method will convert a X509 certificate to PEM format and append |
|
91 |
+ * it to a string buffer. |
|
92 |
+ * |
|
93 |
+ * @note If realloc fails to reserve memory for *cert_data it will free whatever |
|
94 |
+ * is currently in *cert_data before returning. total_buf_len is also set |
|
95 |
+ * to 0 (zero) in this case. |
|
96 |
+ * |
|
97 |
+ * @param[in] *ca_cert Pointer to CA certificate |
|
98 |
+ * @param[out] **cert_data Pointer to allocated string buffer |
|
99 |
+ * @param[out] *total_buf_len Total of string buffer length after appending |
|
100 |
+ * CA certificate (ca_cert) |
|
101 |
+ * @param[in,out] *remaining_buf_len Remaining data left allowed in CA certificate |
|
102 |
+ * chain after appending CA certificate |
|
103 |
+ * (ca_cert) |
|
104 |
+ * |
|
105 |
+ * @return 0 on success, -1 on error |
|
106 |
+ */ |
|
107 |
+static cl_error_t _x509_to_pem_append(X509 *ca_cert, |
|
108 |
+ char **cert_data, |
|
109 |
+ int *total_buf_len, |
|
110 |
+ size_t *remaining_buf_len) |
|
111 |
+{ |
|
112 |
+ char *pem_data = NULL; |
|
113 |
+ char *tmp; |
|
114 |
+ int pem_data_len = 0; |
|
115 |
+ cl_error_t ret = CL_EOPEN; |
|
116 |
+ int current_len = 0; |
|
117 |
+ |
|
118 |
+ if (ca_cert == NULL || total_buf_len == NULL || |
|
119 |
+ remaining_buf_len == NULL || *cert_data == NULL) { |
|
120 |
+ mprintf("!NULL parameter given\n"); |
|
121 |
+ goto done; |
|
122 |
+ } |
|
123 |
+ |
|
124 |
+ current_len = *total_buf_len; |
|
125 |
+ |
|
126 |
+ if (_x509_to_pem(ca_cert, &pem_data, &pem_data_len) != 0) { |
|
127 |
+ mprintf("!Failed to convert x509 certificate to PEM\n"); |
|
128 |
+ goto done; |
|
129 |
+ } |
|
130 |
+ |
|
131 |
+ if (pem_data_len > (int)*remaining_buf_len) { |
|
132 |
+ tmp = realloc(*cert_data, current_len + pem_data_len + 1); |
|
133 |
+ if (tmp == NULL) { |
|
134 |
+ mprintf("!Could not realloc enough memory for PEM " |
|
135 |
+ "certificate\n"); |
|
136 |
+ |
|
137 |
+ free(*cert_data); |
|
138 |
+ *cert_data = NULL; |
|
139 |
+ *total_buf_len = 0; |
|
140 |
+ |
|
141 |
+ goto done; |
|
142 |
+ } |
|
143 |
+ *cert_data = tmp; |
|
144 |
+ tmp = NULL; |
|
145 |
+ *remaining_buf_len = 0; |
|
146 |
+ } else { |
|
147 |
+ *remaining_buf_len -= pem_data_len; |
|
148 |
+ } |
|
149 |
+ |
|
150 |
+ memcpy(&((*cert_data)[current_len]), pem_data, pem_data_len); |
|
151 |
+ *total_buf_len = current_len + pem_data_len; |
|
152 |
+ (*cert_data)[*total_buf_len] = '\0'; |
|
153 |
+ |
|
154 |
+ ret = CL_SUCCESS; |
|
155 |
+ |
|
156 |
+done: |
|
157 |
+ |
|
158 |
+ free(pem_data); |
|
159 |
+ pem_data = NULL; |
|
160 |
+ return ret; |
|
161 |
+} |
|
162 |
+ |
|
163 |
+cert_store_t *cert_store_get_int(void) |
|
164 |
+{ |
|
165 |
+ return &_cert_store; |
|
166 |
+} |
|
167 |
+ |
|
168 |
+void cert_store_unload_int(void) |
|
169 |
+{ |
|
170 |
+ if (_cert_store.loaded) { |
|
171 |
+ cert_store_free_cert_list_int(&_cert_store.system_certs); |
|
172 |
+ cert_store_free_cert_list_int(&_cert_store.trusted_certs); |
|
173 |
+ _cert_store.loaded = false; |
|
174 |
+ } |
|
175 |
+} |
|
176 |
+ |
|
177 |
+void cert_store_free_cert_list_int(cert_list_t *cert_list) |
|
178 |
+{ |
|
179 |
+ size_t i; |
|
180 |
+ |
|
181 |
+ if (cert_list && cert_list->certificates) { |
|
182 |
+ for (i = 0; i < cert_list->count; ++i) { |
|
183 |
+ X509_free(cert_list->certificates[i]); |
|
184 |
+ cert_list->certificates[i] = NULL; |
|
185 |
+ } |
|
186 |
+ |
|
187 |
+ free(cert_list->certificates); |
|
188 |
+ cert_list->certificates = NULL; |
|
189 |
+ cert_list->count = 0L; |
|
190 |
+ } |
|
191 |
+} |
|
192 |
+ |
|
193 |
+void cert_store_unload(void) |
|
194 |
+{ |
|
195 |
+ int pt_err; |
|
196 |
+ |
|
197 |
+ pt_err = pthread_mutex_lock(&_cert_store.mutex); |
|
198 |
+ if (pt_err) { |
|
199 |
+ errno = pt_err; |
|
200 |
+ mprintf("!Mutex lock failed\n"); |
|
201 |
+ } |
|
202 |
+ |
|
203 |
+ cert_store_unload_int(); |
|
204 |
+ |
|
205 |
+ pt_err = pthread_mutex_unlock(&_cert_store.mutex); |
|
206 |
+ if (pt_err) { |
|
207 |
+ errno = pt_err; |
|
208 |
+ mprintf("!Mutex unlock failed\n"); |
|
209 |
+ } |
|
210 |
+} |
|
211 |
+ |
|
212 |
+cl_error_t cert_store_export_pem(char **cert_data, |
|
213 |
+ int *cert_data_len, |
|
214 |
+ X509 *additional_ca_cert) |
|
215 |
+{ |
|
216 |
+ const uint32_t STARTING_RAW_PEM_LENGTH = 350 * 1024; |
|
217 |
+ uint32_t i; |
|
218 |
+ cl_error_t ret = CL_EOPEN; |
|
219 |
+ bool locked = false; |
|
220 |
+ int pt_err; |
|
221 |
+ |
|
222 |
+ size_t remaining_buf_len = STARTING_RAW_PEM_LENGTH; |
|
223 |
+ bool add_additional_ca_cert = true; |
|
224 |
+ |
|
225 |
+ if ((cert_data == NULL) || (cert_data_len == NULL)) { |
|
226 |
+ mprintf("!One or more arguments are NULL\n"); |
|
227 |
+ goto done; |
|
228 |
+ } |
|
229 |
+ |
|
230 |
+ *cert_data = calloc(1, STARTING_RAW_PEM_LENGTH + 1); |
|
231 |
+ if (*cert_data == NULL) { |
|
232 |
+ mprintf("!Could not allocate memory for PEM certs\n"); |
|
233 |
+ goto done; |
|
234 |
+ } |
|
235 |
+ *cert_data_len = 0; |
|
236 |
+ |
|
237 |
+ pt_err = pthread_mutex_lock(&_cert_store.mutex); |
|
238 |
+ if (pt_err) { |
|
239 |
+ errno = pt_err; |
|
240 |
+ mprintf("!Mutex lock failed\n"); |
|
241 |
+ } |
|
242 |
+ locked = true; |
|
243 |
+ |
|
244 |
+ if (!_cert_store.loaded) { |
|
245 |
+ goto done; |
|
246 |
+ } |
|
247 |
+ |
|
248 |
+ /* Load system root ca certs into list */ |
|
249 |
+ for (i = 0; i < _cert_store.system_certs.count; ++i) { |
|
250 |
+ if (_x509_to_pem_append(_cert_store.system_certs.certificates[i], |
|
251 |
+ cert_data, |
|
252 |
+ cert_data_len, |
|
253 |
+ &remaining_buf_len) != 0) { |
|
254 |
+ goto done; |
|
255 |
+ } |
|
256 |
+ /* |
|
257 |
+ * Two certs by the same name can cause conflicts. Trust the |
|
258 |
+ * one in the OS certificate/key store if the additional CA |
|
259 |
+ * name matches that of one in the store. |
|
260 |
+ */ |
|
261 |
+ if (additional_ca_cert && additional_ca_cert->cert_info && |
|
262 |
+ (strcmp(_cert_store.system_certs.certificates[i]->name, |
|
263 |
+ additional_ca_cert->name) == 0)) { |
|
264 |
+ add_additional_ca_cert = false; |
|
265 |
+ } |
|
266 |
+ } |
|
267 |
+ |
|
268 |
+ /* Load trusted ca certs into list */ |
|
269 |
+ for (i = 0; i < _cert_store.trusted_certs.count; ++i) { |
|
270 |
+ if (_x509_to_pem_append(_cert_store.trusted_certs.certificates[i], |
|
271 |
+ cert_data, |
|
272 |
+ cert_data_len, |
|
273 |
+ &remaining_buf_len) != 0) { |
|
274 |
+ goto done; |
|
275 |
+ } |
|
276 |
+ /* |
|
277 |
+ * Two certs by the same name can cause conflicts. Trust the |
|
278 |
+ * one in the OS certificate/key store if the additional CA |
|
279 |
+ * name matches that of one in the store. |
|
280 |
+ */ |
|
281 |
+ if (additional_ca_cert && additional_ca_cert->cert_info && |
|
282 |
+ (strcmp(_cert_store.trusted_certs.certificates[i]->name, |
|
283 |
+ additional_ca_cert->name) == 0)) { |
|
284 |
+ add_additional_ca_cert = false; |
|
285 |
+ } |
|
286 |
+ } |
|
287 |
+ |
|
288 |
+ /* End with the additional CA certificate if provided */ |
|
289 |
+ if (additional_ca_cert && add_additional_ca_cert && *cert_data) { |
|
290 |
+ /* Return an error only if we were unable to allocate memory */ |
|
291 |
+ if (_x509_to_pem_append(additional_ca_cert, |
|
292 |
+ cert_data, |
|
293 |
+ cert_data_len, |
|
294 |
+ &remaining_buf_len) != 0) { |
|
295 |
+ goto done; |
|
296 |
+ } |
|
297 |
+ } |
|
298 |
+ |
|
299 |
+ ret = CL_SUCCESS; |
|
300 |
+done: |
|
301 |
+ if (locked) { |
|
302 |
+ pt_err = pthread_mutex_unlock(&_cert_store.mutex); |
|
303 |
+ if (pt_err) { |
|
304 |
+ errno = pt_err; |
|
305 |
+ mprintf("!Mutex unlock failed\n"); |
|
306 |
+ } |
|
307 |
+ locked = false; |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ if (ret != CL_SUCCESS && cert_data && *cert_data) { |
|
311 |
+ free(*cert_data); |
|
312 |
+ *cert_data = NULL; |
|
313 |
+ } |
|
314 |
+ |
|
315 |
+ return ret; |
|
316 |
+} |
|
317 |
+ |
|
318 |
+cl_error_t cert_store_set_trusted_int(X509 **trusted_certs, size_t trusted_cert_count) |
|
319 |
+{ |
|
320 |
+ cl_error_t ret = CL_EOPEN; |
|
321 |
+ size_t i, j; |
|
322 |
+ cert_list_t tmp_trusted = {0}; |
|
323 |
+ |
|
324 |
+ do { |
|
325 |
+ if ((trusted_certs == NULL) || (trusted_cert_count == 0)) { |
|
326 |
+ mprintf("!Empty trusted certificate list\n"); |
|
327 |
+ break; |
|
328 |
+ } |
|
329 |
+ |
|
330 |
+ tmp_trusted.certificates = calloc(trusted_cert_count, |
|
331 |
+ sizeof(*tmp_trusted.certificates)); |
|
332 |
+ if (!tmp_trusted.certificates) { |
|
333 |
+ mprintf("!Failed to reserve memory for trusted certs\n"); |
|
334 |
+ break; |
|
335 |
+ } |
|
336 |
+ |
|
337 |
+ for (i = 0; i < trusted_cert_count; ++i) { |
|
338 |
+ bool found = false; |
|
339 |
+ |
|
340 |
+ /* Check if certificate already exists in system root cert list */ |
|
341 |
+ for (j = 0; j < _cert_store.system_certs.count; ++j) { |
|
342 |
+ if (X509_cmp(trusted_certs[i], |
|
343 |
+ _cert_store.system_certs.certificates[j]) == 0) { |
|
344 |
+ found = true; |
|
345 |
+ } |
|
346 |
+ } |
|
347 |
+ |
|
348 |
+ if (found) { |
|
349 |
+ continue; /* certificate is already found in cert store */ |
|
350 |
+ } |
|
351 |
+ |
|
352 |
+ tmp_trusted.certificates[tmp_trusted.count] = |
|
353 |
+ X509_dup(trusted_certs[i]); |
|
354 |
+ if (!tmp_trusted.certificates[tmp_trusted.count]) { |
|
355 |
+ mprintf("!X509_dup failed at index: %zu", i); |
|
356 |
+ continue; /* continue on error */ |
|
357 |
+ } |
|
358 |
+ |
|
359 |
+ tmp_trusted.count++; |
|
360 |
+ } |
|
361 |
+ |
|
362 |
+ cert_store_free_cert_list_int(&_cert_store.trusted_certs); |
|
363 |
+ |
|
364 |
+ _cert_store.trusted_certs.certificates = tmp_trusted.certificates; |
|
365 |
+ _cert_store.trusted_certs.count = tmp_trusted.count; |
|
366 |
+ |
|
367 |
+ tmp_trusted.certificates = NULL; |
|
368 |
+ tmp_trusted.count = 0; |
|
369 |
+ |
|
370 |
+ ret = CL_SUCCESS; |
|
371 |
+ } while (0); |
|
372 |
+ |
|
373 |
+ return ret; |
|
374 |
+} |
|
375 |
+ |
|
376 |
+cl_error_t cert_store_set_trusted(X509 **trusted_certs, size_t trusted_cert_count) |
|
377 |
+{ |
|
378 |
+ cl_error_t ret = CL_EOPEN; |
|
379 |
+ int pt_err; |
|
380 |
+ |
|
381 |
+ pt_err = pthread_mutex_lock(&_cert_store.mutex); |
|
382 |
+ if (pt_err) { |
|
383 |
+ errno = pt_err; |
|
384 |
+ mprintf("!Mutex lock failed\n"); |
|
385 |
+ } |
|
386 |
+ |
|
387 |
+ if (_cert_store.loaded) { |
|
388 |
+ ret = cert_store_set_trusted_int(trusted_certs, trusted_cert_count); |
|
389 |
+ } |
|
390 |
+ |
|
391 |
+ pt_err = pthread_mutex_unlock(&_cert_store.mutex); |
|
392 |
+ if (pt_err) { |
|
393 |
+ errno = pt_err; |
|
394 |
+ mprintf("!Mutex unlock failed\n"); |
|
395 |
+ } |
|
396 |
+ |
|
397 |
+ return ret; |
|
398 |
+} |
|
399 |
+ |
|
400 |
+size_t cert_store_remove_trusted(void) |
|
401 |
+{ |
|
402 |
+ size_t count = 0; |
|
403 |
+ int pt_err; |
|
404 |
+ |
|
405 |
+ pt_err = pthread_mutex_lock(&_cert_store.mutex); |
|
406 |
+ if (pt_err) { |
|
407 |
+ errno = pt_err; |
|
408 |
+ mprintf("!Mutex lock failed\n"); |
|
409 |
+ } |
|
410 |
+ |
|
411 |
+ if (_cert_store.loaded) { |
|
412 |
+ count = _cert_store.trusted_certs.count; |
|
413 |
+ cert_store_free_cert_list_int(&_cert_store.trusted_certs); |
|
414 |
+ } |
|
415 |
+ |
|
416 |
+ pt_err = pthread_mutex_unlock(&_cert_store.mutex); |
|
417 |
+ if (pt_err) { |
|
418 |
+ errno = pt_err; |
|
419 |
+ mprintf("!Mutex unlock failed\n"); |
|
420 |
+ } |
|
421 |
+ |
|
422 |
+ return count; |
|
423 |
+} |
|
424 |
+ |
|
425 |
+void cert_fill_X509_store(X509_STORE *store, X509 **certs, size_t cert_count) |
|
426 |
+{ |
|
427 |
+ size_t i; |
|
428 |
+ unsigned long err; |
|
429 |
+ |
|
430 |
+ if (store && certs && cert_count > 0) { |
|
431 |
+ for (i = 0; i < cert_count; ++i) { |
|
432 |
+ if (!certs[i]) { |
|
433 |
+ mprintf("!NULL cert at index %zu in X509 cert list; skipping", i); |
|
434 |
+ continue; |
|
435 |
+ } |
|
436 |
+ if (X509_STORE_add_cert(store, certs[i]) != 1) { |
|
437 |
+ err = ERR_get_error(); |
|
438 |
+ if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) { |
|
439 |
+ mprintf("*Certificate skipped; already exists in store: %s", |
|
440 |
+ (certs[i]->name ? certs[i]->name : "")); |
|
441 |
+ } else { |
|
442 |
+ mprintf("!Failed to add certificate to store: %s (%lu) [%s]", |
|
443 |
+ ERR_error_string(err, NULL), err, |
|
444 |
+ (certs[i]->name ? certs[i]->name : "")); |
|
445 |
+ } |
|
446 |
+ } |
|
447 |
+ } |
|
448 |
+ } |
|
449 |
+} |
|
450 |
+ |
|
451 |
+void cert_store_export_certs(X509_STORE *store, X509 *additional_ca_cert) |
|
452 |
+{ |
|
453 |
+ cert_store_t *cert_store = NULL; |
|
454 |
+ int pt_err; |
|
455 |
+ |
|
456 |
+ do { |
|
457 |
+ if (!store) { |
|
458 |
+ mprintf("!NULL X509 store\n"); |
|
459 |
+ break; |
|
460 |
+ } |
|
461 |
+ |
|
462 |
+ cert_store = cert_store_get_int(); |
|
463 |
+ if (!cert_store) { |
|
464 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
465 |
+ break; |
|
466 |
+ } |
|
467 |
+ |
|
468 |
+ pt_err = pthread_mutex_lock(&cert_store->mutex); |
|
469 |
+ if (pt_err) { |
|
470 |
+ errno = pt_err; |
|
471 |
+ mprintf("!Mutex lock failed\n"); |
|
472 |
+ } |
|
473 |
+ |
|
474 |
+ if (!cert_store->loaded) { |
|
475 |
+ mprintf("!Cert store not loaded\n"); |
|
476 |
+ break; |
|
477 |
+ } |
|
478 |
+ |
|
479 |
+ /* On Linux, system certificates are loaded by OpenSSL */ |
|
480 |
+#if defined(_WIN32) || defined(DARWIN) |
|
481 |
+ cert_fill_X509_store(store, |
|
482 |
+ cert_store->system_certs.certificates, |
|
483 |
+ cert_store->system_certs.count); |
|
484 |
+#endif |
|
485 |
+ |
|
486 |
+ cert_fill_X509_store(store, |
|
487 |
+ cert_store->trusted_certs.certificates, |
|
488 |
+ cert_store->trusted_certs.count); |
|
489 |
+ |
|
490 |
+ /* Adding the additional CA cert to the trustchain */ |
|
491 |
+ if ((additional_ca_cert != NULL) && |
|
492 |
+ (X509_STORE_add_cert(store, additional_ca_cert) != 1)) { |
|
493 |
+ unsigned long err = ERR_get_error(); |
|
494 |
+ if (X509_R_CERT_ALREADY_IN_HASH_TABLE == ERR_GET_REASON(err)) { |
|
495 |
+ mprintf("Certificate is already in trust [%s]", |
|
496 |
+ (additional_ca_cert->name ? additional_ca_cert->name : "")); |
|
497 |
+ } else { |
|
498 |
+ mprintf("!Failed to add CA certificate for the SSL context. " |
|
499 |
+ "Error: %d [%s]", |
|
500 |
+ ERR_GET_REASON(err), |
|
501 |
+ (additional_ca_cert->name ? additional_ca_cert->name : "")); |
|
502 |
+ } |
|
503 |
+ } |
|
504 |
+ } while (0); |
|
505 |
+ |
|
506 |
+ if (cert_store) { |
|
507 |
+ pt_err = pthread_mutex_unlock(&cert_store->mutex); |
|
508 |
+ if (pt_err) { |
|
509 |
+ errno = pt_err; |
|
510 |
+ mprintf("!Mutex unlock failed\n"); |
|
511 |
+ } |
|
512 |
+ } |
|
513 |
+} |
|
514 |
+ |
|
515 |
+CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr) |
|
516 |
+{ |
|
517 |
+ CURLcode status = CURLE_BAD_FUNCTION_ARGUMENT; |
|
518 |
+ cert_store_t *cert_store = NULL; |
|
519 |
+ |
|
520 |
+ UNUSEDPARAM(curl); |
|
521 |
+ UNUSEDPARAM(userptr); |
|
522 |
+ |
|
523 |
+ cert_store = cert_store_get_int(); |
|
524 |
+ if (!cert_store) { |
|
525 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
526 |
+ goto done; |
|
527 |
+ } |
|
528 |
+ |
|
529 |
+ if (!cert_store->loaded) { |
|
530 |
+ if (CL_SUCCESS != cert_store_load(NULL, 0)) { |
|
531 |
+ mprintf("!Failed to load cert store\n"); |
|
532 |
+ goto done; |
|
533 |
+ } |
|
534 |
+ } |
|
535 |
+ |
|
536 |
+ X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx); |
|
537 |
+ |
|
538 |
+ cert_store_export_certs(store, NULL); |
|
539 |
+ |
|
540 |
+ status = CURLE_OK; |
|
541 |
+ |
|
542 |
+done: |
|
543 |
+ |
|
544 |
+ return status; |
|
545 |
+} |
0 | 546 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,121 @@ |
0 |
+/** |
|
1 |
+ * OpenSSL certificate store |
|
2 |
+ * |
|
3 |
+ * @file cert_util.h |
|
4 |
+ * |
|
5 |
+ * @author Russ Kubik |
|
6 |
+ * @date 2016-05-11 |
|
7 |
+ * @copyright Copyright (c) 2016 Cisco Systems, Inc. |
|
8 |
+ * |
|
9 |
+ * @section DESCRIPTION |
|
10 |
+ * OpenSSL certificate store |
|
11 |
+ */ |
|
12 |
+#ifndef _CERT_UTIL_H |
|
13 |
+#define _CERT_UTIL_H |
|
14 |
+ |
|
15 |
+#include <openssl/x509.h> |
|
16 |
+ |
|
17 |
+#include <curl/curl.h> |
|
18 |
+ |
|
19 |
+#include "clamav.h" |
|
20 |
+ |
|
21 |
+/* As defined by ub-common-name in https://www.ietf.org/rfc/rfc3280.txt */ |
|
22 |
+#define X509_COMMON_NAME_MAX_LEN (64) |
|
23 |
+ |
|
24 |
+/** |
|
25 |
+ * @brief Load system and trusted root certificates into memory. Any errors |
|
26 |
+ * while loading trusted certificates will be ignored. If error checking |
|
27 |
+ * is required for trusted certificates please use cert_store_set_trusted |
|
28 |
+ * directly. |
|
29 |
+ * |
|
30 |
+ * @details To load the certificate store with system certificates only pass |
|
31 |
+ * NULL for trusted_certs and 0 (zero) for trusted_cert_count. The |
|
32 |
+ * certificates store will then only load root certificates from the |
|
33 |
+ * system and skip setting trusted certificates (which are |
|
34 |
+ * optional and can be set later with cert_store_set_trusted). |
|
35 |
+ * |
|
36 |
+ * @param[in] trusted_certs - List of X509 trusted root certificates (NULL for |
|
37 |
+ * empty or no trusted certificates) |
|
38 |
+ * @param[in] trusted_cert_count - Number of trusted root certificates (0 for |
|
39 |
+ * empty or no trusted certificates) |
|
40 |
+ * |
|
41 |
+ * @return 0 on success or if the cert store is already loaded, -1 on error |
|
42 |
+ */ |
|
43 |
+cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count); |
|
44 |
+ |
|
45 |
+/** |
|
46 |
+ * @brief Free system and trusted root certificates. |
|
47 |
+ */ |
|
48 |
+void cert_store_unload(void); |
|
49 |
+ |
|
50 |
+/** |
|
51 |
+ * @brief Set trusted root certificates in the cert store. If trusted |
|
52 |
+ * certificates already exist then they are removed. |
|
53 |
+ * |
|
54 |
+ * @param[in] trusted_certs - List of trusted X509 root certificates |
|
55 |
+ * @param[in] trusted_cert_count - Number of trusted X509 root certificates |
|
56 |
+ * |
|
57 |
+ * @return 0 on success or -1 on error |
|
58 |
+ */ |
|
59 |
+cl_error_t cert_store_set_trusted(X509 **trusted_certs, size_t trusted_cert_count); |
|
60 |
+ |
|
61 |
+/** |
|
62 |
+ * @brief Remove trusted root certificates from the cert store. |
|
63 |
+ * |
|
64 |
+ * @return a count of how many trusted certificates were removed. 0 (zero) will |
|
65 |
+ * be returned if the cert store is not initialized |
|
66 |
+ */ |
|
67 |
+size_t cert_store_remove_trusted(void); |
|
68 |
+ |
|
69 |
+/** |
|
70 |
+ * @brief Export all system and trusted root certificates from the cert store |
|
71 |
+ * into an SSL X509_STORE. The additional_ca_cert will also be exported |
|
72 |
+ * if provided (not NULL). |
|
73 |
+ * |
|
74 |
+ * @param[out] store - SSL X509 store context |
|
75 |
+ * @param[in] additional_ca_cert - additional CA certificate to append (if not |
|
76 |
+ * NULL) |
|
77 |
+ */ |
|
78 |
+void cert_store_export_certs(X509_STORE *store, X509 *additional_ca_cert); |
|
79 |
+ |
|
80 |
+/** |
|
81 |
+ * @brief Export all system and trusted root certificates from the cert store as |
|
82 |
+ * a null-terminated string. Certificates within the string will be |
|
83 |
+ * PEM-encoded. |
|
84 |
+ * |
|
85 |
+ * @details An example user of this method is the EST library which, as part of |
|
86 |
+ * its initialization, will ensure that the length of the CA chain |
|
87 |
+ * matches a given length. |
|
88 |
+ * |
|
89 |
+ * @link common/est/src/src/est_client.c |
|
90 |
+ * |
|
91 |
+ * @param[out] cert_data - Root CA certificate PEM buffer |
|
92 |
+ * @param[out] cert_data_len - Length of cert_data buffer |
|
93 |
+ * @param[in] additional_ca_cert - an additional CA certificate to append |
|
94 |
+ * |
|
95 |
+ * @return 0 on success, -1 on error |
|
96 |
+ */ |
|
97 |
+cl_error_t cert_store_export_pem(char **cert_data, |
|
98 |
+ int *cert_data_len, |
|
99 |
+ X509 *additional_ca_cert); |
|
100 |
+ |
|
101 |
+/** |
|
102 |
+ * @brief Add certificates to X509 store. Duplicate certificates are skipped |
|
103 |
+ * and errors are printed to the log. |
|
104 |
+ * |
|
105 |
+ * @param[in] store - Pointer to X509 store |
|
106 |
+ * @param[in] certs - List of X509 certificates |
|
107 |
+ * @param[in] cert_count - Number of X509 certificates |
|
108 |
+ */ |
|
109 |
+void cert_fill_X509_store(X509_STORE *store, X509 **certs, size_t cert_count); |
|
110 |
+ |
|
111 |
+/** |
|
112 |
+ * @brief Callback function for libcurl to verify certificates for HTTPS connections. |
|
113 |
+ * |
|
114 |
+ * @param[in] curl - handle for curl connection. |
|
115 |
+ * @param[in] ssl_ctx - List of X509 certificates |
|
116 |
+ * @param[in] userptr - Number of X509 certificates |
|
117 |
+ */ |
|
118 |
+CURLcode sslctx_function(CURL *curl, void *ssl_ctx, void *userptr); |
|
119 |
+ |
|
120 |
+#endif |
|
0 | 121 |
\ No newline at end of file |
1 | 122 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,80 @@ |
0 |
+/* |
|
1 |
+ * Internal certificate utility methods and data structures. |
|
2 |
+ * |
|
3 |
+ * Copyright (C) 2016-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
|
4 |
+ * |
|
5 |
+ * Authors: Russ Kubik |
|
6 |
+ * |
|
7 |
+ * This program is free software; you can redistribute it and/or modify |
|
8 |
+ * it under the terms of the GNU General Public License version 2 as |
|
9 |
+ * published by the Free Software Foundation. |
|
10 |
+ * |
|
11 |
+ * This program is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License |
|
17 |
+ * along with this program; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
19 |
+ * MA 02110-1301, USA. |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#ifndef _CERT_UTIL_INT_H |
|
23 |
+#define _CERT_UTIL_INT_H |
|
24 |
+ |
|
25 |
+#include <pthread.h> |
|
26 |
+#include <stdbool.h> |
|
27 |
+#include <stdint.h> |
|
28 |
+ |
|
29 |
+#include "clamav.h" |
|
30 |
+ |
|
31 |
+typedef struct { |
|
32 |
+ X509 **certificates; |
|
33 |
+ size_t count; |
|
34 |
+} cert_list_t; |
|
35 |
+ |
|
36 |
+typedef struct { |
|
37 |
+ pthread_mutex_t mutex; |
|
38 |
+ bool loaded; |
|
39 |
+ cert_list_t system_certs; |
|
40 |
+ cert_list_t trusted_certs; |
|
41 |
+} cert_store_t; |
|
42 |
+ |
|
43 |
+/** |
|
44 |
+ * @brief Accessor method for cert store. |
|
45 |
+ * |
|
46 |
+ * @return Pointer to cert store |
|
47 |
+ */ |
|
48 |
+cert_store_t *cert_store_get_int(void); |
|
49 |
+ |
|
50 |
+/** |
|
51 |
+ * @brief Free all certificates loaded by config_store_load. |
|
52 |
+ * |
|
53 |
+ * @details This method does not hold the cert store lock and should not be |
|
54 |
+ * called outside of cert_util. |
|
55 |
+ */ |
|
56 |
+void cert_store_unload_int(void); |
|
57 |
+ |
|
58 |
+/** |
|
59 |
+ * @brief Free memory allocated by a cert_list_t structure. |
|
60 |
+ * |
|
61 |
+ * @param[in] cert_list - Pointer to a cert_list_t structure |
|
62 |
+ */ |
|
63 |
+void cert_store_free_cert_list_int(cert_list_t *cert_list); |
|
64 |
+ |
|
65 |
+/** |
|
66 |
+ * @brief Set trusted root certificates in the cert store. If trusted |
|
67 |
+ * certificates already exist in the cert store then they are removed. |
|
68 |
+ * |
|
69 |
+ * @details This method does not hold the cert store lock and should not be |
|
70 |
+ * called outside of cert_util. |
|
71 |
+ * |
|
72 |
+ * @param[in] trusted_certs - List of X509 trusted root certificates |
|
73 |
+ * @param[in] trusted_cert_count - Number of trusted root certificates |
|
74 |
+ * |
|
75 |
+ * @return 0 on success or -1 on error |
|
76 |
+ */ |
|
77 |
+cl_error_t cert_store_set_trusted_int(X509 **trusted_certs, size_t trusted_cert_count); |
|
78 |
+ |
|
79 |
+#endif |
0 | 80 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,88 @@ |
0 |
+/* |
|
1 |
+ * OpenSSL certificate verification for Linux. |
|
2 |
+ * |
|
3 |
+ * Copyright (C) 2016-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
|
4 |
+ * |
|
5 |
+ * Authors: Russ Kubik |
|
6 |
+ * |
|
7 |
+ * This program is free software; you can redistribute it and/or modify |
|
8 |
+ * it under the terms of the GNU General Public License version 2 as |
|
9 |
+ * published by the Free Software Foundation. |
|
10 |
+ * |
|
11 |
+ * This program is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License |
|
17 |
+ * along with this program; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
19 |
+ * MA 02110-1301, USA. |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#include <openssl/bio.h> |
|
23 |
+#include <openssl/err.h> |
|
24 |
+#include <openssl/pem.h> |
|
25 |
+#include <stdint.h> |
|
26 |
+#include <stdlib.h> |
|
27 |
+#include <inttypes.h> |
|
28 |
+ |
|
29 |
+#include "output.h" |
|
30 |
+ |
|
31 |
+#include "cert_util.h" |
|
32 |
+#include "cert_util_internal.h" |
|
33 |
+ |
|
34 |
+cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count) |
|
35 |
+{ |
|
36 |
+ cl_error_t ret = CL_EOPEN; |
|
37 |
+ cert_store_t *store = NULL; |
|
38 |
+ int pt_err; |
|
39 |
+ |
|
40 |
+ do { |
|
41 |
+ store = cert_store_get_int(); |
|
42 |
+ if (!store) { |
|
43 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
44 |
+ break; |
|
45 |
+ } |
|
46 |
+ |
|
47 |
+ pt_err = pthread_mutex_lock(&store->mutex); |
|
48 |
+ if (pt_err) { |
|
49 |
+ errno = pt_err; |
|
50 |
+ mprintf("!Mutex lock failed", ); |
|
51 |
+ } |
|
52 |
+ |
|
53 |
+ if (store->loaded) { |
|
54 |
+ ret = 0; |
|
55 |
+ break; |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ /* System certs do not need to be added as they can be accessed directly |
|
59 |
+ * by the SSL library. */ |
|
60 |
+ store->system_certs.count = 0; |
|
61 |
+ store->system_certs.certificates = NULL; |
|
62 |
+ |
|
63 |
+ if (trusted_certs && trusted_cert_count > 0) { |
|
64 |
+ if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) { |
|
65 |
+ mprintf("*Trusted certificates loaded: %zu", |
|
66 |
+ store->trusted_certs.count); |
|
67 |
+ } else { |
|
68 |
+ mprintf("^Continuing without trusted certificates\n"); |
|
69 |
+ /* proceed as if we succeeded using only certificates from the |
|
70 |
+ * system */ |
|
71 |
+ } |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ store->loaded = true; |
|
75 |
+ ret = 0; |
|
76 |
+ } while (0); |
|
77 |
+ |
|
78 |
+ if (store) { |
|
79 |
+ pt_err = pthread_mutex_unlock(&cert_store->mutex); |
|
80 |
+ if (pt_err) { |
|
81 |
+ errno = pt_err; |
|
82 |
+ mprintf("!Mutex unlock failed\n"); |
|
83 |
+ } |
|
84 |
+ } |
|
85 |
+ |
|
86 |
+ return ret; |
|
87 |
+} |
0 | 88 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,389 @@ |
0 |
+/* |
|
1 |
+ * OpenSSL certificate verification for macOS. |
|
2 |
+ * |
|
3 |
+ * Copyright (C) 2016-2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
|
4 |
+ * |
|
5 |
+ * Authors: Russ Kubik |
|
6 |
+ * |
|
7 |
+ * This program is free software; you can redistribute it and/or modify |
|
8 |
+ * it under the terms of the GNU General Public License version 2 as |
|
9 |
+ * published by the Free Software Foundation. |
|
10 |
+ * |
|
11 |
+ * This program is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License |
|
17 |
+ * along with this program; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
19 |
+ * MA 02110-1301, USA. |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#include <Foundation/Foundation.h> |
|
23 |
+#import <Security/SecRequirement.h> |
|
24 |
+#import <Security/SecBase.h> |
|
25 |
+#import <Security/SecCode.h> |
|
26 |
+ |
|
27 |
+#include <openssl/x509.h> |
|
28 |
+#include <openssl/pem.h> |
|
29 |
+#include <openssl/err.h> |
|
30 |
+#include <Security/Security.h> |
|
31 |
+ |
|
32 |
+#include <sys/syslimits.h> |
|
33 |
+#import <sys/proc_info.h> |
|
34 |
+#import <libproc.h> |
|
35 |
+ |
|
36 |
+#include <sys/stat.h> |
|
37 |
+#include <libgen.h> |
|
38 |
+#include <mach-o/dyld.h> |
|
39 |
+ |
|
40 |
+#include <curl/curl.h> |
|
41 |
+ |
|
42 |
+#include "output.h" |
|
43 |
+ |
|
44 |
+#include "cert_util.h" |
|
45 |
+#include "cert_util_internal.h" |
|
46 |
+ |
|
47 |
+/* Macro to obtain the number of elements in a fixed sized array that was either |
|
48 |
+ * statically declared or declared on the stack in the same scope. The macro |
|
49 |
+ * will generate a divide-by-zero compiler warning if the input is a pointer. |
|
50 |
+ * |
|
51 |
+ * See also: |
|
52 |
+ * http://stackoverflow.com/questions/8018843/macro-definition-array-size |
|
53 |
+ */ |
|
54 |
+#define ARRAY_SIZE(a) ((sizeof(a) / sizeof(*(a))) / ((size_t)(!(sizeof(a) % sizeof(*(a)))))) |
|
55 |
+ |
|
56 |
+/* Keychain types available on macOS. User specific keychains are omitted for |
|
57 |
+ * simplicity. */ |
|
58 |
+typedef enum keychain_type { |
|
59 |
+ KEYCHAIN_TYPE_SYSTEM_ROOT, |
|
60 |
+ KEYCHAIN_TYPE_SYSTEM |
|
61 |
+} keychain_type_t; |
|
62 |
+ |
|
63 |
+/* Basic information about a keychain */ |
|
64 |
+typedef struct keychain_info { |
|
65 |
+ const char *name; |
|
66 |
+ const char *file_path; |
|
67 |
+} keychain_info_t; |
|
68 |
+ |
|
69 |
+/* Table to support name and file path lookup for each keychain type */ |
|
70 |
+static const keychain_info_t _KEYCHAIN_INFO[] = |
|
71 |
+ { |
|
72 |
+ {.name = "system root", |
|
73 |
+ .file_path = "/System/Library/Keychains/SystemRootCertificates.keychain"}, |
|
74 |
+ {.name = "system", |
|
75 |
+ .file_path = "/Library/Keychains/System.keychain"}}; |
|
76 |
+ |
|
77 |
+/*! |
|
78 |
+ * @brief Get basic information about the specified keychain. |
|
79 |
+ * @param[in] keychain_type Keychain type |
|
80 |
+ * @return The keychain information. All pointers contained in this |
|
81 |
+ * point to read only data and so do not need to be freed. |
|
82 |
+ */ |
|
83 |
+static keychain_info_t _get_keychain_info(keychain_type_t keychain_type) |
|
84 |
+{ |
|
85 |
+ return _KEYCHAIN_INFO[keychain_type]; |
|
86 |
+} |
|
87 |
+ |
|
88 |
+/*! |
|
89 |
+ * @brief Get a reference to an allocated array of certifcates contained |
|
90 |
+ * in the specified keychain. |
|
91 |
+ * @param[in] keychain_type Keychain type |
|
92 |
+ * @return If successful, reference to allocated array of certifcates. The |
|
93 |
+ * caller is responsible for calling CFRelease on the returned |
|
94 |
+ * reference after use. |
|
95 |
+ * @return NULL otherwise |
|
96 |
+ */ |
|
97 |
+static CFTypeRef _get_cert_ref(keychain_type_t keychain_type) |
|
98 |
+{ |
|
99 |
+ keychain_info_t kc_info = _get_keychain_info(keychain_type); |
|
100 |
+ |
|
101 |
+ CFTypeRef keys[] = { |
|
102 |
+ kSecMatchSearchList, |
|
103 |
+ kSecClass, |
|
104 |
+ kSecReturnRef, |
|
105 |
+ kSecMatchLimit, |
|
106 |
+ kSecMatchTrustedOnly, |
|
107 |
+ kSecMatchValidOnDate, |
|
108 |
+ }; |
|
109 |
+ CFTypeRef values[] = { |
|
110 |
+ /* NOTE: must match the order specified above */ |
|
111 |
+ kCFNull, /* place holder for match search list */ |
|
112 |
+ kSecClassCertificate, /* kSecClass */ |
|
113 |
+ kCFBooleanTrue, /* kSecReturnRef */ |
|
114 |
+ kSecMatchLimitAll, /* kSecMatchLimit */ |
|
115 |
+ kCFBooleanTrue, /* kSecMatchTrustedOnly */ |
|
116 |
+ kCFNull, /* kSecMatchValidOnDate */ |
|
117 |
+ }; |
|
118 |
+ |
|
119 |
+ CFDictionaryRef query = NULL; |
|
120 |
+ CFTypeRef items = NULL; |
|
121 |
+ |
|
122 |
+ SecKeychainRef keychain = NULL; |
|
123 |
+ CFArrayRef search_list = NULL; |
|
124 |
+ |
|
125 |
+ SecKeychainStatus keychainStatus = 0; |
|
126 |
+ |
|
127 |
+ OSStatus status; |
|
128 |
+ |
|
129 |
+ status = SecKeychainOpen(kc_info.file_path, &keychain); |
|
130 |
+ |
|
131 |
+ if (status != errSecSuccess) { |
|
132 |
+ mprintf("!Failed to open %s keychain: %s (%d)\n", |
|
133 |
+ kc_info.name, |
|
134 |
+ kc_info.file_path, |
|
135 |
+ status); |
|
136 |
+ goto done; |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ status = SecKeychainGetStatus(keychain, &keychainStatus); |
|
140 |
+ if (status != errSecSuccess) { |
|
141 |
+ mprintf("!Failed to get the status of the %s keychain: %d\n", |
|
142 |
+ kc_info.name, |
|
143 |
+ status); |
|
144 |
+ goto done; |
|
145 |
+ } |
|
146 |
+ if (!(keychainStatus & kSecReadPermStatus)) { |
|
147 |
+ mprintf("!The %s keychain is not readable: %" PRIu32 "\n", |
|
148 |
+ kc_info.name, |
|
149 |
+ keychainStatus); |
|
150 |
+ goto done; |
|
151 |
+ } |
|
152 |
+ |
|
153 |
+ if (keychain_type == KEYCHAIN_TYPE_SYSTEM_ROOT) { |
|
154 |
+ /* |
|
155 |
+ * The SystemRootCertificates.keychain is a system keychain file that should be locked |
|
156 |
+ * and should definitely not have writable permissions. This may indicate that the file |
|
157 |
+ * has been tampered with. |
|
158 |
+ */ |
|
159 |
+ if (keychainStatus & (kSecUnlockStateStatus | kSecWritePermStatus)) { |
|
160 |
+ mprintf("!System Root Certificates Keychain has invalid permissions: %" PRIu32 "\n", |
|
161 |
+ keychainStatus); |
|
162 |
+ /* continue on error */ |
|
163 |
+ } |
|
164 |
+ } |
|
165 |
+ |
|
166 |
+ search_list = CFArrayCreate(kCFAllocatorDefault, |
|
167 |
+ (const void **)&keychain, 1, &kCFTypeArrayCallBacks); |
|
168 |
+ if (search_list == NULL) { |
|
169 |
+ mprintf("!Failed to create %s keychain search list\n", |
|
170 |
+ kc_info.name); |
|
171 |
+ goto done; |
|
172 |
+ } |
|
173 |
+ |
|
174 |
+ /* set the search list for the secItemCopyMatching call */ |
|
175 |
+ values[0] = search_list; |
|
176 |
+ |
|
177 |
+ query = CFDictionaryCreate(NULL, keys, values, ARRAY_SIZE(keys), |
|
178 |
+ &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); |
|
179 |
+ |
|
180 |
+ if (query == NULL) { |
|
181 |
+ mprintf("!Failed to create %s keychain query dictionary\n", |
|
182 |
+ kc_info.name); |
|
183 |
+ goto done; |
|
184 |
+ } |
|
185 |
+ |
|
186 |
+ status = SecItemCopyMatching(query, &items); |
|
187 |
+ if (status != errSecSuccess) { |
|
188 |
+ if (status == errSecItemNotFound) { |
|
189 |
+ mprintf("!No items found in %s keychain\n", |
|
190 |
+ kc_info.name); |
|
191 |
+ } else { |
|
192 |
+ mprintf("!Unable to copy certificates from %s keychain (%d)\n", |
|
193 |
+ kc_info.name, |
|
194 |
+ status); |
|
195 |
+ } |
|
196 |
+ } |
|
197 |
+ |
|
198 |
+ CFRelease(query); |
|
199 |
+ query = NULL; |
|
200 |
+done: |
|
201 |
+ if (keychain) { |
|
202 |
+ CFRelease(keychain); |
|
203 |
+ keychain = NULL; |
|
204 |
+ } |
|
205 |
+ if (search_list) { |
|
206 |
+ CFRelease(search_list); |
|
207 |
+ search_list = NULL; |
|
208 |
+ } |
|
209 |
+ return items; |
|
210 |
+} |
|
211 |
+ |
|
212 |
+cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count) |
|
213 |
+{ |
|
214 |
+ static const keychain_type_t keychains[] = { |
|
215 |
+ KEYCHAIN_TYPE_SYSTEM_ROOT, |
|
216 |
+ KEYCHAIN_TYPE_SYSTEM}; |
|
217 |
+ |
|
218 |
+ typedef struct keychain_cert_data { |
|
219 |
+ CFArrayRef certs; |
|
220 |
+ CFIndex certs_count; |
|
221 |
+ } keychain_cert_data_t; |
|
222 |
+ |
|
223 |
+ keychain_cert_data_t keychain_cert_data_array[ARRAY_SIZE(keychains)] = { |
|
224 |
+ {.certs = NULL, |
|
225 |
+ .certs_count = 0}, |
|
226 |
+ /* All other array values initialized to 0 by default */ |
|
227 |
+ }; |
|
228 |
+ |
|
229 |
+ size_t kc_index = 0; |
|
230 |
+ |
|
231 |
+ cl_error_t ret = CL_EOPEN; |
|
232 |
+ int pt_err; |
|
233 |
+ |
|
234 |
+ cert_store_t *store = NULL; |
|
235 |
+ CFIndex total_certificates = 0; |
|
236 |
+ CFIndex i = 0; |
|
237 |
+ bool locked = false; |
|
238 |
+ |
|
239 |
+ store = cert_store_get_int(); |
|
240 |
+ if (!store) { |
|
241 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
242 |
+ goto done; |
|
243 |
+ } |
|
244 |
+ |
|
245 |
+ /* Load certificates from keychains before entering the critical section. |
|
246 |
+ * On a default 10.12 installation loading the the system roots keychain |
|
247 |
+ * could take up to 300 ms to complete. */ |
|
248 |
+ |
|
249 |
+ for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) { |
|
250 |
+ keychain_type_t kc = keychains[kc_index]; |
|
251 |
+ keychain_info_t kc_info = _get_keychain_info(kc); |
|
252 |
+ keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index]; |
|
253 |
+ CFTypeRef items = NULL; |
|
254 |
+ |
|
255 |
+ items = _get_cert_ref(kc); |
|
256 |
+ if (!items) { |
|
257 |
+ continue; |
|
258 |
+ } |
|
259 |
+ |
|
260 |
+ if (CFGetTypeID(items) != CFArrayGetTypeID()) { |
|
261 |
+ mprintf("!Expected array of certificates from %s keychain, " |
|
262 |
+ "got type %lu\n", |
|
263 |
+ kc_info.name, |
|
264 |
+ CFGetTypeID(items)); |
|
265 |
+ continue; |
|
266 |
+ } |
|
267 |
+ |
|
268 |
+ if (CFArrayGetCount(items) < 1) { |
|
269 |
+ CFRelease(items); |
|
270 |
+ items = NULL; |
|
271 |
+ continue; |
|
272 |
+ } |
|
273 |
+ |
|
274 |
+ kc_data->certs = (CFArrayRef)items; |
|
275 |
+ kc_data->certs_count = CFArrayGetCount(items); |
|
276 |
+ |
|
277 |
+ mprintf("*Found %ld certificates from %s keychain\n", |
|
278 |
+ kc_data->certs_count, |
|
279 |
+ kc_info.name); |
|
280 |
+ |
|
281 |
+ total_certificates += kc_data->certs_count; |
|
282 |
+ } |
|
283 |
+ |
|
284 |
+ if (total_certificates < 1) { |
|
285 |
+ mprintf("!No certificate found in keychains. Expect at least one " |
|
286 |
+ "certificate to be found in system root and system " |
|
287 |
+ "keychains\n"); |
|
288 |
+ goto done; |
|
289 |
+ } |
|
290 |
+ |
|
291 |
+ store = cert_store_get_int(); |
|
292 |
+ if (!store) { |
|
293 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
294 |
+ goto done; |
|
295 |
+ } |
|
296 |
+ |
|
297 |
+ pt_err = pthread_mutex_lock(&store->mutex); |
|
298 |
+ if (pt_err) { |
|
299 |
+ errno = pt_err; |
|
300 |
+ mprintf("!Mutex lock failed\n"); |
|
301 |
+ } |
|
302 |
+ locked = true; |
|
303 |
+ |
|
304 |
+ if (store->loaded) { |
|
305 |
+ mprintf("*Cert store already loaded\n"); |
|
306 |
+ ret = CL_SUCCESS; |
|
307 |
+ goto done; |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ store->system_certs.count = 0; |
|
311 |
+ store->system_certs.certificates = calloc(total_certificates, |
|
312 |
+ sizeof(*store->system_certs.certificates)); |
|
313 |
+ if (store->system_certs.certificates == NULL) { |
|
314 |
+ mprintf("!Failed to reserve memory for system cert list\n"); |
|
315 |
+ goto done; |
|
316 |
+ } |
|
317 |
+ |
|
318 |
+ for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) { |
|
319 |
+ keychain_type_t kc = keychains[kc_index]; |
|
320 |
+ keychain_info_t kc_info = _get_keychain_info(kc); |
|
321 |
+ keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index]; |
|
322 |
+ |
|
323 |
+ for (i = 0; i < kc_data->certs_count; i++) { |
|
324 |
+ const void *value = CFArrayGetValueAtIndex(kc_data->certs, i); |
|
325 |
+ |
|
326 |
+ if (CFGetTypeID(value) == SecCertificateGetTypeID()) { |
|
327 |
+ SecCertificateRef cert = (SecCertificateRef)value; |
|
328 |
+ CFDataRef cert_data = SecCertificateCopyData(cert); /* DER representation of X.509 */ |
|
329 |
+ |
|
330 |
+ if (cert_data) { |
|
331 |
+ const unsigned char *der = CFDataGetBytePtr(cert_data); |
|
332 |
+ CFIndex length = CFDataGetLength(cert_data); |
|
333 |
+ |
|
334 |
+ X509 *x509 = d2i_X509(NULL, &der, length); |
|
335 |
+ |
|
336 |
+ if (x509) { |
|
337 |
+ mprintf("*Found %s trusted certificate %s\n", |
|
338 |
+ kc_info.name, |
|
339 |
+ (x509->name ? x509->name : "<no name>")); |
|
340 |
+ |
|
341 |
+ store->system_certs.certificates[store->system_certs.count++] = x509; |
|
342 |
+ } else { |
|
343 |
+ mprintf("!Failed conversion of DER format to X.509\n"); |
|
344 |
+ } |
|
345 |
+ |
|
346 |
+ CFRelease(cert_data); |
|
347 |
+ cert_data = NULL; |
|
348 |
+ } |
|
349 |
+ } |
|
350 |
+ } |
|
351 |
+ } |
|
352 |
+ |
|
353 |
+ if (trusted_certs && trusted_cert_count > 0) { |
|
354 |
+ if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) { |
|
355 |
+ mprintf("*Trusted certificates loaded: %zu\n", |
|
356 |
+ store->trusted_certs.count); |
|
357 |
+ } else { |
|
358 |
+ mprintf("^Continuing without trusted certificates\n"); |
|
359 |
+ /* proceed as if we succeeded using only certificates from the |
|
360 |
+ * system */ |
|
361 |
+ } |
|
362 |
+ } |
|
363 |
+ |
|
364 |
+ store->loaded = true; |
|
365 |
+ ret = CL_SUCCESS; |
|
366 |
+ |
|
367 |
+done: |
|
368 |
+ if (locked) { |
|
369 |
+ pt_err = pthread_mutex_unlock(&store->mutex); |
|
370 |
+ if (pt_err) { |
|
371 |
+ errno = pt_err; |
|
372 |
+ mprintf("!Mutex unlock failed\n"); |
|
373 |
+ } |
|
374 |
+ locked = false; |
|
375 |
+ } |
|
376 |
+ |
|
377 |
+ for (kc_index = 0; kc_index < ARRAY_SIZE(keychains); kc_index++) { |
|
378 |
+ keychain_cert_data_t *kc_data = &keychain_cert_data_array[kc_index]; |
|
379 |
+ |
|
380 |
+ if (kc_data->certs) { |
|
381 |
+ CFRelease(kc_data->certs); |
|
382 |
+ kc_data->certs = NULL; |
|
383 |
+ kc_data->certs_count = 0; |
|
384 |
+ } |
|
385 |
+ } |
|
386 |
+ |
|
387 |
+ return ret; |
|
388 |
+} |
0 | 389 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,173 @@ |
0 |
+/* |
|
1 |
+ * OpenSSL certificate verification for Windows. |
|
2 |
+ * |
|
3 |
+ * Copyright (C) 2019 Cisco Systems, Inc. and/or its affiliates. All rights reserved. |
|
4 |
+ * |
|
5 |
+ * Authors: Micah Snyder |
|
6 |
+ * |
|
7 |
+ * This program is free software; you can redistribute it and/or modify |
|
8 |
+ * it under the terms of the GNU General Public License version 2 as |
|
9 |
+ * published by the Free Software Foundation. |
|
10 |
+ * |
|
11 |
+ * This program is distributed in the hope that it will be useful, |
|
12 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
13 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
14 |
+ * GNU General Public License for more details. |
|
15 |
+ * |
|
16 |
+ * You should have received a copy of the GNU General Public License |
|
17 |
+ * along with this program; if not, write to the Free Software |
|
18 |
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
|
19 |
+ * MA 02110-1301, USA. |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#include <wincrypt.h> |
|
23 |
+ |
|
24 |
+#include <openssl/x509.h> |
|
25 |
+#include <openssl/pem.h> |
|
26 |
+#include <openssl/err.h> |
|
27 |
+ |
|
28 |
+#include <curl/curl.h> |
|
29 |
+ |
|
30 |
+#include "output.h" |
|
31 |
+ |
|
32 |
+#include "cert_util.h" |
|
33 |
+#include "cert_util_internal.h" |
|
34 |
+ |
|
35 |
+cl_error_t cert_store_load(X509 **trusted_certs, size_t trusted_cert_count) |
|
36 |
+{ |
|
37 |
+ uint32_t numCertificatesFound = 0; |
|
38 |
+ DWORD lastError; |
|
39 |
+ |
|
40 |
+ HCERTSTORE hStore = NULL; |
|
41 |
+ PCCERT_CONTEXT pWinCertContext = NULL; |
|
42 |
+ X509 *x509 = NULL; |
|
43 |
+ |
|
44 |
+ cl_error_t ret = CL_EOPEN; |
|
45 |
+ int pt_err; |
|
46 |
+ |
|
47 |
+ cert_store_t *store = NULL; |
|
48 |
+ CFIndex total_certificates = 0; |
|
49 |
+ CFIndex i = 0; |
|
50 |
+ bool locked = false; |
|
51 |
+ |
|
52 |
+ hStore = CertOpenSystemStoreA(NULL, "ROOT\n"); |
|
53 |
+ if (NULL == hStore) { |
|
54 |
+ mprintf("!Failed to open system certificate store.\n"); |
|
55 |
+ goto done; |
|
56 |
+ } |
|
57 |
+ |
|
58 |
+ X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)ssl_ctx); |
|
59 |
+ |
|
60 |
+ store = cert_store_get_int(); |
|
61 |
+ if (!store) { |
|
62 |
+ mprintf("!Failed to retrieve cert store\n"); |
|
63 |
+ goto done; |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ pt_err = pthread_mutex_lock(&store->mutex); |
|
67 |
+ if (pt_err) { |
|
68 |
+ errno = pt_err; |
|
69 |
+ mprintf("!Mutex lock failed\n"); |
|
70 |
+ } |
|
71 |
+ locked = true; |
|
72 |
+ |
|
73 |
+ if (store->loaded) { |
|
74 |
+ mprintf("Cert store already loaded\n"); |
|
75 |
+ ret = CL_SUCCESS; |
|
76 |
+ goto done; |
|
77 |
+ } |
|
78 |
+ |
|
79 |
+ store->system_certs.count = 0; |
|
80 |
+ store->system_certs.certificates = calloc(total_certificates, |
|
81 |
+ sizeof(*store->system_certs.certificates)); |
|
82 |
+ if (store->system_certs.certificates == NULL) { |
|
83 |
+ mprintf("!Failed to reserve memory for system cert list\n"); |
|
84 |
+ goto done; |
|
85 |
+ } |
|
86 |
+ |
|
87 |
+ while (NULL != (pWinCertContext = CertEnumCertificatesInStore(hStore, pWinCertContext))) { |
|
88 |
+ int addCertResult = 0; |
|
89 |
+ const unsigned char *encoded_cert = pWinCertContext->pbCertEncoded; |
|
90 |
+ |
|
91 |
+ x509 = NULL; |
|
92 |
+ x509 = d2i_X509(NULL, &encoded_cert, pWinCertContext->cbCertEncoded); |
|
93 |
+ if (NULL == x509) { |
|
94 |
+ mprintf("!Failed to convert system certificate to x509.\n"); |
|
95 |
+ continue; |
|
96 |
+ } |
|
97 |
+ |
|
98 |
+ store->system_certs.certificates[store->system_certs.count++] = x509; |
|
99 |
+ |
|
100 |
+ if (mprintf_verbose) { |
|
101 |
+ char *issuer = NULL; |
|
102 |
+ size_t issuerLen = 0; |
|
103 |
+ issuerLen = CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); |
|
104 |
+ |
|
105 |
+ issuer = cli_malloc(issuerLen); |
|
106 |
+ if (NULL == issuer) { |
|
107 |
+ mprintf("!Failed to allocate memory for certificate name.\n"); |
|
108 |
+ ret = CURLE_OUT_OF_MEMORY; |
|
109 |
+ goto done; |
|
110 |
+ } |
|
111 |
+ |
|
112 |
+ if (0 == CertGetNameStringA(pWinCertContext, CERT_NAME_FRIENDLY_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, issuer, issuerLen)) { |
|
113 |
+ mprintf("!Failed to get friendly display name for certificate.\n"); |
|
114 |
+ } else { |
|
115 |
+ mprintf("Certificate loaded from Windows certificate store: %s\n", issuer); |
|
116 |
+ } |
|
117 |
+ |
|
118 |
+ free(issuer); |
|
119 |
+ } |
|
120 |
+ |
|
121 |
+ numCertificatesFound++; |
|
122 |
+ X509_free(x509); |
|
123 |
+ } |
|
124 |
+ |
|
125 |
+ lastError = GetLastError(); |
|
126 |
+ switch (lastError) { |
|
127 |
+ case E_INVALIDARG: |
|
128 |
+ mprintf("!The handle in the hCertStore parameter is not the same as that in the certificate context pointed to by pPrevCertContext.\n"); |
|
129 |
+ break; |
|
130 |
+ case CRYPT_E_NOT_FOUND: |
|
131 |
+ case ERROR_NO_MORE_FILES: |
|
132 |
+ if (0 == numCertificatesFound) { |
|
133 |
+ mprintf("!No certificates were found.\n"); |
|
134 |
+ } |
|
135 |
+ break; |
|
136 |
+ default: |
|
137 |
+ mprintf("!Unexpected error code from CertEnumCertificatesInStore()\n"); |
|
138 |
+ } |
|
139 |
+ |
|
140 |
+ if (trusted_certs && trusted_cert_count > 0) { |
|
141 |
+ if (cert_store_set_trusted_int(trusted_certs, trusted_cert_count) == 0) { |
|
142 |
+ mprintf("*Trusted certificates loaded: %zu\n", |
|
143 |
+ store->trusted_certs.count); |
|
144 |
+ } else { |
|
145 |
+ mprintf("^Continuing without trusted certificates\n"); |
|
146 |
+ /* proceed as if we succeeded using only certificates from the |
|
147 |
+ * system */ |
|
148 |
+ } |
|
149 |
+ } |
|
150 |
+ |
|
151 |
+ store->loaded = true; |
|
152 |
+ ret = CL_SUCCESS; |
|
153 |
+ |
|
154 |
+done: |
|
155 |
+ if (locked) { |
|
156 |
+ pt_err = pthread_mutex_unlock(&store->mutex); |
|
157 |
+ if (pt_err) { |
|
158 |
+ errno = pt_err; |
|
159 |
+ mprintf("!Mutex unlock failed\n"); |
|
160 |
+ } |
|
161 |
+ locked = false; |
|
162 |
+ } |
|
163 |
+ |
|
164 |
+ if (NULL != pWinCertContext) { |
|
165 |
+ CertFreeCertificateContext(pWinCertContext); |
|
166 |
+ } |
|
167 |
+ if (NULL != hStore) { |
|
168 |
+ CertCloseStore(hStore, 0); |
|
169 |
+ } |
|
170 |
+ |
|
171 |
+ return ret; |
|
172 |
+} |