From 4393288f44a9f318f88d1857e85ea8c57ceb3009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= Date: Tue, 11 Oct 2022 01:22:15 +0200 Subject: Force current filters. --- hugo-authentication.lua | 183 +++++++++++++++++++++++++++--------------------- 1 file changed, 102 insertions(+), 81 deletions(-) (limited to 'hugo-authentication.lua') diff --git a/hugo-authentication.lua b/hugo-authentication.lua index 4a56bd2..a6187c1 100644 --- a/hugo-authentication.lua +++ b/hugo-authentication.lua @@ -1,10 +1,15 @@ -- This script may be used with the auth-filter. Be sure to configure it as you wish. -- -- Requirements: --- luacrypto >= 0.3 --- +-- luaossl +-- +-- luaposix +-- -- - +local sysstat = require("posix.sys.stat") +local unistd = require("posix.unistd") +local rand = require("openssl.rand") +local hmac = require("openssl.hmac") -- -- @@ -13,25 +18,51 @@ -- -- A list of password protected repositories along with the users who can access them. -local public_repos = { - vimwiki = true +local protected_repos = { + glouglou = { laurent = true, jason = true }, + qt = { jason = true, bob = true } } --- Please note that, in production, you'll want to replace this simple lookup --- table with either a table of salted and hashed passwords (using something --- smart like scrypt), or replace this table lookup with an external support, --- such as consulting your system's pam / shadow system, or an external --- database, or an external validating web service. For testing, or for --- extremely low-security usage, you may be able, however, to get away with --- compromising on hardcoding the passwords in cleartext, as we have done here. +local public_repos = {} +public_repos["wiki-public"] = true +public_repos["lyslib"] = true +public_repos["calp"] = true +public_repos["texttv"] = true +public_repos["scheme-monad"] = true +public_repos["scheme/math-parse"] = true +public_repos["file-descriptor-graph"] = true +public_repos["rss-filter"] = true +public_repos["puppet-mpd"] = true +public_repos["aur-runner"] = true +public_repos["guile-dns"] = true +public_repos["puppet/blog"] = true +public_repos["puppet/cgit"] = true +public_repos["puppet/dns_record"] = true +public_repos["puppet/image-shower"] = true +public_repos["puppet/networking"] = true +public_repos["puppet/nspawn"] = true +public_repos["puppet/nsupdate"] = true +public_repos["puppet/pacman"] = true +public_repos["puppet/profiles"] = true +public_repos["puppet/rss_filter"] = true +public_repos["puppet/shiori"] = true +public_repos["puppet/syslinux"] = true +public_repos["puppet/systemd_mount"] = true +public_repos["puppet/uwsgi"] = true +public_repos["puppet/webdav_server"] = true +public_repos["puppet/wpa_supplicant"] = true +public_repos["puppet/envvar"] = true + +-- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`. local users = { - hugo = "password" + hugo = "$6$13z1Pf.U8itrCRX6$g2BZpaMk1CLiT6117paWXB2qdQFRc3rsGWL4iF5h5QbHo27oljTdHk69oQWAvqlVf13aLTUF3nYw65lEp88r/1", + gitlab = "$6$rounds=300000$G/80.k.yO$FCzj0kOmxTMQHPsNHEQVZQI4ofPYz3ae6G0bWNEp3v7GnMkEQufpvWK9lvFqG3oPoy3heIC5sSdSmipUf8QVU/", + ove = "$6$rounds=30000$SGWXbaxWPHt$RQsAiAq3xPIyF32X5OZCEfT8gxesN87tqECeFd8h/QmUPpKfzqoJBkN0NRlYodjHImeI5LOrwG.FW9ktPQH/v/", } --- All cookies will be authenticated based on this secret. Make it something --- totally random and impossible to guess. It should be large. -local secret = "Ianae4woof3ial9ahdahng7ahth9yoo2ri6zuw7eeYaengai0s" - +-- 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" -- -- @@ -39,53 +70,9 @@ local secret = "Ianae4woof3ial9ahdahng7ahth9yoo2ri6zuw7eeYaengai0s" -- -- -function printenv(arg) - local keys = { - "CGIT_CONFIG", - "CGIT_REPO_URL", "CGIT_REPO_NAME", "CGIT_REPO_PATH", - "CGIT_REPO_OWNER", "CGIT_REPO_GROUP", "CGIT_REPO_DEFBRANCH", - "CGIT_REPO_SECTION", "CGIT_REPO_CLONE_URL", - - "GIT_REPO", "GIT_CONFIG_NOSYSTEM", - "GIT_ATTR_NOSYSTEM", - "GIT_DIR", "GIT_CONFIG_NOSYSTEM", "GIT_ATTR_NOSYSTEM", - - "QUERY_STRING" - } - - -- print "!!!" - -- html ("!!!") - -- print ("

body

") - io.stderr:write(os.time(os.date("!*t"))) - io.stderr:write("\n") - io.stderr:write(arg); - for _, attr in pairs(keys) do - local l = os.getenv(attr) - -- print("") - io.stderr:write(attr) - io.stderr:write(" = " ) - if l == nil then - io.stderr:write("nil"); - else - io.stderr:write(l) - end - io.stderr:write("\n") - end - -- print ("
KeyValue
" )
-		---print (attr)
-		-- print ("
") - -- print (l) - -- print("
") -end - -- Sets HTTP cookie headers based on post and sets up redirection. --- TODO this tries to authenticate on the root page (good), but fails to set --- the cookie. I think it gets stuck in the redirect filter (bad). --- TODO invalid login also kinda breaks the page... Not found should return the --- user to the login page with an error messsage displaed. function authenticate_post() - printenv("Authenticate Post\n=================\n"); - local password = users[post["username"]] + local hash = users[post["username"]] local redirect = validate_value("redirect", post["redirect"]) if redirect == nil then @@ -95,8 +82,7 @@ function authenticate_post() redirect_to(redirect) - -- Lua hashes strings, so these comparisons are time invariant. - if password == nil or password ~= post["password"] then + if hash == nil or hash ~= unistd.crypt(post["password"], hash) then set_cookie("cgitauth", "") else -- One week expiration time @@ -111,22 +97,21 @@ end -- Returns 1 if the cookie is valid and 0 if it is not. function authenticate_cookie() - printenv("Authenticate Cookie\n===================\n"); -- Everyone has access to the index page. - if os.getenv("CGIT_REPO_NAME") == nil then + -- printenv(os.getenv("CGIT_REPO_NAME")); + if cgit["repo"] == "" then return 1 end + ispublic = public_repos[cgit["repo"]] -- accepted_users = protected_repos[cgit["repo"]] - if public_repos[cgit["repo"]] then - -- We return as valid if the repo is not protected. + if ispublic == true then + -- We return as valid if the repo is public return 1 end - local username = validate_value("username", get_cookie(http["cookie"], "cgitauth")) - -- if username == nil or not accepted_users[username:lower()] then if username == nil then return 0 else @@ -136,11 +121,9 @@ end -- Prints the html for the login form. function body() - printenv("Body\n====\n"); - - html("

New login form!

"); - html("

But still don't contact me to gain access.

"); html("

Authentication Required

") + -- html("HTML = ") + -- html(tostring(cgit["repo"] == "")) html("
") @@ -237,6 +220,13 @@ function get_cookie(cookies, name) return url_decode(string.match(cookies, ";" .. name .. "=(.-);")) end +function tohex(b) + local x = "" + for i = 1, #b do + x = x .. string.format("%.2x", string.byte(b, i)) + end + return x +end -- -- @@ -244,7 +234,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." .. tohex(rand.bytes(16)) + local temporary_file = io.open(temporary_filename, "w") + if temporary_file == nil then + os.exit(177) + end + temporary_file:write(tohex(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) @@ -253,7 +274,7 @@ function validate_value(expected_field, cookie) local field = "" local expiration = 0 local salt = "" - local hmac = "" + local chmac = "" if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then return nil @@ -272,19 +293,19 @@ function validate_value(expected_field, cookie) elseif i == 3 then salt = component elseif i == 4 then - hmac = component + chmac = component else break end i = i + 1 end - if hmac == nil or hmac:len() == 0 then + if chmac == nil or chmac:len() == 0 then return nil end -- Lua hashes strings, so these comparisons are time invariant. - if hmac ~= crypto.hmac.digest("sha1", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, secret) then + if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then return nil end @@ -305,11 +326,11 @@ function secure_value(field, value, expiration) end local authstr = "" - local salt = crypto.hex(crypto.rand.bytes(16)) + local salt = tohex(rand.bytes(16)) value = url_encode(value) field = url_encode(field) authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. crypto.hmac.digest("sha1", authstr, secret) + authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr)) return authstr end -- cgit v1.2.3