public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Web] Separate FIDO2 logins
bc21e7fe
data/web/inc/functions.inc.php | 76 +++++++++++++++++++++++++++++++
data/web/inc/triggers.admin.inc.php | 8 ++++
data/web/inc/triggers.domainadmin.inc.php | 8 ++++
data/web/inc/triggers.user.inc.php | 8 ++++
data/web/json_api.php | 75 ------------------------------
data/web/templates/base.twig | 16 +++----
6 files changed, 107 insertions(+), 84 deletions(-)
Diff
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 8358f1be..2a6968fe 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 5b1061f7..883d9fd5 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 9ee53d67..dd1c653b 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 c16edc10..64282b07 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 14e7c47d..f2a222b8 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 0ef5f668..8ec8e415 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 {