Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MTLS authentication #271

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,13 @@ test_cache_sh = configure_file(
test_cache = find_program(test_cache_sh)
test('test-cache.sh', test_cache,
timeout : 30 * 60)
test_mtls_sh = configure_file(
output : 'test-mtls.sh',
input : 'test/test-mtls.sh.in',
configuration : substs)
test_mtls = find_program(test_mtls_sh)
test('test-mtls.sh', test_mtls,
timeout : 30 * 60)

udev_rule = configure_file(
output : '75-casync.rules',
Expand Down
53 changes: 53 additions & 0 deletions src/caremote.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ struct CaRemote {
char *wstore_url; /* The "primary" store, where we write to */
char **rstore_urls; /* Additional, "secondary" stores we check */

char *arg_tls_cert;
char *arg_tls_ca ;
char *arg_tls_key;

char *cache_path;
int cache_fd;
bool remove_cache;
Expand Down Expand Up @@ -674,6 +678,30 @@ int ca_remote_set_archive_fd(CaRemote *rr, int fd) {
return ca_remote_file_set_fd(&rr->index_file, fd);
}

int ca_remote_set_client_auth(CaRemote *rr, const char *client_cert, const char *client_key, const char *ca_cert) {
if (!rr)
return -EINVAL;
if (!client_cert||!client_key)
return -EINVAL;


rr->arg_tls_cert = strdup(client_cert);
if (!rr->arg_tls_cert)
return -ENOMEM;

rr->arg_tls_key = strdup(client_key);
if (!rr->arg_tls_key)
return -ENOMEM;

if(ca_cert){
rr->arg_tls_ca = strdup(ca_cert);
if (!rr->arg_tls_ca)
return -ENOMEM;
}

return 0;
}

static int ca_remote_init_cache(CaRemote *rr) {
int r;

Expand Down Expand Up @@ -1000,6 +1028,12 @@ static int ca_remote_start(CaRemote *rr) {

if (rr->rate_limit_bps != UINT64_MAX)
argc++;
if (rr->arg_tls_key&&rr->arg_tls_cert){
argc+=2;
}
if(rr->arg_tls_ca){
argc++;
}

args = newa(char*, argc + 1);

Expand Down Expand Up @@ -1045,6 +1079,25 @@ static int ca_remote_start(CaRemote *rr) {

i++;
}
if (rr->arg_tls_key&&rr->arg_tls_cert){
r = asprintf(args + i, "--tls-client-cert=%s", rr->arg_tls_cert);
if (r < 0)
return log_oom();

i++;
r = asprintf(args + i, "--tls-client-key=%s", rr->arg_tls_key);
if (r < 0)
return log_oom();

i++;
}
if(rr->arg_tls_ca){
r = asprintf(args + i, "--tls-ca-cert=%s", rr->arg_tls_ca);
if (r < 0)
return log_oom();

i++;
}

args[i + CA_REMOTE_ARG_OPERATION] = (char*) ((rr->local_feature_flags & (CA_PROTOCOL_PUSH_CHUNKS|CA_PROTOCOL_PUSH_INDEX|CA_PROTOCOL_PUSH_ARCHIVE)) ? "push" : "pull");
args[i + CA_REMOTE_ARG_BASE_URL] = /* rr->base_url ? rr->base_url + skip :*/ (char*) "-";
Expand Down
2 changes: 2 additions & 0 deletions src/caremote.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ int ca_remote_set_index_fd(CaRemote *rr, int fd);
int ca_remote_set_archive_path(CaRemote *rr, const char *path);
int ca_remote_set_archive_fd(CaRemote *rr, int fd);

int ca_remote_set_client_auth(CaRemote *rr, const char *client_cert, const char *client_key, const char *ca_cert);

int ca_remote_step(CaRemote *rr);

int ca_remote_poll(CaRemote *rr, uint64_t timeout_nsec, const sigset_t *ss);
Expand Down
71 changes: 63 additions & 8 deletions src/casync-http.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ static volatile sig_atomic_t quit = false;
static int arg_log_level = -1;
static bool arg_verbose = false;
static curl_off_t arg_rate_limit_bps = 0;
static char *arg_tls_cert = NULL;
static char *arg_tls_ca = NULL;
static char *arg_tls_key = NULL;

static enum {
ARG_PROTOCOL_HTTP,
Expand Down Expand Up @@ -73,6 +76,28 @@ static CURLcode robust_curl_easy_perform(CURL *curl) {
return c;
}

static CURLcode set_curl_tls_opt(CURL *curl){
CURLcode curLcode = CURLE_OK;
if(arg_tls_cert&&arg_tls_key){
curLcode = curl_easy_setopt(curl, CURLOPT_SSLCERT, arg_tls_cert);
if (curLcode != CURLE_OK){
log_error("Failed to add client certificate");
return curLcode;
}
curLcode = curl_easy_setopt(curl, CURLOPT_SSLKEY, arg_tls_key);
if( curLcode != CURLE_OK){
log_error("Failed to add client private key");
return curLcode;
}
}
if(arg_tls_ca){
curLcode = curl_easy_setopt(curl, CURLOPT_CAINFO, arg_tls_ca);
if (curLcode != CURLE_OK)
log_error("Failed to add certificate authority");
}
return curLcode;
}

static int process_remote(CaRemote *rr, ProcessUntil until) {
int r;

Expand Down Expand Up @@ -293,6 +318,7 @@ static int acquire_file(CaRemote *rr,
size_t (*callback)(const void *p, size_t size, size_t nmemb, void *userdata),
void *userdata) {
long protocol_status;
CURLcode curl_code;

assert(curl);
assert(url);
Expand All @@ -313,11 +339,10 @@ static int acquire_file(CaRemote *rr,
log_error("Failed to set CURL private data.");
return -EIO;
}

log_debug("Acquiring %s...", url);

if (robust_curl_easy_perform(curl) != CURLE_OK) {
log_error("Failed to acquire %s", url);
curl_code = robust_curl_easy_perform(curl);
if ( curl_code != CURLE_OK) {
log_error("Failed to acquire %s %s", url, curl_easy_strerror(curl_code));
return -EIO;
}

Expand Down Expand Up @@ -373,6 +398,7 @@ static int run(int argc, char *argv[]) {
const char *base_url, *archive_url, *index_url, *wstore_url;
size_t n_stores = 0, current_store = 0;
CURL *curl = NULL;
CURLcode curl_code;
_cleanup_(ca_remote_unrefp) CaRemote *rr = NULL;
_cleanup_(realloc_buffer_free) ReallocBuffer buffer = {};
_cleanup_free_ char *url_buffer = NULL;
Expand Down Expand Up @@ -476,6 +502,10 @@ static int run(int argc, char *argv[]) {
goto finish;
}

if(set_curl_tls_opt(curl)!=CURLE_OK){
return -EIO;
}

if (archive_url) {
r = acquire_file(rr, curl, archive_url, write_archive, rr);
if (r < 0)
Expand Down Expand Up @@ -509,7 +539,6 @@ static int run(int argc, char *argv[]) {
for (;;) {
const char *store_url;
CaChunkID id;

if (quit) {
log_info("Got exit signal, quitting.");
r = 0;
Expand Down Expand Up @@ -587,10 +616,12 @@ static int run(int argc, char *argv[]) {
goto finish;
}

log_debug("Acquiring %s...", url_buffer);

if (robust_curl_easy_perform(curl) != CURLE_OK) {
log_error("Failed to acquire %s", url_buffer);

log_debug("Acquiring %s...", url_buffer);
curl_code = robust_curl_easy_perform(curl);
if (curl_code != CURLE_OK) {
log_error("Failed to acquire %s a %s", url_buffer, curl_easy_strerror(curl_code));
r = -EIO;
goto finish;
}
Expand Down Expand Up @@ -659,13 +690,19 @@ static int parse_argv(int argc, char *argv[]) {

enum {
ARG_RATE_LIMIT_BPS = 0x100,
ARG_TLS_KEY,
ARG_TLS_CERT,
ARG_TLS_CA
};

static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "log-level", required_argument, NULL, 'l' },
{ "verbose", no_argument, NULL, 'v' },
{ "rate-limit-bps", required_argument, NULL, ARG_RATE_LIMIT_BPS },
{ "tls-client-cert",required_argument, NULL, 't' },
{ "tls-client-key", required_argument, NULL, 'k' },
{ "tls-ca-cert", required_argument, NULL, 'c' },
{}
};

Expand Down Expand Up @@ -715,6 +752,24 @@ static int parse_argv(int argc, char *argv[]) {
arg_rate_limit_bps = strtoll(optarg, NULL, 10);
break;

case 't':
r = free_and_strdup(&arg_tls_cert, optarg);
if (r < 0)
return log_oom();
break;

case 'k':
r = free_and_strdup(&arg_tls_key, optarg);
if (r < 0)
return log_oom();
break;

case 'c':
r = free_and_strdup(&arg_tls_ca, optarg);
if (r < 0)
return log_oom();
break;

case '?':
return -EINVAL;

Expand Down
50 changes: 48 additions & 2 deletions src/casync-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ static char *arg_store = NULL;
static char **arg_extra_stores = NULL;
static char **arg_seeds = NULL;
static char *arg_cache = NULL;
static char *arg_tls_cert = NULL;
static char *arg_tls_ca = NULL;
static char *arg_tls_key = NULL;
static bool arg_cache_auto = false;
static size_t arg_chunk_size_min = 0;
static size_t arg_chunk_size_avg = 0;
Expand Down Expand Up @@ -121,6 +124,9 @@ static void help(void) {
" --seed-output=no Don't implicitly add pre-existing output as seed\n"
" when extracting\n"
" --recursive=no List non-recursively\n"
" --tls-ca-cert Certificate Authority to validate remote tls server\n"
" --tls-client-cert Client cert to authenticate to remote tls server\n"
" --tls-client-key Client key to authenticate to remote tls server\n"
#if HAVE_FUSE
" --mkdir=no Don't automatically create mount directory if it\n"
" is missing\n"
Expand Down Expand Up @@ -347,6 +353,9 @@ static int parse_argv(int argc, char *argv[]) {
ARG_DIGEST,
ARG_COMPRESSION,
ARG_VERSION,
ARG_TLS_KEY,
ARG_TLS_CERT,
ARG_TLS_CA
};

static const struct option options[] = {
Expand Down Expand Up @@ -380,6 +389,9 @@ static int parse_argv(int argc, char *argv[]) {
{ "mkdir", required_argument, NULL, ARG_MKDIR },
{ "digest", required_argument, NULL, ARG_DIGEST },
{ "compression", required_argument, NULL, ARG_COMPRESSION },
{ "tls-client-cert", required_argument, NULL, ARG_TLS_CERT },
{ "tls-client-key", required_argument, NULL, ARG_TLS_KEY },
{ "tls-ca-cert", required_argument, NULL, ARG_TLS_CA },
{}
};

Expand Down Expand Up @@ -666,6 +678,24 @@ static int parse_argv(int argc, char *argv[]) {
break;
}

case ARG_TLS_CERT:
r = free_and_strdup(&arg_tls_cert, optarg);
if (r < 0)
return log_oom();
break;

case ARG_TLS_KEY:
r = free_and_strdup(&arg_tls_key, optarg);
if (r < 0)
return log_oom();
break;

case ARG_TLS_CA:
r = free_and_strdup(&arg_tls_ca, optarg);
if (r < 0)
return log_oom();
break;

case '?':
return -EINVAL;

Expand Down Expand Up @@ -1660,7 +1690,9 @@ static int verb_extract(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to set store: %m");
}

if(arg_tls_key && arg_tls_cert){
ca_sync_set_client_auth(s,arg_tls_cert,arg_tls_key,arg_tls_ca);
}
r = load_seeds_and_extra_stores(s);
if (r < 0)
return r;
Expand Down Expand Up @@ -2105,7 +2137,6 @@ static int verb_list(int argc, char *argv[]) {
_cleanup_(ca_sync_unrefp) CaSync *s = NULL;
bool toplevel_shown = false;
int r;

if (argc > 3) {
log_error("Input path/URL and subtree path expected.");
return -EINVAL;
Expand Down Expand Up @@ -2301,6 +2332,10 @@ static int verb_list(int argc, char *argv[]) {
return log_error_errno(r, "Failed to enable hardlink digest: %m");
}

if(arg_tls_key && arg_tls_cert){
ca_sync_set_client_auth(s,arg_tls_cert,arg_tls_key,arg_tls_ca);
}

(void) send_notify("READY=1");

for (;;) {
Expand Down Expand Up @@ -2577,6 +2612,9 @@ static int verb_digest(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to set store: %m");
}
if(arg_tls_key && arg_tls_cert){
ca_sync_set_client_auth(s,arg_tls_cert,arg_tls_key,arg_tls_ca);
}

r = load_seeds_and_extra_stores(s);
if (r < 0)
Expand Down Expand Up @@ -3470,6 +3508,10 @@ static int verb_pull(int argc, char *argv[]) {
return log_error_errno(r, "Failed to set rate limit: %m");
}

if(arg_tls_key && arg_tls_cert){
ca_remote_set_client_auth(rr, arg_tls_cert, arg_tls_key, arg_tls_ca);
}

r = ca_remote_set_io_fds(rr, STDIN_FILENO, STDOUT_FILENO);
if (r < 0)
return log_error_errno(r, "Failed to set I/O file descriptors: %m");
Expand Down Expand Up @@ -3629,6 +3671,10 @@ static int verb_push(int argc, char *argv[]) {
return log_error_errno(r, "Failed to set rate limit: %m");
}

if(arg_tls_key && arg_tls_cert){
ca_remote_set_client_auth(rr, arg_tls_cert, arg_tls_key, arg_tls_ca);
}

r = ca_remote_set_io_fds(rr, STDIN_FILENO, STDOUT_FILENO);
if (r < 0)
return log_error_errno(r, "Failed to set I/O file descriptors: %m");
Expand Down
Loading