[Web] add verify selected tfa
This commit is contained in:
		| @@ -1493,7 +1493,7 @@ function unset_tfa_key($_data) { | ||||
|     return false; | ||||
|   } | ||||
| } | ||||
| function get_tfa($username = null, $key_id = null) { | ||||
| function get_tfa($username = null, $id = null) { | ||||
|   global $pdo; | ||||
|   if (isset($_SESSION['mailcow_cc_username'])) { | ||||
|     $username = $_SESSION['mailcow_cc_username']; | ||||
| @@ -1502,7 +1502,7 @@ function get_tfa($username = null, $key_id = null) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (!isset($key_id)){ | ||||
|   if (!isset($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'"); | ||||
| @@ -1520,10 +1520,10 @@ function get_tfa($username = null, $key_id = null) { | ||||
|     $data['additional'] = $results; | ||||
|     return $data; | ||||
|   } else { | ||||
|     // fetch specific authenticator details by key_id | ||||
|     // fetch specific authenticator details by id | ||||
|     $stmt = $pdo->prepare("SELECT * FROM `tfa` | ||||
|     WHERE `username` = :username AND `active` = '1'"); | ||||
|     $stmt->execute(array(':username' => $username)); | ||||
|     WHERE `username` = :username AND `id` = :id AND `active` = '1'"); | ||||
|     $stmt->execute(array(':username' => $username, ':id' => $id)); | ||||
|     $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||
|  | ||||
|     if (isset($row["authmech"])) { | ||||
| @@ -1531,9 +1531,10 @@ function get_tfa($username = null, $key_id = null) { | ||||
|           case "yubi_otp": | ||||
|             $data['name'] = "yubi_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 = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username AND `id` = :id"); | ||||
|             $stmt->execute(array( | ||||
|               ':username' => $username, | ||||
|               ':id' => $id | ||||
|             )); | ||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|             while($row = array_shift($rows)) { | ||||
| @@ -1545,9 +1546,10 @@ function get_tfa($username = null, $key_id = null) { | ||||
|           case "u2f": | ||||
|             $data['name'] = "u2f"; | ||||
|             $data['pretty'] = "Fido U2F"; | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username"); | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username AND `id` = :id"); | ||||
|             $stmt->execute(array( | ||||
|               ':username' => $username, | ||||
|               ':id' => $id | ||||
|             )); | ||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|             while($row = array_shift($rows)) { | ||||
| @@ -1563,9 +1565,10 @@ function get_tfa($username = null, $key_id = null) { | ||||
|           case "totp": | ||||
|             $data['name'] = "totp"; | ||||
|             $data['pretty'] = "Time-based OTP"; | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username"); | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username AND `id` = :id"); | ||||
|             $stmt->execute(array( | ||||
|               ':username' => $username, | ||||
|               ':id' => $id | ||||
|             )); | ||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|             while($row = array_shift($rows)) { | ||||
| @@ -1576,9 +1579,10 @@ function get_tfa($username = null, $key_id = null) { | ||||
|           case "webauthn": | ||||
|             $data['name'] = "webauthn"; | ||||
|             $data['pretty'] = "WebAuthn"; | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username"); | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'webauthn' AND `username` = :username AND `id` = :id"); | ||||
|             $stmt->execute(array( | ||||
|               ':username' => $username, | ||||
|               ':id' => $id | ||||
|             )); | ||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|             while($row = array_shift($rows)) { | ||||
| @@ -1609,8 +1613,8 @@ function verify_tfa_login($username, $_data) { | ||||
|  | ||||
|   if ($_data['tfa_method'] != 'u2f'){ | ||||
|     $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa` | ||||
|         WHERE `username` = :username AND `key_id` = :key_id AND `active` = '1'"); | ||||
|     $stmt->execute(array(':username' => $username, ':key_id' => $_data['key_id'])); | ||||
|         WHERE `username` = :username AND `id` = :id AND `active` = '1'"); | ||||
|     $stmt->execute(array(':username' => $username, ':id' => $_data['id'])); | ||||
|     $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||
|  | ||||
|     switch ($row["authmech"]) { | ||||
| @@ -1627,10 +1631,10 @@ function verify_tfa_login($username, $_data) { | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa` | ||||
|                 WHERE `username` = :username | ||||
|                 AND `authmech` = 'yubi_otp' | ||||
|                 AND `key_id` = ':key_id' | ||||
|                 AND `id` = ':id' | ||||
|                 AND `active`='1' | ||||
|                 AND `secret` LIKE :modhex"); | ||||
|             $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id, ':key_id' => $_data['key_id'])); | ||||
|             $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id, ':id' => $_data['id'])); | ||||
|             $row = $stmt->fetch(PDO::FETCH_ASSOC); | ||||
|             $yubico_auth = explode(':', $row['secret']); | ||||
|             $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]); | ||||
| @@ -1663,16 +1667,16 @@ function verify_tfa_login($username, $_data) { | ||||
|             return false; | ||||
|         break; | ||||
|         case "totp": | ||||
|             try { | ||||
|           try { | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa` | ||||
|                 WHERE `username` = :username | ||||
|                 AND `authmech` = 'totp' | ||||
|                 AND `key_id` = :key_id | ||||
|                 AND `id` = :id | ||||
|                 AND `active`='1'"); | ||||
|             $stmt->execute(array(':username' => $username, ':key_id' => $_data['key_id'])); | ||||
|             $stmt->execute(array(':username' => $username, ':id' => $_data['id'])); | ||||
|             $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); | ||||
|             foreach ($rows as $row) { | ||||
|                 if ($tfa->verifyCode($row['secret'], $_data['token']) === true) { | ||||
|               if ($tfa->verifyCode($row['secret'], $_data['token']) === true) { | ||||
|                 $_SESSION['tfa_id'] = $row['id']; | ||||
|                 $_SESSION['return'][] =  array( | ||||
|                     'type' => 'success', | ||||
| @@ -1680,7 +1684,7 @@ function verify_tfa_login($username, $_data) { | ||||
|                     'msg' => 'verified_totp_login' | ||||
|                 ); | ||||
|                 return true; | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|             $_SESSION['return'][] =  array( | ||||
|                 'type' => 'danger', | ||||
| @@ -1688,15 +1692,15 @@ function verify_tfa_login($username, $_data) { | ||||
|                 'msg' => 'totp_verification_failed' | ||||
|             ); | ||||
|             return false; | ||||
|             } | ||||
|             catch (PDOException $e) { | ||||
|           } | ||||
|           catch (PDOException $e) { | ||||
|             $_SESSION['return'][] =  array( | ||||
|                 'type' => 'danger', | ||||
|                 'log' => array(__FUNCTION__, $username, '*'), | ||||
|                 'msg' => array('mysql_error', $e) | ||||
|             ); | ||||
|             return false; | ||||
|             } | ||||
|           } | ||||
|         break; | ||||
|         case "webauthn": | ||||
|             $tokenData = json_decode($_data['token']); | ||||
| @@ -1706,13 +1710,20 @@ function verify_tfa_login($username, $_data) { | ||||
|             $id = base64_decode($tokenData->id); | ||||
|             $challenge = $_SESSION['challenge']; | ||||
|  | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `keyHandle` = :tokenId"); | ||||
|             $stmt->execute(array(':tokenId' => $tokenData->id)); | ||||
|             $stmt = $pdo->prepare("SELECT `id`, `key_id`, `keyHandle`, `username`, `publicKey` FROM `tfa` WHERE `id` = :id"); | ||||
|             $stmt->execute(array(':id' => $_data['id'])); | ||||
|             $process_webauthn = $stmt->fetch(PDO::FETCH_ASSOC); | ||||
|  | ||||
|             if (empty($process_webauthn) || empty($process_webauthn['publicKey']) || empty($process_webauthn['username'])) return false; | ||||
|             if (empty($process_webauthn)){ | ||||
|               $_SESSION['return'][] =  array( | ||||
|                   'type' => 'danger', | ||||
|                   'log' => array(__FUNCTION__, $username, '*'), | ||||
|                   'msg' => array('webauthn_verification_failed', 'authenticator not found') | ||||
|               ); | ||||
|               return false; | ||||
|             }  | ||||
|              | ||||
|             if ($process_webauthn['publicKey'] === false) { | ||||
|             if (empty($process_webauthn['publicKey']) || $process_webauthn['publicKey'] === false) { | ||||
|                 $_SESSION['return'][] =  array( | ||||
|                     'type' => 'danger', | ||||
|                     'log' => array(__FUNCTION__, $username, '*'), | ||||
|   | ||||
| @@ -194,8 +194,8 @@ function recursiveBase64StrToArrayBuffer(obj) { | ||||
|       $(".yubi-authenticator-selection").removeClass("active"); | ||||
|       $(this).addClass("active"); | ||||
|  | ||||
|       var key_id = $(this).children('span').first().text(); | ||||
|       $("#yubi_selected_key_id").val(key_id); | ||||
|       var id = $(this).children('input').first().val(); | ||||
|       $("#yubi_selected_id").val(id); | ||||
|  | ||||
|       $("#collapseYubiTFA").collapse('show'); | ||||
|     }); | ||||
| @@ -211,8 +211,8 @@ function recursiveBase64StrToArrayBuffer(obj) { | ||||
|       $(".totp-authenticator-selection").removeClass("active"); | ||||
|       $(this).addClass("active"); | ||||
|        | ||||
|       var key_id = $(this).children('span').first().text(); | ||||
|       $("#totp_selected_key_id").val(key_id); | ||||
|       var id = $(this).children('input').first().val(); | ||||
|       $("#totp_selected_id").val(id); | ||||
|  | ||||
|       $("#collapseTotpTFA").collapse('show'); | ||||
|     }); | ||||
| @@ -228,8 +228,8 @@ function recursiveBase64StrToArrayBuffer(obj) { | ||||
|       $(".webauthn-authenticator-selection").removeClass("active"); | ||||
|       $(this).addClass("active"); | ||||
|        | ||||
|       var key_id = $(this).children('span').first().text(); | ||||
|       $("#webauthn_selected_key_id").val(key_id); | ||||
|       var id = $(this).children('input').first().val(); | ||||
|       $("#webauthn_selected_id").val(id); | ||||
|        | ||||
|       $("#collapseWebAuthnTFA").collapse('show'); | ||||
|  | ||||
|   | ||||
| @@ -139,7 +139,7 @@ | ||||
|     <div class="modal-content"> | ||||
|       <div class="modal-header"> | ||||
|         <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button> | ||||
|         <h3 class="modal-title">2-Factor-Authentication</h3> | ||||
|         <h3 class="modal-title">{{ lang.tfa.tfa }}</h3> | ||||
|       </div> | ||||
|        | ||||
|         <ul class="nav nav-tabs" id="tabContent"> | ||||
| @@ -173,7 +173,7 @@ | ||||
|                     <form role="form" method="post" id="webauthn_auth_form"> | ||||
|                       <legend> | ||||
|                           <i class="bi bi-shield-fill-check"></i> | ||||
|                           Available Authenticators | ||||
|                           Authenticators | ||||
|                       </legend> | ||||
|                       <div class="list-group"> | ||||
|                         {% for authenticator in pending_tfa_methods %} | ||||
| @@ -181,6 +181,7 @@ | ||||
|                             <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> | ||||
|                               <input type="hidden" value="{{ authenticator["id"] }}" /><br/> | ||||
|                             </a> | ||||
|                           {% endif %} | ||||
|                         {% endfor %} | ||||
| @@ -192,7 +193,7 @@ | ||||
|                       <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/> | ||||
|                       <input type="hidden" name="id" id="webauthn_selected_id" /><br/> | ||||
|                     </form> | ||||
|                   </div> | ||||
|               </div> | ||||
| @@ -205,7 +206,7 @@ | ||||
|                     <form role="form" method="post"> | ||||
|                       <legend> | ||||
|                           <i class="bi bi-shield-fill-check"></i> | ||||
|                           Available Authenticators | ||||
|                           Authenticators | ||||
|                       </legend> | ||||
|                       <div class="list-group"> | ||||
|                         {% for authenticator in pending_tfa_methods %} | ||||
| @@ -213,6 +214,7 @@ | ||||
|                             <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> | ||||
|                               <input type="hidden" value="{{ authenticator["id"] }}" /> | ||||
|                             </a> | ||||
|                           {% endif %} | ||||
|                         {% endfor %} | ||||
| @@ -223,7 +225,7 @@ | ||||
|                             <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="hidden" name="tfa_method" value="yubi_otp"> | ||||
|                             <input type="hidden" name="key_id" id="yubi_selected_key_id" /> | ||||
|                             <input type="hidden" name="id" id="yubi_selected_id" /> | ||||
|                           </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> | ||||
| @@ -240,7 +242,7 @@ | ||||
|                     <form role="form" method="post">         | ||||
|                       <legend> | ||||
|                           <i class="bi bi-shield-fill-check"></i> | ||||
|                           Available Authenticators | ||||
|                           Authenticators | ||||
|                       </legend> | ||||
|                       <div class="list-group"> | ||||
|                         {% for authenticator in pending_tfa_methods %} | ||||
| @@ -248,6 +250,7 @@ | ||||
|                             <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> | ||||
|                               <input type="hidden" value="{{ authenticator["id"] }}" /> | ||||
|                             </a> | ||||
|                           {% endif %} | ||||
|                         {% endfor %} | ||||
| @@ -258,7 +261,7 @@ | ||||
|                             <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="hidden" name="tfa_method" value="totp"> | ||||
|                             <input type="hidden" name="key_id" id="totp_selected_key_id" /><br/> | ||||
|                             <input type="hidden" name="id" id="totp_selected_id" /><br/> | ||||
|                           </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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user