1
0
Fork 0
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
4.9 KiB

<?php
$APC_PREFIX = 'CLIENT_';
$BAN_TTL = 10 * 60;
$SESS_TTL = 60 * 60;
$MAX_TRIES = 6;
$client = $APC_PREFIX . $_SERVER['REMOTE_ADDR'];
$tries = apc_fetch($client);
$realmMessage = "Resitricted Area";
require_once __DIR__ .'/ga.php';
session_start();
$_SESSION['start'] = microtime(true);
openlog("php_http_auth", 0, LOG_LOCAL0);
if (!$tries) {
$tries = 1;
}
if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
//syslog(LOG_ERR, "Got auth");
$basicStr = str_replace('Basic ', '', $_SERVER['HTTP_AUTHORIZATION']);
$basic = explode(':', base64_decode($basicStr));
$login = array_shift($basic);
$password = array_shift($basic);
$permit_auth = false;
$totpToken = false;
$sessionKey = APC_PREFIX . $basicStr;
$invalidKey = $sessionKey .'_invalid';
if (apc_fetch($sessionKey) !== false) {
$permit_auth = true;
} else if ($tries <= $MAX_TRIES) {
syslog(LOG_INFO, 'Client ' . $_SERVER['REMOTE_ADDR'] . ' has done: ' . $tries . ' tries');
$uid = str_replace('_', '@', $login);
$tokenPos = strpos($uid, '|', 1);
if ($tokenPos !== false) {
$oldUid = $uid;
$totpToken = substr($uid, ($tokenPos + 1));
$uid = substr($uid, 0, $tokenPos);
}
if (!strpos($uid, '@')) {
//return false;
$host = explode('.', $_SERVER['SERVER_NAME']);
if (count($host) > 1) {
$tld = array_pop($host);
$dom = array_pop($host);
$uid = $uid.'@'.$dom.'.'.$tld;
}
}
$clientKey = $client . $uid;
$totpKey = apc_fetch($clientKey);
$username = substr($uid, 0, strpos($uid, '@'));
$domain = substr($uid, strpos($uid, '@')+1);
$file_db = new PDO('sqlite:/var/run/www-data/dovecot.sqlite');
$file_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$query = $file_db->prepare( "SELECT username || '@' || domain AS uid, password, http_totp FROM mailbox WHERE username = ? AND domain = ? LIMIT 1" );
$query->execute( array( $username, $domain));
$row = $query->fetch(PDO::FETCH_ASSOC);
if($row) {
syslog(LOG_ERR, "Found row for :" . $username .' :: '. $totpToken);
$storedHash = $row['password'];
if (strpos($storedHash, '}') > 0) {
$storedHash = substr($storedHash, strpos($storedHash, '}') +1);
}
$hashMethod = substr($storedHash, 0, 3);
$hashSalt = substr($storedHash, ($ll = strlen($hashMethod)), strpos($storedHash, '$', $ll) - $ll);
$inputHash = crypt($password, $hashMethod);// . $hashSalt . '$');
if (crypt($password, $storedHash) === $storedHash) {
syslog(LOG_ERR, "Found valid user!");
if (!isset($_SERVER['HTTP_AUTH_PERMIT_USERS'])) {
$permit_auth = true;
} else {
$permited_users = explode(',', preg_replace('/,\s/', '', $_SERVER['HTTP_AUTH_PERMIT_USERS']));
if (in_array($uid, $permited_users) || in_array($username, $permited_users)) {
$permit_auth = true;
}
}
}
$previousToken = apc_fetch($invalidKey);
if ($permit_auth === true && $row['http_totp'] != null && strlen($row['http_totp']) === 16
&& ( $previousToken === false || $previousToken !== $totpToken ) ) {
$permit_auth = false;
$TimeStamp = Google2FA::get_timestamp();
$secretkey = Google2FA::base32_decode($row['http_totp']); // Decode it into binary
$otp = Google2FA::oath_hotp($secretkey, $TimeStamp); // Get current token
if (Google2FA::verify_key($row['http_totp'], $totpToken, 1) === true) {
syslog(LOG_INFO, 'Client ' . $_SERVER['REMOTE_ADDR'] . ' has passed two factor auth.');
$permit_auth = true;
apc_store($invalidKey, $totpToken, $SESS_TTL);
} else {
syslog(LOG_INFO, 'Client ' . $_SERVER['REMOTE_ADDR'] . ' has FAILED two factor auth.');
}
}
if ($previousToken !== false && $previousToken === $totpToken) {
syslog(LOG_WARNING, 'POSSIBLE REPLAY ATTACK FROM HOST: '. $_SERVER['REMOTE_ADDR']);
$tries = $MAX_TRIES+1;
}
}
}
if ($permit_auth === true) {
header('Content-Type: text/html; charset=utf-8', true);
header('Content-Disposition: ""', true);
header('Status: 200 OK', true);
apc_store($sessionKey, $basicStr, $SESS_TTL);
echo "\n\r"; // LEAVE ME HERE!
exit(0);
} else if ($permit_auth === false && $tries <= $MAX_TRIES) {
syslog(LOG_ERR, 'HTTP Auth FAILED for ' . $uid . ' from ' . $_SERVER['REMOTE_ADDR']);
apc_store($client, ++$tries, $BAN_TTL);
sleep(1*$tries); // Delay them losers!
}
}
if ($tries > $MAX_TRIES) {
syslog(LOG_ERR, 'HTTP Auth FAILED for ' . $uid . ' from ' . $_SERVER['REMOTE_ADDR']);
if ($tries == ($MAX_TRIES + 1)) {
syslog(LOG_ERR, 'HTTP Auth BANNED for ' . $uid . ' from ' . $_SERVER['REMOTE_ADDR']);
}
apc_store($client, ++$tries, $BAN_TTL);
sleep(1*$tries);
}
syslog(LOG_INFO, $realmMessage);
header('Content-Type: text/html; charset=utf-8', true);
header('Content-Disposition: ""', true);
header('WWW-Authenticate: Basic realm='. $realmMessage, true);
header('Status: 401 Unauthorized', true);
echo "\n\r"; // LEAVE ME HERE!
exit(0);