Multiple TFA keys
parent
c73cc42a95
commit
622a8872e7
|
@ -4,6 +4,7 @@ require_once("inc/prerequisites.inc.php");
|
||||||
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
|
||||||
require_once("inc/header.inc.php");
|
require_once("inc/header.inc.php");
|
||||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
|
$tfa_data = get_tfa();
|
||||||
?>
|
?>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h4><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['admin']['access'];?></h4>
|
<h4><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?=$lang['admin']['access'];?></h4>
|
||||||
|
@ -43,12 +44,23 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div>
|
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?>:</div>
|
||||||
<div class="col-sm-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<p><?=get_tfa()['pretty'];?></p>
|
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
||||||
|
<div id="tfa_additional">
|
||||||
|
<?php if($tfa_data['additional']):
|
||||||
|
foreach ($tfa_data['additional'] as $key_info): ?>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
|
||||||
|
<div class="label label-default">🔑 <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
|
||||||
|
</form>
|
||||||
|
<?php endforeach;
|
||||||
|
endif;?>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?>:</div>
|
<div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?>:</div>
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<select data-width="auto" id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
<select data-width="auto" id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
||||||
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
||||||
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
||||||
|
|
|
@ -114,7 +114,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-offset-2 col-sm-10">
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label><input type="checkbox" name="delete_tfa"> <?=$lang['tfa']['delete_tfa'];?></label>
|
<label><input type="checkbox" name="disable_tfa"> <?=$lang['tfa']['disable_tfa'];?></label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -74,6 +74,7 @@ $(document).ready(function() {
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
// Set TFA modals
|
// Set TFA modals
|
||||||
|
|
||||||
$('#selectTFA').change(function () {
|
$('#selectTFA').change(function () {
|
||||||
if ($(this).val() == "yubi_otp") {
|
if ($(this).val() == "yubi_otp") {
|
||||||
$('#YubiOTPModal').modal('show');
|
$('#YubiOTPModal').modal('show');
|
||||||
|
|
|
@ -63,6 +63,7 @@ function hasMailboxObjectAccess($username, $role, $object) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function init_db_schema() {
|
function init_db_schema() {
|
||||||
|
// This will be much better in future releases...
|
||||||
global $pdo;
|
global $pdo;
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("SELECT NULL FROM `admin`, `imapsync`, `tfa`");
|
$stmt = $pdo->prepare("SELECT NULL FROM `admin`, `imapsync`, `tfa`");
|
||||||
|
@ -101,7 +102,7 @@ function init_db_schema() {
|
||||||
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'");
|
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'kind'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
if ($num_results == 0) {
|
if ($num_results == 0) {
|
||||||
$pdo->query("ALTER TABLE `mailbox` ADD `kind` varchar(100) NOT NULL DEFAULT ''");
|
$pdo->query("ALTER TABLE `mailbox` ADD `kind` VARCHAR(100) NOT NULL DEFAULT ''");
|
||||||
}
|
}
|
||||||
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'multiple_bookings'");
|
$stmt = $pdo->query("SHOW COLUMNS FROM `mailbox` LIKE 'multiple_bookings'");
|
||||||
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
@ -113,6 +114,11 @@ function init_db_schema() {
|
||||||
if ($num_results == 0) {
|
if ($num_results == 0) {
|
||||||
$pdo->query("ALTER TABLE `mailbox` ADD `wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0'");
|
$pdo->query("ALTER TABLE `mailbox` ADD `wants_tagged_subject` tinyint(1) NOT NULL DEFAULT '0'");
|
||||||
}
|
}
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM `tfa` LIKE 'key_id'");
|
||||||
|
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
|
||||||
|
if ($num_results == 0) {
|
||||||
|
$pdo->query("ALTER TABLE `tfa` ADD `key_id` VARCHAR(255) DEFAULT 'unidentified'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function verify_ssha256($hash, $password) {
|
function verify_ssha256($hash, $password) {
|
||||||
// Remove tag if any
|
// Remove tag if any
|
||||||
|
@ -198,6 +204,8 @@ function check_login($user, $pass) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
unset($_SESSION['ldelay']);
|
unset($_SESSION['ldelay']);
|
||||||
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user");
|
||||||
|
$stmt->execute(array(':user' => $user));
|
||||||
return "domainadmin";
|
return "domainadmin";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1806,6 +1814,10 @@ function set_tfa($postarray) {
|
||||||
|
|
||||||
switch ($postarray["tfa_method"]) {
|
switch ($postarray["tfa_method"]) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
|
(!isset($postarray["key_id"])) ? $key_id = 'unidentified' : $key_id = $postarray["key_id"];
|
||||||
|
$yubico_id = $postarray['yubico_id'];
|
||||||
|
$yubico_key = $postarray['yubico_key'];
|
||||||
|
$yubi = new Auth_Yubico($yubico_id, $yubico_key);
|
||||||
if (!$yubi) {
|
if (!$yubi) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
|
@ -1824,16 +1836,21 @@ function set_tfa($postarray) {
|
||||||
if (PEAR::isError($yauth)) {
|
if (PEAR::isError($yauth)) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'danger',
|
'type' => 'danger',
|
||||||
'msg' => 'Yubico Authentication error: ' . $yauth->getMessage()
|
'msg' => 'Yubico API: ' . $yauth->getMessage()
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
|
// We could also do a modhex translation here
|
||||||
$stmt->execute(array(':username' => $username));
|
$yubico_modhex_id = substr($postarray["otp_token"], 0, 12);
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `authmech`, `active`) VALUES
|
$stmt = $pdo->prepare("DELETE FROM `tfa`
|
||||||
(:username, 'yubi_otp', 1)");
|
WHERE `username` = :username
|
||||||
$stmt->execute(array(':username' => $username));
|
AND (`authmech` != 'yubi_otp')
|
||||||
|
OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
|
||||||
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
|
||||||
|
(:key_id, :username, 'yubi_otp', '1', :secret)");
|
||||||
|
$stmt->execute(array(':key_id' => $key_id, ':username' => $username, ':secret' => $yubico_id . ':' . $yubico_key . ':' . $yubico_modhex_id));
|
||||||
}
|
}
|
||||||
catch (PDOException $e) {
|
catch (PDOException $e) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -1850,9 +1867,12 @@ function set_tfa($postarray) {
|
||||||
|
|
||||||
case "u2f":
|
case "u2f":
|
||||||
try {
|
try {
|
||||||
|
(!isset($postarray["key_id"])) ? $key_id = 'unidentified' : $key_id = $postarray["key_id"];
|
||||||
$reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($postarray['token']));
|
$reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($postarray['token']));
|
||||||
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`) VALUES (?, 'u2f', ?, ?, ?, ?)");
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'u2f'");
|
||||||
$stmt->execute(array($username, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'u2f', ?, ?, ?, ?, '1')");
|
||||||
|
$stmt->execute(array($username, $key_id, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
'type' => 'success',
|
'type' => 'success',
|
||||||
'msg' => sprintf($lang['success']['object_modified'], $username)
|
'msg' => sprintf($lang['success']['object_modified'], $username)
|
||||||
|
@ -1887,6 +1907,55 @@ function set_tfa($postarray) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function unset_tfa_key($postarray) {
|
||||||
|
// Can only unset own keys
|
||||||
|
// Needs at least one key left
|
||||||
|
global $pdo;
|
||||||
|
global $lang;
|
||||||
|
$id = intval($postarray['id']);
|
||||||
|
if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
|
||||||
|
$_SESSION['mailcow_cc_role'] != "admin") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
|
try {
|
||||||
|
if (!is_numeric($id)) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['access_denied'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
|
||||||
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
|
$stmt->execute(array(':username' => $username));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
if ($row['keys'] == "1") {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => sprintf($lang['danger']['last_key'])
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
|
||||||
|
$stmt->execute(array(':username' => $username, ':id' => $id));
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'success',
|
||||||
|
'msg' => sprintf($lang['success']['object_modified'], $username)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (PDOException $e) {
|
||||||
|
$_SESSION['return'] = array(
|
||||||
|
'type' => 'danger',
|
||||||
|
'msg' => 'MySQL: '.$e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
function get_tfa($username = null) {
|
function get_tfa($username = null) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
if (isset($_SESSION['mailcow_cc_username'])) {
|
if (isset($_SESSION['mailcow_cc_username'])) {
|
||||||
|
@ -1896,8 +1965,8 @@ function get_tfa($username = null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT * FROM `tfa`
|
||||||
WHERE `username` = :username");
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
@ -1905,11 +1974,27 @@ function get_tfa($username = null) {
|
||||||
case "yubi_otp":
|
case "yubi_otp":
|
||||||
$data['name'] = "yubi_otp";
|
$data['name'] = "yubi_otp";
|
||||||
$data['pretty'] = "Yubico OTP";
|
$data['pretty'] = "Yubico OTP";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
return $data;
|
return $data;
|
||||||
break;
|
break;
|
||||||
case "u2f":
|
case "u2f":
|
||||||
$data['name'] = "u2f";
|
$data['name'] = "u2f";
|
||||||
$data['pretty'] = "Fido U2F";
|
$data['pretty'] = "Fido U2F";
|
||||||
|
$stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
|
||||||
|
$stmt->execute(array(
|
||||||
|
':username' => $username,
|
||||||
|
));
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
while($row = array_shift($rows)) {
|
||||||
|
$data['additional'][] = $row;
|
||||||
|
}
|
||||||
return $data;
|
return $data;
|
||||||
break;
|
break;
|
||||||
case "hotp":
|
case "hotp":
|
||||||
|
@ -1935,7 +2020,7 @@ function verify_tfa_login($username, $token) {
|
||||||
global $yubi;
|
global $yubi;
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
$stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
|
||||||
WHERE `username` = :username");
|
WHERE `username` = :username AND `active` = '1'");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
@ -1944,6 +2029,16 @@ function verify_tfa_login($username, $token) {
|
||||||
if (!ctype_alnum($token) || strlen($token) != 44) {
|
if (!ctype_alnum($token) || strlen($token) != 44) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$yubico_modhex_id = substr($token, 0, 12);
|
||||||
|
$stmt = $pdo->prepare("SELECT `secret` FROM `tfa`
|
||||||
|
WHERE `username` = :username
|
||||||
|
AND `authmech` = 'yubi_otp'
|
||||||
|
AND `active`='1'
|
||||||
|
AND `secret` LIKE :modhex");
|
||||||
|
$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
|
||||||
|
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
$yubico_auth = explode(':', $row['secret']);
|
||||||
|
$yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
|
||||||
$yauth = $yubi->verify($token);
|
$yauth = $yubi->verify($token);
|
||||||
if (PEAR::isError($yauth)) {
|
if (PEAR::isError($yauth)) {
|
||||||
$_SESSION['return'] = array(
|
$_SESSION['return'] = array(
|
||||||
|
@ -2089,8 +2184,8 @@ function edit_domain_admin($postarray) {
|
||||||
':modified' => date('Y-m-d H:i:s'),
|
':modified' => date('Y-m-d H:i:s'),
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
if (isset($postarray['delete_tfa'])) {
|
if (isset($postarray['disable_tfa'])) {
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username_now));
|
$stmt->execute(array(':username' => $username_now));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2115,8 +2210,8 @@ function edit_domain_admin($postarray) {
|
||||||
':modified' => date('Y-m-d H:i:s'),
|
':modified' => date('Y-m-d H:i:s'),
|
||||||
':active' => $active
|
':active' => $active
|
||||||
));
|
));
|
||||||
if (isset($postarray['delete_tfa'])) {
|
if (isset($postarray['disable_tfa'])) {
|
||||||
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
|
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
|
||||||
$stmt->execute(array(':username' => $username));
|
$stmt->execute(array(':username' => $username));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -4818,23 +4913,8 @@ function mailbox_get_sender_acl_handles($mailbox) {
|
||||||
}
|
}
|
||||||
function get_u2f_registrations($username) {
|
function get_u2f_registrations($username) {
|
||||||
global $pdo;
|
global $pdo;
|
||||||
$sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `username` = ?");
|
$sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'");
|
||||||
$sel->execute(array($username));
|
$sel->execute(array($username));
|
||||||
return $sel->fetchAll(PDO::FETCH_OBJ);
|
return $sel->fetchAll(PDO::FETCH_OBJ);
|
||||||
}
|
}
|
||||||
function add_u2f_registration($username, $reg) {
|
|
||||||
global $pdo;
|
|
||||||
global $lang;
|
|
||||||
$ins = $pdo->prepare("INSERT INTO `tfa` (`username`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`) VALUES (?, 'u2f', ?, ?, ?, ?)");
|
|
||||||
$ins->execute(array($username, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
|
|
||||||
$_SESSION['return'] = array(
|
|
||||||
'type' => 'success',
|
|
||||||
'msg' => sprintf($lang['success']['object_modified'], $username)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
function edit_u2f_registration($reg) {
|
|
||||||
global $pdo;
|
|
||||||
$upd = $pdo->prepare("update tfa set counter = ? where id = ?");
|
|
||||||
$upd->execute(array($reg->counter, $reg->id));
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -22,10 +22,8 @@ if (file_exists('./inc/vars.local.inc.php')) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yubi OTP API
|
// Yubi OTP API
|
||||||
if (!empty($YUBI_API['ID']) && !empty($YUBI_API['KEY'])) {
|
require_once 'inc/lib/Yubico.php';
|
||||||
require_once 'inc/lib/Yubico.php';
|
|
||||||
$yubi = new Auth_Yubico($YUBI_API['ID'], $YUBI_API['KEY']);
|
|
||||||
}
|
|
||||||
// U2F API
|
// U2F API
|
||||||
require_once 'inc/lib/U2F.php';
|
require_once 'inc/lib/U2F.php';
|
||||||
$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://";
|
$scheme = isset($_SERVER['HTTPS']) ? "https://" : "http://";
|
||||||
|
|
|
@ -4,6 +4,18 @@
|
||||||
<div class="modal-header"><b><?=$lang['tfa']['yubi_otp'];?></b></div>
|
<div class="modal-header"><b><?=$lang['tfa']['yubi_otp'];?></b></div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form role="form" method="post">
|
<form role="form" method="post">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<p class="help-block"><?=$lang['tfa']['api_register'];?></p>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="yubico_id" id="yubico_id" placeholder="Yubico API ID" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="yubico_key" id="yubico_key" placeholder="Yubico API Key" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +39,9 @@
|
||||||
<div class="modal-header"><b><?=$lang['tfa']['u2f'];?></b></div>
|
<div class="modal-header"><b><?=$lang['tfa']['u2f'];?></b></div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form role="form" method="post" id="u2f_reg_form">
|
<form role="form" method="post" id="u2f_reg_form">
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="text" class="form-control" name="key_id" id="key_id" placeholder="<?=$lang['tfa']['key_id'];?>" autocomplete="off" required>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -115,6 +115,9 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
|
||||||
if (isset($_POST["set_tfa"])) {
|
if (isset($_POST["set_tfa"])) {
|
||||||
set_tfa($_POST);
|
set_tfa($_POST);
|
||||||
}
|
}
|
||||||
|
if (isset($_POST["unset_tfa_key"])) {
|
||||||
|
unset_tfa_key($_POST);
|
||||||
|
}
|
||||||
if (isset($_POST["add_policy_list_item"])) {
|
if (isset($_POST["add_policy_list_item"])) {
|
||||||
add_policy_list_item($_POST);
|
add_policy_list_item($_POST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,4 @@ $DEFAULT_LANG = "en";
|
||||||
// See https://bootswatch.com/
|
// See https://bootswatch.com/
|
||||||
$DEFAULT_THEME = "lumen";
|
$DEFAULT_THEME = "lumen";
|
||||||
|
|
||||||
// If you want to use Yubico TFA methods, setup an ID and a key here: https://upgrade.yubico.com/getapikey/
|
|
||||||
// Remember to override this value using vars.local.inc.php, do not change it here.
|
|
||||||
$YUBI_API['ID'] = "";
|
|
||||||
$YUBI_API['KEY'] = "";
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -29,6 +29,7 @@ $lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existi
|
||||||
$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
|
$lang['danger']['policy_list_from_invalid'] = 'Eintrag hat ungültiges Format';
|
||||||
$lang['danger']['alias_invalid'] = 'Alias-Adrese ist ungültig';
|
$lang['danger']['alias_invalid'] = 'Alias-Adrese ist ungültig';
|
||||||
$lang['danger']['goto_invalid'] = 'Ziel-Adrese ist ungültig';
|
$lang['danger']['goto_invalid'] = 'Ziel-Adrese ist ungültig';
|
||||||
|
$lang['danger']['last_key'] = 'Letzter Key kann nicht gelöscht werden';
|
||||||
$lang['danger']['alias_domain_invalid'] = 'Alias-Domain ist ungültig';
|
$lang['danger']['alias_domain_invalid'] = 'Alias-Domain ist ungültig';
|
||||||
$lang['danger']['target_domain_invalid'] = 'Ziel-Domain ist ungültig';
|
$lang['danger']['target_domain_invalid'] = 'Ziel-Domain ist ungültig';
|
||||||
$lang['danger']['object_exists'] = 'Objekt %s existiert bereits';
|
$lang['danger']['object_exists'] = 'Objekt %s existiert bereits';
|
||||||
|
@ -374,11 +375,14 @@ $lang['login']['delayed'] = 'Login wurde zur Sicherheit um %s Sekunde/n verzöge
|
||||||
$lang['tfa']['tfa'] = "Two-Factor Authentication";
|
$lang['tfa']['tfa'] = "Two-Factor Authentication";
|
||||||
$lang['tfa']['set_tfa'] = "Konfiguriere Two-Factor Authentication Methode";
|
$lang['tfa']['set_tfa'] = "Konfiguriere Two-Factor Authentication Methode";
|
||||||
$lang['tfa']['yubi_otp'] = "Yubico OTP Authentifizierung";
|
$lang['tfa']['yubi_otp'] = "Yubico OTP Authentifizierung";
|
||||||
|
$lang['tfa']['key_id'] = "Ein Name für diesen YubiKey";
|
||||||
|
$lang['tfa']['api_register'] = 'mailcow verwendet die Yubico Cloud API. Ein API-Key für den Yubico Stick kann <a href="https://upgrade.yubico.com/getapikey/" target="_blank">hier</a> bezogen werden.';
|
||||||
$lang['tfa']['u2f'] = "U2F Authentifizierung";
|
$lang['tfa']['u2f'] = "U2F Authentifizierung";
|
||||||
$lang['tfa']['hotp'] = "HOTP Authentifizierung";
|
$lang['tfa']['hotp'] = "HOTP Authentifizierung";
|
||||||
$lang['tfa']['totp'] = "TOTP Authentifizierung";
|
$lang['tfa']['totp'] = "TOTP Authentifizierung";
|
||||||
$lang['tfa']['none'] = "Deaktiviert";
|
$lang['tfa']['none'] = "Deaktiviert";
|
||||||
$lang['tfa']['delete_tfa'] = "Deaktiviere TFA";
|
$lang['tfa']['delete_tfa'] = "Deaktiviere TFA";
|
||||||
|
$lang['tfa']['disable_tfa'] = "Deaktiviere TFA bis zur nächsten erfolgreichen Anmeldung";
|
||||||
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
||||||
$lang['tfa']['confirm'] = "Bestätigen";
|
$lang['tfa']['confirm'] = "Bestätigen";
|
||||||
$lang['tfa']['otp'] = "Einmalpasswort";
|
$lang['tfa']['otp'] = "Einmalpasswort";
|
||||||
|
|
|
@ -24,6 +24,7 @@ $lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds doma
|
||||||
$lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
|
$lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
|
||||||
$lang['success']['domain_added'] = "Added domain %s";
|
$lang['success']['domain_added'] = "Added domain %s";
|
||||||
$lang['danger']['alias_empty'] = "Alias address must not be empty";
|
$lang['danger']['alias_empty'] = "Alias address must not be empty";
|
||||||
|
$lang['danger']['last_key'] = 'Last key cannot be deleted';
|
||||||
$lang['danger']['goto_empty'] = "Goto address must not be empty";
|
$lang['danger']['goto_empty'] = "Goto address must not be empty";
|
||||||
$lang['danger']['policy_list_from_exists'] = "A record with given name exists";
|
$lang['danger']['policy_list_from_exists'] = "A record with given name exists";
|
||||||
$lang['danger']['policy_list_from_invalid'] = "Record has invalid format";
|
$lang['danger']['policy_list_from_invalid'] = "Record has invalid format";
|
||||||
|
@ -377,11 +378,14 @@ $lang['login']['delayed'] = 'Login was delayed by %s seconds.';
|
||||||
$lang['tfa']['tfa'] = "Two-factor authentication";
|
$lang['tfa']['tfa'] = "Two-factor authentication";
|
||||||
$lang['tfa']['set_tfa'] = "Set two-factor authentication method";
|
$lang['tfa']['set_tfa'] = "Set two-factor authentication method";
|
||||||
$lang['tfa']['yubi_otp'] = "Yubico OTP authentication";
|
$lang['tfa']['yubi_otp'] = "Yubico OTP authentication";
|
||||||
|
$lang['tfa']['key_id'] = "An identifier for your YubiKey";
|
||||||
|
$lang['tfa']['api_register'] = 'mailcow uses the Yubico Cloud API. Please get an API key for your key <a href="https://upgrade.yubico.com/getapikey/" target="_blank">here</a>';
|
||||||
$lang['tfa']['u2f'] = "U2F authentication";
|
$lang['tfa']['u2f'] = "U2F authentication";
|
||||||
$lang['tfa']['hotp'] = "HOTP authentication";
|
$lang['tfa']['hotp'] = "HOTP authentication";
|
||||||
$lang['tfa']['totp'] = "TOTP authentication";
|
$lang['tfa']['totp'] = "TOTP authentication";
|
||||||
$lang['tfa']['none'] = "Deaktiviert";
|
$lang['tfa']['none'] = "Deaktiviert";
|
||||||
$lang['tfa']['delete_tfa'] = "Disable TFA";
|
$lang['tfa']['delete_tfa'] = "Disable TFA";
|
||||||
|
$lang['tfa']['disable_tfa'] = "Disable TFA until next successful login";
|
||||||
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
$lang['tfa']['confirm_tfa'] = "Please confirm your one-time password in the below field";
|
||||||
$lang['tfa']['confirm'] = "Confirm";
|
$lang['tfa']['confirm'] = "Confirm";
|
||||||
$lang['tfa']['otp'] = "One-time password";
|
$lang['tfa']['otp'] = "One-time password";
|
||||||
|
|
|
@ -8,6 +8,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
|
|
||||||
require_once("inc/header.inc.php");
|
require_once("inc/header.inc.php");
|
||||||
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
|
||||||
|
$tfa_data = get_tfa();
|
||||||
$username = $_SESSION['mailcow_cc_username'];
|
$username = $_SESSION['mailcow_cc_username'];
|
||||||
?>
|
?>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
@ -23,8 +24,19 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
|
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-sm-9 col-xs-7">
|
||||||
<p><?=get_tfa()['pretty'];?></p>
|
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
|
||||||
|
<div id="tfa_additional">
|
||||||
|
<?php if($tfa_data['additional']):
|
||||||
|
foreach ($tfa_data['additional'] as $key_info): ?>
|
||||||
|
<form method="post">
|
||||||
|
<input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
|
||||||
|
<div class="label label-default">🔑 <?=$key_info['key_id'];?> <a href="#" style="font-weight:bold;color:white" onClick="$(this).closest('form').submit()">[<?=strtolower($lang['admin']['remove']);?>]</a></div>
|
||||||
|
</form>
|
||||||
|
<?php endforeach;
|
||||||
|
endif;?>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
@ -32,6 +44,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
|
||||||
<div class="col-md-9 col-xs-7">
|
<div class="col-md-9 col-xs-7">
|
||||||
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
|
||||||
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
|
||||||
|
<option value="u2f"><?=$lang['tfa']['u2f'];?></option>
|
||||||
<option value="none"><?=$lang['tfa']['none'];?></option>
|
<option value="none"><?=$lang['tfa']['none'];?></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue