Browse code

Moving Windows cert verification to shared directory, and adding macOS cert verification support.

Micah Snyder authored on 2019/06/17 10:16:16
Showing 12 changed files
... ...
@@ -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
+}