MINOR: ssl: Added cert_key_and_chain struct
Added cert_key_and_chain struct to ssl. This struct will store the
contents of a crt path (from the config file) into memory. This will
allow us to use the data stored in memory instead of reading the file
multiple times.
This will be used to support a later commit to load multiple pkeys/certs
into a single SSL_CTX
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index c67cd56..2ea7f40 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -1582,6 +1582,191 @@
return order;
}
+
+/* The following code is used for loading multiple crt files into
+ * SSL_CTX's based on CN/SAN
+ */
+#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
+/* This is used to preload the certifcate, private key
+ * and Cert Chain of a file passed in via the crt
+ * argument
+ *
+ * This way, we do not have to read the file multiple times
+ */
+struct cert_key_and_chain {
+ X509 *cert;
+ EVP_PKEY *key;
+ unsigned int num_chain_certs;
+ /* This is an array of X509 pointers */
+ X509 **chain_certs;
+};
+
+/* Frees the contents of a cert_key_and_chain
+ */
+static void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
+{
+ int i;
+
+ if (!ckch)
+ return;
+
+ /* Free the certificate and set pointer to NULL */
+ if (ckch->cert)
+ X509_free(ckch->cert);
+ ckch->cert = NULL;
+
+ /* Free the key and set pointer to NULL */
+ if (ckch->key)
+ EVP_PKEY_free(ckch->key);
+ ckch->key = NULL;
+
+ /* Free each certificate in the chain */
+ for (i = 0; i < ckch->num_chain_certs; i++) {
+ if (ckch->chain_certs[i])
+ X509_free(ckch->chain_certs[i]);
+ }
+
+ /* Free the chain obj itself and set to NULL */
+ if (ckch->num_chain_certs > 0) {
+ free(ckch->chain_certs);
+ ckch->num_chain_certs = 0;
+ ckch->chain_certs = NULL;
+ }
+
+}
+
+/* checks if a key and cert exists in the ckch
+ */
+static int ssl_sock_is_ckch_valid(struct cert_key_and_chain *ckch)
+{
+ return (ckch->cert != NULL && ckch->key != NULL);
+}
+
+
+/* Loads the contents of a crt file (path) into a cert_key_and_chain
+ * This allows us to carry the contents of the file without having to
+ * read the file multiple times.
+ *
+ * returns:
+ * 0 on Success
+ * 1 on SSL Failure
+ * 2 on file not found
+ */
+static int ssl_sock_load_crt_file_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
+{
+
+ BIO *in;
+ X509 *ca = NULL;
+ int ret = 1;
+
+ ssl_sock_free_cert_key_and_chain_contents(ckch);
+
+ in = BIO_new(BIO_s_file());
+ if (in == NULL)
+ goto end;
+
+ if (BIO_read_filename(in, path) <= 0)
+ goto end;
+
+ /* Read Certificate */
+ ckch->cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
+ if (ckch->cert == NULL) {
+ memprintf(err, "%sunable to load certificate from file '%s'.\n",
+ err && *err ? *err : "", path);
+ goto end;
+ }
+
+ /* Read Private Key */
+ ckch->key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
+ if (ckch->key == NULL) {
+ memprintf(err, "%sunable to load private key from file '%s'.\n",
+ err && *err ? *err : "", path);
+ goto end;
+ }
+
+ /* Read Certificate Chain */
+ while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
+ /* Grow the chain certs */
+ ckch->num_chain_certs++;
+ ckch->chain_certs = realloc(ckch->chain_certs, (ckch->num_chain_certs * sizeof(X509 *)));
+
+ /* use - 1 here since we just incremented it above */
+ ckch->chain_certs[ckch->num_chain_certs - 1] = ca;
+ }
+ ret = ERR_get_error();
+ if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
+ memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
+ err && *err ? *err : "", path);
+ ret = 1;
+ goto end;
+ }
+
+ ret = 0;
+
+end:
+
+ ERR_clear_error();
+ if (in)
+ BIO_free(in);
+
+ /* Something went wrong in one of the reads */
+ if (ret != 0)
+ ssl_sock_free_cert_key_and_chain_contents(ckch);
+
+ return ret;
+}
+
+/* Loads the info in ckch into ctx
+ * Currently, this does not process any information about ocsp, dhparams or
+ * sctl
+ * Returns
+ * 0 on success
+ * 1 on failure
+ */
+static int ssl_sock_put_ckch_into_ctx(const char *path, const struct cert_key_and_chain *ckch, SSL_CTX *ctx, char **err)
+{
+ int i = 0;
+
+ if (SSL_CTX_use_PrivateKey(ctx, ckch->key) <= 0) {
+ memprintf(err, "%sunable to load SSL private key into SSL Context '%s'.\n",
+ err && *err ? *err : "", path);
+ return 1;
+ }
+
+ if (!SSL_CTX_use_certificate(ctx, ckch->cert)) {
+ memprintf(err, "%sunable to load SSL certificate into SSL Context '%s'.\n",
+ err && *err ? *err : "", path);
+ return 1;
+ }
+
+ /* This only happens for OpenSSL Versions < 1.0.2
+ * Otherwise ctx->extra_certs will always be NULL
+ */
+ if (ctx->extra_certs != NULL) {
+ sk_X509_pop_free(ctx->extra_certs, X509_free);
+ ctx->extra_certs = NULL;
+ }
+
+ /* Load all certs in the ckch into the ctx_chain for the ssl_ctx */
+ for (i = 0; i < ckch->num_chain_certs; i++) {
+ if (!SSL_CTX_add1_chain_cert(ctx, ckch->chain_certs[i])) {
+ memprintf(err, "%sunable to load chain certificate into SSL Context '%s'.\n",
+ err && *err ? *err : "", path);
+ return 1;
+ }
+ }
+
+ if (SSL_CTX_check_private_key(ctx) <= 0) {
+ memprintf(err, "%sinconsistencies between private key and certificate loaded from PEM file '%s'.\n",
+ err && *err ? *err : "", path);
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif /* #if OPENSSL_VERSION_NUMBER >= 0x1000200fL: Support for loading multiple certs into a single SSL_CTX */
+
/* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
* an early error happens and the caller must call SSL_CTX_free() by itelf.
*/