Key.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Second authentication factor handling
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin\Plugins\TwoFactor;
  10. use PhpMyAdmin\Plugins\TwoFactorPlugin;
  11. use PhpMyAdmin\Response;
  12. use PhpMyAdmin\Template;
  13. use PhpMyAdmin\TwoFactor;
  14. use Samyoul\U2F\U2FServer\U2FException;
  15. use Samyoul\U2F\U2FServer\U2FServer;
  16. use stdClass;
  17. use Throwable;
  18. use Twig_Error_Loader;
  19. use Twig_Error_Runtime;
  20. use Twig_Error_Syntax;
  21. /**
  22. * Hardware key based two-factor authentication
  23. *
  24. * Supports FIDO U2F tokens
  25. *
  26. * @package PhpMyAdmin
  27. */
  28. class Key extends TwoFactorPlugin
  29. {
  30. /**
  31. * @var string
  32. */
  33. public static $id = 'key';
  34. /**
  35. * Creates object
  36. *
  37. * @param TwoFactor $twofactor TwoFactor instance
  38. */
  39. public function __construct(TwoFactor $twofactor)
  40. {
  41. parent::__construct($twofactor);
  42. if (! isset($this->_twofactor->config['settings']['registrations'])) {
  43. $this->_twofactor->config['settings']['registrations'] = [];
  44. }
  45. }
  46. /**
  47. * Returns array of U2F registration objects
  48. *
  49. * @return array
  50. */
  51. public function getRegistrations()
  52. {
  53. $result = [];
  54. foreach ($this->_twofactor->config['settings']['registrations'] as $index => $data) {
  55. $reg = new stdClass();
  56. $reg->keyHandle = $data['keyHandle'];
  57. $reg->publicKey = $data['publicKey'];
  58. $reg->certificate = $data['certificate'];
  59. $reg->counter = $data['counter'];
  60. $reg->index = $index;
  61. $result[] = $reg;
  62. }
  63. return $result;
  64. }
  65. /**
  66. * Checks authentication, returns true on success
  67. *
  68. * @return boolean
  69. */
  70. public function check()
  71. {
  72. $this->_provided = false;
  73. if (! isset($_POST['u2f_authentication_response']) || ! isset($_SESSION['authenticationRequest'])) {
  74. return false;
  75. }
  76. $this->_provided = true;
  77. try {
  78. $response = json_decode($_POST['u2f_authentication_response']);
  79. if ($response === null) {
  80. return false;
  81. }
  82. $authentication = U2FServer::authenticate(
  83. $_SESSION['authenticationRequest'],
  84. $this->getRegistrations(),
  85. $response
  86. );
  87. $this->_twofactor->config['settings']['registrations'][$authentication->index]['counter'] = $authentication->counter;
  88. $this->_twofactor->save();
  89. return true;
  90. } catch (U2FException $e) {
  91. $this->_message = $e->getMessage();
  92. return false;
  93. }
  94. }
  95. /**
  96. * Loads needed javascripts into the page
  97. *
  98. * @return void
  99. */
  100. public function loadScripts()
  101. {
  102. $response = Response::getInstance();
  103. $scripts = $response->getHeader()->getScripts();
  104. $scripts->addFile('vendor/u2f-api-polyfill.js');
  105. $scripts->addFile('u2f.js');
  106. }
  107. /**
  108. * Renders user interface to enter two-factor authentication
  109. *
  110. * @return string HTML code
  111. */
  112. public function render()
  113. {
  114. $request = U2FServer::makeAuthentication(
  115. $this->getRegistrations(),
  116. $this->getAppId(true)
  117. );
  118. $_SESSION['authenticationRequest'] = $request;
  119. $this->loadScripts();
  120. return $this->template->render('login/twofactor/key', [
  121. 'request' => json_encode($request),
  122. 'is_https' => $GLOBALS['PMA_Config']->isHttps(),
  123. ]);
  124. }
  125. /**
  126. * Renders user interface to configure two-factor authentication
  127. *
  128. * @return string HTML code
  129. * @throws U2FException
  130. * @throws Throwable
  131. * @throws Twig_Error_Loader
  132. * @throws Twig_Error_Runtime
  133. * @throws Twig_Error_Syntax
  134. */
  135. public function setup()
  136. {
  137. $registrationData = U2FServer::makeRegistration(
  138. $this->getAppId(true),
  139. $this->getRegistrations()
  140. );
  141. $_SESSION['registrationRequest'] = $registrationData['request'];
  142. $this->loadScripts();
  143. return $this->template->render('login/twofactor/key_configure', [
  144. 'request' => json_encode($registrationData['request']),
  145. 'signatures' => json_encode($registrationData['signatures']),
  146. 'is_https' => $GLOBALS['PMA_Config']->isHttps(),
  147. ]);
  148. }
  149. /**
  150. * Performs backend configuration
  151. *
  152. * @return boolean
  153. */
  154. public function configure()
  155. {
  156. $this->_provided = false;
  157. if (! isset($_POST['u2f_registration_response']) || ! isset($_SESSION['registrationRequest'])) {
  158. return false;
  159. }
  160. $this->_provided = true;
  161. try {
  162. $response = json_decode($_POST['u2f_registration_response']);
  163. if ($response === null) {
  164. return false;
  165. }
  166. $registration = U2FServer::register(
  167. $_SESSION['registrationRequest'],
  168. $response
  169. );
  170. $this->_twofactor->config['settings']['registrations'][] = [
  171. 'keyHandle' => $registration->getKeyHandle(),
  172. 'publicKey' => $registration->getPublicKey(),
  173. 'certificate' => $registration->getCertificate(),
  174. 'counter' => $registration->getCounter(),
  175. ];
  176. return true;
  177. } catch (U2FException $e) {
  178. $this->_message = $e->getMessage();
  179. return false;
  180. }
  181. }
  182. /**
  183. * Get user visible name
  184. *
  185. * @return string
  186. */
  187. public static function getName()
  188. {
  189. return __('Hardware Security Key (FIDO U2F)');
  190. }
  191. /**
  192. * Get user visible description
  193. *
  194. * @return string
  195. */
  196. public static function getDescription()
  197. {
  198. return __('Provides authentication using hardware security tokens supporting FIDO U2F.');
  199. }
  200. }