[Web] add verify selected tfa
This commit is contained in:
		
							parent
							
								
									89fdd1986d
								
							
						
					
					
						commit
						f09a3df870
					
				| @ -255,3 +255,13 @@ code { | |||||||
| .flag-icon { | .flag-icon { | ||||||
|   margin-right: 5px; |   margin-right: 5px; | ||||||
| } | } | ||||||
|  | .list-group-item.webauthn-authenticator-selection, | ||||||
|  | .list-group-item.totp-authenticator-selection, | ||||||
|  | .list-group-item.yubi_otp-authenticator-selection { | ||||||
|  |   border-radius: 0px !important; | ||||||
|  | } | ||||||
|  | .pending-tfa-collapse { | ||||||
|  |   padding: 10px; | ||||||
|  |   background: #fbfbfb; | ||||||
|  |   border: 1px solid #ededed; | ||||||
|  | } | ||||||
|  | |||||||
| @ -2,5 +2,5 @@ | |||||||
| session_start(); | session_start(); | ||||||
| unset($_SESSION['pending_mailcow_cc_username']); | unset($_SESSION['pending_mailcow_cc_username']); | ||||||
| unset($_SESSION['pending_mailcow_cc_role']); | unset($_SESSION['pending_mailcow_cc_role']); | ||||||
| unset($_SESSION['pending_tfa_method']); | unset($_SESSION['pending_tfa_methods']); | ||||||
| ?>
 | ?>
 | ||||||
|  | |||||||
| @ -23,6 +23,27 @@ if (is_array($alertbox_log_parser)) { | |||||||
|   unset($_SESSION['return']); |   unset($_SESSION['return']); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // map tfa details for twig
 | ||||||
|  | $pending_tfa_authmechs = []; | ||||||
|  | foreach($_SESSION['pending_tfa_methods'] as $authdata){ | ||||||
|  |   $pending_tfa_authmechs[$authdata['authmech']] = false; | ||||||
|  | } | ||||||
|  | if (isset($pending_tfa_authmechs['webauthn'])) { | ||||||
|  |   $pending_tfa_authmechs['webauthn'] = true; | ||||||
|  | } | ||||||
|  | if (!isset($pending_tfa_authmechs['webauthn'])  | ||||||
|  |     && isset($pending_tfa_authmechs['yubi_otp'])) { | ||||||
|  |   $pending_tfa_authmechs['yubi_otp'] = true; | ||||||
|  | } | ||||||
|  | if (!isset($pending_tfa_authmechs['webauthn'])  | ||||||
|  |     && !isset($pending_tfa_authmechs['yubi_otp']) | ||||||
|  |     && isset($pending_tfa_authmechs['totp'])) { | ||||||
|  |   $pending_tfa_authmechs['totp'] = true; | ||||||
|  | } | ||||||
|  | if (isset($pending_tfa_authmechs['u2f'])) { | ||||||
|  |   $pending_tfa_authmechs['u2f'] = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // globals
 | // globals
 | ||||||
| $globalVariables = [ | $globalVariables = [ | ||||||
|   'mailcow_info' => array( |   'mailcow_info' => array( | ||||||
| @ -30,7 +51,8 @@ $globalVariables = [ | |||||||
|     'git_project_url' => $GLOBALS['MAILCOW_GIT_URL'] |     'git_project_url' => $GLOBALS['MAILCOW_GIT_URL'] | ||||||
|   ), |   ), | ||||||
|   'js_path' => '/cache/'.basename($JSPath), |   'js_path' => '/cache/'.basename($JSPath), | ||||||
|   'pending_tfa_method' => @$_SESSION['pending_tfa_method'], |   'pending_tfa_methods' => @$_SESSION['pending_tfa_methods'], | ||||||
|  |   'pending_tfa_authmechs' => $pending_tfa_authmechs, | ||||||
|   'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'], |   'pending_mailcow_cc_username' => @$_SESSION['pending_mailcow_cc_username'], | ||||||
|   'lang_footer' => json_encode($lang['footer']), |   'lang_footer' => json_encode($lang['footer']), | ||||||
|   'lang_acl' => json_encode($lang['acl']), |   'lang_acl' => json_encode($lang['acl']), | ||||||
|  | |||||||
| @ -830,11 +830,15 @@ function check_login($user, $pass, $app_passwd_data = false) { | |||||||
|   $stmt->execute(array(':user' => $user)); |   $stmt->execute(array(':user' => $user)); | ||||||
|   $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |   $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|   foreach ($rows as $row) { |   foreach ($rows as $row) { | ||||||
|  |     // verify password
 | ||||||
|     if (verify_hash($row['password'], $pass)) { |     if (verify_hash($row['password'], $pass)) { | ||||||
|       if (get_tfa($user)['name'] != "none") { |       // check for tfa authenticators
 | ||||||
|  |       $authenticators = get_tfa($user); | ||||||
|  |       if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) { | ||||||
|  |         // active tfa authenticators found, set pending user login
 | ||||||
|         $_SESSION['pending_mailcow_cc_username'] = $user; |         $_SESSION['pending_mailcow_cc_username'] = $user; | ||||||
|         $_SESSION['pending_mailcow_cc_role'] = "admin"; |         $_SESSION['pending_mailcow_cc_role'] = "admin"; | ||||||
|         $_SESSION['pending_tfa_method'] = get_tfa($user)['name']; |         $_SESSION['pending_tfa_methods'] = $authenticators['additional']; | ||||||
|         unset($_SESSION['ldelay']); |         unset($_SESSION['ldelay']); | ||||||
|         $_SESSION['return'][] =  array( |         $_SESSION['return'][] =  array( | ||||||
|           'type' => 'info', |           'type' => 'info', | ||||||
| @ -842,8 +846,7 @@ function check_login($user, $pass, $app_passwd_data = false) { | |||||||
|           'msg' => 'awaiting_tfa_confirmation' |           'msg' => 'awaiting_tfa_confirmation' | ||||||
|         ); |         ); | ||||||
|         return "pending"; |         return "pending"; | ||||||
|       } |       } else { | ||||||
|       else { |  | ||||||
|         unset($_SESSION['ldelay']); |         unset($_SESSION['ldelay']); | ||||||
|         // Reactivate TFA if it was set to "deactivate TFA for next login"
 |         // Reactivate TFA if it was set to "deactivate TFA for next login"
 | ||||||
|         $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); |         $stmt = $pdo->prepare("UPDATE `tfa` SET `active`='1' WHERE `username` = :user"); | ||||||
| @ -866,11 +869,14 @@ function check_login($user, $pass, $app_passwd_data = false) { | |||||||
|   $stmt->execute(array(':user' => $user)); |   $stmt->execute(array(':user' => $user)); | ||||||
|   $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |   $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|   foreach ($rows as $row) { |   foreach ($rows as $row) { | ||||||
|  |     // verify password
 | ||||||
|     if (verify_hash($row['password'], $pass) !== false) { |     if (verify_hash($row['password'], $pass) !== false) { | ||||||
|       if (get_tfa($user)['name'] != "none") { |       // check for tfa authenticators
 | ||||||
|  |       $authenticators = get_tfa($user); | ||||||
|  |       if (isset($authenticators['additional']) && is_array($authenticators['additional']) && count($authenticators['additional']) > 0) { | ||||||
|         $_SESSION['pending_mailcow_cc_username'] = $user; |         $_SESSION['pending_mailcow_cc_username'] = $user; | ||||||
|         $_SESSION['pending_mailcow_cc_role'] = "domainadmin"; |         $_SESSION['pending_mailcow_cc_role'] = "domainadmin"; | ||||||
|         $_SESSION['pending_tfa_method'] = get_tfa($user)['name']; |         $_SESSION['pending_tfa_method'] = $authenticators['additional']; | ||||||
|         unset($_SESSION['ldelay']); |         unset($_SESSION['ldelay']); | ||||||
|         $_SESSION['return'][] =  array( |         $_SESSION['return'][] =  array( | ||||||
|           'type' => 'info', |           'type' => 'info', | ||||||
| @ -1142,38 +1148,39 @@ function set_tfa($_data) { | |||||||
|   global $yubi; |   global $yubi; | ||||||
|   global $tfa; |   global $tfa; | ||||||
|   $_data_log = $_data; |   $_data_log = $_data; | ||||||
|  |   $access_denied = null; | ||||||
|   !isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*'; |   !isset($_data_log['confirm_password']) ?: $_data_log['confirm_password'] = '*'; | ||||||
|   $username = $_SESSION['mailcow_cc_username']; |   $username = $_SESSION['mailcow_cc_username']; | ||||||
|   if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) { | 
 | ||||||
|       $_SESSION['return'][] =  array( |   // check for empty user and role
 | ||||||
|         'type' => 'danger', |   if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true; | ||||||
|         'log' => array(__FUNCTION__, $_data_log), | 
 | ||||||
|         'msg' => 'access_denied' |   // check admin confirm password
 | ||||||
|       ); |   if ($access_denied === null) { | ||||||
|       return false; |  | ||||||
|   } |  | ||||||
|     $stmt = $pdo->prepare("SELECT `password` FROM `admin`
 |     $stmt = $pdo->prepare("SELECT `password` FROM `admin`
 | ||||||
|         WHERE `username` = :username");
 |         WHERE `username` = :username");
 | ||||||
|     $stmt->execute(array(':username' => $username)); |     $stmt->execute(array(':username' => $username)); | ||||||
|     $row = $stmt->fetch(PDO::FETCH_ASSOC); |     $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||||
|   $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); |     if ($row) { | ||||||
|   if (!empty($num_results)) { |       if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true; | ||||||
|     if (!verify_hash($row['password'], $_data["confirm_password"])) { |       else $access_denied = false; | ||||||
|       $_SESSION['return'][] =  array( |  | ||||||
|         'type' => 'danger', |  | ||||||
|         'log' => array(__FUNCTION__, $_data_log), |  | ||||||
|         'msg' => 'access_denied' |  | ||||||
|       ); |  | ||||||
|       return false; |  | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   // check mailbox confirm password
 | ||||||
|  |   if ($access_denied === null) { | ||||||
|     $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
 |     $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
 | ||||||
|         WHERE `username` = :username");
 |         WHERE `username` = :username");
 | ||||||
|     $stmt->execute(array(':username' => $username)); |     $stmt->execute(array(':username' => $username)); | ||||||
|     $row = $stmt->fetch(PDO::FETCH_ASSOC); |     $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||||
|   $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); |     if ($row) { | ||||||
|   if (!empty($num_results)) { |       if (!verify_hash($row['password'], $_data["confirm_password"])) $access_denied = true; | ||||||
|     if (!verify_hash($row['password'], $_data["confirm_password"])) { |       else $access_denied = false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // set access_denied error
 | ||||||
|  |   if ($access_denied){ | ||||||
|     $_SESSION['return'][] =  array( |     $_SESSION['return'][] =  array( | ||||||
|       'type' => 'danger', |       'type' => 'danger', | ||||||
|       'log' => array(__FUNCTION__, $_data_log), |       'log' => array(__FUNCTION__, $_data_log), | ||||||
| @ -1181,8 +1188,6 @@ function set_tfa($_data) { | |||||||
|     ); |     ); | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|   } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|   switch ($_data["tfa_method"]) { |   switch ($_data["tfa_method"]) { | ||||||
|     case "yubi_otp": |     case "yubi_otp": | ||||||
| @ -1265,9 +1270,6 @@ function set_tfa($_data) { | |||||||
|     case "webauthn": |     case "webauthn": | ||||||
|         $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"]; |         $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"]; | ||||||
| 
 | 
 | ||||||
|         $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'webauthn'"); |  | ||||||
|         $stmt->execute(array(':username' => $username)); |  | ||||||
| 
 |  | ||||||
|         $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
 |         $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`)
 | ||||||
|         VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
 |         VALUES (?, ?, 'webauthn', ?, ?, ?, ?, '1')");
 | ||||||
|         $stmt->execute(array( |         $stmt->execute(array( | ||||||
| @ -1439,18 +1441,18 @@ function unset_tfa_key($_data) { | |||||||
|   global $pdo; |   global $pdo; | ||||||
|   global $lang; |   global $lang; | ||||||
|   $_data_log = $_data; |   $_data_log = $_data; | ||||||
|  |   $access_denied = null; | ||||||
|   $id = intval($_data['unset_tfa_key']); |   $id = intval($_data['unset_tfa_key']); | ||||||
|   $username = $_SESSION['mailcow_cc_username']; |   $username = $_SESSION['mailcow_cc_username']; | ||||||
|   if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) { | 
 | ||||||
|     $_SESSION['return'][] =  array( |   // check for empty user and role
 | ||||||
|       'type' => 'danger', |   if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) $access_denied = true; | ||||||
|       'log' => array(__FUNCTION__, $_data_log), | 
 | ||||||
|       'msg' => 'access_denied' |  | ||||||
|     ); |  | ||||||
|     return false; |  | ||||||
|   } |  | ||||||
|   try { |   try { | ||||||
|     if (!is_numeric($id)) { |     if (!is_numeric($id)) $access_denied = true; | ||||||
|  |      | ||||||
|  |     // set access_denied error
 | ||||||
|  |     if ($access_denied){ | ||||||
|       $_SESSION['return'][] = array( |       $_SESSION['return'][] = array( | ||||||
|         'type' => 'danger', |         'type' => 'danger', | ||||||
|         'log' => array(__FUNCTION__, $_data_log), |         'log' => array(__FUNCTION__, $_data_log), | ||||||
| @ -1458,6 +1460,8 @@ function unset_tfa_key($_data) { | |||||||
|       ); |       ); | ||||||
|       return false; |       return false; | ||||||
|     }  |     }  | ||||||
|  | 
 | ||||||
|  |     // check if it's last key
 | ||||||
|     $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
 |     $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
 | ||||||
|       WHERE `username` = :username AND `active` = '1'");
 |       WHERE `username` = :username AND `active` = '1'");
 | ||||||
|     $stmt->execute(array(':username' => $username)); |     $stmt->execute(array(':username' => $username)); | ||||||
| @ -1470,6 +1474,8 @@ function unset_tfa_key($_data) { | |||||||
|       ); |       ); | ||||||
|       return false; |       return false; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     // delete key
 | ||||||
|     $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id"); |     $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id"); | ||||||
|     $stmt->execute(array(':username' => $username, ':id' => $id)); |     $stmt->execute(array(':username' => $username, ':id' => $id)); | ||||||
|     $_SESSION['return'][] =  array( |     $_SESSION['return'][] =  array( | ||||||
| @ -1487,7 +1493,7 @@ function unset_tfa_key($_data) { | |||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| function get_tfa($username = null) { | function get_tfa($username = null, $key_id = null) { | ||||||
|   global $pdo; |   global $pdo; | ||||||
|   if (isset($_SESSION['mailcow_cc_username'])) { |   if (isset($_SESSION['mailcow_cc_username'])) { | ||||||
|     $username = $_SESSION['mailcow_cc_username']; |     $username = $_SESSION['mailcow_cc_username']; | ||||||
| @ -1495,6 +1501,26 @@ function get_tfa($username = null) { | |||||||
|   elseif (empty($username)) { |   elseif (empty($username)) { | ||||||
|     return false; |     return false; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   if (!isset($key_id)){ | ||||||
|  |     // fetch all tfa methods - just get information about possible authenticators
 | ||||||
|  |     $stmt = $pdo->prepare("SELECT `id`, `key_id`, `authmech` FROM `tfa`
 | ||||||
|  |         WHERE `username` = :username AND `active` = '1'");
 | ||||||
|  |     $stmt->execute(array(':username' => $username)); | ||||||
|  |     $results = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|  |   | ||||||
|  |     // no tfa methods found
 | ||||||
|  |     if (count($results) == 0) { | ||||||
|  |         $data['name'] = 'none'; | ||||||
|  |         $data['pretty'] = "-"; | ||||||
|  |         $data['additional'] = array(); | ||||||
|  |         return $data; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $data['additional'] = $results; | ||||||
|  |     return $data; | ||||||
|  |   } else { | ||||||
|  |     // fetch specific authenticator details by key_id
 | ||||||
|     $stmt = $pdo->prepare("SELECT * FROM `tfa`
 |     $stmt = $pdo->prepare("SELECT * FROM `tfa`
 | ||||||
|     WHERE `username` = :username AND `active` = '1'");
 |     WHERE `username` = :username AND `active` = '1'");
 | ||||||
|     $stmt->execute(array(':username' => $username)); |     $stmt->execute(array(':username' => $username)); | ||||||
| @ -1573,14 +1599,18 @@ function get_tfa($username = null) { | |||||||
|         return $data; |         return $data; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
| function verify_tfa_login($username, $_data, $WebAuthn) { | } | ||||||
|  | function verify_tfa_login($username, $_data) { | ||||||
|   global $pdo; |   global $pdo; | ||||||
|   global $yubi; |   global $yubi; | ||||||
|   global $u2f; |   global $u2f; | ||||||
|   global $tfa; |   global $tfa; | ||||||
|  |   global $WebAuthn; | ||||||
|  | 
 | ||||||
|  |   if ($_data['tfa_method'] != 'u2f'){ | ||||||
|     $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
 |     $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
 | ||||||
|         WHERE `username` = :username AND `active` = '1'");
 |         WHERE `username` = :username AND `key_id` = :key_id AND `active` = '1'");
 | ||||||
|     $stmt->execute(array(':username' => $username)); |     $stmt->execute(array(':username' => $username, ':key_id' => $_data['key_id'])); | ||||||
|     $row = $stmt->fetch(PDO::FETCH_ASSOC); |     $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||||
| 
 | 
 | ||||||
|     switch ($row["authmech"]) { |     switch ($row["authmech"]) { | ||||||
| @ -1597,9 +1627,10 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
|             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
 |             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
 | ||||||
|                 WHERE `username` = :username |                 WHERE `username` = :username | ||||||
|                 AND `authmech` = 'yubi_otp' |                 AND `authmech` = 'yubi_otp' | ||||||
|  |                 AND `key_id` = ':key_id' | ||||||
|                 AND `active`='1' |                 AND `active`='1' | ||||||
|                 AND `secret` LIKE :modhex");
 |                 AND `secret` LIKE :modhex");
 | ||||||
|             $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id)); |             $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id, ':key_id' => $_data['key_id'])); | ||||||
|             $row = $stmt->fetch(PDO::FETCH_ASSOC); |             $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||||
|             $yubico_auth = explode(':', $row['secret']); |             $yubico_auth = explode(':', $row['secret']); | ||||||
|             $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]); |             $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]); | ||||||
| @ -1636,8 +1667,9 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
|             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
 |             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
 | ||||||
|                 WHERE `username` = :username |                 WHERE `username` = :username | ||||||
|                 AND `authmech` = 'totp' |                 AND `authmech` = 'totp' | ||||||
|  |                 AND `key_id` = :key_id | ||||||
|                 AND `active`='1'");
 |                 AND `active`='1'");
 | ||||||
|             $stmt->execute(array(':username' => $username)); |             $stmt->execute(array(':username' => $username, ':key_id' => $_data['key_id'])); | ||||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|             foreach ($rows as $row) { |             foreach ($rows as $row) { | ||||||
|                 if ($tfa->verifyCode($row['secret'], $_data['token']) === true) { |                 if ($tfa->verifyCode($row['secret'], $_data['token']) === true) { | ||||||
| @ -1666,13 +1698,6 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
|             return false; |             return false; | ||||||
|             } |             } | ||||||
|         break; |         break; | ||||||
|         // u2f - deprecated, should be removed
 |  | ||||||
|         case "u2f": |  | ||||||
|             // delete old keys that used u2f
 |  | ||||||
|             $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = :authmech AND `username` = :username"); |  | ||||||
|             $stmt->execute(array(':authmech' => 'u2f', ':username' => $username)); |  | ||||||
| 
 |  | ||||||
|             return true; |  | ||||||
|         case "webauthn": |         case "webauthn": | ||||||
|             $tokenData = json_decode($_data['token']); |             $tokenData = json_decode($_data['token']); | ||||||
|             $clientDataJSON = base64_decode($tokenData->clientDataJSON); |             $clientDataJSON = base64_decode($tokenData->clientDataJSON); | ||||||
| @ -1681,7 +1706,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
|             $id = base64_decode($tokenData->id); |             $id = base64_decode($tokenData->id); | ||||||
|             $challenge = $_SESSION['challenge']; |             $challenge = $_SESSION['challenge']; | ||||||
| 
 | 
 | ||||||
|             $stmt = $pdo->prepare("SELECT `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId"); |             $stmt = $pdo->prepare("SELECT `id`, `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId"); | ||||||
|             $stmt->execute(array(':tokenId' => $tokenData->id)); |             $stmt->execute(array(':tokenId' => $tokenData->id)); | ||||||
|             $process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC); |             $process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC); | ||||||
| 
 | 
 | ||||||
| @ -1738,7 +1763,7 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|             $_SESSION["mailcow_cc_username"] = $process_webauthn['username']; |             $_SESSION["mailcow_cc_username"] = $process_webauthn['username']; | ||||||
|             $_SESSION['tfa_id'] = $process_webauthn['key_id']; |             $_SESSION['tfa_id'] = $process_webauthn['id']; | ||||||
|             $_SESSION['authReq'] = null; |             $_SESSION['authReq'] = null; | ||||||
|             unset($_SESSION["challenge"]); |             unset($_SESSION["challenge"]); | ||||||
|             $_SESSION['return'][] =  array( |             $_SESSION['return'][] =  array( | ||||||
| @ -1759,6 +1784,17 @@ function verify_tfa_login($username, $_data, $WebAuthn) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return false; |     return false; | ||||||
|  |   } else { | ||||||
|  |     // delete old keys that used u2f
 | ||||||
|  |     $stmt = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username"); | ||||||
|  |     $stmt->execute(array(':username' => $username)); | ||||||
|  |     $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|  |     if (count($rows) == 0) return false; | ||||||
|  | 
 | ||||||
|  |     $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username"); | ||||||
|  |     $stmt->execute(array(':username' => $username)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
| } | } | ||||||
| function admin_api($access, $action, $data = null) { | function admin_api($access, $action, $data = null) { | ||||||
|   global $pdo; |   global $pdo; | ||||||
|  | |||||||
| @ -1,24 +1,24 @@ | |||||||
| <?php | <?php | ||||||
| if (isset($_POST["verify_tfa_login"])) { | if (isset($_POST["verify_tfa_login"])) { | ||||||
|   if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST, $WebAuthn)) { |   if (verify_tfa_login($_SESSION['pending_mailcow_cc_username'], $_POST)) { | ||||||
|     $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username']; |     $_SESSION['mailcow_cc_username'] = $_SESSION['pending_mailcow_cc_username']; | ||||||
|     $_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role']; |     $_SESSION['mailcow_cc_role'] = $_SESSION['pending_mailcow_cc_role']; | ||||||
|     unset($_SESSION['pending_mailcow_cc_username']); |     unset($_SESSION['pending_mailcow_cc_username']); | ||||||
|     unset($_SESSION['pending_mailcow_cc_role']); |     unset($_SESSION['pending_mailcow_cc_role']); | ||||||
|     unset($_SESSION['pending_tfa_method']); |     unset($_SESSION['pending_tfa_methods']); | ||||||
| 	 | 	 | ||||||
|     header("Location: /user"); |     header("Location: /user"); | ||||||
|   } else { |   } else { | ||||||
|     unset($_SESSION['pending_mailcow_cc_username']); |     unset($_SESSION['pending_mailcow_cc_username']); | ||||||
|     unset($_SESSION['pending_mailcow_cc_role']); |     unset($_SESSION['pending_mailcow_cc_role']); | ||||||
|     unset($_SESSION['pending_tfa_method']); |     unset($_SESSION['pending_tfa_methods']); | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| if (isset($_GET["cancel_tfa_login"])) { | if (isset($_GET["cancel_tfa_login"])) { | ||||||
|     unset($_SESSION['pending_mailcow_cc_username']); |     unset($_SESSION['pending_mailcow_cc_username']); | ||||||
|     unset($_SESSION['pending_mailcow_cc_role']); |     unset($_SESSION['pending_mailcow_cc_role']); | ||||||
|     unset($_SESSION['pending_tfa_method']); |     unset($_SESSION['pending_tfa_methods']); | ||||||
| 
 | 
 | ||||||
|     header("Location: /"); |     header("Location: /"); | ||||||
| } | } | ||||||
| @ -34,6 +34,7 @@ if (isset($_POST["quick_delete"])) { | |||||||
| if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { | if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { | ||||||
| 	$login_user = strtolower(trim($_POST["login_user"])); | 	$login_user = strtolower(trim($_POST["login_user"])); | ||||||
| 	$as = check_login($login_user, $_POST["pass_user"]); | 	$as = check_login($login_user, $_POST["pass_user"]); | ||||||
|  |    | ||||||
| 	if ($as == "admin") { | 	if ($as == "admin") { | ||||||
| 		$_SESSION['mailcow_cc_username'] = $login_user; | 		$_SESSION['mailcow_cc_username'] = $login_user; | ||||||
| 		$_SESSION['mailcow_cc_role'] = "admin"; | 		$_SESSION['mailcow_cc_role'] = "admin"; | ||||||
| @ -62,7 +63,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) { | |||||||
| 	elseif ($as != "pending") { | 	elseif ($as != "pending") { | ||||||
|         unset($_SESSION['pending_mailcow_cc_username']); |         unset($_SESSION['pending_mailcow_cc_username']); | ||||||
|         unset($_SESSION['pending_mailcow_cc_role']); |         unset($_SESSION['pending_mailcow_cc_role']); | ||||||
|     unset($_SESSION['pending_tfa_method']); |         unset($_SESSION['pending_tfa_methods']); | ||||||
| 		unset($_SESSION['mailcow_cc_username']); | 		unset($_SESSION['mailcow_cc_username']); | ||||||
| 		unset($_SESSION['mailcow_cc_role']); | 		unset($_SESSION['mailcow_cc_role']); | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -197,6 +197,7 @@ if (isset($_GET['query'])) { | |||||||
|               // safe authenticator in mysql `tfa` table
 |               // safe authenticator in mysql `tfa` table
 | ||||||
|               $_data['tfa_method'] = $post->tfa_method; |               $_data['tfa_method'] = $post->tfa_method; | ||||||
|               $_data['key_id'] = $post->key_id; |               $_data['key_id'] = $post->key_id; | ||||||
|  |               $_data['confirm_password'] = $post->confirm_password; | ||||||
|               $_data['registration'] = $data; |               $_data['registration'] = $data; | ||||||
|               set_tfa($_data); |               set_tfa($_data); | ||||||
| 
 | 
 | ||||||
| @ -450,14 +451,15 @@ if (isset($_GET['query'])) { | |||||||
|           $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username"); |           $stmt = $pdo->prepare("SELECT `keyHandle` FROM `tfa` WHERE username = :username"); | ||||||
|           $stmt->execute(array(':username' => $_SESSION['pending_mailcow_cc_username'])); |           $stmt->execute(array(':username' => $_SESSION['pending_mailcow_cc_username'])); | ||||||
|           $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); |           $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||||
|           while($row = array_shift($rows)) { |           if (count($rows) == 0) { | ||||||
|             $cids[] = base64_decode($row['keyHandle']); |  | ||||||
|           } |  | ||||||
|           if (count($cids) == 0) { |  | ||||||
|             print(json_encode(array( |             print(json_encode(array( | ||||||
|                 'type' => 'error', |                 'type' => 'error', | ||||||
|                 'msg' => 'Cannot find matching credentialIds' |                 'msg' => 'Cannot find matching credentialIds' | ||||||
|             ))); |             ))); | ||||||
|  |             exit; | ||||||
|  |           } | ||||||
|  |           while($row = array_shift($rows)) { | ||||||
|  |             $cids[] = base64_decode($row['keyHandle']); | ||||||
|           } |           } | ||||||
| 
 | 
 | ||||||
|           $getArgs = $WebAuthn->getGetArgs($cids, 30, true, true, true, true, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']); |           $getArgs = $WebAuthn->getGetArgs($cids, 30, true, true, true, true, $GLOBALS['WEBAUTHN_UV_FLAG_LOGIN']); | ||||||
|  | |||||||
| @ -176,15 +176,62 @@ function recursiveBase64StrToArrayBuffer(obj) { | |||||||
|     {% endfor %} |     {% endfor %} | ||||||
| 
 | 
 | ||||||
|     // Confirm TFA modal |     // Confirm TFA modal | ||||||
|   {% if pending_tfa_method %} |   {% if pending_tfa_methods %} | ||||||
|     $('#ConfirmTFAModal').modal({ |     $('#ConfirmTFAModal').modal({ | ||||||
|       backdrop: 'static', |       backdrop: 'static', | ||||||
|       keyboard: false |       keyboard: false | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     // validate Yubi OTP tfa | ||||||
|  |     $("#pending_tfa_tab_yubi_otp").click(function(){ | ||||||
|  |       $(".totp-authenticator-selection").removeClass("active"); | ||||||
|  |       $(".webauthn-authenticator-selection").removeClass("active"); | ||||||
|  | 
 | ||||||
|  |       $("#collapseTotpTFA").collapse('hide'); | ||||||
|  |       $("#collapseWebAuthnTFA").collapse('hide'); | ||||||
|  |     }); | ||||||
|  |     $(".yubi-authenticator-selection").click(function(){ | ||||||
|  |       $(".yubi-authenticator-selection").removeClass("active"); | ||||||
|  |       $(this).addClass("active"); | ||||||
|  | 
 | ||||||
|  |       var key_id = $(this).children('span').first().text(); | ||||||
|  |       $("#yubi_selected_key_id").val(key_id); | ||||||
|  | 
 | ||||||
|  |       $("#collapseYubiTFA").collapse('show'); | ||||||
|  |     }); | ||||||
|  |     // validate Time based OTP tfa | ||||||
|  |     $("#pending_tfa_tab_totp").click(function(){ | ||||||
|  |       $(".yubi-authenticator-selection").removeClass("active"); | ||||||
|  |       $(".webauthn-authenticator-selection").removeClass("active"); | ||||||
|  | 
 | ||||||
|  |       $("#collapseYubiTFA").collapse('hide'); | ||||||
|  |       $("#collapseWebAuthnTFA").collapse('hide'); | ||||||
|  |     }); | ||||||
|  |     $(".totp-authenticator-selection").click(function(){ | ||||||
|  |       $(".totp-authenticator-selection").removeClass("active"); | ||||||
|  |       $(this).addClass("active"); | ||||||
|  |        | ||||||
|  |       var key_id = $(this).children('span').first().text(); | ||||||
|  |       $("#totp_selected_key_id").val(key_id); | ||||||
|  | 
 | ||||||
|  |       $("#collapseTotpTFA").collapse('show'); | ||||||
|  |     }); | ||||||
|     // validate WebAuthn tfa |     // validate WebAuthn tfa | ||||||
|     $('#start_webauthn_confirmation').click(function(){ |     $("#pending_tfa_tab_webauthn").click(function(){ | ||||||
|       $('#webauthn_status_auth').html('<p><i class="bi bi-arrow-repeat icon-spin"></i> ' + lang_tfa.init_webauthn + '</p>'); |       $(".totp-authenticator-selection").removeClass("active"); | ||||||
|  |       $(".yubi-authenticator-selection").removeClass("active"); | ||||||
|  | 
 | ||||||
|  |       $("#collapseTotpTFA").collapse('hide'); | ||||||
|  |       $("#collapseYubiTFA").collapse('hide'); | ||||||
|  |     }); | ||||||
|  |     $(".webauthn-authenticator-selection").click(function(){ | ||||||
|  |       $(".webauthn-authenticator-selection").removeClass("active"); | ||||||
|  |       $(this).addClass("active"); | ||||||
|  |        | ||||||
|  |       var key_id = $(this).children('span').first().text(); | ||||||
|  |       $("#webauthn_selected_key_id").val(key_id); | ||||||
|  |        | ||||||
|  |       $("#collapseWebAuthnTFA").collapse('show'); | ||||||
| 
 | 
 | ||||||
|       $(this).find('input[name=token]').focus(); |       $(this).find('input[name=token]').focus(); | ||||||
|       if(document.getElementById("webauthn_auth_data") !== null) { |       if(document.getElementById("webauthn_auth_data") !== null) { | ||||||
| @ -199,6 +246,7 @@ function recursiveBase64StrToArrayBuffer(obj) { | |||||||
|             return response.json(); |             return response.json(); | ||||||
|         }).then(json => { |         }).then(json => { | ||||||
|           if (json.success === false) throw new Error(); |           if (json.success === false) throw new Error(); | ||||||
|  |           if (json.type === "error") throw new Error(json.msg); | ||||||
|        |        | ||||||
|           recursiveBase64StrToArrayBuffer(json); |           recursiveBase64StrToArrayBuffer(json); | ||||||
|           return json; |           return json; | ||||||
| @ -238,6 +286,8 @@ function recursiveBase64StrToArrayBuffer(obj) { | |||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   {% endif %} |   {% endif %} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|     // Validate FIDO2 |     // Validate FIDO2 | ||||||
|   $("#fido2-login").click(function(){ |   $("#fido2-login").click(function(){ | ||||||
|     $('#fido2-alerts').html(); |     $('#fido2-alerts').html(); | ||||||
| @ -358,6 +408,7 @@ function recursiveBase64StrToArrayBuffer(obj) { | |||||||
| 
 | 
 | ||||||
|         $("#start_webauthn_register").click(() => { |         $("#start_webauthn_register").click(() => { | ||||||
|             var key_id = document.getElementsByName('key_id')[1].value; |             var key_id = document.getElementsByName('key_id')[1].value; | ||||||
|  |             var confirm_password = document.getElementsByName('confirm_password')[1].value; | ||||||
| 
 | 
 | ||||||
|             // fetch WebAuthn create args |             // fetch WebAuthn create args | ||||||
|             window.fetch("/api/v1/get/webauthn-tfa-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}", {method:'GET',cache:'no-cache'}).then(response => { |             window.fetch("/api/v1/get/webauthn-tfa-registration/{{ mailcow_cc_username|url_encode(true)|default('null') }}", {method:'GET',cache:'no-cache'}).then(response => { | ||||||
| @ -375,7 +426,8 @@ function recursiveBase64StrToArrayBuffer(obj) { | |||||||
|                     clientDataJSON: cred.response.clientDataJSON  ? arrayBufferToBase64(cred.response.clientDataJSON) : null, |                     clientDataJSON: cred.response.clientDataJSON  ? arrayBufferToBase64(cred.response.clientDataJSON) : null, | ||||||
|                     attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null, |                     attestationObject: cred.response.attestationObject ? arrayBufferToBase64(cred.response.attestationObject) : null, | ||||||
|                     key_id: key_id, |                     key_id: key_id, | ||||||
|                     tfa_method: "webauthn" |                     tfa_method: "webauthn", | ||||||
|  |                     confirm_password: confirm_password | ||||||
|                 }; |                 }; | ||||||
|             }).then(JSON.stringify).then(AuthenticatorAttestationResponse => { |             }).then(JSON.stringify).then(AuthenticatorAttestationResponse => { | ||||||
|                 // send request |                 // send request | ||||||
|  | |||||||
| @ -133,73 +133,171 @@ | |||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
| {% endif %} | {% endif %} | ||||||
| {% if pending_tfa_method %} | {% if pending_tfa_methods %} | ||||||
| <div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel"> | <div class="modal fade" id="ConfirmTFAModal" tabindex="-1" role="dialog" aria-labelledby="ConfirmTFAModalLabel"> | ||||||
|   <div class="modal-dialog" role="document"> |   <div class="modal-dialog" role="document"> | ||||||
|     <div class="modal-content"> |     <div class="modal-content"> | ||||||
|       <div class="modal-header"> |       <div class="modal-header"> | ||||||
|         <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button> |         <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button> | ||||||
|         <h3 class="modal-title">{{ lang.tfa[pending_tfa_method] }}</h3> |         <h3 class="modal-title">2-Factor-Authentication</h3> | ||||||
|       </div> |       </div> | ||||||
|       <div class="modal-body"> |        | ||||||
|         {% if pending_tfa_method == 'yubi_otp' %} |         <ul class="nav nav-tabs" id="tabContent"> | ||||||
|  |             {% if pending_tfa_authmechs["webauthn"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |               <li class="active"><a href="#tfa_tab_webauthn" data-toggle="tab" id="pending_tfa_tab_webauthn"><i class="bi bi-fingerprint"></i> WebAuthn</a></li> | ||||||
|  |             {% endif %} | ||||||
|  | 
 | ||||||
|  |             {% if pending_tfa_authmechs["yubi_otp"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |               <li class="tab-pane {% if pending_tfa_authmechs["yubi_otp"] %}active{% endif %}"> | ||||||
|  |                 <a href="#tfa_tab_yubi_otp" data-toggle="tab" id="pending_tfa_tab_yubi_otp"><i class="bi bi-usb-drive"></i> Yubi OTP</a> | ||||||
|  |               </li> | ||||||
|  |             {% endif %} | ||||||
|  | 
 | ||||||
|  |             {% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |               <li class="tab-pane {% if pending_tfa_authmechs["totp"] %}active{% endif %}"> | ||||||
|  |                 <a href="#tfa_tab_totp" data-toggle="tab" id="pending_tfa_tab_totp"><i class="bi bi-clock-history"></i> Time based OTP</a> | ||||||
|  |               </li> | ||||||
|  |             {% endif %} | ||||||
|  | 
 | ||||||
|  |             <!-- <li><a href="#tfa_tab_hotp" data-toggle="tab">HOTP</a></li> --> | ||||||
|  |             {% if pending_tfa_authmechs["u2f"] is defined %} | ||||||
|  |               <li class="active"><a href="#tfa_tab_u2f" data-toggle="tab"><i class="bi bi-x-octagon"></i> U2F</a></li> | ||||||
|  |             {% endif %} | ||||||
|  |         </ul> | ||||||
|  | 
 | ||||||
|  |         <div class="tab-content"> | ||||||
|  |           {% if pending_tfa_authmechs["webauthn"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |             <div role="tabpanel" class="tab-pane active" id="tfa_tab_webauthn"> | ||||||
|  |               <div class="panel panel-default" style="margin-bottom: 0px;"> | ||||||
|  |                   <div class="panel-body"> | ||||||
|  |                     <form role="form" method="post" id="webauthn_auth_form"> | ||||||
|  |                       <legend> | ||||||
|  |                           <i class="bi bi-shield-fill-check"></i> | ||||||
|  |                           Available Authenticators | ||||||
|  |                       </legend> | ||||||
|  |                       <div class="list-group"> | ||||||
|  |                         {% for authenticator in pending_tfa_methods %} | ||||||
|  |                           {% if authenticator["authmech"] == "webauthn" %} | ||||||
|  |                             <a href="#" class="list-group-item webauthn-authenticator-selection"> | ||||||
|  |                               <i class="bi bi-key-fill" style="margin-right: 5px"></i> | ||||||
|  |                               <span>{{ authenticator["key_id"] }}</span> | ||||||
|  |                             </a> | ||||||
|  |                           {% endif %} | ||||||
|  |                         {% endfor %} | ||||||
|  |                       </div> | ||||||
|  |                       <div class="collapse pending-tfa-collapse" id="collapseWebAuthnTFA"> | ||||||
|  |                         <p id="webauthn_status_auth"><p><i class="bi bi-arrow-repeat icon-spin"></i> {{ lang.tfa.init_webauthn }}</p></p> | ||||||
|  |                         <div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div> | ||||||
|  |                       </div> | ||||||
|  |                       <input type="hidden" name="token" id="webauthn_auth_data"/> | ||||||
|  |                       <input type="hidden" name="tfa_method" value="webauthn"> | ||||||
|  |                       <input type="hidden" name="verify_tfa_login"/><br/> | ||||||
|  |                       <input type="hidden" name="key_id" id="webauthn_selected_key_id" /><br/> | ||||||
|  |                     </form> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |           {% endif %} | ||||||
|  |           {% if pending_tfa_authmechs["yubi_otp"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |             <div role="tabpanel" class="tab-pane {% if pending_tfa_authmechs["yubi_otp"] %}active{% endif %}" id="tfa_tab_yubi_otp"> | ||||||
|  |               <div class="panel panel-default" style="margin-bottom: 0px;"> | ||||||
|  |                   <div class="panel-body"> | ||||||
|                     <form role="form" method="post"> |                     <form role="form" method="post"> | ||||||
|  |                       <legend> | ||||||
|  |                           <i class="bi bi-shield-fill-check"></i> | ||||||
|  |                           Available Authenticators | ||||||
|  |                       </legend> | ||||||
|  |                       <div class="list-group"> | ||||||
|  |                         {% for authenticator in pending_tfa_methods %} | ||||||
|  |                           {% if authenticator["authmech"] == "yubi_otp" %} | ||||||
|  |                             <a href="#" class="list-group-item yubi-authenticator-selection"> | ||||||
|  |                               <i class="bi bi-key-fill" style="margin-right: 5px"></i> | ||||||
|  |                               <span>{{ authenticator["key_id"] }}</span> | ||||||
|  |                             </a> | ||||||
|  |                           {% endif %} | ||||||
|  |                         {% endfor %} | ||||||
|  |                       </div> | ||||||
|  |                       <div class="collapse pending-tfa-collapse" id="collapseYubiTFA"> | ||||||
|                         <div class="form-group"> |                         <div class="form-group"> | ||||||
|                           <div class="input-group"> |                           <div class="input-group"> | ||||||
|                             <span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span> |                             <span class="input-group-addon" id="yubi-addon"><img alt="Yubicon Icon" src="/img/yubi.ico"></span> | ||||||
|                             <input type="text" name="token" class="form-control" autocomplete="off" placeholder="Touch Yubikey" aria-describedby="yubi-addon"> |                             <input type="text" name="token" class="form-control" autocomplete="off" placeholder="Touch Yubikey" aria-describedby="yubi-addon"> | ||||||
|                             <input type="hidden" name="tfa_method" value="yubi_otp"> |                             <input type="hidden" name="tfa_method" value="yubi_otp"> | ||||||
|  |                             <input type="hidden" name="key_id" id="yubi_selected_key_id" /> | ||||||
|                           </div> |                           </div> | ||||||
|                         </div> |                         </div> | ||||||
|                         <button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-sm btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button> |                         <button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-sm btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button> | ||||||
|  |                       </div> | ||||||
|                     </form> |                     </form> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         {% if pending_tfa_method == 'totp' %} |           {% if pending_tfa_authmechs["totp"] is defined and pending_tfa_authmechs["u2f"] is not defined %} | ||||||
|  |             <div role="tabpanel" class="tab-pane {% if pending_tfa_authmechs["totp"] %}active{% endif %}" id="tfa_tab_totp"> | ||||||
|  |               <div class="panel panel-default" style="margin-bottom: 0px;"> | ||||||
|  |                   <div class="panel-body"> | ||||||
|                     <form role="form" method="post">         |                     <form role="form" method="post">         | ||||||
|  |                       <legend> | ||||||
|  |                           <i class="bi bi-shield-fill-check"></i> | ||||||
|  |                           Available Authenticators | ||||||
|  |                       </legend> | ||||||
|  |                       <div class="list-group"> | ||||||
|  |                         {% for authenticator in pending_tfa_methods %} | ||||||
|  |                           {% if authenticator["authmech"] == "totp" %} | ||||||
|  |                             <a href="#" class="list-group-item totp-authenticator-selection"> | ||||||
|  |                               <i class="bi bi-key-fill" style="margin-right: 5px"></i> | ||||||
|  |                               <span>{{ authenticator["key_id"] }}</span> | ||||||
|  |                             </a> | ||||||
|  |                           {% endif %} | ||||||
|  |                         {% endfor %} | ||||||
|  |                       </div> | ||||||
|  |                       <div class="collapse pending-tfa-collapse" id="collapseTotpTFA"> | ||||||
|                         <div class="form-group"> |                         <div class="form-group"> | ||||||
|                           <div class="input-group"> |                           <div class="input-group"> | ||||||
|                             <span class="input-group-addon" id="tfa-addon"><i class="bi bi-shield-lock-fill"></i></span> |                             <span class="input-group-addon" id="tfa-addon"><i class="bi bi-shield-lock-fill"></i></span> | ||||||
|                             <input type="number" min="000000" max="999999" name="token" class="form-control" placeholder="123456" autocomplete="one-time-code" aria-describedby="tfa-addon"> |                             <input type="number" min="000000" max="999999" name="token" class="form-control" placeholder="123456" autocomplete="one-time-code" aria-describedby="tfa-addon"> | ||||||
|                             <input type="hidden" name="tfa_method" value="totp"> |                             <input type="hidden" name="tfa_method" value="totp"> | ||||||
|  |                             <input type="hidden" name="key_id" id="totp_selected_key_id" /><br/> | ||||||
|                           </div> |                           </div> | ||||||
|                         </div> |                         </div> | ||||||
|                         <button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button> |                         <button class="btn btn-sm visible-xs-block visible-sm-inline visible-md-inline visible-lg-inline btn-default" type="submit" name="verify_tfa_login">{{ lang.login.login }}</button> | ||||||
|         </form> |  | ||||||
|         {% endif %} |  | ||||||
|         {% if pending_tfa_method == 'hotp' %} |  | ||||||
|         <div class="empty"></div> |  | ||||||
|         {% endif %} |  | ||||||
| 
 |  | ||||||
|         {% if pending_tfa_method == 'webauthn' %} |  | ||||||
|         <form role="form" method="post" id="webauthn_auth_form"> |  | ||||||
|           <center> |  | ||||||
|             <div style="cursor:pointer" id="start_webauthn_confirmation"> |  | ||||||
|               <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 24 24"> |  | ||||||
|                 <path d="M17.81 4.47c-.08 0-.16-.02-.23-.06C15.66 3.42 14 3 12.01 3c-1.98 0-3.86.47-5.57 1.41-.24.13-.54.04-.68-.2-.13-.24-.04-.55.2-.68C7.82 2.52 9.86 2 12.01 2c2.13 0 3.99.47 6.03 1.52.25.13.34.43.21.67-.09.18-.26.28-.44.28zM3.5 9.72c-.1 0-.2-.03-.29-.09-.23-.16-.28-.47-.12-.7.99-1.4 2.25-2.5 3.75-3.27C9.98 4.04 14 4.03 17.15 5.65c1.5.77 2.76 1.86 3.75 3.25.16.22.11.54-.12.7-.23.16-.54.11-.7-.12-.9-1.26-2.04-2.25-3.39-2.94-2.87-1.47-6.54-1.47-9.4.01-1.36.7-2.5 1.7-3.4 2.96-.08.14-.23.21-.39.21zm6.25 12.07c-.13 0-.26-.05-.35-.15-.87-.87-1.34-1.43-2.01-2.64-.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39c0 .28-.22.5-.5.5s-.5-.22-.5-.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44.32 2.77.93 3.85.64 1.15 1.08 1.64 1.85 2.42.19.2.19.51 0 .71-.11.1-.24.15-.37.15zm7.17-1.85c-1.19 0-2.24-.3-3.1-.89-1.49-1.01-2.38-2.65-2.38-4.39 0-.28.22-.5.5-.5s.5.22.5.5c0 1.41.72 2.74 1.94 3.56.71.48 1.54.71 2.54.71.24 0 .64-.03 1.04-.1.27-.05.53.13.58.41.05.27-.13.53-.41.58-.57.11-1.07.12-1.21.12zM14.91 22c-.04 0-.09-.01-.13-.02-1.59-.44-2.63-1.03-3.72-2.1-1.4-1.39-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07.93 1.94 2.08 1.94s2.08-.87 2.08-1.94c0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.44 1.58-6.61 4.03-.39.81-.59 1.76-.59 2.8 0 .78.07 2.01.67 3.61.1.26-.03.55-.29.64-.26.1-.55-.04-.64-.29-.49-1.31-.73-2.61-.73-3.96 0-1.2.23-2.29.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.55 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94s-3.08-1.32-3.08-2.94c0-1.07-.93-1.94-2.08-1.94s-2.08.87-2.08 1.94c0 1.71.66 3.31 1.87 4.51.95.94 1.86 1.46 3.27 1.85.27.07.42.35.35.61-.05.23-.26.38-.47.38z"></path> |  | ||||||
|               </svg> |  | ||||||
|               <p>{{ lang.tfa.start_webauthn_validation }}</p> |  | ||||||
|               <hr> |  | ||||||
|                       </div> |                       </div> | ||||||
|           </center> |  | ||||||
|           <p id="webauthn_status_auth"></p> |  | ||||||
|           <div class="alert alert-danger" style="display:none" id="webauthn_return_code"></div> |  | ||||||
|           <input type="hidden" name="token" id="webauthn_auth_data"/> |  | ||||||
|           <input type="hidden" name="tfa_method" value="webauthn"> |  | ||||||
|           <input type="hidden" name="verify_tfa_login"/><br/> |  | ||||||
|                     </form> |                     </form> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|           {% endif %} |           {% endif %} | ||||||
|  |             <!-- | ||||||
|  |             <div role="tabpanel" class="tab-pane" id="tfa_tab_hotp"> | ||||||
|  |               <div class="panel panel-default" style="margin-bottom: 0px;"> | ||||||
|  |                   <div class="panel-body"> | ||||||
|  |                       <div class="empty"></div> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|  |             --> | ||||||
|  |           {% if pending_tfa_authmechs["u2f"] is defined %} | ||||||
|  |             <div role="tabpanel" class="tab-pane active" id="tfa_tab_u2f"> | ||||||
|  |               <div class="panel panel-default" style="margin-bottom: 0px;"> | ||||||
|  |                   <div class="panel-body"> | ||||||
|                     {# leave this here to inform users that u2f is deprecated #} |                     {# leave this here to inform users that u2f is deprecated #} | ||||||
|         {% if pending_tfa_method == 'u2f' %} |  | ||||||
|                     <form role="form" method="post" id="u2f_auth_form"> |                     <form role="form" method="post" id="u2f_auth_form"> | ||||||
|  |                       <div> | ||||||
|                         <p>{{ lang.tfa.u2f_deprecated }}</p> |                         <p>{{ lang.tfa.u2f_deprecated }}</p> | ||||||
|                         <p><b>{{ lang.tfa.u2f_deprecated_important }}</b></p> |                         <p><b>{{ lang.tfa.u2f_deprecated_important }}</b></p> | ||||||
|                         <input type="hidden" name="token" value="destroy" /> |                         <input type="hidden" name="token" value="destroy" /> | ||||||
|                         <input type="hidden" name="tfa_method" value="u2f"> |                         <input type="hidden" name="tfa_method" value="u2f"> | ||||||
|                         <input type="hidden" name="verify_tfa_login"/><br/> |                         <input type="hidden" name="verify_tfa_login"/><br/> | ||||||
|                         <button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> |                         <button type="submit" class="btn btn-xs-lg btn-success" value="Login">{{ lang.login.login }}</button> | ||||||
|  |                       </div> | ||||||
|                     </form> |                     </form> | ||||||
|  |                   </div> | ||||||
|  |               </div> | ||||||
|  |             </div> | ||||||
|           {% endif %} |           {% endif %} | ||||||
|         </div> |         </div> | ||||||
|  | 
 | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 FreddleSpl0it
						FreddleSpl0it