Commit 7e2adf8f authored by jacmoe2's avatar jacmoe2

PhpBB authentication method added. Enables users to authenticate against a...

PhpBB authentication method added. Enables users to authenticate against a phpBB database (username/password), much like ldap.
parent d62d179a
......@@ -1438,8 +1438,10 @@ lib/ajax/lib/activex_off.js -text
lib/ajax/tiki-ajax.js -text
lib/articles/artlib.php -text
lib/articles/index.php -text
lib/auth/PasswordHash.php -text
lib/auth/index.php -text
lib/auth/ldap.php -text
lib/auth/phpbb.php -text
lib/auth/tokens.php -text
lib/bablotron.php -text
lib/ban/banlib.php -text
......
<?php
# Portable PHP password hashing framework.
# Modified by Steve Streeting to be compatible with phpBB3 hashes ($H$ instead of $P$)
#
# Version 0.1 / genuine.
#
# Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
# the public domain.
#
# There's absolutely no warranty.
#
# The homepage URL for this framework is:
#
# http://www.openwall.com/phpass/
#
# Please be sure to update the Version line if you edit this file in any way.
# It is suggested that you leave the main version number intact, but indicate
# your project name (after the slash) and add your own revision information.
#
# Please do not change the "private" password hashing method implemented in
# here, thereby making your hashes incompatible. However, if you must, please
# change the hash type identifier (the "$P$") to something different.
#
# Obviously, since this code is in the public domain, the above are not
# requirements (there can be none), but merely suggestions.
#
class PasswordHash {
var $itoa64;
var $iteration_count_log2;
var $portable_hashes;
var $random_state;
function PasswordHash($iteration_count_log2, $portable_hashes)
{
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
$iteration_count_log2 = 8;
$this->iteration_count_log2 = $iteration_count_log2;
$this->portable_hashes = $portable_hashes;
$this->random_state = microtime() . getmypid();
}
function get_random_bytes($count)
{
$output = '';
if (($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);
fclose($fh);
}
if (strlen($output) < $count) {
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$this->random_state =
md5(microtime() . $this->random_state);
$output .=
pack('H*', md5($this->random_state));
}
$output = substr($output, 0, $count);
}
return $output;
}
function encode64($input, $count)
{
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
if ($i < $count)
$value |= ord($input[$i]) << 8;
$output .= $this->itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count)
break;
if ($i < $count)
$value |= ord($input[$i]) << 16;
$output .= $this->itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count)
break;
$output .= $this->itoa64[($value >> 18) & 0x3f];
} while ($i < $count);
return $output;
}
function gensalt_private($input)
{
// SJS modified for compat with phpBB3 hashes - $P$ to $H$
$output = '$H$';
// end SJS modified
$output .= $this->itoa64[min($this->iteration_count_log2 +
((PHP_VERSION >= '5') ? 5 : 3), 30)];
$output .= $this->encode64($input, 6);
return $output;
}
function crypt_private($password, $setting)
{
$output = '*0';
if (substr($setting, 0, 2) == $output)
$output = '*1';
// SJS modified for compat with phpBB3 hashes - $P$ to $H$
if (substr($setting, 0, 3) != '$H$')
return $output;
$count_log2 = strpos($this->itoa64, $setting[3]);
if ($count_log2 < 7 || $count_log2 > 30)
return $output;
$count = 1 << $count_log2;
$salt = substr($setting, 4, 8);
if (strlen($salt) != 8)
return $output;
# We're kind of forced to use MD5 here since it's the only
# cryptographic primitive available in all versions of PHP
# currently in use. To implement our own low-level crypto
# in PHP would result in much worse performance and
# consequently in lower iteration counts and hashes that are
# quicker to crack (by non-PHP code).
if (PHP_VERSION >= '5') {
$hash = md5($salt . $password, TRUE);
do {
$hash = md5($hash . $password, TRUE);
} while (--$count);
} else {
$hash = pack('H*', md5($salt . $password));
do {
$hash = pack('H*', md5($hash . $password));
} while (--$count);
}
$output = substr($setting, 0, 12);
$output .= $this->encode64($hash, 16);
return $output;
}
function gensalt_extended($input)
{
$count_log2 = min($this->iteration_count_log2 + 8, 24);
# This should be odd to not reveal weak DES keys, and the
# maximum valid value is (2**24 - 1) which is odd anyway.
$count = (1 << $count_log2) - 1;
$output = '_';
$output .= $this->itoa64[$count & 0x3f];
$output .= $this->itoa64[($count >> 6) & 0x3f];
$output .= $this->itoa64[($count >> 12) & 0x3f];
$output .= $this->itoa64[($count >> 18) & 0x3f];
$output .= $this->encode64($input, 3);
return $output;
}
function gensalt_blowfish($input)
{
# This one needs to use a different order of characters and a
# different encoding scheme from the one in encode64() above.
# We care because the last character in our encoded string will
# only represent 2 bits. While two known implementations of
# bcrypt will happily accept and correct a salt string which
# has the 4 unused bits set to non-zero, we do not want to take
# chances and we also do not want to waste an additional byte
# of entropy.
$itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$output = '$2a$';
$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
$output .= '$';
$i = 0;
do {
$c1 = ord($input[$i++]);
$output .= $itoa64[$c1 >> 2];
$c1 = ($c1 & 0x03) << 4;
if ($i >= 16) {
$output .= $itoa64[$c1];
break;
}
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 4;
$output .= $itoa64[$c1];
$c1 = ($c2 & 0x0f) << 2;
$c2 = ord($input[$i++]);
$c1 |= $c2 >> 6;
$output .= $itoa64[$c1];
$output .= $itoa64[$c2 & 0x3f];
} while (1);
return $output;
}
function HashPassword($password)
{
$random = '';
if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
$random = $this->get_random_bytes(16);
$hash =
crypt($password, $this->gensalt_blowfish($random));
if (strlen($hash) == 60)
return $hash;
}
if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
if (strlen($random) < 3)
$random = $this->get_random_bytes(3);
$hash =
crypt($password, $this->gensalt_extended($random));
if (strlen($hash) == 20)
return $hash;
}
if (strlen($random) < 6)
$random = $this->get_random_bytes(6);
$hash =
$this->crypt_private($password,
$this->gensalt_private($random));
if (strlen($hash) == 34)
return $hash;
# Returning '*' on error is safe here, but would _not_ be safe
# in a crypt(3)-like function used _both_ for generating new
# hashes and for validating passwords against existing hashes.
return '*';
}
function CheckPassword($password, $stored_hash)
{
$hash = $this->crypt_private($password, $stored_hash);
if ($hash[0] == '*')
$hash = crypt($password, $stored_hash);
return $hash == $stored_hash;
}
}
?>
<?php
// Tikiwiki authentication backend for phpBB3 with mysql4
// By Jacob Moen 10 Dec 2009
// Based on:
// Mediawiki authentication plugin for phpBB3 with mysql4
// By Steve Streeting 26 Dec 2008
require_once ("lib/auth/PasswordHash.php");
// some definitions for helping with authentication
// Er, what about definition clashes ?
define("PHPBB_INVALID_CREDENTIALS", -21);
define("PHPBB_INVALID_SYNTAX", -23);
define("PHPBB_NO_SUCH_USER", -25);
define("PHPBB_SUCCESS", -29);
define("SERVER_ERROR", -1);
//TODO: support other database types
class TikiPhpBBLib {
function check($user, $pass) {
// no need to progress further if the user doesn't even exist
if(!$this->userExists($user)) {
return PHPBB_NO_SUCH_USER;
}
// if the user does exist, authenticate
if($this->authenticate($user, $pass)) {
return PHPBB_SUCCESS;
} else {
return PHPBB_INVALID_CREDENTIALS;
}
// shouldn't happen..
return PHPBB_INVALID_SYNTAX;
}
function connectdb() {
global $prefs;
$dbhost = $prefs['auth_phpbb_dbhost'];
$dbuser = $prefs['auth_phpbb_dbuser'];
$dbpasswd = $prefs['auth_phpbb_dbpasswd'];
$dbname = $prefs['auth_phpbb_dbname'];
// not really used - will it be used?
//$dbport = $prefs['auth_phpbb_dbport'];
//$dbconnection = mysql_connect($dbhost.':'.$dbport, $dbuser, $dbpasswd)
$dbconnection = mysql_connect($dbhost, $dbuser, $dbpasswd)
or die('AuthPhpBB : Could not connect: ' . mysql_error());
if($dbconnection) {
mysql_select_db($dbname, $dbconnection);
return $dbconnection;
}
return false;
}
/**
* Check whether there exists a user account with the given name.
*
* @param string $username
* @return bool
* @access public
*/
function userExists( $username ) {
global $prefs;
$dbconnection = $this->connectdb();
// MySQL queries are case insensitive anyway
$query = "select username from ".$prefs['auth_phpbb_table_prefix']."users where lcase(username) = lcase('". $username ."')";
$result = mysql_query($query, $dbconnection)
or die('AuthPhpBB : Query failed: ' . mysql_error());
$numrows = mysql_num_rows($result);
// Free resultset
mysql_free_result($result);
return $numrows > 0;
}
/**
* Check if a username+password pair is a valid login.
*
* @param string $username
* @param string $password
* @return bool
* @access public
*/
function authenticate( $username, $password ) {
global $prefs;
$dbconnection = $this->connectdb();
$query = "select user_password from ".$prefs['auth_phpbb_table_prefix']."users where lcase(username) = lcase('". $username ."')";
$result = mysql_query($query, $dbconnection)
or die('AuthPhpBB : Query failed: ' . mysql_error());
if (mysql_num_rows($result) == 0) {
return false;
}
else {
// TODO: check for phpBB version here, and select a different hasher, if needed.
// This one is hardcoded for phpbb3
$PasswordHasher = new PasswordHash(8, TRUE);
$row = mysql_fetch_row($result);
if ($PasswordHasher->CheckPassword($password, $row[0])) {
return true;
}
else {
return false;
}
}
}
/**
* Returns a users email from the phpbb3 user table.
* @param Username $username
* @access public
* @return email or 0
*/
function grabEmail( &$username ) {
global $prefs;
$dbconnection = $this->connectdb();
// Just add email
$query = "select user_email from ".$prefs['auth_phpbb_table_prefix'] . "users where lcase(username) = lcase('". $username ."')";
$result = mysql_query($query, $dbconnection)
or die('AuthPhpBB : Query failed: ' . mysql_error());
if (mysql_num_rows($result) > 0) {
$row = mysql_fetch_row($result);
return $row[0];
}
return 0;
}
}
......@@ -15,6 +15,7 @@ function prefs_auth_list() {
'cas' => tra('CAS (Central Authentication Service)'),
'shib' => tra('Shibboleth'),
'ws' => tra('Web Server'),
'phpbb' => tra('phpBB'),
),
),
'auth_token_access' => array(
......
......@@ -1014,6 +1014,17 @@ function get_default_prefs() {
'auth_ldap_syncuserattr' => 'uid',
'auth_ldap_syncgroupattr' => 'cn',
'auth_phpbb_version' => 3,
'auth_phpbb_skip_admin' => 'y',
'auth_phpbb_create_tiki' => 'n',
'auth_phpbb_dbhost' => '',
'auth_phpbb_dbport' => '',
'auth_phpbb_disable_tikionly' => 'n',
'auth_phpbb_dbuser' => '',
'auth_phpbb_dbpasswd' => '',
'auth_phpbb_dbname' => '',
'auth_phpbb_table_prefix' => 'phpbb_',
'https_login' => 'allowed',
'https_external_links_for_users' => 'n',
'feature_show_stay_in_ssl_mode' => 'y',
......
......@@ -344,6 +344,12 @@ class UsersLib extends TikiLib
$cas_create_tiki_ldap = ($prefs['cas_create_user_tiki_ldap'] == 'y');
$cas_skip_admin = ($prefs['cas_skip_admin'] == 'y');
// read basic phpbb options
$auth_phpbb = ($prefs['auth_method'] == 'phpbb');
$phpbb_create_tiki = ($prefs['auth_phpbb_create_tiki'] == 'y');
$phpbb_skip_admin = ($prefs['auth_phpbb_skip_admin'] == 'y');
$phpbb_disable_tikionly = ($prefs['auth_phpbb_disable_tikionly'] == 'y');
// see if we are to use Shibboleth
$auth_shib = ($prefs['auth_method'] == 'shib');
$shib_create_tiki = ($prefs['shib_create_user_tiki'] == 'y');
......@@ -375,9 +381,9 @@ class UsersLib extends TikiLib
// if we aren't using LDAP this will be quick
// if we are using tiki auth or if we're using an alternative auth except for admin
if ((!$auth_ldap && !$auth_pam && !$auth_cas && !$auth_shib) || ((($auth_ldap && $skip_admin) || ($auth_shib && $shib_skip_admin) || ($auth_pam && $pam_skip_admin) || ($auth_cas && $cas_skip_admin)) && $user == "admin") || ($auth_ldap && ($prefs['auth_ldap_permit_tiki_users']=='y' && $userTiki))) { // todo: bad hack. better search for a more general solution here
// if the user verified ok, log them in
if ($userTiki) //user validated in tiki, update lastlogin and be done
if ((!$auth_ldap && !$auth_pam && !$auth_cas && !$auth_shib && !$auth_phpbb) || ((($auth_ldap && $skip_admin) || ($auth_shib && $shib_skip_admin) || ($auth_pam && $pam_skip_admin) || ($auth_cas && $cas_skip_admin) || ($auth_phpbb && $phpbb_skip_admin)) && $user == "admin") || ($auth_ldap && ($prefs['auth_ldap_permit_tiki_users']=='y' && $userTiki))) { // todo: bad hack. better search for a more general solution here
// if the user verified ok, log them in
if ($userTiki) //user validated in tiki, update lastlogin and be done
return array($this->update_lastlogin($user), $user, $result);
// if the user password was incorrect but the account was there, give an error
elseif ($userTikiPresent) //user ixists in tiki but bad password
......@@ -726,6 +732,68 @@ class UsersLib extends TikiLib
return array($this->update_lastlogin($user), $user, $result);
}
elseif ($auth_phpbb) {
$result = $this->validate_user_phpbb($user, $pass);
switch ($result) {
case USER_VALID:
$userPhpbb = true;
break;
case PASSWORD_INCORRECT:
$userPhpbb = false;
break;
}
// start off easy
// if the user verified in Tiki and phpBB, log in
if ($userPhpbb && $userTiki) {
return array($this->update_lastlogin($user), $user, $result);
}
// if the user wasn't found in either system, just fail
elseif (!$userTikiPresent && !$userPhpbb) {
return array(false, $user, USER_UNKNOWN);
}
// if the user was logged into phpBB but not found in Tiki
elseif ($userPhpbb && !$userTikiPresent) {
// see if we can create a new account
if ($phpbb_create_tiki) {
// get the user email and then add the user to Tiki
$the_email = $this->phpbbauth->grabEmail($user);
$result = $this->add_user($user, $pass, $the_email);
// if it worked ok, just log in
if ($result == USER_VALID)
// before we log in, update the login counter
return array($this->update_lastlogin($user), $user, $result);
// if the server didn't work, do something!
elseif ($result == SERVER_ERROR) {
// check the notification status for this type of error
return array(false, $user, $result);
}
// otherwise don't log in.
else
return array(false, $user, $result);
}
// otherwise
else
// just say no!
return array(false, $user, $result);
}
// if the user was found in Tiki, but not found in phpBB, we should probably disable the user
elseif ($userTikiPresent && !$userPhpbb) {
if($phpbb_disable_tikionly) {
// would probably be better do flag the user as not active? How do you do that?
// and it also would be better to check if the user is active first.. :)
$this->invalidate_account($user);
global $logslib;
$logslib->add_log('auth_phpbb','NOTICE: Invalidated user ' . $user . ' due to missing phpBB account.');
}
return array(false, $user, ACCOUNT_DISABLED);
}
// if the user was logged into phpBB and found in Tiki (no password in Tiki user table necessary)
elseif ($userPhpbb && $userTikiPresent)
return array($this->update_lastlogin($user), $user, $result);
}
// we will never get here
return array(false, $user, $result);
}
......@@ -874,6 +942,28 @@ class UsersLib extends TikiLib
die('Assertion failed '.__FILE__.':'.__LINE__);
}
// validate the user from a phpBB database
function validate_user_phpbb($user, $pass) {
require_once('auth/phpbb.php');
$this->phpbbauth = new TikiPhpBBLib();
switch($this->phpbbauth->check($user, $pass)) {
case PHPBB_INVALID_CREDENTIALS:
return PASSWORD_INCORRECT;
break;
case PHPBB_INVALID_SYNTAX:
case PHPBB_NO_SUCH_USER:
return USER_NOT_FOUND;
break;
case PHPBB_SUCCESS:
//$logslib->add_log('phpbb','PhpBB user validation successful.');
return USER_VALID;
break;
default:
return SERVER_ERROR;
}
// this should never happen
die('Assertion failed '.__FILE__.':'.__LINE__);
}
// Help function to disable the user password. Used, whenever the user password
// shall not be hold in the tiki db but in LDAP or somewhere else
......
......@@ -121,6 +121,19 @@ if (isset($_REQUEST['auth_shib'])) {
simple_set_toggle('shib_usegroup');
simple_set_value('shib_group');
}
if (isset($_REQUEST['auth_phpbb'])) {
check_ticket('admin-inc-login');
simple_set_toggle('auth_phpbb_create_tiki');
simple_set_toggle('auth_phpbb_skip_admin');
simple_set_toggle('auth_phpbb_disable_tikionly');
simple_set_value('auth_phpbb_dbhost');
simple_set_value('auth_phpbb_dbuser');
simple_set_value('auth_phpbb_dbpasswd');
simple_set_value('auth_phpbb_dbname');
simple_set_value('auth_phpbb_table_prefix');
simple_set_value('auth_phpbb_version');
simple_set_value('auth_phpbb_dbport');
}
$smarty->assign('gd_lib_found', function_exists('gd_info') ? 'y' : 'n');
// Get list of available languages
$languages = array();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment