Skip to content

Commit 9341a74

Browse files
committed
credential_loading.{c,h}: generalize cert and CRL loading to support HTTP download of PEM/DER encoded data
1 parent 3651d6c commit 9341a74

2 files changed

Lines changed: 142 additions & 39 deletions

File tree

src/credential_loading.c

Lines changed: 140 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
#include <openssl/provider.h>
1818
#include <openssl/store.h>
1919
#include <openssl/core_names.h>
20+
#include <openssl/http.h>
21+
#include <openssl/pem.h>
2022
#include <secutils/credentials/credentials.h>
2123
#include <secutils/credentials/store.h>
2224
#include <secutils/credentials/cert.h>
@@ -638,25 +640,133 @@ EVP_PKEY *FILES_load_pubkey_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const cha
638640
return pkey;
639641
}
640642

643+
static BIO *bio_to_mem_bio(BIO *in, size_t max_len)
644+
{
645+
char buf[4096];
646+
size_t total = 0;
647+
648+
if (in == NULL)
649+
return NULL;
650+
651+
BIO *mem = BIO_new(BIO_s_mem());
652+
if (mem == NULL)
653+
return NULL;
654+
655+
for (;;) {
656+
int n = BIO_read(in, buf, sizeof(buf));
657+
if (n == 0)
658+
break; /* EOF */
659+
660+
if (n > 0) {
661+
if (max_len != 0 && total + (size_t)n > max_len) {
662+
BIO_free(mem);
663+
return NULL;
664+
}
665+
666+
if (BIO_write(mem, buf, n) != n) {
667+
BIO_free(mem);
668+
return NULL;
669+
}
670+
671+
total += (size_t)n;
672+
continue;
673+
}
674+
675+
if (BIO_should_retry(in))
676+
continue;
677+
678+
BIO_free(mem);
679+
return NULL;
680+
}
681+
682+
return mem;
683+
}
684+
685+
static bool contains_str(const char *buf, size_t len, const char *str)
686+
{
687+
size_t nlen = strlen(str);
688+
if (nlen == 0 || len < nlen)
689+
return false;
690+
691+
const char *max = buf + len - nlen;
692+
while (buf <= max)
693+
if (memcmp(buf++, str, nlen) == 0)
694+
return true;
695+
return false;
696+
}
697+
698+
static BIO *http_get_mem(const char *uri, int timeout, const char *str, bool *found, const char *desc)
699+
{
700+
*found = false;
701+
if (CONN_IS_HTTPS(uri)) {
702+
LOG(FL_ERR, "Loading %s over HTTPS is unsupported; uri=%s", desc, uri);
703+
return NULL;
704+
}
705+
BIO *res = OSSL_HTTP_get(uri, NULL /* proxy */, NULL /* no_proxy */,
706+
NULL /* bio */, NULL /* rbio */, NULL /* cb */, NULL /* arg */,
707+
0 /* buf_size */, NULL /* headers */,
708+
NULL /* expected_ct */, 0 /* expect_asn1 */,
709+
100000 /* max_resp_len */, timeout);
710+
if (res == NULL) {
711+
LOG(FL_ERR, "Unable to download %s from %s\n", desc, uri);
712+
return NULL;
713+
}
714+
if (str == NULL)
715+
return res;
716+
717+
BIO *mem = bio_to_mem_bio(res, 0);
718+
BIO_free(res);
719+
if (mem == NULL) {
720+
LOG(FL_ERR, "Error reading %s data received from %s", desc, uri);
721+
return NULL;
722+
}
723+
724+
char *data = NULL;
725+
long len = BIO_get_mem_data(mem, &data);
726+
if (len <= 0 || data == NULL) {
727+
LOG(FL_ERR, "Error peeking at %s data received from %s", desc, uri);
728+
BIO_free(mem);
729+
return NULL;
730+
}
731+
732+
*found = contains_str(data, (size_t)len, str);
733+
return mem;
734+
}
735+
641736
X509 *FILES_load_cert_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const char *propq,
642737
OPTIONAL const char *uri, file_format_t format, bool maybe_stdin,
643-
OPTIONAL const char *source, OPTIONAL const char *desc,
738+
int timeout, OPTIONAL const char *source, OPTIONAL const char *desc,
644739
int type_CA, OPTIONAL const X509_VERIFY_PARAM *vpm)
645740
{
646741
char *pass;
647-
X509 *cert;
742+
X509 *cert = NULL;
648743

649744
if (desc == NULL)
650745
desc = "certificate";
651746
LOG(FL_DEBUG, "Loading %s from %s", desc, uri != NULL ? uri : "<stdin>");
652-
pass = FILES_get_pass(source, desc);
653-
(void)load_key_certs_crls(libctx, propq, uri, format, maybe_stdin, pass, desc, false,
654-
NULL, NULL, NULL, &cert, NULL, 1, NULL, NULL, 0);
655-
UTIL_cleanse_free(pass);
747+
if (CONN_IS_HTTP(uri) || CONN_IS_HTTPS(uri)) {
748+
bool is_pem;
749+
BIO *mem = http_get_mem(uri, timeout, "-----BEGIN CERTIFICATE-----", &is_pem, desc);
750+
if (mem != NULL) {
751+
cert = is_pem ? PEM_read_bio_X509(mem, NULL, NULL, NULL) : d2i_X509_bio(mem, NULL);
752+
BIO_free(mem);
753+
if (cert == NULL)
754+
LOG(FL_ERR, "Unable to decode %s from %s", desc, uri);
755+
}
756+
} else {
757+
pass = FILES_get_pass(source, desc);
758+
(void)load_key_certs_crls(libctx, propq, uri, format, maybe_stdin, pass, desc, false,
759+
NULL, NULL, NULL, &cert, NULL, 1, NULL, NULL, 0);
760+
UTIL_cleanse_free(pass);
761+
}
656762
if (!CERT_check(uri, cert, type_CA, vpm) && vpm != NULL) {
657763
X509_free(cert);
658764
cert = NULL;
659765
}
766+
if (cert == NULL) {
767+
ERR_print_errors(bio_err);
768+
LOG(FL_ERR, "Unable to load %s from %s\n", desc, uri != NULL ? uri : "<stdin>");
769+
}
660770
return cert;
661771
}
662772

@@ -692,26 +802,20 @@ bool FILES_load_certs_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const char *pro
692802

693803
if (desc == NULL)
694804
desc = "certs";
695-
LOG(FL_DEBUG, "Loading %s from %s", desc, srcs);
805+
if (!CONN_IS_HTTP(srcs)) /* well, this suppresses output also if only part of the srcs is HTTP-based */
806+
LOG(FL_DEBUG, "Loading %s from %s", desc, srcs);
696807
pass = FILES_get_pass(source, desc);
697808

698809
if (names == NULL || (all_crts = sk_X509_new_null()) == NULL)
699810
goto oom;
700811
for (src = UTIL_first_item(names); src != NULL; src = next) {
701812
next = UTIL_next_item(src); /* must do this here to split string */
702813

703-
if (CONN_IS_HTTPS(src)) {
704-
LOG(FL_ERR, "Loading %s over HTTPS is unsupported; uri=%s\n", desc, src);
705-
goto err;
706-
}
707-
708-
if (CONN_IS_HTTP(src)) {
709-
crt = X509_load_http(src, NULL, NULL, timeout);
710-
if (crt == NULL) {
711-
ERR_print_errors(bio_err);
712-
LOG(FL_ERR, "Unable to load %s from %s\n", desc, src);
814+
if (CONN_IS_HTTP(src) || CONN_IS_HTTPS(src)) {
815+
crt = FILES_load_cert_ex(libctx, propq, src, format, false,
816+
timeout, NULL, desc, type_CA, vpm);
817+
if (crt == NULL)
713818
goto err;
714-
}
715819
goto handle_crt;
716820
} else {
717821
if (!load_key_certs_crls(libctx, propq, src,
@@ -793,26 +897,27 @@ X509_CRL *FILES_load_crl_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const char *
793897
if (desc == NULL)
794898
desc = "CRL";
795899
LOG(FL_DEBUG, "Loading %s from %s", desc, uri != NULL ? uri : "<stdin>");
796-
if (CONN_IS_HTTPS(uri)) {
797-
LOG(FL_ERR, "Loading %s over HTTPS is unsupported; uri=%s\n", desc, uri);
798-
} else if (CONN_IS_HTTP(uri)) { /* TODO maybe also support PEM format */
799-
#if 1
800-
crl = CONN_load_crl_http(uri, timeout, 0, desc);
801-
#else
802-
crl = X509_CRL_load_http(uri, NULL, NULL, timeout);
803-
if (crl == NULL) {
804-
ERR_print_errors(bio_err);
805-
LOG(FL_ERR, "Unable to load %s from %s\n", desc, uri != NULL ? uri : "<stdin>");
900+
if (CONN_IS_HTTP(uri) || CONN_IS_HTTPS(uri)) {
901+
bool is_pem;
902+
BIO *mem = http_get_mem(uri, timeout, "-----BEGIN X509 CRL-----", &is_pem, desc);
903+
if (mem != NULL) {
904+
crl = is_pem ? PEM_read_bio_X509_CRL(mem, NULL, NULL, NULL) : d2i_X509_CRL_bio(mem, NULL);
905+
BIO_free(mem);
906+
if (crl == NULL)
907+
LOG(FL_ERR, "Unable to decode %s from %s", desc, uri);
806908
}
807-
#endif
808909
} else {
809910
(void)load_key_certs_crls(libctx, propq,
810911
uri, format, maybe_stdin, NULL, desc, false,
811912
NULL, NULL, NULL, NULL, NULL, 0, &crl, NULL, 1);
812913
}
813914
if (!CRL_check(uri, crl, vpm) && vpm != NULL) {
814915
X509_CRL_free(crl);
815-
return NULL;
916+
crl = NULL;
917+
}
918+
if (crl == NULL) {
919+
ERR_print_errors(bio_err);
920+
LOG(FL_ERR, "Unable to load %s from %s\n", desc, uri != NULL ? uri : "<stdin>");
816921
}
817922
return crl;
818923
}
@@ -828,20 +933,18 @@ STACK_OF(X509_CRL) *FILES_load_crls_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL c
828933

829934
if (desc == NULL)
830935
desc = "CRLs";
831-
LOG(FL_DEBUG, "Loading %s from %s", desc, srcs);
936+
if (!CONN_IS_HTTP(srcs)) /* well, this suppresses output also if only part of the srcs is HTTP-based */
937+
LOG(FL_DEBUG, "Loading %s from %s", desc, srcs);
832938

833939
if (names == NULL || (all_crls = sk_X509_CRL_new_null()) == NULL)
834940
goto oom;
835941
for (src = UTIL_first_item(names); src != NULL; src = next) {
836942
next = UTIL_next_item(src); /* must do this here to split string */
837943

838-
if (CONN_IS_HTTPS(src)) {
839-
LOG(FL_ERR, "Loading %s over HTTPS is unsupported; uri=%s\n", desc, src);
840-
goto err;
841-
}
842944

843-
if (CONN_IS_HTTP(src)) {
844-
if ((crl = CONN_load_crl_http(src, timeout, 0, desc)) == NULL)
945+
if (CONN_IS_HTTP(src) || CONN_IS_HTTPS(src)) {
946+
if ((crl = FILES_load_crl_ex(libctx, propq, src, format, false,
947+
timeout, desc, vpm)) == NULL)
845948
goto err;
846949
goto handle_crl;
847950
} else {

src/credential_loading.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ EVP_PKEY *FILES_load_pubkey_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const cha
4343

4444
X509 *FILES_load_cert_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const char *propq,
4545
OPTIONAL const char *uri, file_format_t format, bool maybe_stdin,
46-
OPTIONAL const char *source, OPTIONAL const char *desc,
46+
int timeout, OPTIONAL const char *source, OPTIONAL const char *desc,
4747
int type_CA, OPTIONAL const X509_VERIFY_PARAM *vpm);
4848
#define load_cert_pwd(uri, source, desc, type_CA, vpm) \
4949
FILES_load_cert_ex(app_get0_libctx(), app_get0_propq(), uri, \
50-
FILES_get_format(uri), false, source, desc, type_CA, vpm)
50+
FILES_get_format(uri), false, 0, source, desc, type_CA, vpm)
5151

5252
bool FILES_load_certs_ex(OPTIONAL OSSL_LIB_CTX *libctx, OPTIONAL const char *propq,
5353
const char *srcs, file_format_t format, int timeout,

0 commit comments

Comments
 (0)