# HG changeset patch # User Waqas Hussain # Date 1309484433 -18000 # Node ID 81c7b36e6cddeec17d3b77a06e2dc1df1c7fb31b # Parent 000f1d1c6ca50112c54e968d572abc911053b358 mod_auth_phpbb3: Initial commit. diff -r 000f1d1c6ca5 -r 81c7b36e6cdd mod_auth_phpbb3/mod_auth_phpbb3.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_phpbb3/mod_auth_phpbb3.lua Fri Jul 01 06:40:33 2011 +0500 @@ -0,0 +1,188 @@ +-- phpbb3 authentication backend for Prosody +-- +-- Copyright (C) 2011 Waqas Hussain +-- + +local log = require "util.logger".init("auth_sql"); +local new_sasl = require "util.sasl".new; +local nodeprep = require "util.encodings".stringprep.nodeprep; +local DBI = require "DBI" +local md5 = require "util.hashes".md5; + +local connection; +local params = module:get_option("sql"); + +local resolve_relative_path = require "core.configmanager".resolve_relative_path; + +local function test_connection() + if not connection then return nil; end + if connection:ping() then + return true; + else + module:log("debug", "Database connection closed"); + connection = nil; + end +end +local function connect() + if not test_connection() then + prosody.unlock_globals(); + local dbh, err = DBI.Connect( + params.driver, params.database, + params.username, params.password, + params.host, params.port + ); + prosody.lock_globals(); + if not dbh then + module:log("debug", "Database connection failed: %s", tostring(err)); + return nil, err; + end + module:log("debug", "Successfully connected to database"); + dbh:autocommit(true); -- don't run in transaction + connection = dbh; + return connection; + end +end + +do -- process options to get a db connection + params = params or { driver = "SQLite3" }; + + if params.driver == "SQLite3" then + params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite"); + end + + assert(params.driver and params.database, "Both the SQL driver and the database need to be specified"); + + assert(connect()); +end + +local function getsql(sql, ...) + if params.driver == "PostgreSQL" then + sql = sql:gsub("`", "\""); + end + if not test_connection() then connect(); end + -- do prepared statement stuff + local stmt, err = connection:prepare(sql); + if not stmt and not test_connection() then error("connection failed"); end + if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end + -- run query + local ok, err = stmt:execute(...); + if not ok and not test_connection() then error("connection failed"); end + if not ok then return nil, err; end + + return stmt; +end + +local function get_password(username) + local stmt, err = getsql("SELECT `user_password` FROM `phpbb_users` WHERE `username`=?", username); + if stmt then + for row in stmt:rows(true) do + return row.user_password; + end + end +end + +local itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +local function hashEncode64(input) + local count = 16; + local output = ""; + local i, value = 0, 0; + + while true do + value = input:byte(i+1) + i = i+1; + local idx = value % 0x40 + 1; + output = output .. itoa64:sub(idx, idx); + + if i < count then + value = value + input:byte(i+1) * 256; + end + local _ = value % (2^6); + local idx = ((value - _) / (2^6)) % 0x40 + 1 + output = output .. itoa64:sub(idx, idx); + + if i >= count then break; end + i = i+1; + + if i < count then + value = value + input:byte(i+1) * 256 * 256; + end + local _ = value % (2^12); + local idx = ((value - _) / (2^12)) % 0x40 + 1 + output = output .. itoa64:sub(idx, idx); + + if i >= count then break; end + i = i+1; + + local _ = value % (2^18); + local idx = ((value - _) / (2^18)) % 0x40 + 1 + output = output .. itoa64:sub(idx, idx); + + if not(i < count) then break; end + end + return output; +end +local function hashCryptPrivate(password, genSalt, itoa64) + local output = "*"; + if not genSalt:match("^%$H%$") then return output; end + + local count_log2 = itoa64:find(genSalt:sub(4,4)) - 1; + if count_log2 < 7 or count_log2 > 30 then return output; end + + local count = 2 ^ count_log2; + local salt = genSalt:sub(5, 12); + + if #salt ~= 8 then return output; end + + local hash = md5(salt..password); + + while true do + hash = md5(hash..password); + if not(count > 1) then break; end + count = count-1; + end + + output = genSalt:sub(1, 12); + output = output .. hashEncode64(hash); + + return output; +end +local function phpbbCheckHash(password, hash) + return #hash == 34 and hashCryptPrivate(password, hash, itoa64) == hash; +end + +provider = { name = "phpbb3" }; + +function provider.test_password(username, password) + module:log("debug", "test_password '%s' for user %s", password, username); + + local hash = get_password(username); + return phpbbCheckHash(password, hash); +end +function provider.user_exists(username) + module:log("debug", "test user %s existence", username); + return get_password(username) and true; +end + +function provider.get_password(username) + return nil, "Getting password is not supported."; +end +function provider.set_password(username, password) + return nil, "Setting password is not supported."; +end +function provider.create_user(username, password) + return nil, "Account creation/modification not supported."; +end + +function provider.get_sasl_handler() + local profile = { + plain_test = function(username, password, realm) + -- TODO stringprep + return provider.test_password(username, password), true; + end; + }; + return new_sasl(module.host, profile); +end + +module:add_item("auth-provider", provider); +