ht.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <?php declare(strict_types=1);
  2. const SUBPATH_REGEX = '^[a-z0-9-]{4,63}$';
  3. const ED25519_PUBKEY_REGEX = '^[a-zA-Z0-9/+]{68}$';
  4. function htSetupUserFs(string $id): void {
  5. // Setup SFTP directory
  6. if (mkdir(CONF['ht']['ht_path'] . '/fs/' . $id, 0000) !== true)
  7. output(500, 'Can\'t create user directory.');
  8. if (chmod(CONF['ht']['ht_path'] . '/fs/' . $id, 0775) !== true)
  9. output(500, 'Can\'t chmod user directory.');
  10. exescape([
  11. CONF['ht']['sudo_path'],
  12. '--',
  13. CONF['ht']['chgrp_path'],
  14. '--no-dereference',
  15. '--',
  16. CONF['ht']['sftpgo_group'],
  17. CONF['ht']['ht_path'] . '/fs/' . $id,
  18. ], result_code: $code);
  19. if ($code !== 0)
  20. output(500, 'Can\'t change user directory group.');
  21. // Setup Tor config directory
  22. if (mkdir(CONF['ht']['tor_config_path'] . '/' . $id, 0000) !== true)
  23. output(500, 'Can\'t create Tor config directory.');
  24. if (chmod(CONF['ht']['tor_config_path'] . '/' . $id, 0775) !== true)
  25. output(500, 'Can\'t chmod Tor config directory.');
  26. // Setup Tor keys directory
  27. exescape([
  28. CONF['ht']['sudo_path'],
  29. '-u',
  30. CONF['ht']['tor_user'],
  31. '--',
  32. CONF['ht']['mkdir_path'],
  33. '--mode=0700',
  34. '--',
  35. CONF['ht']['tor_keys_path'] . '/' . $id,
  36. ], result_code: $code);
  37. if ($code !== 0)
  38. output(500, 'Can\'t create Tor keys directory.');
  39. }
  40. function checkDomainFormat(string $domain): void {
  41. // If the domain must end without a dot
  42. if (!filter_var($domain, FILTER_VALIDATE_DOMAIN) OR !preg_match('/^(?=^.{1,254}$)([a-z0-9_-]{1,63}\.){1,126}[a-z0-9]{1,63}$/D', $domain))
  43. output(403, _('Domain malformed.'));
  44. }
  45. function formatDomain(string $domain): string {
  46. $domain = rtrim(strtolower($domain), '.');
  47. checkDomainFormat($domain);
  48. return $domain;
  49. }
  50. function listFsDirs(string $username): array {
  51. if ($username === '')
  52. return [];
  53. $absoluteDirs = glob(CONF['ht']['ht_path'] . '/fs/' . $username . '/*/', GLOB_ONLYDIR);
  54. $dirs = [];
  55. foreach ($absoluteDirs as $absoluteDir)
  56. if (preg_match('/^[a-zA-Z0-9_-]{1,64}$/D', basename($absoluteDir)))
  57. array_push($dirs, basename($absoluteDir));
  58. return $dirs;
  59. }
  60. function addSite(string $username, string $siteDir, string $address, string $type): void {
  61. insert('sites', [
  62. 'username' => $username,
  63. 'site_dir' => $siteDir,
  64. 'address' => $address,
  65. 'type' => $type,
  66. 'creation_date' => date('Y-m-d H:i:s'),
  67. ]);
  68. }
  69. function dirsStatuses(string $type): array {
  70. if (isset($_SESSION['id']) !== true)
  71. return [];
  72. $dbDirs = query('select', 'sites', [
  73. 'username' => $_SESSION['id'],
  74. 'type' => $type,
  75. ], ['site_dir']);
  76. $dirs = [];
  77. foreach (listFsDirs($_SESSION['id']) as $fsDir)
  78. $dirs[$fsDir] = in_array($fsDir, $dbDirs);
  79. return $dirs;
  80. }
  81. function htRelativeSymlink(string $target, string $name): void {
  82. chdir(pathinfo($name)['dirname']);
  83. $symlink = symlink($target, pathinfo($name)['basename']);
  84. chdir(ROOT_PATH);
  85. if ($symlink !== true)
  86. output(500, 'Unable to create symlink.');
  87. }
  88. function reloadTor() { // Using Tor control protocol <https://spec.torproject.org/control-spec/>
  89. $sock = stream_socket_client('unix://' . CONF['ht']['tor_control_socket']);
  90. if ($sock === false)
  91. output(500, 'Failed to connect to Tor control socket.', [$out]);
  92. fwrite($sock, 'AUTHENTICATE' . CRLF);
  93. if (($out = fgets($sock)) !== '250 OK' . CRLF)
  94. output(500, 'Failed to authenticate to Tor control socket.', [$out]);
  95. fwrite($sock, 'SIGNAL RELOAD' . CRLF);
  96. if (($out = fgets($sock)) !== '250 OK' . CRLF)
  97. output(500, 'Failed to reload Tor.', [$out]);
  98. fwrite($sock, 'QUIT' . CRLF);
  99. if (($out = fgets($sock)) !== '250 closing connection' . CRLF)
  100. output(500, 'Failed to close connection to Tor control socket.', [$out]);
  101. fclose($sock);
  102. }
  103. function htDeleteSite(string $address, string $type, string $user_id): void {
  104. if ($type === 'onion') {
  105. $dir = query('select', 'sites', [
  106. 'username' => $user_id,
  107. 'address' => $address,
  108. 'type' => $type,
  109. ], ['site_dir'])[0];
  110. // Delete Tor config
  111. if (unlink(CONF['ht']['tor_config_path'] . '/' . $user_id . '/' . $dir) !== true)
  112. output(500, 'Failed to delete Tor configuration.');
  113. // Reload Tor
  114. reloadTor();
  115. // Delete Tor keys
  116. exescape([
  117. CONF['ht']['sudo_path'],
  118. '-u',
  119. CONF['ht']['tor_user'],
  120. '--',
  121. CONF['ht']['rm_path'],
  122. '-r',
  123. '--',
  124. CONF['ht']['tor_keys_path'] . '/' . $user_id . '/' . $dir,
  125. ], result_code: $code);
  126. if ($code !== 0)
  127. output(500, 'Failed to delete Tor keys.');
  128. }
  129. if ($type === 'dns') {
  130. // Delete Let's Encrypt certificate
  131. exescape([
  132. CONF['ht']['sudo_path'],
  133. CONF['ht']['certbot_path'],
  134. '--config',
  135. CONF['ht']['certbot_config_path'],
  136. 'delete',
  137. '--quiet',
  138. '--cert-name',
  139. $address,
  140. ], result_code: $code);
  141. if ($code !== 0)
  142. output(500, 'Certbot failed to delete the Let\'s Encrypt certificate.');
  143. }
  144. $link = CONF['ht']['ht_path'] . '/uri/' . match ($type) {
  145. 'onion', 'dns' => $address,
  146. 'subdomain' => $address . '.' . CONF['ht']['subdomain_domain'],
  147. 'subpath' => CONF['ht']['subpath_domain'] . '/' . $address,
  148. };
  149. if (unlink($link) !== true)
  150. output(500, 'Unable to delete symlink.');
  151. query('delete', 'sites', [
  152. 'username' => $user_id,
  153. 'type' => $type,
  154. 'address' => $address,
  155. ]);
  156. }