From 367fbc823fa17da4389a9654ce296e65c7fb3bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Rolim?= Date: Fri, 16 Oct 2020 01:05:24 -0300 Subject: [PATCH] Fix read_certs to receive FILE instead of a path. This change lead to completely overhauling the bearssl_read_certs() function, which now deals directly with FILES, instead of depending on mmap_file. There is some slight added complexity for dealing with the file reads. The idea for this came from the idea of implementing path resolution using openat() instead of path concatenation, so there was a need to pass either fds or file streams to functions instead of specific paths. --- gemi.c | 79 ++++++++++++++++++++++++++++++++++------------------ read_certs.c | 53 ++++++++++++++++++++++------------- read_certs.h | 2 +- 3 files changed, 86 insertions(+), 48 deletions(-) diff --git a/gemi.c b/gemi.c index 4277cba..6f98d9f 100644 --- a/gemi.c +++ b/gemi.c @@ -1,10 +1,13 @@ -#define _POSIX_C_SOURCE 200809L /* getopt, scandir */ +#define _POSIX_C_SOURCE 200809L /* getopt, scandir, openat */ #include +#include #include #include #include #include #include +#include +#include #include "purr.h" #include "mmap_file.h" @@ -137,35 +140,57 @@ int main(int argc, char **argv) } const char *home = getenv("HOME"); - const char *config = ".config/gemi"; - const int path_max = PATH_MAX; - char config_tmp[PATH_MAX]; if (home) { - if (snprintf(config_tmp, path_max, "%s/%s", home, config) >= path_max) { - fputs("path is too long!\n", stderr); - goto early_out; + char config[PATH_MAX]; + if (snprintf(config, PATH_MAX, "%s/%s", home, ".config/gemi") >= PATH_MAX) { + fputs("HOME is too long!\n", stderr); + goto stop_search; } - config = config_tmp; + int config_fd = open(config, O_DIRECTORY | O_PATH | O_CLOEXEC); + if (config_fd < 0) { + perror("open()"); + goto stop_search; + } + + struct dirent **files = NULL; + // XXX: ideally, should use scandirat(2), but that's currently glibc only + int filenum = scandir(config, &files, dirfilter, alphasort); + if (filenum < 0) { + if (debug) { + perror("scandir()"); + fprintf(stderr, "error reading configuration directory '%s'\n", config); + } + goto stop_search; + } + for (int i = 0; i < filenum; i++) { + int file = openat(config_fd, files[i]->d_name, O_RDONLY | O_CLOEXEC); + if (file < 0) { + perror("openat()"); + goto loop_end; + } + + struct stat st; + fstat(file, &st); + if ((st.st_mode & S_IFMT) == S_IFREG) { + FILE *file_stream = fdopen(file, "re"); + if (file_stream == NULL) { + close(file); + goto loop_end; + } + + if (bearssl_read_certs(&btas, file_stream) == 0) { + if (debug) fprintf(stderr, "error reading cert file: '%s'\n", files[i]->d_name); + } + } else { + close(file); + } + + loop_end: + free(files[i]); + } + free(files); } - struct dirent **files = NULL; - int filenum = scandir(config, &files, dirfilter, alphasort); - if (filenum < 0) { - if (debug) { - perror("scandir()"); - fprintf(stderr, "error reading configuration directory '%s'\n", config); - } - } - for (int i = 0; i < filenum; i++) { - char filename_tmp[PATH_MAX]; - if (snprintf(filename_tmp, path_max, "%s/%s", config, files[i]->d_name) >= path_max) { - continue; - } - if (bearssl_read_certs(&btas, filename_tmp) == 0) { - if (debug) fprintf(stderr, "error reading cert file '%s'\n", filename_tmp); - } - free(files[i]); - } - free(files); + stop_search: br_ssl_client_init_full(&sc, &xc, btas.ta, btas.n); br_ssl_engine_set_buffer(&sc.eng, iobuf, sizeof iobuf, 1); diff --git a/read_certs.c b/read_certs.c index ccc3b37..2452830 100644 --- a/read_certs.c +++ b/read_certs.c @@ -4,7 +4,6 @@ #include #include "read_certs.h" -#include "mmap_file.h" struct append_dn_status { uint8_t *dn; @@ -104,21 +103,22 @@ static void push_x509(void *dest_ctx, const void *src, size_t len) /* * Reads certs from file if set, otherwise from default location. */ -size_t bearssl_read_certs(struct trust_anchors *tas, const char *file) +size_t bearssl_read_certs(struct trust_anchors *tas, FILE *file) { - const char *cert_path = file ? file : getenv("CA_CERT_SSL_FILE"); - if (cert_path == NULL) { - cert_path = "/etc/ssl/certs.pem"; - } + size_t rv = 0; - struct mmap_file cert_map = create_mmap_from_file(cert_path, PROT_READ); - if (ERROR_MMAP(cert_map)) { - perror("create_mmap_from_file()"); - return 0; - } + if (file == NULL) { + const char *cert_path = getenv("CA_CERT_SSL_FILE"); + if (cert_path == NULL) { + cert_path = "/etc/ssl/certs.pem"; + } - off_t len = cert_map.size; - uint8_t *data = cert_map.data; + file = fopen(cert_path, "re"); + if (file == NULL) { + perror("fopen()"); + return rv; + } + } br_pem_decoder_context pem; br_x509_decoder_context x509; @@ -127,10 +127,20 @@ size_t bearssl_read_certs(struct trust_anchors *tas, const char *file) struct append_dn_status dn_status; - while (len > 0) { - size_t pushed = br_pem_decoder_push(&pem, data, len); - data += pushed; + uint8_t data[512]; + uint8_t *datap; + size_t len = 0; + while (1) { + if (len == 0) { + len = fread(data, 1, 512, file); + if (len == 0) { + break; + } + datap = data; + } + size_t pushed = br_pem_decoder_push(&pem, datap, len); len -= pushed; + datap += pushed; switch(br_pem_decoder_event(&pem)) { const char *name; @@ -179,7 +189,7 @@ size_t bearssl_read_certs(struct trust_anchors *tas, const char *file) .e = malloc(k.elen), .elen = k.elen}; if (rsa.n == NULL || rsa.e == NULL) { perror("malloc()"); - return 0; + goto out; } memcpy(rsa.n, k.n, k.nlen); memcpy(rsa.e, k.e, k.elen); @@ -193,7 +203,7 @@ size_t bearssl_read_certs(struct trust_anchors *tas, const char *file) .q = malloc(k.qlen), .qlen = k.qlen}; if (ec.q == NULL) { perror("malloc()"); - return 0; + goto out; } memcpy(ec.q, k.q, k.qlen); @@ -205,12 +215,15 @@ size_t bearssl_read_certs(struct trust_anchors *tas, const char *file) ta.pkey = new_key; if (append_ta(tas, ta) == -1) { - return 0; + goto out; } } break; } } - return tas->n; + rv = tas->n; + out: + fclose(file); + return rv; } diff --git a/read_certs.h b/read_certs.h index 8f0e1e6..b087751 100644 --- a/read_certs.h +++ b/read_certs.h @@ -12,6 +12,6 @@ struct trust_anchors { void bearssl_read_certs_help(FILE *); void bearssl_free_certs(struct trust_anchors); -size_t bearssl_read_certs(struct trust_anchors *, const char *); +size_t bearssl_read_certs(struct trust_anchors *, FILE *); #endif // __READ_CERTS_H_