Skip to content

Commit

Permalink
Validate headers and footers of POST'ed certificates.
Browse files Browse the repository at this point in the history
Change-Id: I38e5511cc89fdfe1d81697f5a4ceae4e3b00862a
  • Loading branch information
stepheng-axis committed Feb 28, 2024
1 parent 2f8697c commit 4d2b2f8
Showing 1 changed file with 129 additions and 20 deletions.
149 changes: 129 additions & 20 deletions app/dockerdwrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,14 @@
#include <unistd.h>
#include "fastcgi_cert_manager.h"

#define syslog_v(...) if (g_verbose) {syslog(__VA_ARGS__);}

static bool g_verbose = false;


/**
* @brief Callback called when a well formed fcgi request is received.
*/
void callback_action(__attribute__((unused)) fcgi_handle handle,
int request_method,
char *cert_name,
Expand Down Expand Up @@ -68,12 +75,110 @@ static const char *ax_parameters[] = {"IPCSocket",

static const char *tls_cert_path = "/usr/local/packages/dockerdwrapper/localdata/";

static const char *tls_certs[] = {"ca.pem",
"server-cert.pem",
"server-key.pem"};
typedef enum {PEM_CERT = 0,
RSA_PRIVATE_KEY,
NUM_CERT_TYPES} cert_types;

static const char *headers[NUM_CERT_TYPES] = {"-----BEGIN CERTIFICATE-----\n",
"-----BEGIN RSA PRIVATE KEY-----\n"};

static const char *footers[NUM_CERT_TYPES] = {"-----END CERTIFICATE-----\n",
"-----END RSA PRIVATE KEY-----\n"};

typedef struct {const char *name;
int type;
} cert;

static cert tls_certs[] = {{"ca.pem", PEM_CERT },
{"server-cert.pem", PEM_CERT},
{"server-key.pem", RSA_PRIVATE_KEY}};

#define NUM_TLS_CERTS sizeof(tls_certs)/sizeof(cert)
#define CERT_FILE_MODE 0400


/**
* @brief Checks if the certificate name is supported
* and optionally updates the certificate type.
*
* @param cert_name the certificate to check
* @param cert_type the type to be updated or NULL
* @return true if valid, false otherwise.
*/
static bool
supported_cert(char *cert_name, int *cert_type)
{
for (size_t i = 0; i < NUM_TLS_CERTS; ++i) {
if (strcmp(cert_name, tls_certs[i].name) == 0){
if (cert_type)
*cert_type = tls_certs[i].type; /* Update cert_type as well */
return true;
}
}

syslog(LOG_ERR,"The file_name is not supported. Supported names are \"%s\", \"%s\" and \"%s\".",
tls_certs[0].name, tls_certs[1].name, tls_certs[2].name);
return false;
}

/**
* @brief Checks if the certificate appears valid according to type.
*
* @param file_path the certificate to check
* @param cert_type the type to validate against
* @return true if valid, false otherwise.
*/
static bool
valid_cert(char *file_path, int cert_type)
{
char buffer[128];
size_t toread;
bool valid = false;

FILE *fp = fopen(file_path, "r");
if (fp == NULL) {
syslog(LOG_ERR,"Could not fopen %s", file_path);
return false;
}

/* Check header */
toread = strlen(headers[cert_type]);
if (fseek(fp, 0, SEEK_SET) != 0) {
syslog(LOG_ERR, "Could not fseek(0, SEEK_SET) bytes, err: %s", strerror(errno));
goto end;
}
if (fread(buffer, toread, 1, fp) != 1) {
syslog(LOG_ERR, "Could not fread %d bytes, err: %s", toread, strerror(errno));
goto end;
}
if (strncmp(buffer, headers[cert_type], toread) != 0) {
syslog(LOG_ERR, "Invalid header found");
syslog_v(LOG_INFO, "Expected %.*s, found %.*s", toread, headers[cert_type], toread, buffer);
goto end;
}

/* Check footer */
toread = strlen(footers[cert_type]);
if (fseek(fp, -toread, SEEK_END) != 0) {
syslog(LOG_ERR, "Could not fseek(%d, SEEK_END) bytes, err: %s", -toread, strerror(errno));
goto end;
}
if (fread(buffer, toread, 1, fp) != 1) {
syslog(LOG_ERR, "Could not fread %d bytes, err: %s", toread, strerror(errno));
goto end;
}
if (strncmp(buffer, footers[cert_type], toread) != 0) {
syslog(LOG_ERR, "Invalid footer found");
syslog_v(LOG_INFO, "Expected %.*s, found %.*s", toread, footers[cert_type], toread, buffer);
goto end;
}
valid = true;

end:
fclose(fp);
return valid;
}

/**
* @brief Signals handling
*
Expand Down Expand Up @@ -334,9 +439,9 @@ static gboolean get_and_verify_tls_selection(bool *use_tls_ret)
if (use_tls_value != NULL) {
use_tls = (strcmp(use_tls_value, "yes") == 0);
if (use_tls) {
char *ca_path = g_strdup_printf("%s%s",tls_cert_path,tls_certs[0]);
char *cert_path = g_strdup_printf("%s%s",tls_cert_path,tls_certs[1]);
char *key_path = g_strdup_printf("%s%s",tls_cert_path,tls_certs[2]);
char *ca_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[0].name);
char *cert_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[1].name);
char *key_path = g_strdup_printf("%s%s", tls_cert_path, tls_certs[2].name);

bool ca_exists = access(ca_path, F_OK) == 0;
bool cert_exists = access(cert_path, F_OK) == 0;
Expand Down Expand Up @@ -441,10 +546,10 @@ get_verbose_selection(bool *use_verbose_ret)
use_verbose = strcmp(use_verbose_value, "yes") == 0;
syslog(LOG_INFO,"Verbose set to %d", use_verbose);
*use_verbose_ret = use_verbose;
g_verbose = use_verbose;
return_value = true;
}
free(use_verbose_value);
syslog(LOG_INFO,"Verbose set to %d", *use_verbose_ret); /* TODO: cleanup */
return return_value;
}

Expand Down Expand Up @@ -897,27 +1002,29 @@ callback_action(__attribute__((unused)) fcgi_handle handle,
char *cert_name,
char *file_path)
{
// Check that the cert name is supported
bool supported_cert = false;
for (size_t i = 0; i < sizeof(tls_certs) / sizeof(tls_certs[0]); ++i) {
if (strcmp(cert_name, tls_certs[i]) == 0){
supported_cert = true;
}
}

if (!supported_cert) {
syslog(LOG_ERR,"The cert_name is not supported. Supported names are \"%s\", \"%s\" and \"%s\".",tls_certs[0],tls_certs[1],tls_certs[2]);
/* Is cert supported? */
int cert_type;
if (!supported_cert(cert_name, &cert_type)) {
if (file_path && (g_remove(file_path) != 0)) {
syslog(LOG_ERR, "Failed to remove %s, err: %s", file_path, strerror(errno));
return;
}
return;
}

/* Action requested method */
char* cert_file_with_path = NULL;
switch (request_method) {
case POST:
cert_file_with_path = g_strdup_printf("%s%s",tls_cert_path,cert_name);
/* Is cert valid? */
if (!valid_cert(file_path, cert_type)) {
if (g_remove(file_path) != 0) {
syslog(LOG_ERR, "Failed to remove invalid %s, err: %s", file_path, strerror(errno));
}
return;
}

/* Use new and update cert mode */
cert_file_with_path = g_strdup_printf("%s%s", tls_cert_path, cert_name);
syslog(LOG_INFO,"Moving %s to %s", file_path, cert_file_with_path);
if (g_rename(file_path, cert_file_with_path) != 0) {
syslog(LOG_ERR, "Failed to move %s to %s, err: %s", file_path, cert_file_with_path, strerror(errno));
Expand All @@ -930,7 +1037,8 @@ callback_action(__attribute__((unused)) fcgi_handle handle,
break;

case DELETE:
cert_file_with_path = g_strdup_printf("%s%s", tls_cert_path,cert_name);
/* Delete cert */
cert_file_with_path = g_strdup_printf("%s%s", tls_cert_path, cert_name);
syslog(LOG_INFO, "Removing %s", cert_file_with_path);
if (g_file_test(cert_file_with_path, G_FILE_TEST_EXISTS )) {
if (g_remove(cert_file_with_path) != 0) {
Expand All @@ -946,6 +1054,7 @@ callback_action(__attribute__((unused)) fcgi_handle handle,
}

end:
/* Cleanup */
free(cert_file_with_path);
return;
}

0 comments on commit 4d2b2f8

Please sign in to comment.