index.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <?php
  2. use function Hestiacp\quoteshellarg\quoteshellarg;
  3. define("NO_AUTH_REQUIRED", true);
  4. // Main include
  5. include $_SERVER["DOCUMENT_ROOT"] . "/inc/main.php";
  6. $TAB = "LOGIN";
  7. if (isset($_GET["logout"])) {
  8. unset($_SESSION);
  9. session_unset();
  10. session_destroy();
  11. header("Location: /login/");
  12. }
  13. /* ACTIONS FOR CURRENT USER SESSION */
  14. if (isset($_SESSION["user"])) {
  15. // User impersonation
  16. // Allow administrators to view and manipulate contents of other user accounts
  17. if ($_SESSION["userContext"] === "admin" && !empty($_GET["loginas"])) {
  18. // Ensure token is passed and matches before granting user impersonation access
  19. if (verify_csrf($_GET)) {
  20. $v_user = quoteshellarg($_GET["loginas"]);
  21. $v_impersonator = quoteshellarg($_SESSION["user"]);
  22. exec(HESTIA_CMD . "v-list-user " . $v_user . " json", $output, $return_var);
  23. if ($return_var == 0) {
  24. $data = json_decode(implode("", $output), true);
  25. reset($data);
  26. $_SESSION["look"] = key($data);
  27. // Log impersonation events
  28. exec(
  29. HESTIA_CMD .
  30. "v-log-action " .
  31. $v_impersonator .
  32. " 'Info' 'Security' 'Logged in as another user (User: $v_user)'",
  33. $output,
  34. $return_var,
  35. );
  36. exec(
  37. HESTIA_CMD .
  38. "v-log-action system 'Warning' 'Security' 'User impersonation session started (User: $v_user, Administrator: $v_impersonator)'",
  39. $output,
  40. $return_var,
  41. );
  42. // Reset account details for File Manager to impersonated user
  43. unset($_SESSION["_sf2_attributes"]);
  44. unset($_SESSION["_sf2_meta"]);
  45. header("Location: /login/");
  46. } else {
  47. # User doesn't exists
  48. header("Location: /");
  49. }
  50. }
  51. exit();
  52. }
  53. // Set view based on account properties
  54. if (empty($_GET["loginas"])) {
  55. // Default view to Users list for administrator accounts
  56. if ($_SESSION["userContext"] === "admin" && !isset($_SESSION["look"])) {
  57. header("Location: /list/user/");
  58. exit();
  59. }
  60. // Obtain account properties
  61. $v_user = quoteshellarg(
  62. $_SESSION[
  63. $_SESSION["userContext"] === "admin" && isset($_SESSION["look"]) ? "look" : "user"
  64. ],
  65. );
  66. exec(HESTIA_CMD . "v-list-user " . $v_user . " json", $output, $return_var);
  67. $data = json_decode(implode("", $output), true);
  68. unset($output);
  69. // Determine package features and land user at the first available page
  70. if ($data[$user_plain]["WEB_DOMAINS"] !== "0") {
  71. header("Location: /list/web/");
  72. } elseif ($data[$user_plain]["DNS_DOMAINS"] !== "0") {
  73. header("Location: /list/dns/");
  74. } elseif ($data[$user_plain]["MAIL_DOMAINS"] !== "0") {
  75. header("Location: /list/mail/");
  76. } elseif ($data[$user_plain]["DATABASES"] !== "0") {
  77. header("Location: /list/db/");
  78. } elseif ($data[$user_plain]["CRON_JOBS"] !== "0") {
  79. header("Location: /list/cron/");
  80. } elseif ($data[$user_plain]["BACKUPS"] !== "0") {
  81. header("Location: /list/backup/");
  82. } else {
  83. header("Location: /error/");
  84. }
  85. exit();
  86. }
  87. // Do not allow non-administrators to access account impersonation
  88. if ($_SESSION["userContext"] !== "admin" && !empty($_GET["loginas"])) {
  89. header("Location: /login/");
  90. exit();
  91. }
  92. exit();
  93. }
  94. function authenticate_user($user, $password, $twofa = "") {
  95. unset($_SESSION["login"]);
  96. if (verify_csrf($_POST, true)) {
  97. $v_user = quoteshellarg($user);
  98. $ip = $_SERVER["REMOTE_ADDR"];
  99. $user_agent = $_SERVER["HTTP_USER_AGENT"];
  100. if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
  101. if (!empty($_SERVER["HTTP_CF_CONNECTING_IP"])) {
  102. $ip = $_SERVER["HTTP_CF_CONNECTING_IP"];
  103. }
  104. }
  105. $v_ip = quoteshellarg($ip);
  106. $v_user_agent = quoteshellarg($user_agent);
  107. // Get user's salt
  108. $output = "";
  109. exec(
  110. HESTIA_CMD . "v-get-user-salt " . $v_user . " " . $v_ip . " json",
  111. $output,
  112. $return_var,
  113. );
  114. $pam = json_decode(implode("", $output), true);
  115. unset($output);
  116. if ($return_var > 0) {
  117. sleep(2);
  118. if ($return_var == 5) {
  119. $error = '<p class="error">' . _("Account has been suspended") . "</p>";
  120. } elseif ($return_var == 1) {
  121. $error = '<p class="error">' . _("Unsupported hash method") . "</p>";
  122. } else {
  123. $error = '<p class="error">' . _("Invalid username or password") . "</p>";
  124. }
  125. return $error;
  126. } else {
  127. $salt = $pam[$user]["SALT"];
  128. $method = $pam[$user]["METHOD"];
  129. if ($method == "md5") {
  130. $hash = crypt($password, '$1$' . $salt . '$');
  131. }
  132. if ($method == "sha-512") {
  133. $hash = crypt($password, '$6$rounds=5000$' . $salt . '$');
  134. $hash = str_replace('$rounds=5000', "", $hash);
  135. }
  136. if ($method == "yescrypt") {
  137. $fp = tmpfile();
  138. $v_password = stream_get_meta_data($fp)["uri"];
  139. fwrite($fp, $password . "\n");
  140. exec(
  141. HESTIA_CMD .
  142. "v-check-user-password " .
  143. $v_user .
  144. " " .
  145. quoteshellarg($v_password) .
  146. " " .
  147. $v_ip .
  148. " yes",
  149. $output,
  150. $return_var,
  151. );
  152. $hash = $output[0];
  153. fclose($fp);
  154. unset($output, $fp, $v_password);
  155. }
  156. if ($method == "des") {
  157. $hash = crypt($password, $salt);
  158. }
  159. // Send hash via tmp file
  160. $v_hash = exec("mktemp -p /tmp");
  161. $fp = fopen($v_hash, "w");
  162. fwrite($fp, $hash . "\n");
  163. fclose($fp);
  164. // Check user hash
  165. exec(
  166. HESTIA_CMD . "v-check-user-hash " . $v_user . " " . $v_hash . " " . $v_ip,
  167. $output,
  168. $return_var,
  169. );
  170. unset($output);
  171. // Remove tmp file
  172. unlink($v_hash);
  173. // Check API answer
  174. if ($return_var > 0) {
  175. sleep(2);
  176. $error = '<p class="error">' . _("Invalid username or password") . "</p>";
  177. $v_session_id = quoteshellarg($_POST["token"]);
  178. exec(
  179. HESTIA_CMD .
  180. "v-log-user-login " .
  181. $v_user .
  182. " " .
  183. $v_ip .
  184. " failed " .
  185. $v_session_id .
  186. " " .
  187. $v_user_agent,
  188. $output,
  189. $return_var,
  190. );
  191. return $error;
  192. } else {
  193. // Get user specific parameters
  194. exec(HESTIA_CMD . "v-list-user " . $v_user . " json", $output, $return_var);
  195. $data = json_decode(implode("", $output), true);
  196. unset($output);
  197. if ($data[$user]["LOGIN_DISABLED"] === "yes") {
  198. sleep(2);
  199. $error = '<p class="error">' . _("Invalid username or password") . "</p>";
  200. $v_session_id = quoteshellarg($_POST["token"]);
  201. exec(
  202. HESTIA_CMD .
  203. "v-log-user-login " .
  204. $v_user .
  205. " " .
  206. $v_ip .
  207. " failed " .
  208. $v_session_id .
  209. " " .
  210. $v_user_agent .
  211. ' yes "Login disabled for this user"',
  212. $output,
  213. $return_var,
  214. );
  215. return $error;
  216. }
  217. if ($data[$user]["LOGIN_USE_IPLIST"] === "yes") {
  218. $v_login_user_allowed_ips = explode(",", $data[$user]["LOGIN_ALLOW_IPS"]);
  219. $v_login_user_allowed_ips = array_map("trim", $v_login_user_allowed_ips);
  220. if (!in_array($ip, $v_login_user_allowed_ips, true)) {
  221. sleep(2);
  222. $error = '<p class="error">' . _("Invalid username or password") . "</p>";
  223. $v_session_id = quoteshellarg($_POST["token"]);
  224. exec(
  225. HESTIA_CMD .
  226. "v-log-user-login " .
  227. $v_user .
  228. " " .
  229. $v_ip .
  230. " failed " .
  231. $v_session_id .
  232. " " .
  233. $v_user_agent .
  234. ' yes "Ip not in allowed list"',
  235. $output,
  236. $return_var,
  237. );
  238. return $error;
  239. }
  240. }
  241. if ($data[$user]["TWOFA"] != "") {
  242. exec(
  243. HESTIA_CMD . "v-check-user-2fa " . $v_user . " " . $v_twofa,
  244. $output,
  245. $return_var,
  246. );
  247. $error = "<p class=\"error\">" . _("Invalid or missing 2FA token") . "</p>";
  248. if (empty($twofa)) {
  249. $_SESSION["login"]["username"] = $user;
  250. $_SESSION["login"]["password"] = $password;
  251. return false;
  252. } else {
  253. $v_twofa = quoteshellarg($twofa);
  254. exec(
  255. HESTIA_CMD . "v-check-user-2fa " . $v_user . " " . $v_twofa,
  256. $output,
  257. $return_var,
  258. );
  259. unset($output);
  260. if ($return_var > 0) {
  261. sleep(2);
  262. $error =
  263. '<p class="error">' . _("Invalid or missing 2FA token") . "</p>";
  264. $_SESSION["login"]["username"] = $user;
  265. $_SESSION["login"]["password"] = $password;
  266. $v_session_id = quoteshellarg($_POST["token"]);
  267. if (isset($_SESSION["failed_twofa"])) {
  268. //allow a few failed attemps before start of logging.
  269. if ($_SESSION["failed_twofa"] > 2) {
  270. exec(
  271. HESTIA_CMD .
  272. "v-log-user-login " .
  273. $v_user .
  274. " " .
  275. $v_ip .
  276. " failed " .
  277. $v_session_id .
  278. " " .
  279. $v_user_agent .
  280. ' yes "Invalid or missing 2FA token"',
  281. $output,
  282. $return_var,
  283. );
  284. }
  285. $_SESSION["failed_twofa"]++;
  286. } else {
  287. $_SESSION["failed_twofa"] = 1;
  288. }
  289. unset($_POST["twofa"]);
  290. return $error;
  291. }
  292. }
  293. }
  294. // Define session user
  295. $_SESSION["user"] = key($data);
  296. $v_user = $_SESSION["user"];
  297. //log successfull login attempt
  298. $v_session_id = quoteshellarg($_POST["token"]);
  299. exec(
  300. HESTIA_CMD .
  301. "v-log-user-login " .
  302. $v_user .
  303. " " .
  304. $v_ip .
  305. " success " .
  306. $v_session_id .
  307. " " .
  308. $v_user_agent,
  309. $output,
  310. $return_var,
  311. );
  312. $_SESSION["LAST_ACTIVITY"] = time();
  313. $_SESSION["MURMUR"] = $_POST["murmur"];
  314. // Define user role / context
  315. $_SESSION["userContext"] = $data[$user]["ROLE"];
  316. // Set active user theme on login
  317. $_SESSION["userTheme"] = $data[$user]["THEME"];
  318. if ($_SESSION["POLICY_USER_CHANGE_THEME"] !== "yes") {
  319. unset($_SESSION["userTheme"]);
  320. }
  321. $_SESSION["userSortOrder"] = !empty($data[$user]["PREF_UI_SORT"])
  322. ? $data[$user]["PREF_UI_SORT"]
  323. : "name";
  324. // Define language
  325. $output = "";
  326. exec(HESTIA_CMD . "v-list-sys-languages json", $output, $return_var);
  327. $languages = json_decode(implode("", $output), true);
  328. $_SESSION["language"] = in_array($data[$v_user]["LANGUAGE"], $languages)
  329. ? $data[$user]["LANGUAGE"]
  330. : "en";
  331. // Regenerate session id to prevent session fixation
  332. session_regenerate_id(true);
  333. // Redirect request to control panel interface
  334. if (!empty($_SESSION["request_uri"])) {
  335. header("Location: " . $_SESSION["request_uri"]);
  336. unset($_SESSION["request_uri"]);
  337. exit();
  338. } else {
  339. if ($_SESSION["userContext"] === "admin") {
  340. header("Location: /list/user/");
  341. } else {
  342. if ($data[$user]["WEB_DOMAINS"] != "0") {
  343. header("Location: /list/web/");
  344. } elseif ($data[$user]["DNS_DOMAINS"] != "0") {
  345. header("Location: /list/dns/");
  346. } elseif ($data[$user]["MAIL_DOMAINS"] != "0") {
  347. header("Location: /list/mail/");
  348. } elseif ($data[$user]["DATABASES"] != "0") {
  349. header("Location: /list/db/");
  350. } elseif ($data[$user]["CRON_JOBS"] != "0") {
  351. header("Location: /list/cron/");
  352. } elseif ($data[$user]["BACKUPS"] != "0") {
  353. header("Location: /list/backup/");
  354. } else {
  355. header("Location: /error/");
  356. }
  357. }
  358. exit();
  359. }
  360. }
  361. }
  362. } else {
  363. unset($_POST);
  364. unset($_GET);
  365. unset($_SESSION);
  366. // Delete old session and start a new one
  367. session_write_close();
  368. session_unset();
  369. session_destroy();
  370. session_start();
  371. return false;
  372. }
  373. }
  374. if (empty($_POST["user"])) {
  375. $user = "";
  376. } else {
  377. if (preg_match('/^[[:alnum:]][-|\.|_[:alnum:]]{0,28}[[:alnum:]]$/', $_POST["user"])) {
  378. $_SESSION["login"]["username"] = $_POST["user"];
  379. } else {
  380. $user = "";
  381. }
  382. }
  383. if (
  384. !empty($_SESSION["login"]["username"]) &&
  385. !empty($_SESSION["login"]["password"]) &&
  386. !empty($_POST["twofa"])
  387. ) {
  388. $error = authenticate_user(
  389. $_SESSION["login"]["username"],
  390. $_SESSION["login"]["password"],
  391. $_POST["twofa"],
  392. );
  393. unset($_POST);
  394. } elseif (!empty($_SESSION["login"]["username"]) && !empty($_POST["password"])) {
  395. $error = authenticate_user($_SESSION["login"]["username"], $_POST["password"]);
  396. unset($_POST);
  397. }
  398. // Check system configuration
  399. load_hestia_config();
  400. // Detect language
  401. if (empty($_SESSION["language"])) {
  402. $output = "";
  403. exec(HESTIA_CMD . "v-list-sys-config json", $output, $return_var);
  404. $config = json_decode(implode("", $output), true);
  405. $lang = $config["config"]["LANGUAGE"];
  406. $output = "";
  407. exec(HESTIA_CMD . "v-list-sys-languages json", $output, $return_var);
  408. $languages = json_decode(implode("", $output), true);
  409. $_SESSION["language"] = in_array($lang, $languages) ? $lang : "en";
  410. }
  411. // Generate CSRF token
  412. $token = bin2hex(random_bytes(16));
  413. $_SESSION["token"] = $token;
  414. require_once "../templates/header.php";
  415. if (!empty($_SESSION["login"]["password"])) {
  416. require_once "../templates/pages/login/login_2.php";
  417. } elseif (empty($_SESSION["login"]["username"])) {
  418. require_once "../templates/pages/login/login" .
  419. ($_SESSION["LOGIN_STYLE"] != "old" ? "" : "_a") .
  420. ".php";
  421. } elseif (empty($_POST["password"])) {
  422. require_once "../templates/pages/login/login_1.php";
  423. } else {
  424. require_once "../templates/pages/login/login" .
  425. ($_SESSION["LOGIN_STYLE"] != "old" ? "" : "_a") .
  426. ".php";
  427. }