diff options
author | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-07-15 03:22:12 +0200 |
---|---|---|
committer | Jason A. Donenfeld <Jason@zx2c4.com> | 2018-07-15 03:30:57 +0200 |
commit | b73df8098f261ecbd4bc5ba689f9766a1a75f9a0 (patch) | |
tree | aebcaab97994951531547194cf9f51ba353dd4ad | |
parent | auth-filters: do not crash on nil username (diff) | |
download | cgit-pink-b73df8098f261ecbd4bc5ba689f9766a1a75f9a0.tar.gz cgit-pink-b73df8098f261ecbd4bc5ba689f9766a1a75f9a0.zip |
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>
-rw-r--r-- | filters/gentoo-ldap-authentication.lua | 53 | ||||
-rw-r--r-- | filters/simple-authentication.lua | 50 |
2 files changed, 85 insertions, 18 deletions
diff --git a/filters/gentoo-ldap-authentication.lua b/filters/gentoo-ldap-authentication.lua index 3b6564b..b4d98c2 100644 --- a/filters/gentoo-ldap-authentication.lua +++ b/filters/gentoo-ldap-authentication.lua @@ -5,7 +5,13 @@ -- <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 @@ local protected_repos = { 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" -- @@ -102,8 +106,6 @@ end -- -- -local lualdap = require("lualdap") - function gentoo_ldap_user_groups(username, password) -- Ensure the user is alphanumeric if username == nil or username:match("%W") then @@ -231,7 +233,38 @@ end -- -- -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 @@ function validate_value(expected_field, cookie) 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 @@ function secure_value(field, value, expiration) 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 diff --git a/filters/simple-authentication.lua b/filters/simple-authentication.lua index 596c041..bf35632 100644 --- a/filters/simple-authentication.lua +++ b/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 @@ local users = { 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 @@ end -- -- -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 @@ function validate_value(expected_field, cookie) 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 @@ function secure_value(field, value, expiration) 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 |