diff options
-rw-r--r-- | auth/acfpasswd.lua | 8 | ||||
-rw-r--r-- | auth/shadow.lua | 70 | ||||
-rw-r--r-- | testauth.lua | 52 |
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 + + |