mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2025-12-20 13:21:30 +00:00
[Web] Separate FIDO2 logins
This commit is contained in:
@@ -1250,6 +1250,7 @@ function set_tfa($_data) {
|
|||||||
}
|
}
|
||||||
function fido2($_data) {
|
function fido2($_data) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
|
global $WebAuthn;
|
||||||
$_data_log = $_data;
|
$_data_log = $_data;
|
||||||
// Not logging registration data, only actions
|
// Not logging registration data, only actions
|
||||||
// Silent errors for "get" requests
|
// Silent errors for "get" requests
|
||||||
@@ -1383,6 +1384,81 @@ function fido2($_data) {
|
|||||||
'msg' => array('object_modified', htmlspecialchars($username))
|
'msg' => array('object_modified', htmlspecialchars($username))
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "verify":
|
||||||
|
$tokenData = json_decode($_data['token']);
|
||||||
|
$clientDataJSON = base64_decode($tokenData->clientDataJSON);
|
||||||
|
$authenticatorData = base64_decode($tokenData->authenticatorData);
|
||||||
|
$signature = base64_decode($tokenData->signature);
|
||||||
|
$id = base64_decode($tokenData->id);
|
||||||
|
$challenge = $_SESSION['challenge'];
|
||||||
|
$process_fido2 = fido2(array("action" => "get_by_b64cid", "cid" => $tokenData->id));
|
||||||
|
if ($process_fido2['pub_key'] === false) {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array("fido2_login", $_data['user'], $process_fido2['username']),
|
||||||
|
'msg' => "login_failed"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_fido2['pub_key'], $challenge, null, $GLOBALS['FIDO2_UV_FLAG_LOGIN'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
|
||||||
|
}
|
||||||
|
catch (Throwable $ex) {
|
||||||
|
unset($process_fido2);
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array("fido2_login", $_data['user'], $process_fido2['username'], $ex->getMessage()),
|
||||||
|
'msg' => "login_failed"
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$return = new stdClass();
|
||||||
|
$return->success = true;
|
||||||
|
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $process_fido2['username']));
|
||||||
|
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($obj_props['superadmin'] === 1 && (!$_data['user'] || $_data['user'] == "admin")) {
|
||||||
|
$_SESSION["mailcow_cc_role"] = "admin";
|
||||||
|
}
|
||||||
|
elseif ($obj_props['superadmin'] === 0 && (!$_data['user'] || $_data['user'] == "domainadmin")) {
|
||||||
|
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
||||||
|
}
|
||||||
|
elseif (!isset($obj_props['superadmin']) && (!$_data['user'] || $_data['user'] == "user")) {
|
||||||
|
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
||||||
|
$stmt->execute(array(':username' => $process_fido2['username']));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row['username'] == $process_fido2['username']) {
|
||||||
|
$_SESSION["mailcow_cc_role"] = "user";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array("fido2_login", $_data['user'], $process_fido2['username']),
|
||||||
|
'msg' => 'login_failed'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (empty($_SESSION["mailcow_cc_role"])) {
|
||||||
|
session_unset();
|
||||||
|
session_destroy();
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'log' => array("fido2_login", $_data['user'], $process_fido2['username']),
|
||||||
|
'msg' => 'login_failed'
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$_SESSION["mailcow_cc_username"] = $process_fido2['username'];
|
||||||
|
$_SESSION["fido2_cid"] = $process_fido2['cid'];
|
||||||
|
unset($_SESSION["challenge"]);
|
||||||
|
$_SESSION['return'][] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'log' => array("fido2_login", $_data['user'], $process_fido2['username']),
|
||||||
|
'msg' => array('logged_in_as', $process_fido2['username'])
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function unset_tfa_key($_data) {
|
function unset_tfa_key($_data) {
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ if (isset($_POST["verify_tfa_login"])) {
|
|||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_methods']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
}
|
}
|
||||||
|
if (isset($_POST["verify_fido2_login"])) {
|
||||||
|
fido2(array(
|
||||||
|
"action" => "verify",
|
||||||
|
"token" => $_POST["token"],
|
||||||
|
"user" => "admin"
|
||||||
|
));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_GET["cancel_tfa_login"])) {
|
if (isset($_GET["cancel_tfa_login"])) {
|
||||||
unset($_SESSION['pending_pw_reset_token']);
|
unset($_SESSION['pending_pw_reset_token']);
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ if (isset($_POST["verify_tfa_login"])) {
|
|||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_methods']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
}
|
}
|
||||||
|
if (isset($_POST["verify_fido2_login"])) {
|
||||||
|
fido2(array(
|
||||||
|
"action" => "verify",
|
||||||
|
"token" => $_POST["token"],
|
||||||
|
"user" => "domainadmin"
|
||||||
|
));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_GET["cancel_tfa_login"])) {
|
if (isset($_GET["cancel_tfa_login"])) {
|
||||||
unset($_SESSION['pending_pw_reset_token']);
|
unset($_SESSION['pending_pw_reset_token']);
|
||||||
|
|||||||
@@ -83,6 +83,14 @@ if (isset($_POST["verify_tfa_login"])) {
|
|||||||
unset($_SESSION['pending_mailcow_cc_role']);
|
unset($_SESSION['pending_mailcow_cc_role']);
|
||||||
unset($_SESSION['pending_tfa_methods']);
|
unset($_SESSION['pending_tfa_methods']);
|
||||||
}
|
}
|
||||||
|
if (isset($_POST["verify_fido2_login"])) {
|
||||||
|
fido2(array(
|
||||||
|
"action" => "verify",
|
||||||
|
"token" => $_POST["token"],
|
||||||
|
"user" => "user"
|
||||||
|
));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($_GET["cancel_tfa_login"])) {
|
if (isset($_GET["cancel_tfa_login"])) {
|
||||||
unset($_SESSION['pending_pw_reset_token']);
|
unset($_SESSION['pending_pw_reset_token']);
|
||||||
|
|||||||
@@ -334,81 +334,6 @@ if (isset($_GET['query'])) {
|
|||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "process":
|
|
||||||
// only allow POST requests to process API endpoints
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
|
|
||||||
http_response_code(405);
|
|
||||||
echo json_encode(array(
|
|
||||||
'type' => 'error',
|
|
||||||
'msg' => 'only POST method is allowed'
|
|
||||||
));
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
switch ($category) {
|
|
||||||
case "fido2-args":
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
$post = trim(file_get_contents('php://input'));
|
|
||||||
if ($post) {
|
|
||||||
$post = json_decode($post);
|
|
||||||
}
|
|
||||||
$clientDataJSON = base64_decode($post->clientDataJSON);
|
|
||||||
$authenticatorData = base64_decode($post->authenticatorData);
|
|
||||||
$signature = base64_decode($post->signature);
|
|
||||||
$id = base64_decode($post->id);
|
|
||||||
$challenge = $_SESSION['challenge'];
|
|
||||||
$process_fido2 = fido2(array("action" => "get_by_b64cid", "cid" => $post->id));
|
|
||||||
if ($process_fido2['pub_key'] === false) {
|
|
||||||
$return = new stdClass();
|
|
||||||
$return->success = false;
|
|
||||||
echo json_encode($return);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $process_fido2['pub_key'], $challenge, null, $GLOBALS['FIDO2_UV_FLAG_LOGIN'], $GLOBALS['FIDO2_USER_PRESENT_FLAG']);
|
|
||||||
}
|
|
||||||
catch (Throwable $ex) {
|
|
||||||
unset($process_fido2);
|
|
||||||
$return = new stdClass();
|
|
||||||
$return->success = false;
|
|
||||||
echo json_encode($return);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$return = new stdClass();
|
|
||||||
$return->success = true;
|
|
||||||
$stmt = $pdo->prepare("SELECT `superadmin` FROM `admin` WHERE `username` = :username");
|
|
||||||
$stmt->execute(array(':username' => $process_fido2['username']));
|
|
||||||
$obj_props = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
if ($obj_props['superadmin'] === 1) {
|
|
||||||
$_SESSION["mailcow_cc_role"] = "admin";
|
|
||||||
}
|
|
||||||
elseif ($obj_props['superadmin'] === 0) {
|
|
||||||
$_SESSION["mailcow_cc_role"] = "domainadmin";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
|
|
||||||
$stmt->execute(array(':username' => $process_fido2['username']));
|
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
if ($row['username'] == $process_fido2['username']) {
|
|
||||||
$_SESSION["mailcow_cc_role"] = "user";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (empty($_SESSION["mailcow_cc_role"])) {
|
|
||||||
session_unset();
|
|
||||||
session_destroy();
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$_SESSION["mailcow_cc_username"] = $process_fido2['username'];
|
|
||||||
$_SESSION["fido2_cid"] = $process_fido2['cid'];
|
|
||||||
unset($_SESSION["challenge"]);
|
|
||||||
$_SESSION['return'][] = array(
|
|
||||||
'type' => 'success',
|
|
||||||
'log' => array("fido2_login"),
|
|
||||||
'msg' => array('logged_in_as', $process_fido2['username'])
|
|
||||||
);
|
|
||||||
echo json_encode($return);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "get":
|
case "get":
|
||||||
function process_get_return($data, $object = true) {
|
function process_get_return($data, $object = true) {
|
||||||
if ($object === true) {
|
if ($object === true) {
|
||||||
|
|||||||
@@ -391,16 +391,14 @@ function recursiveBase64StrToArrayBuffer(obj) {
|
|||||||
signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
|
signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null
|
||||||
};
|
};
|
||||||
}).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
|
}).then(JSON.stringify).then(function(AuthenticatorAttestationResponse) {
|
||||||
return window.fetch("/api/v1/process/fido2-args", {method:'POST', body: AuthenticatorAttestationResponse, cache:'no-cache'});
|
var formData = new FormData();
|
||||||
|
formData.append("token", AuthenticatorAttestationResponse);
|
||||||
|
formData.append("verify_fido2_login", "true");
|
||||||
|
|
||||||
|
return window.fetch(window.location.href, {method:'POST', body: formData, cache:'no-cache'});
|
||||||
}).then(function(response) {
|
}).then(function(response) {
|
||||||
return response.json();
|
window.location = window.location.href.split("#")[0];
|
||||||
}).then(function(json) {
|
}).catch(function(err) {
|
||||||
if (json.success) {
|
|
||||||
window.location = window.location.href.split("#")[0];
|
|
||||||
} else {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}).catch(function(err) {
|
|
||||||
if (typeof err.message === 'undefined') {
|
if (typeof err.message === 'undefined') {
|
||||||
mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger");
|
mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user