diff --git a/application/composer.json b/application/composer.json index 83f86d8..b0a3ed6 100644 --- a/application/composer.json +++ b/application/composer.json @@ -6,6 +6,7 @@ "phpmailer/phpmailer": "^6.9", "gregwar/captcha": "^1.2", "phpgangsta/googleauthenticator": "dev-master", - "doctrine/dbal": "^4.0" + "doctrine/dbal": "^4.0", + "ext-gmp": "*" } } diff --git a/application/config/config.php.sample b/application/config/config.php.sample index 558d92e..e76987b 100755 --- a/application/config/config.php.sample +++ b/application/config/config.php.sample @@ -66,6 +66,13 @@ $config['battlenet_support'] = false; // SRP6 Password Encryption - Enable if your password encryption is of the SRP6 type, for last version of TC/AC you need to enable this. $config['srp6_support'] = true; // Important: Enable the GMP extension for PHP in your php.ini. +/* Choose SRP6 version from below. +0 = SRP6 - no srp_version field in auth database battlenet_accounts table +1 = SRP6v1 +2 = SRP6v2 + */ +$config['srp6_version'] = 2; + // Feature Toggles - Control whether certain pages or features should be disabled. $config['disable_top_players'] = false; // Set to true to hide the top players page. $config['disable_online_players'] = false; // Set to true to hide the online players page. diff --git a/application/include/functions.php b/application/include/functions.php index f90812e..6e86a96 100644 --- a/application/include/functions.php +++ b/application/include/functions.php @@ -154,7 +154,7 @@ function send_phpmailer($email, $subject, $message) $mail->addReplyTo(get_config('smtp_mail')); // Content - $mail->CharSet = 'UTF-8'; + $mail->CharSet = 'UTF-8'; $mail->isHTML(true); $mail->Subject = $subject; $mail->Body = $message; @@ -322,12 +322,12 @@ function calculateSRP6Verifier($username, $password, $salt) $h1 = sha1(strtoupper($username . ':' . $password), TRUE); // calculate second hash - if(get_config('server_core') == 5) - { - $h2 = sha1(strrev($salt) . $h1, TRUE); // From haukw - } else { - $h2 = sha1($salt . $h1, TRUE); - } + if(get_config('server_core') == 5) + { + $h2 = sha1(strrev($salt) . $h1, TRUE); // From haukw + } else { + $h2 = sha1($salt . $h1, TRUE); + } // convert to integer (little-endian) $h2 = gmp_import($h2, 1, GMP_LSW_FIRST); @@ -342,12 +342,66 @@ function calculateSRP6Verifier($username, $password, $salt) $verifier = str_pad($verifier, 32, chr(0), STR_PAD_RIGHT); // done! - if(get_config('server_core') == 5) - { - return strrev($verifier); // From haukw - } else { - return $verifier; - } + if(get_config('server_core') == 5) + { + return strrev($verifier); // From haukw + } else { + return $verifier; + } +} + +function calculateSRP6VerifierBnetV1($email, $password, $salt) +{ + // algorithm constants + $g = gmp_init(2); + $N = gmp_init('86A7F6DEEB306CE519770FE37D556F29944132554DED0BD68205E27F3231FEF5A10108238A3150C59CAF7B0B6478691C13A6ACF5E1B5ADAFD4A943D4A21A142B800E8A55F8BFBAC700EB77A7235EE5A609E350EA9FC19F10D921C2FA832E4461B7125D38D254A0BE873DFC27858ACB3F8B9F258461E4373BC3A6C2A9634324AB', 16); + + // calculate x + $srpPassword = hash('sha256',strtoupper($username . ':' . $password), TRUE); + $x = hash('sha256', $salt . $srpPassword, TRUE); + + // g^h2 mod N + $verifier = gmp_powm($g, $x, $N); + + // convert back to a byte array (little-endian) + $verifier = gmp_export($verifier, 1, GMP_LSW_FIRST); + + // pad to 256 bytes, remember that zeros go on the end in little-endian! + $verifier = str_pad($verifier, 256, chr(0), STR_PAD_RIGHT); + + // done! + return $verifier; +} + +function calculateSRP6VerifierBnetV2($email, $password, $salt) +{ + // algorithm constants + $g = gmp_init(2); + $N = gmp_init('AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73', 16); + + $srpPassword = strtoupper(hash('sha256', strtoupper($email), false)) . ":" . $password; + + // calculate x + $xBytes = hash_pbkdf2("sha512", $srpPassword, $salt, 15000, 64, true); + $x = gmp_import($xBytes, 1, GMP_MSW_FIRST); + if (ord($xBytes[0]) & 0x80) + { + $fix = gmp_init('100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', 16); + $x = gmp_sub($x, $fix); + } + $x = gmp_mod($x, gmp_sub($N, 1)); + + // g^h2 mod N + $verifier = gmp_powm($g, $x, $N); + + // convert back to a byte array (little-endian) + $verifier = gmp_export($verifier, 1, GMP_LSW_FIRST); + + // pad to 256 bytes, remember that zeros go on the end in little-endian! + $verifier = str_pad($verifier, 256, chr(0), STR_PAD_RIGHT); + + // done! + return $verifier; } // Returns SRP6 parameters to register this username/password combination with @@ -360,12 +414,36 @@ function getRegistrationData($username, $password) $verifier = calculateSRP6Verifier($username, $password, $salt); // done - this is what you put in the account table! - if(get_config('server_core') == 5) - { - $salt = strtoupper(bin2hex($salt)); // From haukw - $verifier = strtoupper(bin2hex($verifier)); // From haukw - } - + if(get_config('server_core') == 5) + { + $salt = strtoupper(bin2hex($salt)); // From haukw + $verifier = strtoupper(bin2hex($verifier)); // From haukw + } + + return array($salt, $verifier); +} + +function getRegistrationDataBnetV1($email, $password) +{ + // generate a random salt + $salt = random_bytes(32); + + // calculate verifier using this salt + $verifier = calculateSRP6VerifierBnetV1($email, $password, $salt); + + // done - this is what you put in the battlenet_accounts table! + return array($salt, $verifier); +} + +function getRegistrationDataBnetV2($email, $password) +{ + // generate a random salt + $salt = random_bytes(32); + + // calculate verifier using this salt + $verifier = calculateSRP6VerifierBnetV2($email, $password, $salt); + + // done - this is what you put in the battlenet_accounts table! return array($salt, $verifier); } @@ -383,6 +461,16 @@ function verifySRP6($user, $pass, $salt, $verifier) return ($verifier === str_pad(gmp_export($v, 1, GMP_LSW_FIRST), 32, chr(0), STR_PAD_RIGHT)); } +function verifySRP6BnetV1($email, $pass, $salt, $verifier) +{ + return ($verifier === calculateSRP6VerifierBnetV1($email, $pass, $salt)); +} + +function verifySRP6BnetV2($email, $pass, $salt, $verifier) +{ + return ($verifier === calculateSRP6VerifierBnetV2($email, $pass, $salt)); +} + // Get language text function lang($val) { diff --git a/application/include/user.php b/application/include/user.php index 822b061..d132ae9 100644 --- a/application/include/user.php +++ b/application/include/user.php @@ -89,9 +89,17 @@ public static function bnet_register() return false; } - if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 16)) { - error_msg(lang('passwords_length')); - return false; + if(get_config('srp6_support') && get_config('srp6_version') == 2) { + if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 128)) { + error_msg(lang('passwords_length')); + return false; + } + } + else { + if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 16)) { + error_msg(lang('passwords_length')); + return false; + } } if (!self::check_email_exists(strtoupper($_POST["email"]))) { @@ -99,47 +107,110 @@ public static function bnet_register() return false; } - if (empty(get_config('srp6_support'))) { - $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($_POST['email'])) . ':' . strtoupper($_POST['password'])))))))); - database::$auth->insert('battlenet_accounts', [ - 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), - 'sha_pass_hash' => $antiXss->xss_clean($bnet_hashed_pass), - ]); + if (empty(get_config('soap_for_register'))) { + if (empty(get_config('srp6_support'))) { + $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($_POST['email'])) . ':' . strtoupper($_POST['password'])))))))); + database::$auth->insert('battlenet_accounts', [ + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'sha_pass_hash' => $antiXss->xss_clean($bnet_hashed_pass), + ]); - $bnet_account_id = database::$auth->lastInsertId(); - $username = $bnet_account_id . '#1'; - $hashed_pass = strtoupper(sha1(strtoupper($username . ':' . $_POST['password']))); - database::$auth->insert('account', [ - 'username' => $antiXss->xss_clean(strtoupper($username)), - 'sha_pass_hash' => $antiXss->xss_clean($hashed_pass), - 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), - 'expansion' => $antiXss->xss_clean(get_config('expansion')), - 'battlenet_account' => $bnet_account_id, - 'battlenet_index' => 1, - ]); - success_msg(lang('account_created')); - return true; + $bnet_account_id = database::$auth->lastInsertId(); + $username = $bnet_account_id . '#1'; + $hashed_pass = strtoupper(sha1(strtoupper($username . ':' . $_POST['password']))); + database::$auth->insert('account', [ + 'username' => $antiXss->xss_clean(strtoupper($username)), + 'sha_pass_hash' => $antiXss->xss_clean($hashed_pass), + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'expansion' => $antiXss->xss_clean(get_config('expansion')), + 'battlenet_account' => $bnet_account_id, + 'battlenet_index' => 1, + ]); + success_msg(lang('account_created')); + return true; + } + + if(get_config('srp6_version') == 0) { + list($salt, $verifier) = getRegistrationData(strtoupper($_POST['username']), $_POST['password']); + $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($_POST['email'])) . ':' . strtoupper($_POST['password'])))))))); + database::$auth->insert('battlenet_accounts', [ + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'sha_pass_hash' => $antiXss->xss_clean($bnet_hashed_pass), + ]); + + $bnet_account_id = database::$auth->lastInsertId(); + $username = $bnet_account_id . '#1'; + database::$auth->insert('account', [ + 'username' => $antiXss->xss_clean(strtoupper($username)), + get_core_config("salt_field") => $salt, + get_core_config("verifier_field") => $verifier, + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'expansion' => $antiXss->xss_clean(get_config('expansion')), + 'battlenet_account' => $bnet_account_id, + 'battlenet_index' => 1, + ]); + success_msg(lang('account_created')); + return true; + } + + if(get_config('srp6_version') == 1) { + list($salt, $verifier) = getRegistrationDataBnetV1(strtoupper($_POST['email']), $_POST['password']); + database::$auth->insert('battlenet_accounts', [ + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'srp_version' => 1, + get_core_config("salt_field") => $salt, + get_core_config("verifier_field") => $verifier, + ]); + + $bnet_account_id = database::$auth->lastInsertId(); + $game_account_name = $bnet_account_id . '#1'; + list($game_account_salt, $game_account_verifier) = getRegistrationData($game_account_name, $_POST['password']); + database::$auth->insert('account', [ + 'username' => $antiXss->xss_clean($game_account_name), + get_core_config("salt_field") => $game_account_salt, + get_core_config("verifier_field") => $game_account_verifier, + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'expansion' => $antiXss->xss_clean(get_config('expansion')), + 'battlenet_account' => $bnet_account_id, + 'battlenet_index' => 1, + ]); + success_msg(lang('account_created')); + return true; + } + + if(get_config('srp6_version') == 2) { + list($salt, $verifier) = getRegistrationDataBnetV2(strtoupper($_POST['email']), $_POST['password']); + database::$auth->insert('battlenet_accounts', [ + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'srp_version' => 2, + get_core_config("salt_field") => $salt, + get_core_config("verifier_field") => $verifier, + ]); + + $bnet_account_id = database::$auth->lastInsertId(); + $game_account_name = $bnet_account_id . '#1'; + list($game_account_salt, $game_account_verifier) = getRegistrationData($game_account_name, substr($_POST['password'], 0, 16)); + database::$auth->insert('account', [ + 'username' => $antiXss->xss_clean($game_account_name), + get_core_config("salt_field") => $game_account_salt, + get_core_config("verifier_field") => $game_account_verifier, + 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), + 'expansion' => $antiXss->xss_clean(get_config('expansion')), + 'battlenet_account' => $bnet_account_id, + 'battlenet_index' => 1, + ]); + success_msg(lang('account_created')); + return true; + } } - list($salt, $verifier) = getRegistrationData(strtoupper($_POST['username']), $_POST['password']); - $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($_POST['email'])) . ':' . strtoupper($_POST['password'])))))))); - database::$auth->insert('battlenet_accounts', [ - 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), - 'sha_pass_hash' => $antiXss->xss_clean($bnet_hashed_pass), - ]); - - $bnet_account_id = database::$auth->lastInsertId(); - $username = $bnet_account_id . '#1'; - database::$auth->insert('account', [ - 'username' => $antiXss->xss_clean(strtoupper($username)), - get_core_config("salt_field") => $salt, - get_core_config("verifier_field") => $verifier, - 'email' => $antiXss->xss_clean(strtoupper($_POST['email'])), - 'expansion' => $antiXss->xss_clean(get_config('expansion')), - 'battlenet_account' => $bnet_account_id, - 'battlenet_index' => 1, - ]); - success_msg(lang('account_created')); + $command = str_replace('{USERNAME}', $antiXss->xss_clean($_POST['email']), get_config('soap_ca_command')); + $command = str_replace('{PASSWORD}', $antiXss->xss_clean($_POST['password']), $command); + if (RemoteCommandWithSOAP($command)) { + success_msg(lang('account_created')); + } else { + error_msg(lang('error_try_again')); + } return true; } @@ -277,13 +348,27 @@ public static function bnet_changepass() return false; } - if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 16)) { - error_msg(lang('passwords_length')); - return true; + if(get_config('srp6_support') && get_config('srp6_version') == 2) { + if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 128)) { + error_msg(lang('passwords_length')); + return false; + } + } + else { + if (!(strlen($_POST['password']) >= 4 && strlen($_POST['password']) <= 16)) { + error_msg(lang('passwords_length')); + return false; + } } $userinfo = self::get_user_by_email(strtoupper($_POST['email'])); - if (empty($userinfo['username'])) { + if ((empty(get_config('srp6_support')) && empty($userinfo['username'])) || (!empty(get_config('srp6_support')) && (get_config('srp6_version') == 0) && empty($userinfo['username']))) { + error_msg(lang('email_not_correct')); + return false; + } + + $bnetAccountInfo = self::get_bnetaccount_by_email(strtoupper($_POST['email'])); + if (empty($bnetAccountInfo['email']) && !empty(get_config('srp6_support')) && (get_config('srp6_version') > 0)) { error_msg(lang('email_not_correct')); return false; } @@ -307,34 +392,103 @@ public static function bnet_changepass() ->setParameter('sha_pass_hash', $antiXss->xss_clean($hashed_pass)) ->setParameter('id', $userinfo['id']); $queryBuilder->executeQuery(); - } else { - if (!verifySRP6($userinfo['username'], $_POST['old_password'], $userinfo[get_core_config("salt_field")], $userinfo[get_core_config("verifier_field")])) { - error_msg(lang('old_password_not_valid')); - return false; - } - list($salt, $verifier) = getRegistrationData(strtoupper($userinfo['username']), $_POST['password']); + $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($userinfo['email'])) . ':' . strtoupper($_POST['password'])))))))); $queryBuilder = database::$auth->createQueryBuilder(); - $queryBuilder->update('account') - ->set(get_core_config("salt_field"), ':salt') - ->set(get_core_config("verifier_field"), ':verifier') + $queryBuilder->update('battlenet_accounts') + ->set('sha_pass_hash', ':sha_pass_hash') + ->set('sessionkey', '') + ->set('v', '') + ->set('s', '') ->where('id = :id') - ->setParameter('salt', $salt) - ->setParameter('verifier', $verifier) - ->setParameter('id', $userinfo['id']); + ->setParameter('sha_pass_hash', $antiXss->xss_clean($bnet_hashed_pass)) + ->setParameter('id', $userinfo['battlenet_account']); $queryBuilder->executeQuery(); - } - - $bnet_hashed_pass = strtoupper(bin2hex(strrev(hex2bin(strtoupper(hash('sha256', strtoupper(hash('sha256', strtoupper($userinfo['email'])) . ':' . strtoupper($_POST['password'])))))))); + } else { + if (get_config('srp6_version') == 0) { + if (!verifySRP6($userinfo['username'], $_POST['old_password'], $userinfo[get_core_config("salt_field")], $userinfo[get_core_config("verifier_field")])) { + error_msg(lang('old_password_not_valid')); + return false; + } + + list($salt, $verifier) = getRegistrationData(strtoupper($userinfo['username']), $_POST['password']); + + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->update('account') + ->set(get_core_config("salt_field"), ':salt') + ->set(get_core_config("verifier_field"), ':verifier') + ->where('id = :id') + ->setParameter('salt', $salt) + ->setParameter('verifier', $verifier) + ->setParameter('id', $userinfo['id']); + $queryBuilder->executeQuery(); + } + if (get_config('srp6_version') == 1) { + if (!verifySRP6BnetV1($bnetAccountInfo['email'], $_POST['old_password'], $bnetAccountInfo[get_core_config("salt_field")], $bnetAccountInfo[get_core_config("verifier_field")])) { + error_msg(lang('old_password_not_valid')); + return false; + } - $queryBuilder = database::$auth->createQueryBuilder(); - $queryBuilder->update('battlenet_accounts') - ->set('sha_pass_hash', ':sha_pass_hash') - ->where('id = :id') - ->setParameter('sha_pass_hash', $antiXss->xss_clean($bnet_hashed_pass)) - ->setParameter('id', $userinfo['battlenet_account']); - $queryBuilder->executeQuery(); + $game_account_name = $bnetAccountInfo['id'] . '#1'; + list($salt, $verifier) = getRegistrationData($game_account_name, substr($_POST['password'], 0, 16)); + + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->update('account') + ->set(get_core_config("salt_field"), ':salt') + ->set(get_core_config("verifier_field"), ':verifier') + ->where('email = :email') + ->setParameter('salt', $salt) + ->setParameter('verifier', $verifier) + ->setParameter('email', $bnetAccountInfo['email']); + $queryBuilder->executeQuery(); + + list($salt, $verifier) = getRegistrationDataBnetV1($bnetAccountInfo['email'], $_POST['password']); + + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->update('battlenet_accounts') + ->set('srp_version', 1) + ->set(get_core_config("salt_field"), ':salt') + ->set(get_core_config("verifier_field"), ':verifier') + ->where('id = :id') + ->setParameter('salt', $salt) + ->setParameter('verifier', $verifier) + ->setParameter('id', $bnetAccountInfo['id']); + $queryBuilder->executeQuery(); + } + if (get_config('srp6_version') == 2) { + if (!verifySRP6BnetV2($bnetAccountInfo['email'], $_POST['old_password'], $bnetAccountInfo[get_core_config("salt_field")], $bnetAccountInfo[get_core_config("verifier_field")])) { + error_msg($bnetAccountInfo[get_core_config("salt_field")]); + return false; + } + + $game_account_name = $bnetAccountInfo['id'] . '#1'; + list($salt, $verifier) = getRegistrationData($game_account_name, substr($_POST['password'], 0, 16)); + + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->update('account') + ->set(get_core_config("salt_field"), ':salt') + ->set(get_core_config("verifier_field"), ':verifier') + ->where('email = :email') + ->setParameter('salt', $salt) + ->setParameter('verifier', $verifier) + ->setParameter('email', $bnetAccountInfo['email']); + $queryBuilder->executeQuery(); + + list($salt, $verifier) = getRegistrationDataBnetV2($bnetAccountInfo['email'], $_POST['password']); + + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->update('battlenet_accounts') + ->set('srp_version', 2) + ->set(get_core_config("salt_field"), ':salt') + ->set(get_core_config("verifier_field"), ':verifier') + ->where('id = :id') + ->setParameter('salt', $salt) + ->setParameter('verifier', $verifier) + ->setParameter('id', $bnetAccountInfo['id']); + $queryBuilder->executeQuery(); + } + } success_msg(lang('password_changed')); return true; @@ -656,6 +810,25 @@ public static function get_user_by_email($email) return false; } + public static function get_bnetaccount_by_email($email) + { + if (!empty($email)) { + $queryBuilder = database::$auth->createQueryBuilder(); + $queryBuilder->select('*') + ->from('battlenet_accounts') + ->where('email = :email') + ->setParameter('email', strtoupper($email)); + + $statement = $queryBuilder->executeQuery(); + $datas = $statement->fetchAllAssociative(); + + if (!empty($datas[0]['email'])) { + return $datas[0]; + } + } + return false; + } + public static function get_user_by_username($username) { if (!empty($username)) {