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
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);
|