index.php 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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 = preg_replace(
  100. "/(\[?[^]]*\]?):([0-9]{1,5})$/",
  101. "$2",
  102. $_SERVER["HTTP_HOST"],
  103. );
  104. } else {
  105. $port = "";
  106. }
  107. } else {
  108. $port = ":" . $_SERVER["SERVER_PORT"];
  109. }
  110. $from = !empty($_SESSION["FROM_EMAIL"])
  111. ? $_SESSION["FROM_EMAIL"]
  112. : "noreply@" . $hostname;
  113. $from_name = !empty($_SESSION["FROM_NAME"])
  114. ? $_SESSION["FROM_NAME"]
  115. : $_SESSION["APP_NAME"];
  116. putenv("LANGUAGE=" . $data[$user]["LANGUAGE"]);
  117. $name = empty($data[$user]["NAME"]) ? $user : $data[$user]["NAME"];
  118. $mailtext = translate_email($template, [
  119. "name" => $name,
  120. "hostname" => $hostname . $port,
  121. "user" => $user,
  122. "resetcode" => $rkey,
  123. "appname" => $_SESSION["APP_NAME"],
  124. ]);
  125. send_email($to, $subject, $mailtext, $from, $from_name, $data[$user]["NAME"]);
  126. putenv("LANGUAGE=" . detect_user_language());
  127. $error = _(
  128. "Password reset instructions have been sent to the email address associated with this account.",
  129. );
  130. }
  131. $error = _(
  132. "Password reset instructions have been sent to the email address associated with this account.",
  133. );
  134. } else {
  135. # Prevent user enumeration and let hackers guess username and working email
  136. $error = _(
  137. "Password reset instructions have been sent to the email address associated with this account.",
  138. );
  139. }
  140. } else {
  141. $error = _("Please wait 15 minutes before sending a new request.");
  142. }
  143. } else {
  144. # Prevent user enumeration and let hackers guess username and working email
  145. $error = _(
  146. "Password reset instructions have been sent to the email address associated with this account.",
  147. );
  148. }
  149. unset($output);
  150. }
  151. if (!empty($_POST["user"]) && !empty($_POST["code"]) && !empty($_POST["password"])) {
  152. // Check token
  153. verify_csrf($_POST);
  154. if ($_POST["password"] == $_POST["password_confirm"]) {
  155. $v_user = quoteshellarg($_POST["user"]);
  156. $user = $_POST["user"];
  157. exec(HESTIA_CMD . "v-list-user " . $v_user . " json", $output, $return_var);
  158. if ($return_var == 0) {
  159. $data = json_decode(implode("", $output), true);
  160. $rkey = $data[$user]["RKEY"];
  161. if (password_verify($_POST["code"], $rkey)) {
  162. unset($output);
  163. exec(HESTIA_CMD . "v-get-user-value " . $v_user . " RKEYEXP", $output, $return_var);
  164. if ($output[0] > time() - 900) {
  165. $v_password = tempnam("/tmp", "vst");
  166. $fp = fopen($v_password, "w");
  167. fwrite($fp, $_POST["password"] . "\n");
  168. fclose($fp);
  169. exec(
  170. HESTIA_CMD . "v-change-user-password " . $v_user . " " . $v_password,
  171. $output,
  172. $return_var,
  173. );
  174. unlink($v_password);
  175. if ($return_var > 0) {
  176. sleep(5);
  177. $error = _("An internal error occurred");
  178. } else {
  179. $_SESSION["user"] = $_POST["user"];
  180. header("Location: /");
  181. exit();
  182. }
  183. } else {
  184. sleep(5);
  185. $error = _("Code has been expired");
  186. exec(
  187. HESTIA_CMD .
  188. "v-log-user-login " .
  189. $v_user .
  190. " " .
  191. $v_ip .
  192. " failed " .
  193. $v_session_id .
  194. " " .
  195. $v_user_agent .
  196. ' yes "Reset code has been expired"',
  197. $output,
  198. $return_var,
  199. );
  200. }
  201. } else {
  202. sleep(5);
  203. $error = _("Invalid username or code");
  204. exec(
  205. HESTIA_CMD .
  206. "v-log-user-login " .
  207. $v_user .
  208. " " .
  209. $v_ip .
  210. " failed " .
  211. $v_session_id .
  212. " " .
  213. $v_user_agent .
  214. ' yes "Invalid Username or Code"',
  215. $output,
  216. $return_var,
  217. );
  218. }
  219. } else {
  220. sleep(5);
  221. $error = _("Invalid username or code");
  222. }
  223. } else {
  224. $error = _("Passwords do not match");
  225. }
  226. }
  227. if (empty($_GET["action"])) {
  228. require_once "../templates/header.php";
  229. require_once "../templates/pages/login/reset_1.php";
  230. } else {
  231. require_once "../templates/header.php";
  232. if ($_GET["action"] == "code") {
  233. require_once "../templates/pages/login/reset_2.php";
  234. }
  235. if ($_GET["action"] == "confirm" && !empty($_GET["code"])) {
  236. require_once "../templates/pages/login/reset_3.php";
  237. }
  238. }