BioTorrents.de’s version of Gazelle
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2fa.php 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. <?php
  2. declare(strict_types = 1);
  3. $ENV = ENV::go();
  4. require_once SERVER_ROOT.'/classes/twofa.class.php';
  5. require_once SERVER_ROOT.'/classes/u2f.class.php';
  6. $TwoFA = new TwoFactorAuth($ENV->SITE_NAME);
  7. $U2F = new u2f\U2F('https://'.SITE_DOMAIN);
  8. if ($Type = $_POST['type'] ?? false) {
  9. if ($Type === 'PGP') {
  10. if (!empty($_POST['publickey']) && (strpos($_POST['publickey'], 'BEGIN PGP PUBLIC KEY BLOCK') === false || strpos($_POST['publickey'], 'END PGP PUBLIC KEY BLOCK') === false)) {
  11. $Error = "Invalid PGP public key";
  12. } else {
  13. $DB->query("
  14. UPDATE users_main
  15. SET PublicKey = '".db_string($_POST['publickey'])."'
  16. WHERE ID = $UserID");
  17. $Message = 'Public key '.(empty($_POST['publickey']) ? 'removed' : 'updated') ;
  18. }
  19. }
  20. if ($Type === '2FA-E') {
  21. if ($TwoFA->verifyCode($_POST['twofasecret'], $_POST['twofa'])) {
  22. $DB->query("
  23. UPDATE users_main
  24. SET TwoFactor='".db_string($_POST['twofasecret'])."'
  25. WHERE ID = $UserID");
  26. $Message = "Two Factor Authentication enabled";
  27. } else {
  28. $Error = "Invalid 2FA verification code";
  29. }
  30. }
  31. if ($Type === '2FA-D') {
  32. $DB->query("
  33. UPDATE users_main
  34. SET TwoFactor = NULL
  35. WHERE ID = $UserID");
  36. $Message = "Two Factor Authentication disabled";
  37. }
  38. if ($Type === 'U2F-E') {
  39. try {
  40. $U2FReg = $U2F->doRegister(json_decode($_POST['u2f-request']), json_decode($_POST['u2f-response']));
  41. $DB->query("
  42. INSERT INTO u2f
  43. (UserID, KeyHandle, PublicKey, Certificate, Counter, Valid)
  44. Values ($UserID, '".db_string($U2FReg->keyHandle)."', '".db_string($U2FReg->publicKey)."', '".db_string($U2FReg->certificate)."', '".db_string($U2FReg->counter)."', '1')");
  45. $Message = "U2F token registered";
  46. } catch (Exception $e) {
  47. $Error = "Failed to register U2F token";
  48. }
  49. }
  50. if ($Type === 'U2F-D') {
  51. $DB->query("
  52. DELETE FROM u2f
  53. WHERE UserID = $UserID");
  54. $Message = 'U2F tokens deregistered';
  55. }
  56. }
  57. $U2FRegs = [];
  58. $DB->query("
  59. SELECT KeyHandle, PublicKey, Certificate, Counter
  60. FROM u2f
  61. WHERE UserID = $UserID");
  62. // Needs to be an array of objects, so we can't use to_array()
  63. while (list($KeyHandle, $PublicKey, $Certificate, $Counter) = $DB->next_record()) {
  64. $U2FRegs[] = (object)['keyHandle'=>$KeyHandle, 'publicKey'=>$PublicKey, 'certificate'=>$Certificate, 'counter'=>$Counter];
  65. }
  66. $DB->query("
  67. SELECT PublicKey, TwoFactor
  68. FROM users_main
  69. WHERE ID = $UserID");
  70. list($PublicKey, $TwoFactor) = $DB->next_record();
  71. list($U2FRequest, $U2FSigs) = $U2F->getRegisterData($U2FRegs);
  72. View::show_header("2FA Settings", 'u2f');
  73. ?>
  74. <h2>Additional Account Security Options</h2>
  75. <div>
  76. <?php if (isset($Message)) { ?>
  77. <div class="alertbar"><?=$Message?>
  78. </div>
  79. <?php }
  80. if (isset($Error)) { ?>
  81. <div class="alertbar error"><?=$Error?>
  82. </div>
  83. <?php } ?>
  84. <div class="box">
  85. <div class="head">
  86. <strong>PGP Public Key</strong>
  87. </div>
  88. <div class="pad">
  89. <?php if (empty($PublicKey)) {
  90. if (!empty($TwoFactor) || sizeof($U2FRegs) > 0) { ?>
  91. <strong class="important_text">
  92. You have a form of 2FA enabled but no PGP key associated with your account.
  93. If you lose access to your 2FA device, you will permanently lose access to your account.
  94. </strong>
  95. <?php } ?>
  96. <p>
  97. When setting up any form of second factor authentication, it is strongly recommended that you add your PGP
  98. public key as a form of secure recovery in the event that you lose access to your second factor device.
  99. </p>
  100. <p>
  101. After adding a PGP public key to your account, you will be able to disable your account's second factor
  102. protection by solving a challenge that only someone with your private key could solve.
  103. </p>
  104. <p>
  105. Additionally, being able to solve such a challenge when given manually by staff will suffice to provide proof of
  106. ownership of your account, provided no revocation certificate has been published for your key.
  107. </p>
  108. <p>
  109. Before adding your PGP public key, please make sure that you have taken the necessary precautions to protect it
  110. from loss (backup) or theft (revocation certificate).
  111. </p>
  112. <?php
  113. } else { ?>
  114. <p>
  115. The PGP public key associated with your account is shown below.
  116. </p>
  117. <p>
  118. This key can be used to create challenges that are only solvable by the holder of the related private key.
  119. Successfully solving these challenges is necessary for disabling any form of second factor authentication or
  120. proving ownership of this account to staff when you are unable to login.
  121. </p>
  122. <?php } ?>
  123. <form method="post">
  124. <input type="hidden" name="type" value="PGP">
  125. Public Key
  126. <br />
  127. <textarea name="publickey" id="publickey" spellcheck="false" cols="64"
  128. rows="8"><?=display_str($PublicKey)?></textarea>
  129. <br />
  130. <button type="submit" name="type" value="PGP">Update Public Key</button>
  131. </form>
  132. </div>
  133. </div>
  134. <div class="box">
  135. <div class="head">
  136. <strong>Two-Factor Authentication (2FA-TOTP)</strong>
  137. </div>
  138. <div class="pad">
  139. <?php $TwoFASecret = empty($TwoFactor) ? $TwoFA->createSecret() : $TwoFactor;
  140. if (empty($TwoFactor)) {
  141. if (sizeof($U2FRegs) === 0) { ?>
  142. <p>
  143. Two Factor Authentication is not currently enabled for this account.
  144. To enable it, add the secret key below to your 2FA client either manually or by scanning the QR code, then enter
  145. a verification code generated by your 2FA client and click the "Enable 2FA" button.
  146. </p>
  147. <form method="post">
  148. <input type="text" size="60" name="twofasecret" id="twofasecret"
  149. value="<?=$TwoFASecret?>" readonly><br>
  150. <img
  151. src="<?=$TwoFA->getQRCodeImageAsDataUri($ENV->SITE_NAME.':'.$LoggedUser['Username'], $TwoFASecret)?>" />
  152. <br />
  153. <input type="text" size="20" maxlength="6" pattern="[0-9]{0,6}" name="twofa" id="twofa"
  154. placeholder="Verification Code" autocomplete="off">
  155. <br /><br />
  156. <button type="submit" name="type" value="2FA-E">Enable 2FA</button>
  157. </form>
  158. <?php } else { ?>
  159. <p>
  160. Two Factor Authentication is not currently enabled for this account.
  161. To enable 2FA, you must first disable U2F below.
  162. </p>
  163. <?php }
  164. } else {?>
  165. <form method="post">
  166. <input type="hidden" name="type" value="2FA-D">
  167. <p>
  168. 2FA is enabled for this account with the following secret:
  169. </p>
  170. <p>
  171. <input type="text" size="50" name="twofasecret" id="twofasecret"
  172. value="<?=$TwoFASecret?>" onclick="this.select()"
  173. readonly />
  174. </p>
  175. <p>
  176. <img
  177. src="<?=$TwoFA->getQRCodeImageAsDataUri($ENV->SITE_NAME.':'.$LoggedUser['Username'], $TwoFASecret)?>" />
  178. </p>
  179. <p>
  180. To disable 2FA, click the button below.
  181. </p>
  182. <button type="submit" name="type" value="2FA-D">Disable 2FA</button>
  183. </form>
  184. <?php } ?>
  185. </div>
  186. </div>
  187. <div class="box">
  188. <div class="head">
  189. <strong>Universal Two Factor (FIDO U2F)</strong>
  190. </div>
  191. <div class="pad">
  192. <?php if (sizeof($U2FRegs) === 0) { ?>
  193. <?php if (empty($TwoFactor)) { ?>
  194. <form method="post" id="u2f_register_form">
  195. <input type="hidden" name="u2f-request"
  196. value='<?=json_encode($U2FRequest)?>' />
  197. <input type="hidden" name="u2f-sigs"
  198. value='<?=json_encode($U2FSigs)?>' />
  199. <input type="hidden" name="u2f-response" />
  200. <input type="hidden" value="U2F-E" />
  201. </form>
  202. <p>
  203. Universal Two Factor is not currently enabled for this account.
  204. To enable Universal Two Factor, plug in your U2F token and press the button on it.
  205. </p>
  206. <?php } else { ?>
  207. <p>
  208. Universal Two Factor is not currently enabled for this account.
  209. To enable Universal Two Factor, you must first disable normal 2FA above.
  210. </p>
  211. <?php } ?>
  212. <?php } else { ?>
  213. <form method="post" id="u2f_register_form">
  214. <input type="hidden" name="u2f-request"
  215. value='<?=json_encode($U2FRequest)?>' />
  216. <input type="hidden" name="u2f-sigs"
  217. value='<?=json_encode($U2FSigs)?>' />
  218. <input type="hidden" name="u2f-response" />
  219. <input type="hidden" value="U2F-E" />
  220. <p>
  221. Universal Two Factor is enabled.
  222. To add an additional U2F token, plug it in and press the button on it.
  223. To disable U2F completely and deregister all tokens, press the button below.
  224. </p>
  225. <button type="submit" name="type" value="U2F-D">Disable U2F</button>
  226. </form>
  227. <?php } ?>
  228. </div>
  229. </div>
  230. </div>
  231. <?php
  232. View::show_footer();