Commit 8c3d34e5 authored by Karel Koci's avatar Karel Koci 🤘 Committed by Karel Koci

Add support for ssl OCSP

parent 8cab9897
......@@ -170,9 +170,13 @@ ca::
into system authorities to be used. `system_cas` is also default value.
crl::
An URI or table of URIs with CRLs relevant to the server. If set into
`ignore_crl`, CRL is not checked. Note that the `crl` field is also
inherited, therefore you may want to set it manually to `ignore_crl`.
Default value is `ignore_crl`.
`no_crl`, CRL is not checked. Note that the `crl` field is also
inherited, therefore you may want to set it manually to `no_crl`.
Default value is `no_crl`.
ocsp::
`true` of `false` if you want or don't want to use OCSP (Online Certificate
Status Protocol). Default value is `true`. Inheritance is same as with
`pubkey`.
The file signature is verified using the `usign` utility.
......
......@@ -47,11 +47,11 @@ local sha256 = sha256
module "uri"
-- luacheck: globals wait signature_check parse new system_cas ignore_crl
-- luacheck: globals wait signature_check parse new system_cas no_crl
-- Constants used for inheritance breakage
system_cas = "uri_system_cas"
ignore_crl = "uri_ignore_crl"
no_crl = "uri_no_crl"
local function percent_decode(text)
return text:gsub('%%(..)', function (encoded)
......@@ -148,14 +148,14 @@ local function handler_internal(uri, err_cback, done_cback)
end
-- Actually, both for http and https
local function handler_http(uri, err_cback, done_cback, ca, crl, use_ssl)
local function handler_http(uri, err_cback, done_cback, ca, crl, ocsp, use_ssl)
return download(function (status, answer)
if status == 200 then
done_cback(answer)
else
err_cback(utils.exception("unreachable", uri .. ": " .. tostring(answer)))
end
end, uri, ca, crl, use_ssl)
end, uri, ca, crl, ocsp, use_ssl)
end
local function match_check(uri, context)
......@@ -332,7 +332,7 @@ function new(context, uri, verification)
error(utils.exception('bad value', "Unknown verification mode " .. vermode))
end
local do_cert = handler.can_check_cert and (vermode == 'both' or vermode == 'cert')
local explicit_ca, use_crl
local explicit_ca, use_crl, use_ocsp
if do_cert then
local function pem_get(uris, context)
if type(uris) == 'string' then
......@@ -361,13 +361,14 @@ function new(context, uri, verification)
return result
end
end
local crl, crl_context = ver_lookup('crl', ignore_crl, true)
if crl ~= ignore_crl then
local crl, crl_context = ver_lookup('crl', no_crl, true)
if crl ~= no_crl then
use_crl = pem_get(crl, crl_context)
if not use_crl then
return result
end
end
use_ocsp = ver_lookup('ocsp', true, false)
end
local do_sig = vermode == 'both' or vermode == 'sig'
local sig_data
......@@ -463,7 +464,7 @@ function new(context, uri, verification)
result.content = content
dispatch()
end
result.events = {handler.handler(uri, err_cback, done_cback, explicit_ca, use_crl, do_cert)}
result.events = {handler.handler(uri, err_cback, done_cback, explicit_ca, use_crl, use_ocsp, do_cert)}
-- Wait for the sub uris and include them in our events
local function sub_cback()
wait_sub_uris = wait_sub_uris - 1
......
......@@ -196,7 +196,7 @@ local rest_available_funcs = {
"INFO",
"DBG",
"system_cas",
"ignore_crl"
"no_crl"
}
state_vars = nil
......
......@@ -813,7 +813,7 @@ static size_t download_write_callback(char *ptr, size_t size, size_t nmemb, void
return rsize;
}
struct wait_id download(struct events *events, download_callback_t callback, void *data, const char *url, const char *cacert, const char *crl, bool ssl) {
struct wait_id download(struct events *events, download_callback_t callback, void *data, const char *url, const char *cacert, const char *crl, bool ocsp, bool ssl) {
DBG("Downloading %s", url);
struct download_data *res = malloc(sizeof *res);
*res = (struct download_data) {
......@@ -839,6 +839,7 @@ struct wait_id download(struct events *events, download_callback_t callback, voi
CURL_SETOPT(CURLOPT_CAINFO, cacert);
if (crl)
CURL_SETOPT(CURLOPT_CRLFILE, crl);
CURL_SETOPT(CURLOPT_SSL_VERIFYSTATUS, ocsp);
} else
CURL_SETOPT(CURLOPT_SSL_VERIFYPEER, 0L);
CURL_SETOPT(CURLOPT_WRITEFUNCTION, download_write_callback);
......
......@@ -171,9 +171,10 @@ typedef void (*download_callback_t)(struct wait_id id, void *data, int status, s
* Optionally, check certificate and revocation list specified by parameters
* cacert or crl respectively (paths to .pem files). If no certificate is specified,
* system certificate is used. Specifying ssl as false insecure https connections
* are allowed. Specifying ssl as true for http has no effect.
* are allowed. Specifying ssl as true for http has no effect. And ocsp allows you
* to set if you want to use OSCP.
*/
struct wait_id download(struct events *events, download_callback_t callback, void *data, const char *url, const char *cacert, const char *crl, bool ssl) __attribute__((nonnull(1, 2, 4)));
struct wait_id download(struct events *events, download_callback_t callback, void *data, const char *url, const char *cacert, const char *crl, bool ocsp, bool ssl) __attribute__((nonnull(1, 2, 4)));
/*
* Set the number of maximum parallel downloads
*
......
......@@ -379,7 +379,8 @@ static int lua_download(lua_State *L) {
const char *crl = NULL;
if (pcount >= 4 && !lua_isnil(L, 4))
crl = luaL_checkstring(L, 4);
bool ssl = lua_toboolean(L, 5);
bool ocsp = lua_toboolean(L, 5);
bool ssl = lua_toboolean(L, 6);
// Handle the callback
struct lua_download_data *data = malloc(sizeof *data);
data->L = L;
......@@ -387,7 +388,7 @@ static int lua_download(lua_State *L) {
// Run the download
struct events *events = extract_registry(L, "events");
ASSERT(events);
struct wait_id id = download(events, download_callback, data, url, cacert, crl, ssl);
struct wait_id id = download(events, download_callback, data, url, cacert, crl, ocsp, ssl);
// Return the ID
push_wid(L, &id);
return 1;
......
......@@ -37,6 +37,7 @@ local script_options = {
security = "Remote",
ca = "file:///etc/ssl/updater.pem",
crl = "file:///tmp/crl.pem",
ocsp = false,
pubkey = {
"file:///etc/updater/keys/release.pub",
"file:///etc/updater/keys/standby.pub",
......
......@@ -281,7 +281,7 @@ static void download_failed_callback(struct wait_id id __attribute__((unused)),
// Just like the done callback, but download one more thing from within this callback (and wait for it).
static void download_recursive_callback(struct wait_id id, void *data, int status, size_t out_size, const char *out) {
download_done_callback_api(id, data, status, out_size, out);
struct wait_id new_id = download(data, download_done_callback_api, NULL, "https://api.turris.cz/index.html", NULL, NULL, false);
struct wait_id new_id = download(data, download_done_callback_api, NULL, "https://api.turris.cz/index.html", NULL, NULL, false, false);
events_wait(data, 1, &new_id);
}
......@@ -297,15 +297,15 @@ START_TEST(command_download) {
download_slot_count_set(events, 2);
for (size_t i = 0; i < cnt; i++) {
ids[i] = download(events, download_done_callback_api, NULL, "https://api.turris.cz/index.html", cert_file, NULL, true);
ids[i + cnt] = download(events, download_failed_callback, NULL, "https://api.turris.cz/does_not_exist.dat", cert_file, NULL, true);
ids[i + 2 * cnt] = download(events, download_recursive_callback, events, "https://api.turris.cz/index.html", cert_file, NULL, true);
ids[i] = download(events, download_done_callback_api, NULL, "https://api.turris.cz/index.html", cert_file, NULL, false, true);
ids[i + cnt] = download(events, download_failed_callback, NULL, "https://api.turris.cz/does_not_exist.dat", cert_file, NULL, false, true);
ids[i + 2 * cnt] = download(events, download_recursive_callback, events, "https://api.turris.cz/index.html", cert_file, NULL, false, true);
// api has special ca so this should fail
ids[i + 3 * cnt] = download(events, download_failed_callback, NULL, "https://api.turris.cz/index.html", NULL, NULL, true);
ids[i + 3 * cnt] = download(events, download_failed_callback, NULL, "https://api.turris.cz/index.html", NULL, NULL, false, true);
// but with disabled ssl it should work
ids[i + 4 * cnt] = download(events, download_done_callback_api, NULL, "https://api.turris.cz/index.html", NULL, NULL, false);
ids[i + 4 * cnt] = download(events, download_done_callback_api, NULL, "https://api.turris.cz/index.html", NULL, NULL, false, false);
// repo uses public ca so this should work with system one
ids[i + 5 * cnt] = download(events, download_done_callback_repo, NULL, "https://repo.turris.cz", NULL, NULL, true);
ids[i + 5 * cnt] = download(events, download_done_callback_repo, NULL, "https://repo.turris.cz", NULL, NULL, false, true);
}
events_wait(events, cnt * 6, ids);
......
......@@ -178,13 +178,13 @@ function test_https_cert()
local context = sandbox.new("Local")
local ca_file = "file://" .. dir .. "tests/data/updater.pem"
-- It should succeed with the correct CA
local u1 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = ca_file})
local u1 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = ca_file, ocsp = false})
-- But should fail with a wrong one
local u2 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = "file:///dev/null"})
local u2 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = "file:///dev/null", ocsp = false})
-- We may specify the ca as a table of possibilities
local u3 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = {"file:///dev/null", ca_file}})
local u3 = uri(context, "https://api.turris.cz/", {verification = "cert", ca = {"file:///dev/null", ca_file}, ocsp = false})
-- nil ca should result in failure as api has certificate not added to standard paths
local u4 = uri(context, "https://api.turris.cz/", {verification = "cert"})
local u4 = uri(context, "https://api.turris.cz/", {verification = "cert", ocsp = false})
-- nil ca should result in success on repo as it's signed by common authority
local u5 = uri(context, "https://repo.turris.cz/", {verification = "cert"})
local ok1 = u1:get()
......@@ -200,6 +200,7 @@ function test_https_cert()
-- Check we can put the verification stuff into the context
context.ca = ca_file
context.verification = "cert"
context.ocsp = false
u1 = uri(context, "https://api.turris.cz/")
u2 = uri(context, "https://api.turris.cz/", {ca = "file:///dev/null"})
ok1 = u1:get()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment