Rework authentication system
The validity of authentication tokens was determined by the mtime of respective authentication tokens on filesystem stored in $sessionpath. Talking about hardware without RTC or without a prior connection to a time server, date/time usually around 1970 - so is the mtime of the authentication token file in $sessionpath. When now configuring an internet connection via LuCI, the system might fetch the current date/time (e.g. via ntp) which invalidates the token, returns "403 Forbidden" and kicks the user out of the interface. This patch changes the authentication system to use time values based on the uptime of the machine - rather than values based upon gettimeofday() and {a|m}time values - and save them inside the token. That way can always determine the difference between login (last interaction respectively) and the current time, in- dependant of the system clock jumping backwards/forwards. Warning: This patch removes the clean() function and respective calls. This means, invalid tokens will NOT be determined and removed from filesystem automatically anymore. Before, every HTTP-call caused a scan for invalid tokens, which is quite expensive. Instead consider using a cron job deleting all stalled files periodically. Contributed by T-Labs, Deutsche Telekom Innovation Laboratories Signed-off-by: Mirko Vogt <mirko@openwrt.org>
This commit is contained in:
parent
69aa218335
commit
a58370ab74
2 changed files with 57 additions and 42 deletions
|
@ -26,36 +26,62 @@ luci.config.sauth = luci.config.sauth or {}
|
||||||
sessionpath = luci.config.sauth.sessionpath
|
sessionpath = luci.config.sauth.sessionpath
|
||||||
sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
|
sessiontime = tonumber(luci.config.sauth.sessiontime) or 15 * 60
|
||||||
|
|
||||||
--- Manually clean up expired sessions.
|
|
||||||
function clean()
|
|
||||||
local now = os.time()
|
|
||||||
local files = fs.dir(sessionpath)
|
|
||||||
|
|
||||||
if not files then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for file in files do
|
|
||||||
local fname = sessionpath .. "/" .. file
|
|
||||||
local stat = fs.stat(fname)
|
|
||||||
if stat and stat.type == "reg" and stat.mtime + sessiontime < now then
|
|
||||||
fs.unlink(fname)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Prepare session storage by creating the session directory.
|
--- Prepare session storage by creating the session directory.
|
||||||
function prepare()
|
function prepare()
|
||||||
fs.mkdir(sessionpath, 700)
|
fs.mkdir(sessionpath, 700)
|
||||||
|
|
||||||
if not sane() then
|
if not sane() then
|
||||||
error("Security Exception: Session path is not sane!")
|
error("Security Exception: Session path is not sane!")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function encode(t)
|
||||||
|
return luci.util.get_bytecode({
|
||||||
|
user=t.user,
|
||||||
|
token=t.token,
|
||||||
|
secret=t.secret,
|
||||||
|
atime=luci.sys.uptime()
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function decode(blob)
|
||||||
|
local t = loadstring(blob)()
|
||||||
|
return {
|
||||||
|
user = t.user,
|
||||||
|
token = t.token,
|
||||||
|
secret = t.secret,
|
||||||
|
atime = t.atime
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
--- Read a session and return its content.
|
--- Read a session and return its content.
|
||||||
-- @param id Session identifier
|
-- @param id Session identifier
|
||||||
-- @return Session data
|
-- @return Session data
|
||||||
|
local function _read(id)
|
||||||
|
local blob = fs.readfile(sessionpath .. "/" .. id)
|
||||||
|
return blob
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Write session data to a session file.
|
||||||
|
-- @param id Session identifier
|
||||||
|
-- @param data Session data
|
||||||
|
local function _write(id, data)
|
||||||
|
local f = nixio.open(sessionpath .. "/" .. id, "w", 600)
|
||||||
|
f:writeall(data)
|
||||||
|
f:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function write(id, data)
|
||||||
|
if not sane() then
|
||||||
|
prepare()
|
||||||
|
end
|
||||||
|
|
||||||
|
if not id or #id == 0 or not id:match("^%w+$") then
|
||||||
|
error("Session ID is not sane!")
|
||||||
|
end
|
||||||
|
|
||||||
|
_write(id, data)
|
||||||
|
end
|
||||||
|
|
||||||
function read(id)
|
function read(id)
|
||||||
if not id or #id == 0 then
|
if not id or #id == 0 then
|
||||||
return
|
return
|
||||||
|
@ -63,12 +89,19 @@ function read(id)
|
||||||
if not id:match("^%w+$") then
|
if not id:match("^%w+$") then
|
||||||
error("Session ID is not sane!")
|
error("Session ID is not sane!")
|
||||||
end
|
end
|
||||||
clean()
|
|
||||||
if not sane(sessionpath .. "/" .. id) then
|
if not sane(sessionpath .. "/" .. id) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
fs.utimes(sessionpath .. "/" .. id)
|
|
||||||
return fs.readfile(sessionpath .. "/" .. id)
|
local blob = _read(id)
|
||||||
|
if decode(blob).atime + sessiontime < luci.sys.uptime()then
|
||||||
|
fs.unlink(sessionpath .. "/" .. id)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
-- refresh atime in session
|
||||||
|
refreshed = encode(decode(blob))
|
||||||
|
write(id, refreshed)
|
||||||
|
return blob
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,24 +114,6 @@ function sane(file)
|
||||||
== (file and "rw-------" or "rwx------")
|
== (file and "rw-------" or "rwx------")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Write session data to a session file.
|
|
||||||
-- @param id Session identifier
|
|
||||||
-- @param data Session data
|
|
||||||
function write(id, data)
|
|
||||||
if not sane() then
|
|
||||||
prepare()
|
|
||||||
end
|
|
||||||
if not id:match("^%w+$") then
|
|
||||||
error("Session ID is not sane!")
|
|
||||||
end
|
|
||||||
|
|
||||||
local f = nixio.open(sessionpath .. "/" .. id, "w", 600)
|
|
||||||
f:writeall(data)
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Kills a session
|
--- Kills a session
|
||||||
-- @param id Session identifier
|
-- @param id Session identifier
|
||||||
function kill(id)
|
function kill(id)
|
||||||
|
|
|
@ -27,7 +27,7 @@ function index()
|
||||||
if auth then -- if authentication token was given
|
if auth then -- if authentication token was given
|
||||||
local sdat = luci.sauth.read(auth)
|
local sdat = luci.sauth.read(auth)
|
||||||
if sdat then -- if given token is valid
|
if sdat then -- if given token is valid
|
||||||
user = loadstring(sdat)().user
|
user = luci.sauth.decode(sdat).user
|
||||||
if user and luci.util.contains(accs, user) then
|
if user and luci.util.contains(accs, user) then
|
||||||
return user, auth
|
return user, auth
|
||||||
end
|
end
|
||||||
|
@ -68,7 +68,7 @@ function rpc_auth()
|
||||||
secret = sys.uniqueid(16)
|
secret = sys.uniqueid(16)
|
||||||
|
|
||||||
http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
http.header("Set-Cookie", "sysauth=" .. sid.."; path=/")
|
||||||
sauth.write(sid, util.get_bytecode({
|
sauth.write(sid, sauth.encode({
|
||||||
user=user,
|
user=user,
|
||||||
token=token,
|
token=token,
|
||||||
secret=secret
|
secret=secret
|
||||||
|
|
Loading…
Reference in a new issue