auth-filters: generate secret securely This is much better than having the user generate it themselves. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Jason A. Donenfeld Jason@zx2c4.com
Sun, 15 Jul 2018 03:22:12 +0200
2 files changed,
85 insertions(+),
18 deletions(-)
M
filters/gentoo-ldap-authentication.lua
→
filters/gentoo-ldap-authentication.lua
@@ -5,7 +5,13 @@ -- luacrypto >= 0.3
-- <http://mkottman.github.io/luacrypto/> -- lualdap >= 1.2 -- <https://git.zx2c4.com/lualdap/about/> +-- luaposix +-- <https://github.com/luaposix/luaposix> -- +local sysstat = require("posix.sys.stat") +local unistd = require("posix.unistd") +local crypto = require("crypto") +local lualdap = require("lualdap") --@@ -21,11 +27,9 @@ glouglou = "infra",
portage = "dev" } - --- All cookies will be authenticated based on this secret. Make it something --- totally random and impossible to guess. It should be large. -local secret = "BE SURE TO CUSTOMIZE THIS STRING TO SOMETHING BIG AND RANDOM" - +-- Set this to a path this script can write to for storing a persistent +-- cookie secret, which should be guarded. +local secret_filename = "/var/cache/cgit/auth-secret" --@@ -101,8 +105,6 @@ --
-- Gentoo LDAP support. -- -- - -local lualdap = require("lualdap") function gentoo_ldap_user_groups(username, password) -- Ensure the user is alphanumeric@@ -231,7 +233,38 @@ -- Cookie construction and validation helpers.
-- -- -local crypto = require("crypto") +local secret = nil + +-- Loads a secret from a file, creates a secret, or returns one from memory. +function get_secret() + if secret ~= nil then + return secret + end + local secret_file = io.open(secret_filename, "r") + if secret_file == nil then + local old_umask = sysstat.umask(63) + local temporary_filename = secret_filename .. ".tmp." .. crypto.hex(crypto.rand.bytes(16)) + local temporary_file = io.open(temporary_filename, "w") + if temporary_file == nil then + os.exit(177) + end + temporary_file:write(crypto.hex(crypto.rand.bytes(32))) + temporary_file:close() + unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. + unistd.unlink(temporary_filename) + sysstat.umask(old_umask) + secret_file = io.open(secret_filename, "r") + end + if secret_file == nil then + os.exit(177) + end + secret = secret_file:read() + secret_file:close() + if secret:len() ~= 64 then + os.exit(177) + end + return secret +end -- Returns value of cookie if cookie is valid. Otherwise returns nil. function validate_value(expected_field, cookie)@@ -271,7 +304,7 @@ return nil
end -- Lua hashes strings, so these comparisons are time invariant. - if hmac ~= crypto.hmac.digest("sha256", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, secret) then + if hmac ~= crypto.hmac.digest("sha256", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, get_secret()) then return nil end@@ -296,7 +329,7 @@ local salt = crypto.hex(crypto.rand.bytes(16))
value = url_encode(value) field = url_encode(field) authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. crypto.hmac.digest("sha256", authstr, secret) + authstr = authstr .. "|" .. crypto.hmac.digest("sha256", authstr, get_secret()) return authstr end
M
filters/simple-authentication.lua
→
filters/simple-authentication.lua
@@ -3,7 +3,12 @@ --
-- Requirements: -- luacrypto >= 0.3 -- <http://mkottman.github.io/luacrypto/> +-- luaposix +-- <https://github.com/luaposix/luaposix> -- +local sysstat = require("posix.sys.stat") +local unistd = require("posix.unistd") +local crypto = require("crypto") --@@ -31,11 +36,9 @@ laurent = "s3cr3t",
bob = "ilikelua" } --- All cookies will be authenticated based on this secret. Make it something --- totally random and impossible to guess. It should be large. -local secret = "BE SURE TO CUSTOMIZE THIS STRING TO SOMETHING BIG AND RANDOM" - - +-- Set this to a path this script can write to for storing a persistent +-- cookie secret, which should be guarded. +local secret_filename = "/var/cache/cgit/auth-secret" -- --@@ -191,7 +194,38 @@ -- Cookie construction and validation helpers.
-- -- -local crypto = require("crypto") +local secret = nil + +-- Loads a secret from a file, creates a secret, or returns one from memory. +function get_secret() + if secret ~= nil then + return secret + end + local secret_file = io.open(secret_filename, "r") + if secret_file == nil then + local old_umask = sysstat.umask(63) + local temporary_filename = secret_filename .. ".tmp." .. crypto.hex(crypto.rand.bytes(16)) + local temporary_file = io.open(temporary_filename, "w") + if temporary_file == nil then + os.exit(177) + end + temporary_file:write(crypto.hex(crypto.rand.bytes(32))) + temporary_file:close() + unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. + unistd.unlink(temporary_filename) + sysstat.umask(old_umask) + secret_file = io.open(secret_filename, "r") + end + if secret_file == nil then + os.exit(177) + end + secret = secret_file:read() + secret_file:close() + if secret:len() ~= 64 then + os.exit(177) + end + return secret +end -- Returns value of cookie if cookie is valid. Otherwise returns nil. function validate_value(expected_field, cookie)@@ -231,7 +265,7 @@ return nil
end -- Lua hashes strings, so these comparisons are time invariant. - if hmac ~= crypto.hmac.digest("sha256", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, secret) then + if hmac ~= crypto.hmac.digest("sha256", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, get_secret()) then return nil end@@ -256,7 +290,7 @@ local salt = crypto.hex(crypto.rand.bytes(16))
value = url_encode(value) field = url_encode(field) authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. crypto.hmac.digest("sha256", authstr, secret) + authstr = authstr .. "|" .. crypto.hmac.digest("sha256", authstr, get_secret()) return authstr end