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