index.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <?php
  2. use function Hestiacp\quoteshellarg\quoteshellarg;
  3. define("NO_AUTH_REQUIRED", true);
  4. $TAB = "RESET PASSWORD";
  5. // Main include
  6. include $_SERVER["DOCUMENT_ROOT"] . "/inc/main.php";
  7. if (isset($_SESSION["user"])) {
  8. header("Location: /list/user");
  9. }
  10. if ($_SESSION["POLICY_SYSTEM_PASSWORD_RESET"] == "no") {
  11. header("Location: /login/");
  12. exit();
  13. }
  14. if (!empty($_POST["user"]) && empty($_POST["code"])) {
  15. // Check token
  16. verify_csrf($_POST);
  17. $v_user = quoteshellarg($_POST["user"]);
  18. $user = $_POST["user"];
  19. $email = $_POST["email"];
  20. $cmd = "/usr/bin/sudo /usr/local/hestia/bin/v-list-user";
  21. exec($cmd . " " . $v_user . " json", $output, $return_var);
  22. if ($return_var == 0) {
  23. $data = json_decode(implode("", $output), true);
  24. unset($output);
  25. exec(HESTIA_CMD . "v-get-user-value " . $v_user . " RKEYEXP", $output, $return_var);
  26. $rkeyexp = json_decode(implode("", $output), true);
  27. if ($rkeyexp === null || $rkeyexp < time() - 1) {
  28. if ($email == $data[$user]["CONTACT"]) {
  29. $rkey = substr(password_hash("", PASSWORD_DEFAULT), 8, 12);
  30. $hash = password_hash($rkey, PASSWORD_DEFAULT);
  31. $v_rkey = tempnam("/tmp", "vst");
  32. $fp = fopen($v_rkey, "w");
  33. fwrite($fp, $hash . "\n");
  34. fclose($fp);
  35. exec(
  36. HESTIA_CMD . "v-change-user-rkey " . $v_user . " " . $v_rkey . "",
  37. $output,
  38. $return_var,
  39. );
  40. unset($output);
  41. unlink($v_rkey);
  42. $template = get_email_template("reset_password", $data[$user]["LANGUAGE"]);
  43. if (!empty($template)) {
  44. preg_match("/<subject>(.*?)<\/subject>/si", $template, $matches);
  45. $subject = $matches[1];
  46. $subject = str_replace(
  47. ["{{date}}", "{{hostname}}", "{{appname}}", "{{user}}"],
  48. [date("Y-m-d H:i:s"), get_hostname(), $_SESSION["APP_NAME"], $user],
  49. $subject,
  50. );
  51. $template = str_replace($matches[0], "", $template);
  52. } else {
  53. putenv("LANGUAGE=" . $data[$user]["LANGUAGE"]);
  54. $template = _(
  55. "Hello {{name}},\n" .
  56. "\n" .
  57. "To reset your {{appname}} password, please follow this link:\n" .
  58. "https://{{hostname}}/reset/?action=confirm&user={{user}}&code={{resetcode}}\n" .
  59. "\n" .
  60. "Alternatively, you may go to https://{{hostname}}/reset/?action=code&user={{user}} and enter the following reset code:\n" .
  61. "{{resetcode}}\n" .
  62. "\n" .
  63. "If you did not request password reset, please ignore this message and accept our apologies.\n" .
  64. "\n" .
  65. "Best regards,\n" .
  66. "\n" .
  67. "--\n" .
  68. "{{appname}}",
  69. );
  70. putenv("LANGUAGE=" . detect_user_language());
  71. }
  72. $name = $data[$user]["NAME"];
  73. $contact = $data[$user]["CONTACT"];
  74. $to = $data[$user]["CONTACT"];
  75. if (empty($subject)) {
  76. $subject = str_replace(
  77. ["{{subject}}", "{{hostname}}", "{{appname}}"],
  78. [
  79. sprintf(_("Password Reset at %s"), date("Y-m-d H:i:s")),
  80. get_hostname(),
  81. $_SESSION["APP_NAME"],
  82. ],
  83. $_SESSION["SUBJECT_EMAIL"],
  84. );
  85. }
  86. $hostname = get_hostname();
  87. if ($hostname) {
  88. $host = preg_replace(
  89. "/(\[?[^]]*\]?):([0-9]{1,5})$/",
  90. "$1",
  91. $_SERVER["HTTP_HOST"],
  92. );
  93. if ($host == $hostname) {
  94. $port_is_defined = preg_match(
  95. "/\[?[^]]*\]?:[0-9]{1,5}$/",
  96. $_SERVER["HTTP_HOST"],
  97. );
  98. if ($port_is_defined) {
  99. $port =
  100. ":" .
  101. preg_replace(
  102. "/(\[?[^]]*\]?):([0-9]{1,5})$/",
  103. "$2",
  104. $_SERVER["HTTP_HOST"],
  105. );
  106. } else {
  107. $port = "";
  108. }
  109. } else {
  110. $port = ":" . $_SERVER["SERVER_PORT"];
  111. }
  112. $from = !empty($_SESSION["FROM_EMAIL"])
  113. ? $_SESSION["FROM_EMAIL"]
  114. : "noreply@" . $hostname;
  115. $from_name = !empty($_SESSION["FROM_NAME"])
  116. ? $_SESSION["FROM_NAME"]
  117. : $_SESSION["APP_NAME"];
  118. putenv("LANGUAGE=" . $data[$user]["LANGUAGE"]);
  119. $name = empty($data[$user]["NAME"]) ? $user : $data[$user]["NAME"];
  120. $mailtext = translate_email($template, [
  121. "name" => htmlentities($name),
  122. "hostname" => htmlentities($hostname . $port),
  123. "user" => htmlentities($user),
  124. "resetcode" => htmlentities($rkey),
  125. "appname" => $_SESSION["APP_NAME"],
  126. ]);
  127. send_email($to, $subject, $mailtext, $from, $from_name, $data[$user]["NAME"]);
  128. putenv("LANGUAGE=" . detect_user_language());
  129. $error = _(
  130. "Password reset instructions have been sent to the email address associated with this account.",
  131. );
  132. }
  133. $error = _(
  134. "Password reset instructions have been sent to the email address associated with this account.",
  135. );
  136. } else {
  137. # Prevent user enumeration and let hackers guess username and working email
  138. $error = _(
  139. "Password reset instructions have been sent to the email address associated with this account.",
  140. );
  141. }
  142. } else {
  143. $error = _("Please wait 15 minutes before sending a new request.");
  144. }
  145. } else {
  146. # Prevent user enumeration and let hackers guess username and working email
  147. $error = _(
  148. "Password reset instructions have been sent to the email address associated with this account.",
  149. );
  150. }
  151. unset($output);
  152. }
  153. if (!empty($_POST["user"]) && !empty($_POST["code"]) && !empty($_POST["password"])) {
  154. // Check token
  155. verify_csrf($_POST);
  156. if ($_POST["password"] == $_POST["password_confirm"]) {
  157. $v_user = quoteshellarg($_POST["user"]);
  158. $user = $_POST["user"];
  159. exec(HESTIA_CMD . "v-list-user " . $v_user . " json", $output, $return_var);
  160. if ($return_var == 0) {
  161. $data = json_decode(implode("", $output), true);
  162. $rkey = $data[$user]["RKEY"];
  163. if (password_verify($_POST["code"], $rkey)) {
  164. unset($output);
  165. exec(HESTIA_CMD . "v-get-user-value " . $v_user . " RKEYEXP", $output, $return_var);
  166. if ($output[0] > time() - 900) {
  167. $v_password = tempnam("/tmp", "vst");
  168. $fp = fopen($v_password, "w");
  169. fwrite($fp, $_POST["password"] . "\n");
  170. fclose($fp);
  171. exec(
  172. HESTIA_CMD . "v-change-user-password " . $v_user . " " . $v_password,
  173. $output,
  174. $return_var,
  175. );
  176. unlink($v_password);
  177. if ($return_var > 0) {
  178. sleep(5);
  179. $error = _("An internal error occurred");
  180. } else {
  181. $_SESSION["user"] = $_POST["user"];
  182. header("Location: /");
  183. exit();
  184. }
  185. } else {
  186. sleep(5);
  187. $error = _("Code has been expired");
  188. exec(
  189. HESTIA_CMD .
  190. "v-log-user-login " .
  191. $v_user .
  192. " " .
  193. $v_ip .
  194. " failed " .
  195. $v_session_id .
  196. " " .
  197. $v_user_agent .
  198. ' yes "Reset code has been expired"',
  199. $output,
  200. $return_var,
  201. );
  202. }
  203. } else {
  204. sleep(5);
  205. $error = _("Invalid username or code");
  206. exec(
  207. HESTIA_CMD .
  208. "v-log-user-login " .
  209. $v_user .
  210. " " .
  211. $v_ip .
  212. " failed " .
  213. $v_session_id .
  214. " " .
  215. $v_user_agent .
  216. ' yes "Invalid Username or Code"',
  217. $output,
  218. $return_var,
  219. );
  220. }
  221. } else {
  222. sleep(5);
  223. $error = _("Invalid username or code");
  224. }
  225. } else {
  226. $error = _("Passwords do not match");
  227. }
  228. }
  229. if (empty($_GET["action"])) {
  230. require_once "../templates/header.php";
  231. require_once "../templates/pages/login/reset_1.php";
  232. } else {
  233. require_once "../templates/header.php";
  234. if ($_GET["action"] == "code") {
  235. require_once "../templates/pages/login/reset_2.php";
  236. }
  237. if ($_GET["action"] == "confirm" && !empty($_GET["code"])) {
  238. require_once "../templates/pages/login/reset_3.php";
  239. }
  240. }
  241. require_once "../templates/includes/login-footer.php";