Skip to content

Commit

Permalink
Reject upload of malformed TLS files
Browse files Browse the repository at this point in the history
  • Loading branch information
killenheladagen committed Apr 16, 2024
1 parent a61bb25 commit c5ed261
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ For more information on how to generate these files, please consult the official
[Docker documentation](https://docs.docker.com/engine/security/protect-access/).

The files can be uploaded to the device using HTTP.
The dockerd service will restart, or try to start, after each HTTP POST request.
The request will be rejected if the file being uploaded has the incorrect header or footer for that file type.
The dockerd service will restart, or try to start, after each successful HTTP POST request.

```sh
curl --anyauth -u "root:$DEVICE_PASSWORD" -F [email protected] -X POST \
Expand Down
7 changes: 6 additions & 1 deletion app/http_request.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "app_paths.h"
#include "fcgi_write_file_from_stream.h"
#include "log.h"
#include "tls.h"
#include <gio/gio.h>
#include <sys/stat.h>

Expand Down Expand Up @@ -81,7 +82,11 @@ post_request(FCGX_Request* request, const char* filename, restart_dockerd_t rest
response_msg(request, HTTP_422_UNPROCESSABLE_CONTENT, "Upload to temporary file failed.");
return;
}
if (!copy_to_localdata(temp_file, filename))
if (!tls_file_has_correct_format(filename, temp_file)) {
g_autofree char* msg =
g_strdup_printf("File is not a valid %s.", tls_file_description(filename));
response_msg(request, HTTP_400_BAD_REQUEST, msg);
} else if (!copy_to_localdata(temp_file, filename))
response_msg(request, HTTP_500_INTERNAL_SERVER_ERROR, "Failed to copy file to localdata");
else {
response_204_no_content(request);
Expand Down
68 changes: 68 additions & 0 deletions app/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "app_paths.h"
#include "log.h"
#include <glib.h>
#include <stdio.h>
#include <unistd.h>

#define TLS_CERT_PATH APP_LOCALDATA
Expand All @@ -18,6 +19,17 @@ static struct cert tls_certs[] = {{"--tlscacert", "ca.pem", "CA certificate"},

#define NUM_TLS_CERTS (sizeof(tls_certs) / sizeof(tls_certs[0]))

#define BEGIN(x) "-----BEGIN " x "-----\n"
#define END(x) "-----END " x "-----\n"
#define CERTIFICATE "CERTIFICATE"
#define PRIVATE_KEY "PRIVATE KEY"
#define RSA_PRIVATE_KEY "RSA PRIVATE KEY"

// Filename is assumed to be one of those listed in tls_certs[].
static bool is_key_file(const char* filename) {
return strstr(filename, "key");
}

static bool cert_file_exists(const struct cert* tls_cert) {
g_autofree char* full_path = g_strdup_printf("%s/%s", TLS_CERT_PATH, tls_cert->filename);
return access(full_path, F_OK) == 0;
Expand All @@ -39,6 +51,13 @@ void tls_log_missing_cert_warnings(void) {
tls_certs[i].filename);
}

const char* tls_file_description(const char* filename) {
for (size_t i = 0; i < NUM_TLS_CERTS; ++i)
if (strcmp(filename, tls_certs[i].filename) == 0)
return tls_certs[i].description;
return NULL;
}

const char* tls_args_for_dockerd(void) {
static char args[512]; // Too small buffer will cause truncated options, nothing more.
const char* end = args + sizeof(args);
Expand All @@ -53,3 +72,52 @@ const char* tls_args_for_dockerd(void) {
tls_certs[i].filename);
return args;
}

static bool read_bytes_from(FILE* fp, int whence, char* buffer, int num_bytes) {
const long offset = whence == SEEK_SET ? 0 : -num_bytes;
if (fseek(fp, offset, whence) != 0) {
log_error("Could not reposition stream to %s%ld: %s",
whence == SEEK_SET ? "SEEK_SET+" : "SEEK_END",
offset,
strerror(errno));
return false;
}
if (fread(buffer, num_bytes, 1, fp) != 1) {
log_error("Could not read %d bytes: %s", num_bytes, strerror(errno));
return false;
}
return true;
}

static bool is_file_section_equal_to(FILE* fp, int whence, const char* section) {
char buffer[128];
int to_read = strlen(section);
if (!read_bytes_from(fp, whence, buffer, to_read))
return false;
buffer[to_read] = '\0';
return strncmp(buffer, section, to_read) == 0;
}

static bool has_header_and_footer(FILE* fp, const char* header, const char* footer) {
return is_file_section_equal_to(fp, SEEK_SET, header) &&
is_file_section_equal_to(fp, SEEK_END, footer);
}

bool tls_file_has_correct_format(const char* filename, const char* path_to_file) {
FILE* fp = fopen(path_to_file, "r");
if (!fp) {
log_error("Could not read %s", path_to_file);
return false;
}

bool correct = is_key_file(filename)
? (has_header_and_footer(fp, BEGIN(PRIVATE_KEY), END(PRIVATE_KEY)) ||
has_header_and_footer(fp, BEGIN(RSA_PRIVATE_KEY), END(RSA_PRIVATE_KEY)))
: has_header_and_footer(fp, BEGIN(CERTIFICATE), END(CERTIFICATE));
if (!correct)
log_error("%s does not contain the headers and footers for a %s.",
path_to_file,
tls_file_description(filename));
fclose(fp);
return correct;
}
2 changes: 2 additions & 0 deletions app/tls.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@

bool tls_missing_certs(void);
void tls_log_missing_cert_warnings(void);
const char* tls_file_description(const char* filename);
const char* tls_args_for_dockerd(void);
bool tls_file_has_correct_format(const char* filename, const char* path_to_file);

0 comments on commit c5ed261

Please sign in to comment.