summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--auth/acfpasswd.lua8
-rw-r--r--auth/shadow.lua70
-rw-r--r--testauth.lua52
3 files changed, 130 insertions, 0 deletions
diff --git a/auth/acfpasswd.lua b/auth/acfpasswd.lua
new file mode 100644
index 0000000..36d0fab
--- /dev/null
+++ b/auth/acfpasswd.lua
@@ -0,0 +1,8 @@
+
+local db = require('auth/shadow')
+
+-- similar to /etc/shadow, just different fields
+db.file = "/etc/acf/passwd"
+db.fields = { 'name', 'passwd', 'gecos', 'roles', 'skin', 'home' }
+
+return db
diff --git a/auth/shadow.lua b/auth/shadow.lua
new file mode 100644
index 0000000..3a4a534
--- /dev/null
+++ b/auth/shadow.lua
@@ -0,0 +1,70 @@
+
+local posix = require('posix')
+local db = {}
+
+db.file = "/etc/shadow"
+db.fields = { 'name', 'passwd', 'lastchanged', 'minimum', 'maximum', 'warn', 'inactive', 'expire', 'dummy' }
+
+function db.splitent(line)
+ local ent = {}
+ local i = 1
+ for value in string.gmatch(line or "", "([^:]*):?") do
+ ent[db.fields[i]] = value
+ i = i + 1
+ end
+ return ent
+end
+
+function db.getent(username)
+ local f = io.open(db.file)
+ if f == nil then
+ return nil
+ end
+ for line in f:lines() do
+ local ent = db.splitent(line)
+ if ent.name == username then
+ f:close()
+ return ent
+ end
+ end
+ f:close()
+ return nil, db.file..": user '"..tostring(username).."' not found"
+end
+
+function db.verify_passwd(cleartext, pwhash)
+ --[[
+ from man crypt(3):
+
+ If salt is a character string starting with the characters "$id$" fol-
+ lowed by a string terminated by "$":
+
+ $id$salt$encrypted
+
+ then instead of using the DES machine, id identifies the encryption
+ method used and this then determines how the rest of the password
+ string is interpreted. The following values of id are supported:
+
+ ID | Method
+ ---------------------------------------------------------
+ 1 | MD5
+ 2a | Blowfish (not in mainline glibc; added in some
+ | Linux distributions)
+ 5 | SHA-256 (since glibc 2.7)
+ 6 | SHA-512 (since glibc 2.7)
+ ]]--
+ local algo_salt, hash = string.match(pwhash, "^(%$%d%$[a-zA-Z0-9./]+%$)(.*)")
+ local userhash = posix.crypt(cleartext, algo_salt)
+ print("user hash:", userhash)
+ print("db hash:", pwhash)
+ return (pwhash == userhash)
+end
+
+function db.authenticate(username, cleartextpw)
+ local ent = db.getent(username)
+ if ent == nil then
+ return nil, tostring(username)..": no such user"
+ end
+ return db.verify_passwd(cleartextpw, ent.passwd)
+end
+
+return db
diff --git a/testauth.lua b/testauth.lua
new file mode 100644
index 0000000..67d703a
--- /dev/null
+++ b/testauth.lua
@@ -0,0 +1,52 @@
+#!/usr/bin/lua
+
+--[[
+
+test authenticate
+
+* 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
+ If success then create new user with no roles in acf db.
+
+]]--
+
+shadow = require("auth.shadow")
+acfdb = require("auth.acfpasswd")
+
+user = arg[1]
+entry, errmsg = acfdb.getent(user)
+
+authenticate = acfdb.authenticate
+if entry == nil then
+ print("Failed to read user '"..user.."' in "..acfdb.file)
+ if not shadow.getent(user) then
+ print("Faild to read user in "..shadow.file)
+ -- We could fallback to ldap, imaps or similar here
+ return 1
+ 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
+
+io.write("Enter password (WARNING: will echo): ")
+passwd = io.read("*line")
+
+if not authenticate(user, passwd) then
+ print("Authentication failed")
+ return 1
+end
+
+print("User "..user.." is authenticated")
+if entry == nil then
+ print("A new account should be created here")
+ -- passwd = confirm_password(passwd)
+ -- acfdb.setent(user, passwd, "New User", "NEWUSER")
+end
+
+