index.php 13 KB

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