diff options
-rw-r--r--custom/extensions/auth-file.lua16
-rw-r--r--custom/extensions/auth-inline.lua16
-rw-r--r--tests/extensions/test-auth.lua13
-rwxr-xr-xtests/t0505-auth.sh36
4 files changed, 63 insertions, 18 deletions
diff --git a/custom/extensions/auth-file.lua b/custom/extensions/auth-file.lua
index 18a80fd..0573549 100644
--- a/custom/extensions/auth-file.lua
+++ b/custom/extensions/auth-file.lua
@@ -20,7 +20,9 @@
-- posix.sys.stat and posix.unistd and lives at
-- <https://github.com/luaposix/luaposix>. The reliable cross-platform way to
-- install them is LuaRocks matched to your Lua version, and luaossl also needs
--- the OpenSSL development headers present.
+-- the OpenSSL development headers present. Each is loaded only once a request
+-- needs it, so a public repository is served without them while a protected
+-- one is refused.
--
-- # Debian and Ubuntu
-- sudo apt install luarocks libssl-dev
@@ -45,11 +47,6 @@
-- secret file accept each other's cookies. That is acceptable for gating
-- read access to a git browser, so weigh it before guarding anything more.
-local sysstat = require("posix.sys.stat")
-local unistd = require("posix.unistd")
-local rand = require("openssl.rand")
-local hmac = require("openssl.hmac")
-
-- The values that follow are the configuration and are meant to be edited.
-- Accounts live one per line as username:hash, where the hash is any kind this
@@ -272,6 +269,9 @@ function get_secret()
end
local secret_file = io.open(secret_filename, "r")
if secret_file == nil then
+ local sysstat = require("posix.sys.stat")
+ local unistd = require("posix.unistd")
+ local rand = require("openssl.rand")
-- The secret is written under a tightened mask so it is not
-- created readable by anyone but the user cgit runs as, and
-- the old mask goes back on every way out.
@@ -365,6 +365,7 @@ function validate_value(expected_field, cookie)
end
local payload = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
+ local hmac = require("openssl.hmac")
local expected_signature = tohex(hmac.new(get_secret(), "sha256"):final(payload))
if not constant_equals(signature, expected_signature) then
return nil
@@ -395,6 +396,8 @@ function secure_value(field, value, expiration)
return ""
end
+ local rand = require("openssl.rand")
+ local hmac = require("openssl.hmac")
local salt = tohex(rand.bytes(16))
value = url_encode(value)
field = url_encode(field)
@@ -469,6 +472,7 @@ function authenticate_post()
local password = post["password"]
local ok = false
if username ~= nil and password ~= nil and #password <= max_password_bytes then
+ local unistd = require("posix.unistd")
local hash, stand_in = account_hash(username)
if hash == nil then
-- Spend the work anyway, see account_hash.
diff --git a/custom/extensions/auth-inline.lua b/custom/extensions/auth-inline.lua
index f6a0521..1b46fba 100644
--- a/custom/extensions/auth-inline.lua
+++ b/custom/extensions/auth-inline.lua
@@ -20,7 +20,9 @@
-- posix.sys.stat and posix.unistd and lives at
-- <https://github.com/luaposix/luaposix>. The reliable cross-platform way to
-- install them is LuaRocks matched to your Lua version, and luaossl also needs
--- the OpenSSL development headers present.
+-- the OpenSSL development headers present. Each is loaded only once a request
+-- needs it, so a public repository is served without them while a protected
+-- one is refused.
--
-- # Debian and Ubuntu
-- sudo apt install luarocks libssl-dev
@@ -45,11 +47,6 @@
-- secret file accept each other's cookies. That is acceptable for gating
-- read access to a git browser, so weigh it before guarding anything more.
-local sysstat = require("posix.sys.stat")
-local unistd = require("posix.unistd")
-local rand = require("openssl.rand")
-local hmac = require("openssl.hmac")
-
-- The values that follow are the configuration and are meant to be edited.
-- Protected repositories and the users allowed into each. A repository named
@@ -233,6 +230,9 @@ function get_secret()
end
local secret_file = io.open(secret_filename, "r")
if secret_file == nil then
+ local sysstat = require("posix.sys.stat")
+ local unistd = require("posix.unistd")
+ local rand = require("openssl.rand")
-- The secret is written under a tightened mask so it is not
-- created readable by anyone but the user cgit runs as, and
-- the old mask goes back on every way out.
@@ -326,6 +326,7 @@ function validate_value(expected_field, cookie)
end
local payload = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
+ local hmac = require("openssl.hmac")
local expected_signature = tohex(hmac.new(get_secret(), "sha256"):final(payload))
if not constant_equals(signature, expected_signature) then
return nil
@@ -356,6 +357,8 @@ function secure_value(field, value, expiration)
return ""
end
+ local rand = require("openssl.rand")
+ local hmac = require("openssl.hmac")
local salt = tohex(rand.bytes(16))
value = url_encode(value)
field = url_encode(field)
@@ -430,6 +433,7 @@ function authenticate_post()
local password = post["password"]
local ok = false
if username ~= nil and password ~= nil and #password <= max_password_bytes then
+ local unistd = require("posix.unistd")
local hash, stand_in = account_hash(username)
if hash == nil then
-- Spend the work anyway, see account_hash.
diff --git a/tests/extensions/test-auth.lua b/tests/extensions/test-auth.lua
index e0d2fe5..eb402da 100644
--- a/tests/extensions/test-auth.lua
+++ b/tests/extensions/test-auth.lua
@@ -328,4 +328,17 @@ out, ret = run_action("authenticate-post", {
})
h.contains("even a signed unsafe destination is not followed", out, "Status: 404")
+-- The libraries load only once a request needs one, so with every one of
+-- them gone a public repository is still served, while a protected one
+-- fails closed instead of reaching its content.
+for _, name in ipairs({ "openssl.rand", "openssl.hmac", "posix.sys.stat", "posix.unistd" }) do
+ package.loaded[name] = nil
+ h.preload_failure(name)
+end
+out, ret = run_action("authenticate-cookie", { repo = "unlisted" })
+h.equals("without the libraries a public repository is served", ret, 1)
+h.check("while a protected one fails closed", not pcall(run_action,
+ "authenticate-cookie", { repo = "secret-repo", cookie = "cgitauth=" .. session }))
+h.check("and so does its login form", not pcall(run_action, "body", {}))
+
h.finish()
diff --git a/tests/t0505-auth.sh b/tests/t0505-auth.sh
index 2ebee10..526840d 100755
--- a/tests/t0505-auth.sh
+++ b/tests/t0505-auth.sh
@@ -4,9 +4,9 @@
# differ only in where accounts live. The unit checks meet luaossl and
# luaposix with deterministic stand-ins from the harness, proving tampered
# and expired cookies turned away, header injection stripped, unsafe
-# redirects refused and the login flows answering as documented. The run
-# through cgit itself needs the real modules inside the binary's own Lua, so
-# it is probed for, and proves an unedited copy protects nothing.
+# redirects refused and the login flows answering as documented. Through cgit
+# itself an unedited copy protects nothing, with or without the modules, and
+# when the binary's own Lua lacks them a protected repository stays closed.
test_description='Check the shipped auth extensions'
CGIT_TEST_NO_CREATE_REPOS=YesPlease
@@ -41,11 +41,16 @@ if test "$CGIT_HAS_LUA" -eq 1 &&
cgit_lua_probe "$PWD/repos/authy/.git" openssl.rand openssl.hmac posix.sys.stat posix.unistd
then
test_set_prereq CGIT_LUA_AUTH
+elif test "$CGIT_HAS_LUA" -eq 1
+then
+ test_set_prereq CGIT_LUA_NOAUTH
+ say 'cgit lua lacks luaossl or luaposix, expecting protected pages refused'
else
- say 'cgit lua lacks luaossl or luaposix, checks through cgit skipped'
+ say 'cgit built without lua, checks through cgit skipped'
fi
+test "$CGIT_HAS_LUA" -eq 1 && test_set_prereq CGIT_LUA
-test_expect_success CGIT_LUA_AUTH 'point cgit at the unedited auth filter' '
+test_expect_success CGIT_LUA 'point cgit at the unedited auth filter' '
cat >cgitrc <<-EOF
virtual-root=/
cache-size=0
@@ -55,9 +60,28 @@ test_expect_success CGIT_LUA_AUTH 'point cgit at the unedited auth filter' '
EOF
'
-test_expect_success CGIT_LUA_AUTH 'an unedited copy protects nothing' '
+test_expect_success CGIT_LUA 'an unedited copy protects nothing' '
cgit_url "authy/log/" >tmp &&
grep ">guarded commit</a>" tmp
'
+test_expect_success CGIT_LUA_NOAUTH 'without the modules a protected repository stays closed' '
+ cat >protect.awk <<-\EOF &&
+ { print }
+ /^local protected_repos = [{]$/ { print "\t[\"authy\"] = { alice = true }," }
+ EOF
+ awk -f protect.awk "$EXTENSIONS_DIRECTORY/auth-inline.lua" >guarded.lua &&
+ grep "\"authy\"" guarded.lua &&
+ cat >guardedrc <<-EOF &&
+ virtual-root=/
+ cache-size=0
+ auth-filter=lua:$PWD/guarded.lua
+ repo.url=authy
+ repo.path=$PWD/repos/authy/.git
+ EOF
+ CGIT_CONFIG="$PWD/guardedrc" QUERY_STRING="url=authy/log/" cgit >tmp &&
+ ! grep "guarded commit" tmp &&
+ grep "Unable to complete the request" tmp
+'
+
test_done