Przeglądaj źródła

[Web] Various fixes; Allow users to login with FIDO2, SOGo SSO is a wip

andryyy 4 lat temu
rodzic
commit
d156a93a84

+ 54 - 44
data/web/inc/functions.inc.php

@@ -1097,8 +1097,7 @@ function set_tfa($_data) {
   $_data_log = $_data;
   $_data_log = $_data;
   !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 ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
+  if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
       $_SESSION['return'][] =  array(
       $_SESSION['return'][] =  array(
         'type' => 'danger',
         'type' => 'danger',
         'log' => array(__FUNCTION__, $_data_log),
         'log' => array(__FUNCTION__, $_data_log),
@@ -1107,18 +1106,35 @@ function set_tfa($_data) {
       return false;
       return false;
   }
   }
   $stmt = $pdo->prepare("SELECT `password` FROM `admin`
   $stmt = $pdo->prepare("SELECT `password` FROM `admin`
-      WHERE `username` = :user");
-  $stmt->execute(array(':user' => $username));
+      WHERE `username` = :username");
+  $stmt->execute(array(':username' => $username));
   $row = $stmt->fetch(PDO::FETCH_ASSOC);
   $row = $stmt->fetch(PDO::FETCH_ASSOC);
-  if (!verify_hash($row['password'], $_data["confirm_password"])) {
-    $_SESSION['return'][] =  array(
-      'type' => 'danger',
-      'log' => array(__FUNCTION__, $_data_log),
-      'msg' => 'access_denied'
-    );
-    return false;
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if (!empty($num_results)) {
+    if (!verify_hash($row['password'], $_data["confirm_password"])) {
+      $_SESSION['return'][] =  array(
+        'type' => 'danger',
+        'log' => array(__FUNCTION__, $_data_log),
+        'msg' => 'access_denied'
+      );
+      return false;
+    }
+  }
+  $stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
+      WHERE `username` = :username");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+  if (!empty($num_results)) {
+    if (!verify_hash($row['password'], $_data["confirm_password"])) {
+      $_SESSION['return'][] =  array(
+        'type' => 'danger',
+        'log' => array(__FUNCTION__, $_data_log),
+        'msg' => 'access_denied'
+      );
+      return false;
+    }
   }
   }
-
 	switch ($_data["tfa_method"]) {
 	switch ($_data["tfa_method"]) {
 		case "yubi_otp":
 		case "yubi_otp":
       $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
       $key_id = (!isset($_data["key_id"])) ? 'unidentified' : $_data["key_id"];
@@ -1241,8 +1257,7 @@ function fido2($_data) {
 	switch ($_data["action"]) {
 	switch ($_data["action"]) {
 		case "register":
 		case "register":
       $username = $_SESSION['mailcow_cc_username'];
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
           $_SESSION['return'][] =  array(
           $_SESSION['return'][] =  array(
             'type' => 'danger',
             'type' => 'danger',
             'log' => array(__FUNCTION__, $_data["action"]),
             'log' => array(__FUNCTION__, $_data["action"]),
@@ -1273,9 +1288,8 @@ function fido2($_data) {
 		case "get_user_cids":
 		case "get_user_cids":
       // Used to exclude existing CredentialIds while registering
       // Used to exclude existing CredentialIds while registering
       $username = $_SESSION['mailcow_cc_username'];
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        return false;
       }
       }
       $stmt = $pdo->prepare("SELECT `credentialId` FROM `fido2` WHERE `username` = :username");
       $stmt = $pdo->prepare("SELECT `credentialId` FROM `fido2` WHERE `username` = :username");
       $stmt->execute(array(':username' => $username));
       $stmt->execute(array(':username' => $username));
@@ -1312,9 +1326,8 @@ function fido2($_data) {
 		break;
 		break;
 		case "get_friendly_names":
 		case "get_friendly_names":
       $username = $_SESSION['mailcow_cc_username'];
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        return false;
       }
       }
       $stmt = $pdo->prepare("SELECT SHA2(`credentialId`, 256) AS `cid`, `created`, `certificateSubject`, `friendlyName` FROM `fido2` WHERE `username` = :username");
       $stmt = $pdo->prepare("SELECT SHA2(`credentialId`, 256) AS `cid`, `created`, `certificateSubject`, `friendlyName` FROM `fido2` WHERE `username` = :username");
       $stmt->execute(array(':username' => $username));
       $stmt->execute(array(':username' => $username));
@@ -1330,14 +1343,13 @@ function fido2($_data) {
 		break;
 		break;
 		case "unset_fido2_key":
 		case "unset_fido2_key":
       $username = $_SESSION['mailcow_cc_username'];
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          $_SESSION['return'][] =  array(
-            'type' => 'danger',
-            'log' => array(__FUNCTION__, $_data["action"]),
-            'msg' => 'access_denied'
-          );
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        $_SESSION['return'][] =  array(
+          'type' => 'danger',
+          'log' => array(__FUNCTION__, $_data["action"]),
+          'msg' => 'access_denied'
+        );
+        return false;
       }
       }
       $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username AND SHA2(`credentialId`, 256) = :cid");
       $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username AND SHA2(`credentialId`, 256) = :cid");
       $stmt->execute(array(
       $stmt->execute(array(
@@ -1352,14 +1364,13 @@ function fido2($_data) {
 		break;
 		break;
     case "edit_fn":
     case "edit_fn":
       $username = $_SESSION['mailcow_cc_username'];
       $username = $_SESSION['mailcow_cc_username'];
-      if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-        $_SESSION['mailcow_cc_role'] != "admin") {
-          $_SESSION['return'][] =  array(
-            'type' => 'danger',
-            'log' => array(__FUNCTION__, $_data["action"]),
-            'msg' => 'access_denied'
-          );
-          return false;
+      if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+        $_SESSION['return'][] =  array(
+          'type' => 'danger',
+          'log' => array(__FUNCTION__, $_data["action"]),
+          'msg' => 'access_denied'
+        );
+        return false;
       }
       }
       $stmt = $pdo->prepare("UPDATE `fido2` SET `friendlyName` = :friendlyName WHERE SHA2(`credentialId`, 256) = :cid AND `username` = :username");
       $stmt = $pdo->prepare("UPDATE `fido2` SET `friendlyName` = :friendlyName WHERE SHA2(`credentialId`, 256) = :cid AND `username` = :username");
       $stmt->execute(array(
       $stmt->execute(array(
@@ -1383,14 +1394,13 @@ function unset_tfa_key($_data) {
   $_data_log = $_data;
   $_data_log = $_data;
   $id = intval($_data['unset_tfa_key']);
   $id = intval($_data['unset_tfa_key']);
   $username = $_SESSION['mailcow_cc_username'];
   $username = $_SESSION['mailcow_cc_username'];
-  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
-      $_SESSION['return'][] =  array(
-        'type' => 'danger',
-        'log' => array(__FUNCTION__, $_data_log),
-        'msg' => 'access_denied'
-      );
-      return false;
+  if (!isset($_SESSION['mailcow_cc_role']) || empty($username)) {
+    $_SESSION['return'][] =  array(
+      'type' => 'danger',
+      'log' => array(__FUNCTION__, $_data_log),
+      'msg' => 'access_denied'
+    );
+    return false;
   }
   }
   try {
   try {
     if (!is_numeric($id)) {
     if (!is_numeric($id)) {

+ 8 - 0
data/web/inc/functions.mailbox.inc.php

@@ -4263,6 +4263,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             $stmt->execute(array(
             $stmt->execute(array(
               ':username' => $username
               ':username' => $username
             ));
             ));
+            $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
+            $stmt->execute(array(
+              ':username' => $username,
+            ));
+            $stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
+            $stmt->execute(array(
+              ':username' => $username,
+            ));
             $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
             $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
                 WHERE `goto` REGEXP :username");
                 WHERE `goto` REGEXP :username");
             $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
             $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));

+ 1 - 1
data/web/inc/triggers.inc.php

@@ -79,7 +79,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['acl']['login_as'] == "1")
   }
   }
 }
 }
 
 
-if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
+if (isset($_SESSION['mailcow_cc_role'])) {
 	if (isset($_POST["set_tfa"])) {
 	if (isset($_POST["set_tfa"])) {
 		set_tfa($_POST);
 		set_tfa($_POST);
 	}
 	}

+ 3 - 1
data/web/js/site/user.js

@@ -1,3 +1,5 @@
+// Base64 functions
+var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
 $(document).ready(function() {
 $(document).ready(function() {
   // Spam score slider
   // Spam score slider
   var spam_slider = $('#spam_score')[0];
   var spam_slider = $('#spam_score')[0];
@@ -75,7 +77,7 @@ jQuery(function($){
   $('.clear-last-logins').on('click', function () {if (confirm(lang.delete_ays)) {last_logins('reset');}})
   $('.clear-last-logins').on('click', function () {if (confirm(lang.delete_ays)) {last_logins('reset');}})
   $(".login-history").on('click', function(e) {e.preventDefault(); last_logins('get', $(this).data('days'));$(this).addClass('active').siblings().removeClass('active');});
   $(".login-history").on('click', function(e) {e.preventDefault(); last_logins('get', $(this).data('days'));$(this).addClass('active').siblings().removeClass('active');});
 
 
-  function last_logins(action, days = 7) {
+  function last_logins(action, days = 1) {
     if (action == 'get') {
     if (action == 'get') {
       $('.last-login').html('<i class="bi bi-hourglass"></i>' +  lang.waiting);
       $('.last-login').html('<i class="bi bi-hourglass"></i>' +  lang.waiting);
       $.ajax({
       $.ajax({

+ 25 - 16
data/web/json_api.php

@@ -141,7 +141,7 @@ if (isset($_GET['query'])) {
         // fido2-registration via POST
         // fido2-registration via POST
         case "fido2-registration":
         case "fido2-registration":
           header('Content-Type: application/json');
           header('Content-Type: application/json');
-          if (isset($_SESSION["mailcow_cc_role"]) && ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin")) {
+          if (isset($_SESSION["mailcow_cc_role"])) {
             $post = trim(file_get_contents('php://input'));
             $post = trim(file_get_contents('php://input'));
             if ($post) {
             if ($post) {
               $post = json_decode($post);
               $post = json_decode($post);
@@ -302,9 +302,22 @@ if (isset($_GET['query'])) {
           if ($obj_props['superadmin'] === 1) {
           if ($obj_props['superadmin'] === 1) {
             $_SESSION["mailcow_cc_role"] = "admin";
             $_SESSION["mailcow_cc_role"] = "admin";
           }
           }
-          else {
+          elseif ($obj_props['superadmin'] === 0) {
             $_SESSION["mailcow_cc_role"] = "domainadmin";
             $_SESSION["mailcow_cc_role"] = "domainadmin";
           }
           }
+          else {
+            $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :username");
+            $stmt->execute(array(':username' => $process_fido2['username']));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if ($row['username'] == $process_fido2['username']) {
+              $_SESSION["mailcow_cc_role"] = "user";
+            }
+          }
+          if (empty($_SESSION["mailcow_cc_role"])) {
+            session_unset();
+            session_destroy();
+            exit;
+          }
           $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
           $_SESSION["mailcow_cc_username"] = $process_fido2['username'];
           $_SESSION["fido2_cid"] = $process_fido2['cid'];
           $_SESSION["fido2_cid"] = $process_fido2['cid'];
           unset($_SESSION["challenge"]);
           unset($_SESSION["challenge"]);
@@ -339,17 +352,15 @@ if (isset($_GET['query'])) {
       switch ($category) {
       switch ($category) {
         case "u2f-registration":
         case "u2f-registration":
           header('Content-Type: application/javascript');
           header('Content-Type: application/javascript');
-          if (isset($_SESSION["mailcow_cc_role"]) &&
-            ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") &&
-            $_SESSION["mailcow_cc_username"] == $object) {
-              list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object));
-              $_SESSION['regReq'] = json_encode($req);
-              $_SESSION['regSigs'] = json_encode($sigs);
-              echo 'var req = ' . json_encode($req) . ';';
-              echo 'var registeredKeys = ' . json_encode($sigs) . ';';
-              echo 'var appId = req.appId;';
-              echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
-              return;
+          if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_username"] == $object) {
+            list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object));
+            $_SESSION['regReq'] = json_encode($req);
+            $_SESSION['regSigs'] = json_encode($sigs);
+            echo 'var req = ' . json_encode($req) . ';';
+            echo 'var registeredKeys = ' . json_encode($sigs) . ';';
+            echo 'var appId = req.appId;';
+            echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
+            return;
           }
           }
           else {
           else {
             return;
             return;
@@ -358,9 +369,7 @@ if (isset($_GET['query'])) {
         // fido2-registration via GET
         // fido2-registration via GET
         case "fido2-registration":
         case "fido2-registration":
           header('Content-Type: application/json');
           header('Content-Type: application/json');
-          if (isset($_SESSION["mailcow_cc_role"]) &&
-            ($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") &&
-            $_SESSION["mailcow_cc_username"] == $object) {
+          if (isset($_SESSION["mailcow_cc_role"])) {
               // Exclude existing CredentialIds, if any
               // Exclude existing CredentialIds, if any
               $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
               $excludeCredentialIds = fido2(array("action" => "get_user_cids"));
               $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);
               $createArgs = $WebAuthn->getCreateArgs($_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], $_SESSION["mailcow_cc_username"], 30, true, $GLOBALS['FIDO2_UV_FLAG_REGISTER'], $excludeCredentialIds);

+ 2 - 2
data/web/modals/footer.php

@@ -1,5 +1,5 @@
 <?php
 <?php
-if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")):
+if (isset($_SESSION['mailcow_cc_role'])) {
 ?>
 ?>
 <div class="modal fade" id="YubiOTPModal" tabindex="-1" role="dialog" aria-labelledby="YubiOTPModalLabel">
 <div class="modal fade" id="YubiOTPModal" tabindex="-1" role="dialog" aria-labelledby="YubiOTPModalLabel">
   <div class="modal-dialog" role="document">
   <div class="modal-dialog" role="document">
@@ -127,7 +127,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 </div>
 </div>
 
 
 <?php
 <?php
-endif;
+}
 if (isset($_SESSION['pending_tfa_method'])):
 if (isset($_SESSION['pending_tfa_method'])):
   $tfa_method = $_SESSION['pending_tfa_method'];
   $tfa_method = $_SESSION['pending_tfa_method'];
 ?>
 ?>

Plik diff jest za duży
+ 127 - 36
data/web/user.php


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików