const char *full_addr = NULL;
bool pin_exists = false;
- bool ca_file_exists = false;
+ bool hostname_exists = false;
if ((lua_gettop(L) == 1) && lua_isstring(L, 1)) {
full_addr = lua_tostring(L, 1);
} else if ((lua_gettop(L) == 2) && lua_isstring(L, 1) && lua_istable(L, 2)) {
pin_exists = true;
} else if ((lua_gettop(L) == 3) && lua_isstring(L, 1) && lua_istable(L, 2)) {
full_addr = lua_tostring(L, 1);
- ca_file_exists = true;
+ hostname_exists = true;
} else if ((lua_gettop(L) == 4) && lua_isstring(L, 1) &&
lua_istable(L, 2) && lua_istable(L, 3)) {
full_addr = lua_tostring(L, 1);
pin_exists = true;
- ca_file_exists = true;
+ hostname_exists = true;
} else {
format_error(L, "net.tls_client takes one parameter (\"address\"), two parameters (\"address\",\"pin\"), three parameters (\"address\", \"ca_file\", \"hostname\") or four ones: (\"address\", \"pin\", \"ca_file\", \"hostname\")");
lua_error(L);
port = 853;
}
- if (!pin_exists && !ca_file_exists) {
+ if (!pin_exists && !hostname_exists) {
int r = tls_client_params_set(&net->tls_client_params,
- addr, port, NULL, NULL, NULL);
+ addr, port, NULL,
+ TLS_CLIENT_PARAM_NONE);
if (r != 0) {
lua_pushstring(L, kr_strerror(r));
lua_error(L);
/* pin now at index -1, key at index -2*/
const char *pin = lua_tostring(L, -1);
int r = tls_client_params_set(&net->tls_client_params,
- addr, port, NULL, NULL, pin);
+ addr, port, pin,
+ TLS_CLIENT_PARAM_PIN);
if (r != 0) {
lua_pushstring(L, kr_strerror(r));
lua_error(L);
int ca_table_index = 2;
int hostname_table_index = 3;
- if (ca_file_exists) {
+ if (hostname_exists) {
if (pin_exists) {
ca_table_index = 3;
hostname_table_index = 4;
return 1;
}
- /* iterate over ca filenames */
+ /* iterate over hostnames,
+ * it must be done before iterating over ca filenames */
lua_pushnil(L);
- while (lua_next(L, ca_table_index)) {
- const char *ca_file = lua_tostring(L, -1);
+ while (lua_next(L, hostname_table_index)) {
+ const char *hostname = lua_tostring(L, -1);
int r = tls_client_params_set(&net->tls_client_params,
- addr, port, ca_file, NULL, NULL);
+ addr, port, hostname,
+ TLS_CLIENT_PARAM_HOSTNAME);
if (r != 0) {
lua_pushstring(L, kr_strerror(r));
lua_error(L);
lua_pop(L, 1);
}
- /* iterate over hostnames */
+ /* iterate over ca filenames */
lua_pushnil(L);
- while (lua_next(L, hostname_table_index)) {
- const char *hostname = lua_tostring(L, -1);
+ size_t num_of_ca_files = 0;
+ while (lua_next(L, ca_table_index)) {
+ const char *ca_file = lua_tostring(L, -1);
int r = tls_client_params_set(&net->tls_client_params,
- addr, port, NULL, hostname, NULL);
+ addr, port, ca_file,
+ TLS_CLIENT_PARAM_CA);
if (r != 0) {
lua_pushstring(L, kr_strerror(r));
lua_error(L);
}
+ num_of_ca_files += 1;
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(L, 1);
}
+ if (num_of_ca_files == 0) {
+ /* No ca files were explicitly configured, so use system CA */
+ int r = tls_client_params_set(&net->tls_client_params,
+ addr, port, NULL,
+ TLS_CLIENT_PARAM_CA);
+ if (r != 0) {
+ lua_pushstring(L, kr_strerror(r));
+ lua_error(L);
+ }
+ }
+
lua_pushboolean(L, true);
return 1;
}
int tls_client_params_set(map_t *tls_client_paramlist,
const char *addr, uint16_t port,
- const char *ca_file, const char *hostname, const char *pin)
+ const char *param, tls_client_param_t param_type)
{
if (!tls_client_paramlist || !addr) {
return kr_error(EINVAL);
}
+ /* TLS_CLIENT_PARAM_CA can be empty */
+ if (param_type == TLS_CLIENT_PARAM_HOSTNAME ||
+ param_type == TLS_CLIENT_PARAM_PIN) {
+ if (param == NULL || param[0] == 0) {
+ return kr_error(EINVAL);
+ }
+ }
+
+ /* Parameters are OK */
+
char key[INET6_ADDRSTRLEN + 6];
size_t keylen = sizeof(key);
if (kr_straddr_join(addr, port, key, &keylen) != kr_ok()) {
}
int ret = kr_ok();
- if (ca_file && ca_file[0] != 0) {
+
+ if (param_type == TLS_CLIENT_PARAM_HOSTNAME) {
+ const char *hostname = param;
+ bool already_exists = false;
+ for (size_t i = 0; i < entry->hostnames.len; ++i) {
+ if (strcmp(entry->hostnames.at[i], hostname) == 0) {
+ kr_log_error("[tls_client] error: hostname '%s' for address '%s' already was set, ignoring\n", hostname, key);
+ already_exists = true;
+ break;
+ }
+ }
+ if (!already_exists) {
+ const char *value = strdup(hostname);
+ if (!value) {
+ ret = kr_error(ENOMEM);
+ } else if (array_push(entry->hostnames, value) < 0) {
+ free ((void *)value);
+ ret = kr_error(ENOMEM);
+ }
+ }
+ } else if (param_type == TLS_CLIENT_PARAM_CA) {
+ /* Import ca files only when hostname is already set */
+ if (entry->hostnames.len == 0) {
+ return kr_error(ENOENT);
+ }
+ const char *ca_file = param;
bool already_exists = false;
for (size_t i = 0; i < entry->ca_files.len; ++i) {
- if (strcmp(entry->ca_files.at[i], ca_file) == 0) {
- kr_log_error("[tls_client] error: ca file '%s'for address '%s' already was set, ignoring\n", ca_file, key);
+ const char *imported_ca = entry->ca_files.at[i];
+ if (imported_ca[0] == 0 && (ca_file == NULL || ca_file[0] == 0)) {
+ kr_log_error("[tls_client] error: system ca for address '%s' already was set, ignoring\n", key);
+ already_exists = true;
+ break;
+ } else if (strcmp(imported_ca, ca_file) == 0) {
+ kr_log_error("[tls_client] error: ca file '%s' for address '%s' already was set, ignoring\n", ca_file, key);
already_exists = true;
break;
}
}
if (!already_exists) {
- const char *value = strdup(ca_file);
+ const char *value = strdup(ca_file != NULL ? ca_file : "");
if (!value) {
ret = kr_error(ENOMEM);
} else if (array_push(entry->ca_files, value) < 0) {
free ((void *)value);
ret = kr_error(ENOMEM);
- } else if (strcmp(ca_file, "system ca store") == 0) {
+ } else if (value[0] == 0) {
int res = gnutls_certificate_set_x509_system_trust (entry->credentials);
if (res <= 0) {
kr_log_error("[tls_client] failed to import certs from system store (%s)\n",
}
}
}
- }
-
- if ((ret == kr_ok()) && hostname && hostname[0] != 0) {
- bool already_exists = false;
- for (size_t i = 0; i < entry->hostnames.len; ++i) {
- if (strcmp(entry->hostnames.at[i], hostname) == 0) {
- kr_log_error("[tls_client] error: hostname '%s' for address '%s' already was set, ignoring\n", hostname, key);
- already_exists = true;
- break;
- }
- }
- if (!already_exists) {
- const char *value = strdup(hostname);
- if (!value) {
- ret = kr_error(ENOMEM);
- } else if (array_push(entry->hostnames, value) < 0) {
- free ((void *)value);
- ret = kr_error(ENOMEM);
- }
- }
- }
-
- if ((ret == kr_ok()) && pin && pin[0] != 0) {
+ } else if (param_type == TLS_CLIENT_PARAM_PIN) {
+ const char *pin = param;
for (size_t i = 0; i < entry->pins.len; ++i) {
if (strcmp(entry->pins.at[i], pin) == 0) {
kr_log_error("[tls_client] warning: pin '%s' for address '%s' already was set, ignoring\n", pin, key);
typedef int (*tls_handshake_cb) (struct session *session, int status);
+typedef enum tls_client_param {
+ TLS_CLIENT_PARAM_NONE = 0,
+ TLS_CLIENT_PARAM_PIN,
+ TLS_CLIENT_PARAM_HOSTNAME,
+ TLS_CLIENT_PARAM_CA,
+} tls_client_param_t;
+
struct tls_common_ctx {
bool client_side;
gnutls_session_t tls_session;
/*! Set TLS handshake state. */
int tls_set_hs_state(struct tls_common_ctx *ctx, tls_hs_state_t state);
-/*! Set TLS authentication parameters for given address. */
+/*! Set TLS authentication parameters for given address.
+ * Note: hostnames must be imported before ca files,
+ * otherwise ca files will not be imported at all.
+ */
int tls_client_params_set(map_t *tls_client_paramlist,
const char *addr, uint16_t port,
- const char *ca_file, const char *hostname, const char *pin);
+ const char *param, tls_client_param_t param_type);
/*! Free TLS authentication parameters. */
int tls_client_params_free(map_t *tls_client_paramlist);
return 'pin_sha256'
elseif (target.insecure and not (target.ca_file or target.hostname or target.pin_sha256)) then
return 'insecure'
- elseif (target.ca_file and target.hostname and not (target.insecure or target.pin_sha256)) then
- if not (is_nonempty_string_or_table(target.hostname)
- and is_nonempty_string_or_table(target.ca_file)) then
+ elseif (target.hostname and not (target.insecure or target.pin_sha256)) then
+ if not (is_nonempty_string_or_table(target.hostname)) then
error('TLS_FORWARD target authentication is invalid at position '
- .. idx .. '; hostname and ca_file must be string or list of strings')
+ .. idx .. '; hostname must be string or list of strings')
end
+ -- if target.ca_file is empty, system CA will be used
return 'cert'
else
error('TLS_FORWARD authentication options at position ' .. idx
- .. ' are invalid; specify one of: pin_sha256 / hostname+ca_file / insecure')
+ .. ' are invalid; specify one of: pin_sha256 / hostname [+ca_file] / insecure')
end
end
assert(#pins[sockaddr_lua] > 0)
net.tls_client(config.string_addr, pins[sockaddr_lua])
elseif config.auth_type == 'cert' then
- assert(#ca_files[sockaddr_lua] > 0)
assert(#hostnames[sockaddr_lua] > 0)
net.tls_client(config.string_addr, ca_files[sockaddr_lua], hostnames[sockaddr_lua])
else
}}}), 'TLS_FORWARD with table of pins')
-- ok(policy.TLS_FORWARD({{'::1', hostname='test.', ca_file='/tmp/ca.crt'}}), 'TLS_FORWARD with hostname + CA cert')
- boom(policy.TLS_FORWARD, {{{'::1', hostname='test.'}}}, 'TLS_FORWARD with just hostname')
+ -- boom(policy.TLS_FORWARD, {{{'::1', hostname='test.'}}}, 'TLS_FORWARD with just hostname')
boom(policy.TLS_FORWARD, {{{'::1', ca_file='/tmp/ca.crt'}}}, 'TLS_FORWARD with just CA cert')
boom(policy.TLS_FORWARD, {{{'::1', hostname='', ca_file='/tmp/ca.crt'}}}, 'TLS_FORWARD with empty hostname + CA cert')
boom(policy.TLS_FORWARD, {{{'::1', hostname='test.', ca_file='/dev/a_file_which_surely_does_NOT_exist!'}}},