diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 8358f1bed..2a6968fe2 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -1250,6 +1250,7 @@ function set_tfa($_data) { } function fido2($_data) { global $pdo; + global $WebAuthn; $_data_log = $_data; // Not logging registration data, only actions // Silent errors for "get" requests @@ -1383,6 +1384,81 @@ function fido2($_data) { 'msg' => array('object_modified', htmlspecialchars($username)) ); 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) { diff --git a/data/web/inc/triggers.admin.inc.php b/data/web/inc/triggers.admin.inc.php index 5b1061f7e..883d9fd5c 100644 --- a/data/web/inc/triggers.admin.inc.php +++ b/data/web/inc/triggers.admin.inc.php @@ -18,6 +18,14 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_role']); 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"])) { unset($_SESSION['pending_pw_reset_token']); diff --git a/data/web/inc/triggers.domainadmin.inc.php b/data/web/inc/triggers.domainadmin.inc.php index 9ee53d67a..dd1c653bd 100644 --- a/data/web/inc/triggers.domainadmin.inc.php +++ b/data/web/inc/triggers.domainadmin.inc.php @@ -29,6 +29,14 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_role']); 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"])) { unset($_SESSION['pending_pw_reset_token']); diff --git a/data/web/inc/triggers.user.inc.php b/data/web/inc/triggers.user.inc.php index c16edc10e..64282b075 100644 --- a/data/web/inc/triggers.user.inc.php +++ b/data/web/inc/triggers.user.inc.php @@ -83,6 +83,14 @@ if (isset($_POST["verify_tfa_login"])) { unset($_SESSION['pending_mailcow_cc_role']); 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"])) { unset($_SESSION['pending_pw_reset_token']); diff --git a/data/web/json_api.php b/data/web/json_api.php index 14e7c47d3..f2a222b85 100644 --- a/data/web/json_api.php +++ b/data/web/json_api.php @@ -334,81 +334,6 @@ if (isset($_GET['query'])) { exit(); } 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": function process_get_return($data, $object = true) { if ($object === true) { diff --git a/data/web/templates/base.twig b/data/web/templates/base.twig index 0ef5f668e..8ec8e4159 100644 --- a/data/web/templates/base.twig +++ b/data/web/templates/base.twig @@ -391,16 +391,14 @@ function recursiveBase64StrToArrayBuffer(obj) { signature : cred.response.signature ? arrayBufferToBase64(cred.response.signature) : null }; }).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) { - return response.json(); - }).then(function(json) { - if (json.success) { - window.location = window.location.href.split("#")[0]; - } else { - throw new Error(); - } - }).catch(function(err) { + window.location = window.location.href.split("#")[0]; + }).catch(function(err) { if (typeof err.message === 'undefined') { mailcow_alert_box(lang_fido2.fido2_validation_failed, "danger"); } else {