summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Stradling <rob@comodo.com>2015-09-02 14:12:30 +0100
committerRob Stradling <rob@comodo.com>2015-09-02 14:12:30 +0100
commit7613b8d6cfbe52e4ef4c93b1dd052835ba5e815e (patch)
tree971cd0f25eebde42946887543d442f241e0bfbdd
parent6b4d53c5d72ab993ba1654771b9ce6a63430f75d (diff)
downloadct_monitor-7613b8d6cfbe52e4ef4c93b1dd052835ba5e815e.zip
ct_monitor-7613b8d6cfbe52e4ef4c93b1dd052835ba5e815e.tar.gz
ct_monitor-7613b8d6cfbe52e4ef4c93b1dd052835ba5e815e.tar.bz2
Initial code drop
-rw-r--r--Makefile19
-rw-r--r--ct_monitor.c783
2 files changed, 802 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8c76fa9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+all: clean ct_monitor
+
+# C Compiler name and flags
+CC = gcc
+CCFLAGS = -O3 -ansi -pedantic -Wall -Wno-long-long -D_DEFAULT_SOURCE -D_GNU_SOURCE
+COMPILE = $(CC) $(CCFLAGS) -c
+LINK = gcc
+
+# Rule for compiling a source file to a .o object file.
+.c.o:
+ $(COMPILE) -o $@ $<
+
+# Tidy up files created by compiler/linker.
+clean:
+ rm -f *.o *~ ct_monitor
+
+ct_monitor: ct_monitor.o
+ $(LINK) -o $@ $< -lcrypto -lpq -lcurl -ljson-c
+ rm $<
diff --git a/ct_monitor.c b/ct_monitor.c
new file mode 100644
index 0000000..6669080
--- /dev/null
+++ b/ct_monitor.c
@@ -0,0 +1,783 @@
+/* ct_monitor - Certificate Transparency Log Monitor
+ * Written by Rob Stradling
+ * Copyright (C) 2015 COMODO CA Limited
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <arpa/inet.h>
+#include <endian.h>
+#include <errno.h>
+#include <libpq-fe.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timeb.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "openssl/bio.h"
+#include "openssl/evp.h"
+#include "openssl/x509.h"
+
+#include "curl/curl.h"
+
+#include "json-c/json.h"
+
+
+#define CURL_EASY_SETOPT(NAME, VALUE) \
+ t_curlCode = curl_easy_setopt(t_hEasy, NAME, VALUE); \
+ if (t_curlCode != CURLE_OK) { \
+ printError( \
+ "curl_easy_setopt: "#NAME, \
+ curl_easy_strerror(t_curlCode) \
+ ); \
+ goto label_exit; \
+ }
+
+#ifdef __LP64__
+ /* 64-bit */
+ #define LENGTH32 ""
+ #define LENGTH64 "l"
+#else
+ /* 32-bit */
+ #define LENGTH32 "l"
+ #define LENGTH64 "ll"
+#endif
+
+
+typedef struct tDataBuffer {
+ char* data;
+ size_t size;
+} tDataBuffer;
+
+
+static int g_terminateNow = 0;
+
+
+/******************************************************************************
+ * printError() *
+ ******************************************************************************/
+static void printError(
+ const char* const v_errorMessage1, /* IN */
+ const char* const v_errorMessage2 /* IN */
+)
+{
+ time_t t_now;
+ char t_now_string[27];
+ char* t_offset;
+
+ /* Convert the current date/time to a string */
+ t_now = time(NULL);
+ (void)ctime_r(&t_now, t_now_string);
+
+ /* Strip trailing LF characters from the date/time string */
+ for (t_offset = t_now_string + strlen(t_now_string) - 1;
+ (t_offset > t_now_string) && (*t_offset == '\n');
+ t_offset--)
+ *t_offset = '\0';
+
+ /* Output the error line to stderr */
+ fprintf(stderr, "%s: %s", t_now_string, v_errorMessage1);
+ if (v_errorMessage2)
+ fprintf(stderr, " (%s)", v_errorMessage2);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+
+/******************************************************************************
+ * signalHandler() *
+ ******************************************************************************/
+static void signalHandler(
+ const int v_signalNumber /* IN */
+)
+{
+ printError(
+ "Signal received",
+ (v_signalNumber == SIGHUP) ? "SIGHUP" :
+ ((v_signalNumber == SIGINT) ? "SIGINT" :
+ ((v_signalNumber == SIGQUIT) ? "SIGQUIT" :
+ "SIGTERM"))
+ );
+
+ g_terminateNow = 1;
+}
+
+
+int calcDecodeLength(const char* b64input) { /*Calculates the length of a decoded base64 string*/
+ int len = strlen(b64input);
+ int padding = 0;
+
+ if (b64input[len-1] == '=' && b64input[len-2] == '=') /*last two chars are =*/
+ padding = 2;
+ else if (b64input[len-1] == '=') /*last char is =*/
+ padding = 1;
+
+ return (int)len*0.75 - padding;
+}
+
+int Base64Decode(char* b64message, char** buffer) { /*Decodes a base64 encoded string*/
+ BIO *bio, *b64;
+ FILE* stream;
+ int decodeLen = calcDecodeLength(b64message),
+ len = 0;
+ *buffer = (char*)malloc(decodeLen+1);
+ stream = fmemopen(b64message, strlen(b64message), "r");
+
+ b64 = BIO_new(BIO_f_base64());
+ bio = BIO_new_fp(stream, BIO_NOCLOSE);
+ bio = BIO_push(b64, bio);
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); /*Do not use newlines to flush buffer*/
+ len = BIO_read(bio, *buffer, strlen(b64message));
+ /*Can test here if len == decodeLen - if not, then return an error*/
+ (*buffer)[len] = '\0';
+
+ BIO_free_all(bio);
+ fclose(stream);
+
+ return (0); /*success*/
+}
+
+
+size_t curlDataReceivedFunction(
+ char* v_dataBuffer,
+ size_t v_dataSize,
+ size_t v_dataSizeMultiplier,
+ void* v_responseBuffer
+)
+{
+#define t_responseBuffer ((tDataBuffer*)v_responseBuffer)
+ size_t t_dataSize = v_dataSize * v_dataSizeMultiplier;
+
+ /* Resize the response buffer */
+ t_responseBuffer->data = (char*)realloc(
+ t_responseBuffer->data, t_responseBuffer->size + t_dataSize + 1
+ );
+
+ /* Append this block of received data */
+ memcpy(
+ t_responseBuffer->data + t_responseBuffer->size,
+ v_dataBuffer, t_dataSize
+ );
+
+ /* Update the size */
+ t_responseBuffer->size += t_dataSize;
+
+ /* NULL-terminate (the NULL-terminator is not included in the size) */
+ t_responseBuffer->data[t_responseBuffer->size] = '\0';
+
+ return t_dataSize;
+#undef t_responseBuffer
+}
+
+
+static int compareSHA256Hashes(
+ const void* v_sha256Hash1,
+ const void* v_sha256Hash2
+)
+{
+ return memcmp(v_sha256Hash1, v_sha256Hash2, 32);
+}
+
+
+int main(
+)
+{
+ PGconn* t_PGconn = NULL;
+ PGresult* t_PGresult_select = NULL;
+ PGresult* t_PGresult;
+ int t_returnCode = EXIT_FAILURE;
+ int i = -1;
+ int j;
+ int k;
+ int q;
+
+ CURLcode t_curlCode;
+ CURL* t_hEasy = NULL;
+ char t_curlErrorMessage[CURL_ERROR_SIZE];
+ tDataBuffer t_responseBuffer = { NULL, 0 };
+ long t_httpResponseCode;
+
+ json_object* j_getSTH = NULL;
+ json_object* j_treeSize = NULL;
+ json_object* j_timestamp = NULL;
+ json_object* j_getEntries = NULL;
+ json_object* j_entries = NULL;
+ json_object* j_entry = NULL;
+ json_object* j_leafInput = NULL;
+ json_object* j_extraData = NULL;
+ array_list* t_entriesArr = NULL;
+ uint32_t t_entryID;
+ uint32_t t_confirmedEntryID;
+ uint64_t t_timestamp;
+ int64_t t_sthTimestamp;
+ int t_treeSize;
+ uint16_t t_logEntryType;
+ char t_temp[255];
+ char* t_pointer;
+ char* t_pointer1;
+ char* t_b64Data;
+ char* t_data = NULL;
+ uint32_t t_totalLength;
+
+ X509* t_x509 = NULL;
+ uint32_t t_certSize;
+ char* t_query[32];
+ char* t_subjectName;
+
+ uint8_t* t_cachedCACerts = NULL;
+ uint32_t t_nCachedCACerts = 0;
+ uint32_t t_nCertsInChain;
+ EVP_MD_CTX t_mdctx;
+ const EVP_MD* t_md;
+ unsigned char t_sha256Hash_data[32];
+ uint32_t t_sha256Hash_size;
+
+ /* Initialize the OpenSSL library */
+ OpenSSL_add_all_algorithms();
+ t_md = EVP_sha256();
+ EVP_MD_CTX_init(&t_mdctx);
+
+ /* Install signal handlers */
+ signal(SIGHUP, signalHandler);
+ signal(SIGINT, signalHandler);
+ signal(SIGQUIT, signalHandler);
+ signal(SIGTERM, signalHandler);
+
+ for (q = 0; q < 32; q++)
+ t_query[q] = malloc(128 * 1024);
+
+ /* Connect to the database */
+ t_PGconn = PQconnectdb(
+ "user=crtsh dbname=certwatch host=/run/postgresql/9.4/"
+ " connect_timeout=5 client_encoding=auto"
+ " application_name=ct_monitor"
+ );
+ if (PQstatus(t_PGconn) != CONNECTION_OK) {
+ printError("PQconnectdb()", PQerrorMessage(t_PGconn));
+ return EXIT_FAILURE;
+ }
+
+ printError("Connected OK", NULL);
+
+ /* Get the latest CT Entry ID that we've added to the DB already */
+ t_PGresult_select = PQexec(
+ t_PGconn,
+ "SELECT ctl.ID, ctl.URL, ctl.LATEST_ENTRY_ID, ctl.NAME"
+ " FROM ct_log ctl"
+ " WHERE ctl.IS_ACTIVE"
+ " ORDER BY ctl.ID"
+ );
+ if (PQresultStatus(t_PGresult_select) != PGRES_TUPLES_OK) {
+ /* The SQL query failed */
+ printError("Query failed", PQerrorMessage(t_PGconn));
+ goto label_exit;
+ }
+
+ /* curl_global_init() must be called EXACTLY once */
+ t_curlCode = curl_global_init(CURL_GLOBAL_ALL);
+ if (t_curlCode != CURLE_OK) {
+ printError(
+ "curl_global_init()", curl_easy_strerror(t_curlCode)
+ ); /* Something went wrong, so we cannot continue */
+ return EXIT_FAILURE;
+ }
+
+ for (i = 0; i < PQntuples(t_PGresult_select); i++) {
+ t_confirmedEntryID = -1;
+
+ /* Initialize the "easy handle" */
+ t_hEasy = curl_easy_init();
+ if (!t_hEasy) {
+ printError("curl_easy_init()", "returned NULL");
+ goto label_exit;
+ }
+
+ /* SETUP CURL BEHAVIOUR OPTIONS */
+ /* CURLOPT_NOPROGRESS: No progress meter */
+ CURL_EASY_SETOPT(CURLOPT_NOPROGRESS, 0)
+ /* CURLOPT_NOSIGNAL: Don't use signals */
+ CURL_EASY_SETOPT(CURLOPT_NOSIGNAL, 1)
+
+ /* SETUP CURL CALLBACK OPTIONS */
+ /* CURLOPT_WRITEFUNCTION: "Data Received" Callback Function */
+ CURL_EASY_SETOPT(CURLOPT_WRITEFUNCTION, curlDataReceivedFunction)
+ /* CURLOPT_WRITEDATA: Data Pointer to pass to "Data Received" Callback
+ Function */
+ CURL_EASY_SETOPT(CURLOPT_WRITEDATA, &t_responseBuffer)
+
+ /* SETUP CURL ERROR OPTIONS */
+ /* CURLOPT_ERRORBUFFER: Buffer for potential error message */
+ CURL_EASY_SETOPT(CURLOPT_ERRORBUFFER, t_curlErrorMessage)
+
+ /* SETUP CURL NETWORK OPTIONS */
+ /* CURLOPT_URL: The URL to deal with */
+ sprintf(t_temp, "%s/ct/v1/get-sth",
+ PQgetvalue(t_PGresult_select, i, 1));
+ printError(t_temp, PQgetvalue(t_PGresult_select, i, 3));
+ CURL_EASY_SETOPT(CURLOPT_URL, t_temp)
+
+ /* Use IPv4 rather than IPv6 */
+ CURL_EASY_SETOPT(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4)
+
+ /* SETUP CURL HTTP OPTIONS */
+ /* CURLOPT_FOLLOWLOCATION: Don't follow "Location:" redirects */
+ CURL_EASY_SETOPT(CURLOPT_FOLLOWLOCATION, 0)
+
+ /* SETUP CURL CONNECTION OPTIONS */
+ /* CURLOPT_TIMEOUT: Transfer Timeout (in seconds) */
+ CURL_EASY_SETOPT(CURLOPT_TIMEOUT, 300)
+ /* CURLOPT_CONNECTTIMEOUT: Connect Timeout (in seconds) */
+ CURL_EASY_SETOPT(CURLOPT_CONNECTTIMEOUT, 300)
+
+ /* Perform the transfer */
+ t_curlCode = curl_easy_perform(t_hEasy);
+ if (t_curlCode != CURLE_OK) {
+ printError("curl_easy_perform()", t_curlErrorMessage);
+ goto label_exit;
+ }
+
+ /* Get the HTTP response code */
+ t_curlCode = curl_easy_getinfo(
+ t_hEasy, CURLINFO_RESPONSE_CODE, &t_httpResponseCode
+ );
+ if (t_curlCode != CURLE_OK) {
+ printError("curl_easy_getinfo()", t_curlErrorMessage);
+ goto label_exit;
+ }
+ else if (t_httpResponseCode != 200) {
+ printError(
+ "curl_easy_getinfo()", "Unexpected HTTP Response Code"
+ );
+ goto label_exit;
+ }
+
+ if (PQgetisnull(t_PGresult_select, i, 2))
+ t_entryID = -1;
+ else
+ t_entryID = strtoul(
+ PQgetvalue(t_PGresult_select, i, 2), NULL, 10
+ );
+ printf("Highest Entry ID stored: %d\n", t_entryID);
+
+ j_getSTH = json_tokener_parse(t_responseBuffer.data);
+ if (!json_object_object_get_ex(j_getSTH, "tree_size", &j_treeSize))
+ goto label_exit;
+ t_treeSize = json_object_get_int(j_treeSize);
+ printf("Current Tree Size: %d\n", t_treeSize);
+ if (!json_object_object_get_ex(j_getSTH, "timestamp", &j_timestamp))
+ goto label_exit;
+ t_sthTimestamp = json_object_get_int64(j_timestamp);
+ printf("Timestamp: %" LENGTH64 "d\n", t_sthTimestamp);
+
+ if (json_object_put(j_getSTH) != 1) {
+ printError("json_object_put(j_getSTH)", "Did not return 1");
+ goto label_exit;
+ }
+
+ free(t_responseBuffer.data);
+ t_responseBuffer.data = NULL;
+ t_responseBuffer.size = 0;
+
+ /* TODO: Verify the STH signature */
+
+ for (t_entryID++; t_entryID < t_treeSize; t_entryID = t_confirmedEntryID + 1) {
+ sprintf(
+ t_temp, "%s/ct/v1/get-entries?start=%d&end=%d",
+ PQgetvalue(t_PGresult_select, i, 1), t_entryID,
+ (t_treeSize > (t_entryID + 1023)) ?
+ (t_entryID + 1023) : (t_treeSize - 1)
+ );
+ printError(t_temp, NULL);
+
+ CURL_EASY_SETOPT(CURLOPT_URL, t_temp)
+
+ /* Perform the transfer */
+ t_curlCode = curl_easy_perform(t_hEasy);
+ if (t_curlCode != CURLE_OK) {
+ printError("curl_easy_perform()", t_curlErrorMessage);
+ goto label_exit;
+ }
+
+ /* Get the HTTP response code */
+ t_curlCode = curl_easy_getinfo(
+ t_hEasy, CURLINFO_RESPONSE_CODE, &t_httpResponseCode
+ );
+ if (t_curlCode != CURLE_OK) {
+ printError("curl_easy_getinfo()", t_curlErrorMessage);
+ goto label_exit;
+ }
+ else if (t_httpResponseCode != 200) {
+ printError(
+ "curl_easy_getinfo()",
+ "Unexpected HTTP Response Code"
+ );
+ goto label_exit;
+ }
+
+ /* Parse the JSON response */
+ j_getEntries = json_tokener_parse(t_responseBuffer.data);
+ if (!json_object_object_get_ex(j_getEntries, "entries",
+ &j_entries))
+ goto label_exit;
+
+ t_entriesArr = json_object_get_array(j_entries);
+
+ for (j = 0; j < json_object_array_length(j_entries); j++) {
+ j_entry = array_list_get_idx(t_entriesArr, j);
+
+ if (!json_object_object_get_ex(j_entry, "leaf_input",
+ &j_leafInput))
+ goto label_exit;
+
+ /* Decode the Base64 leaf_input string */
+ t_b64Data = (char*)json_object_get_string(j_leafInput);
+ t_data = NULL;
+ Base64Decode(t_b64Data, &t_data);
+ if (!t_data) {
+ printError("Base64 decode error", "");
+ goto label_exit;
+ }
+
+ /* Check the header fields */
+ if (*(unsigned char*)t_data != 0) {
+ sprintf(
+ t_temp, "%u", *(unsigned char*)t_data
+ );
+ printError("Unexpected Version", t_temp);
+ goto label_exit;
+ }
+ if (*(unsigned char*)(t_data + 1) != 0) {
+ sprintf(
+ t_temp, "%u", *(unsigned char*)(t_data + 1)
+ );
+ printError("Unexpected MerkleLeafType", t_temp);
+ goto label_exit;
+ }
+
+ t_timestamp = be64toh(*(uint64_t*)(t_data + 2));
+
+ t_logEntryType = be16toh(*(uint16_t*)(t_data + 10));
+ if (t_logEntryType == 1) {
+ /* Precertificate. The leaf_input contains a
+ SHA-256 hash of the Issuer Public Key, then
+ the TBSCertificate. Ignore both of these.
+ The submitted Precertificate is the first
+ cert in the extra_data */
+ }
+ else if (t_logEntryType == 0) {
+ /* Parse the certificate */
+ t_certSize = 0;
+ memcpy(((char*)&t_certSize) + 1, t_data + 12, 3);
+ t_certSize = be32toh(t_certSize);
+
+ t_pointer = t_data + 15;
+ t_x509 = d2i_X509(
+ NULL, (const unsigned char**)&t_pointer,
+ t_certSize
+ );
+ if (!t_x509) {
+ printError("Failed to decode EE cert", t_b64Data);
+ goto label_exit;
+ }
+
+ t_subjectName = X509_NAME_oneline(
+ X509_get_subject_name(t_x509), NULL, 0
+ );
+ printf("%d: %s\n", (t_entryID + j), t_subjectName);
+ X509_free(t_x509);
+ if (t_subjectName)
+ OPENSSL_free(t_subjectName);
+
+ /* Construct the "INSERT" query */
+ sprintf(t_query[0],
+ "SELECT import_ct_cert(%s::smallint, %d, %"
+ LENGTH64 "u, E'\\\\x",
+ PQgetvalue(t_PGresult_select, i, 0),
+ (t_entryID + j), t_timestamp);
+
+ for (k = 0; k < t_certSize; k++)
+ sprintf(
+ t_query[0] + strlen(t_query[0]), "%02X",
+ *(unsigned char*)(t_data + 15 + k));
+ strcat(t_query[0], "')");
+ }
+ else {
+ sprintf(t_temp, "%u", t_logEntryType);
+ printError("Unexpected LogEntryType", t_temp);
+ goto label_exit;
+ }
+
+ free(t_data);
+
+ t_nCertsInChain = 1;
+
+ if (!json_object_object_get_ex(j_entry, "extra_data",
+ &j_extraData))
+ goto label_addCerts;
+
+ /* Decode the Base64 extra_data string */
+ t_b64Data = (char*)json_object_get_string(j_extraData);
+ t_data = NULL;
+ Base64Decode(t_b64Data, &t_data);
+ if (!t_data) {
+ printError("Base64 decode error", "");
+ goto label_exit;
+ }
+
+ t_pointer1 = t_data;
+
+ if (t_logEntryType == 1) {
+ t_certSize = 0;
+ memcpy(((char*)&t_certSize) + 1, t_pointer1, 3);
+ t_certSize = be32toh(t_certSize);
+
+ t_pointer1 += 3;
+ t_pointer = t_pointer1;
+
+ t_x509 = d2i_X509(
+ NULL, (const unsigned char**)&t_pointer,
+ t_certSize
+ );
+ if (!t_x509) {
+ printError("Failed to decode Precertificate", t_b64Data);
+ goto label_exit;
+ }
+
+ t_subjectName = X509_NAME_oneline(
+ X509_get_subject_name(t_x509), NULL, 0
+ );
+ printf("Precertificate: %s\n", t_subjectName);
+ X509_free(t_x509);
+ if (t_subjectName)
+ OPENSSL_free(t_subjectName);
+
+ /* Construct the "INSERT" query */
+ sprintf(t_query[0],
+ "SELECT import_ct_cert(%s::smallint, %d, %"
+ LENGTH64 "u, E'\\\\x",
+ PQgetvalue(t_PGresult_select, i, 0),
+ (t_entryID + j), t_timestamp);
+
+ for (k = 0; k < t_certSize; k++)
+ sprintf(
+ t_query[0] + strlen(t_query[0]), "%02X",
+ *(unsigned char*)(t_pointer1 + k));
+ strcat(t_query[0], "')");
+
+ t_pointer1 = t_pointer;
+ }
+
+ /* Find the total length of the CA certificate array */
+ t_totalLength = 0;
+ memcpy(((char*)&t_totalLength) + 1, t_pointer1, 3);
+ t_totalLength = be32toh(t_totalLength);
+ t_pointer1 += 3;
+
+ /* Parse each CA Certificate */
+ while (t_pointer1 < (t_data + t_totalLength)) {
+ t_certSize = 0;
+ memcpy(((char*)&t_certSize) + 1, t_pointer1, 3);
+ t_certSize = be32toh(t_certSize);
+
+ t_pointer1 += 3;
+ t_pointer = t_pointer1;
+
+ t_x509 = d2i_X509(
+ NULL, (const unsigned char**)&t_pointer,
+ t_certSize
+ );
+ if (!t_x509) {
+ printError("Failed to decode CA cert", t_b64Data);
+ goto label_exit;
+ }
+
+ t_subjectName = X509_NAME_oneline(
+ X509_get_subject_name(t_x509), NULL, 0
+ );
+ printf("CA: %s", t_subjectName);
+ X509_free(t_x509);
+ if (t_subjectName)
+ OPENSSL_free(t_subjectName);
+
+ /* Generate SHA-256(CACertificate) */
+ EVP_DigestInit_ex(&t_mdctx, t_md, NULL);
+ EVP_DigestUpdate(&t_mdctx, t_pointer1, t_certSize);
+ EVP_DigestFinal_ex(&t_mdctx, t_sha256Hash_data, &t_sha256Hash_size);
+
+ if ((!t_cachedCACerts) || (!bsearch(t_sha256Hash_data, t_cachedCACerts, t_nCachedCACerts, 32, compareSHA256Hashes))) {
+ /* We've not cached this CA Certificate yet, so let's "INSERT" it and cache it */
+ /* Construct the "INSERT" query */
+ strcpy(t_query[t_nCertsInChain], "SELECT import_cert(E'\\\\x");
+ for (k = 0; k < t_certSize; k++)
+ sprintf(
+ t_query[t_nCertsInChain] + strlen(t_query[t_nCertsInChain]), "%02X",
+ *(unsigned char*)(t_pointer1 + k));
+ strcat(t_query[t_nCertsInChain], "')");
+ t_nCertsInChain++;
+
+ /* Cache this SHA-256(CACertificate), then re-sort the list */
+ t_nCachedCACerts++;
+ t_cachedCACerts = realloc(t_cachedCACerts, t_nCachedCACerts * 32);
+ (void)memcpy(t_cachedCACerts + ((t_nCachedCACerts - 1) * 32), t_sha256Hash_data, t_sha256Hash_size);
+ qsort(t_cachedCACerts, t_nCachedCACerts, 32, compareSHA256Hashes);
+ }
+ else
+ printf(" (Already cached)");
+
+ printf("\n");
+
+ t_pointer1 = t_pointer;
+ }
+
+ free(t_data);
+
+
+ label_addCerts:
+ /* Execute the "INSERT" quer(ies) */
+ printf("Import %d cert%s: ", t_nCertsInChain, (t_nCertsInChain == 1) ? "" : "s");
+ for (q = t_nCertsInChain - 1; q >= 0; q--) {
+ t_PGresult = PQexec(t_PGconn, t_query[q]);
+ if (PQresultStatus(t_PGresult) != PGRES_TUPLES_OK) {
+ /* The SQL query failed */
+ printError("Query failed", PQerrorMessage(t_PGconn));
+ goto label_exit;
+ }
+ else if (PQgetisnull(t_PGresult, 0, 0)) {
+ /* The SQL query failed */
+ printError("Query failed", t_query[q]);
+ goto label_exit;
+ }
+
+ PQclear(t_PGresult);
+ }
+ printf("OK\n");
+
+ t_confirmedEntryID = t_entryID + j;
+
+ if (g_terminateNow)
+ goto label_exit;
+
+ printf("\n");
+ }
+
+ if (json_object_put(j_getEntries) != 1) {
+ printError(
+ "json_object_put(j_getEntries)",
+ "Did not return 1"
+ );
+ goto label_exit;
+ }
+
+ free(t_responseBuffer.data);
+ t_responseBuffer.data = NULL;
+ t_responseBuffer.size = 0;
+
+ if (g_terminateNow)
+ goto label_exit;
+ }
+
+ /* Cleanup the "easy handle" */
+ curl_easy_cleanup(t_hEasy);
+ t_hEasy = NULL;
+
+ if (t_confirmedEntryID == -1)
+ t_entryID--;
+ else
+ t_entryID = t_confirmedEntryID;
+
+ sprintf(
+ t_query[0],
+ "UPDATE ct_log"
+ " SET LATEST_ENTRY_ID=%d,"
+ " LATEST_UPDATE=statement_timestamp(),"
+ " LATEST_STH_TIMESTAMP=TIMESTAMP WITH TIME ZONE 'epoch'"
+ " + interval'%" LENGTH64 "d seconds'"
+ " + interval'%" LENGTH64 "d milliseconds'"
+ " WHERE ID=%s",
+ t_entryID,
+ t_sthTimestamp / 1000,
+ t_sthTimestamp % 1000,
+ PQgetvalue(t_PGresult_select, i, 0)
+ );
+ t_PGresult = PQexec(t_PGconn, t_query[0]);
+ if (PQresultStatus(t_PGresult) != PGRES_COMMAND_OK) {
+ /* The SQL query failed */
+ printError(
+ "UPDATE Query failed",
+ PQerrorMessage(t_PGconn)
+ );
+ }
+ PQclear(t_PGresult);
+ }
+
+ t_returnCode = EXIT_SUCCESS;
+
+label_exit:
+ if (t_returnCode == EXIT_FAILURE)
+ if (t_confirmedEntryID != -1) {
+ sprintf(
+ t_query[0],
+ "UPDATE ct_log"
+ " SET LATEST_ENTRY_ID=%d,"
+ " LATEST_UPDATE=statement_timestamp()"
+ " WHERE ID=%s",
+ t_confirmedEntryID,
+ PQgetvalue(t_PGresult_select, i, 0)
+ );
+ t_PGresult = PQexec(t_PGconn, t_query[0]);
+ if (PQresultStatus(t_PGresult) != PGRES_COMMAND_OK) {
+ /* The SQL query failed */
+ printError(
+ "UPDATE Query failed",
+ PQerrorMessage(t_PGconn)
+ );
+ }
+ PQclear(t_PGresult);
+ }
+
+ printError("Terminated", NULL);
+
+ /* Clear the query results */
+ if (t_PGresult_select)
+ PQclear(t_PGresult_select);
+
+ /* Close this DB connection */
+ if (t_PGconn)
+ PQfinish(t_PGconn);
+
+ /* Free the Response Buffer */
+ if (t_responseBuffer.data)
+ free(t_responseBuffer.data);
+
+ /* Cleanup the "easy handle" */
+ if (t_hEasy)
+ curl_easy_cleanup(t_hEasy);
+
+ /* curl_global_cleanup() must be called EXACTLY once */
+ curl_global_cleanup();
+
+ for (q = 0; q < 32; q++)
+ if (t_query[q])
+ free(t_query[q]);
+
+ if (t_cachedCACerts)
+ free(t_cachedCACerts);
+
+ EVP_MD_CTX_cleanup(&t_mdctx);
+
+ return t_returnCode;
+}