diff options
-rw-r--r-- | auth/session.lua | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/auth/session.lua b/auth/session.lua new file mode 100644 index 0000000..421e9ff --- /dev/null +++ b/auth/session.lua @@ -0,0 +1,104 @@ +--[[ + +* If user exist in acf db and passwd field is not 'x' then use this password. + +* If user exist in acf db and passwd field is 'x' then use password hash in + /etc/shadow. + +* If user does not exit in acf db, then authenticate against /etc/shadow + +]]-- + +ml = require("ml") +sha256 = require("sha256") +shadow = require("auth.shadow") +acfdb = require("auth.acfpasswd") + +base64url = require("base64url") + +local privkey_file = "/etc/acf/session-privkey" + +-- log function. If global 'logger' is a function, then use that, otherwise +-- log to stderr. +local function log(format, ...) + io.stderr:write(string.format(tostring(format), ...).."\n") +end + +if type(logger) == "function" then + local log = logger +end + + + +local session = {} +session.ttl = 600 +session.privkey = nil --"supersecret" +-- not an exported function as we dont want reveal our privkey +local function get_privkey() + if session.privkey ~= nil then + return session.privkey + end + session.privkey = ml.readfile(privkey_file) + if session.privkey == nil then + local f = io.open("/dev/urandom") + session.privkey = f:read(128) + f:close() + posix.umask("rw-------") + f = io.open(privkey_file, "w") + if f then + f:write(session.privkey) + f:close() + end + end + return session.privkey +end + +local function token_hash(user, expire) + local sha = sha256.new() + sha:update(string.format("%s:%s:%s", expire, user, get_privkey())) + return sha:digest() +end + +local function generate_token(user, ttl) + local expire = os.time() + ttl + local tokenstr = string.format("%s:%s:%s", expire, user, token_hash(user, expire)) + return base64url.encode(tokenstr) +end + +function session.new(user, cleartextpassw) + local entry, errmsg = acfdb.getent(user) + local authenticate = acfdb.authenticate + + if entry == nil then + log("User '%s' not found in %s", user, acfdb.file) + if not shadow.getent(user) then + log("User '%s' not found in %s. Authentication failed.", user, shadow.file) + return nil + end + authenticate = shadow.authenticate + elseif entry.passwd == "x" then + -- if passwd field is set to 'x' it means we use password in shadow + authenticate = shadow.authenticate + end + + if not authenticate(user, cleartextpassw) then + log("User '%s' authentication failed.", user) + return nil + end + + return generate_token(user, session.ttl) +end + +function session.renew(token) + local tmp = base64url.decode(token) + local expire, user, hash = string.match(tmp, "(%d+):([^:]+):(.*)") + if os.time() > tonumber(expire) then + return nil, "Token expired" + end + if hash ~= token_hash(user, expire) then + return nil, "Invalid token hash" + end + return generate_token(user, session.ttl) +end + +return session |