[CLEANUP] The correct case must be used for standard PHP types in phpdoc
[Packages/TYPO3.CMS.git] / typo3 / sysext / rsaauth / Classes / Backend / CommandLineBackend.php
1 <?php
2 namespace TYPO3\CMS\Rsaauth\Backend;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\CommandUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\StringUtility;
20
21 /**
22 * This class contains an OpenSSL backend for the TYPO3 RSA authentication
23 * service. It uses shell version of OpenSSL to perform tasks. See class
24 * \TYPO3\CMS\Rsaauth\Backend\AbstractBackend for the information on using backends.
25 */
26 class CommandLineBackend extends AbstractBackend
27 {
28 /**
29 * @var int
30 */
31 const DEFAULT_EXPONENT = 65537;
32
33 /**
34 * A path to the openssl binary or FALSE if the binary does not exist
35 *
36 * @var string|bool
37 */
38 protected $opensslPath;
39
40 /**
41 * Temporary directory. It is best of it is outside of the web site root and
42 * not publicly readable.
43 * For now we use typo3temp/var/ (stored in the variable without the trailing slash).
44 *
45 * @var string
46 */
47 protected $temporaryDirectory;
48
49 /**
50 * Creates an instance of this class. It obtains a path to the OpenSSL
51 * binary.
52 */
53 public function __construct()
54 {
55 $this->opensslPath = CommandUtility::getCommand('openssl');
56 // Get temporary directory from the configuration
57 $extconf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['rsaauth'], ['allowed_classes' => false]);
58 if (
59 $extconf['temporaryDirectory'] !== ''
60 && $extconf['temporaryDirectory'][0] === '/'
61 && @is_dir($extconf['temporaryDirectory'])
62 && is_writable($extconf['temporaryDirectory'])
63 ) {
64 $this->temporaryDirectory = $extconf['temporaryDirectory'];
65 } else {
66 $this->temporaryDirectory = PATH_site . 'typo3temp/var/transient';
67 }
68 }
69
70 /**
71 * Creates a new key pair for the encryption or gets the existing key pair (if one already has been generated).
72 *
73 * There should only be one key pair per request because the second private key would overwrites the first private
74 * key. So the submitting the form with the first public key would not work anymore.
75 *
76 * @return \TYPO3\CMS\Rsaauth\Keypair|null a key pair or NULL in case of error
77 */
78 public function createNewKeyPair()
79 {
80 /** @var $keyPair \TYPO3\CMS\Rsaauth\Keypair */
81 $keyPair = GeneralUtility::makeInstance(\TYPO3\CMS\Rsaauth\Keypair::class);
82 if ($keyPair->isReady()) {
83 return $keyPair;
84 }
85
86 if ($this->opensslPath === false) {
87 return null;
88 }
89
90 // Create a temporary file. Security: tempnam() sets permissions to 0600
91 $privateKeyFile = tempnam($this->temporaryDirectory, StringUtility::getUniqueId());
92
93 // Generate the private key.
94 //
95 // PHP generates 1024 bit key files. We force command line version
96 // to do the same and use the F4 (0x10001) exponent. This is the most
97 // secure.
98 $command = $this->opensslPath . ' genrsa -out ' . escapeshellarg($privateKeyFile) . ' 1024';
99 if (TYPO3_OS === 'WIN') {
100 $command .= ' 2>NUL';
101 } else {
102 $command .= ' 2>/dev/null';
103 }
104 CommandUtility::exec($command);
105 // Test that we got a private key
106 $privateKey = file_get_contents($privateKeyFile);
107 if (false !== strpos($privateKey, 'BEGIN RSA PRIVATE KEY')) {
108 // Ok, we got the private key. Get the modulus.
109 $command = $this->opensslPath . ' rsa -noout -modulus -in ' . escapeshellarg($privateKeyFile);
110 $value = CommandUtility::exec($command);
111 if (substr($value, 0, 8) === 'Modulus=') {
112 $publicKey = substr($value, 8);
113
114 $keyPair->setExponent(self::DEFAULT_EXPONENT);
115 $keyPair->setPrivateKey($privateKey);
116 $keyPair->setPublicKey($publicKey);
117 }
118 } else {
119 $keyPair = null;
120 }
121
122 @unlink($privateKeyFile);
123 return $keyPair;
124 }
125
126 /**
127 * @param string $privateKey The private key (obtained from a call to createNewKeyPair())
128 * @param string $data Data to decrypt (base64-encoded)
129 * @return string Decrypted data or NULL in case of an error
130 * @see \TYPO3\CMS\Rsaauth\Backend\AbstractBackend::decrypt()
131 */
132 public function decrypt($privateKey, $data)
133 {
134 // Key must be put to the file
135 $privateKeyFile = tempnam($this->temporaryDirectory, StringUtility::getUniqueId());
136 file_put_contents($privateKeyFile, $privateKey);
137 $dataFile = tempnam($this->temporaryDirectory, StringUtility::getUniqueId());
138 file_put_contents($dataFile, base64_decode($data));
139 // Prepare the command
140 $command = $this->opensslPath . ' rsautl -inkey ' . escapeshellarg($privateKeyFile) . ' -in ' . escapeshellarg($dataFile) . ' -decrypt';
141 // Execute the command and capture the result
142 $output = [];
143 CommandUtility::exec($command, $output);
144 // Remove the file
145 @unlink($privateKeyFile);
146 @unlink($dataFile);
147 return implode(LF, $output);
148 }
149
150 /**
151 * Checks if command line version of the OpenSSL is available and can be
152 * executed successfully.
153 *
154 * @return bool
155 * @see \TYPO3\CMS\Rsaauth\Backend\AbstractBackend::isAvailable()
156 */
157 public function isAvailable()
158 {
159 $result = false;
160 if ($this->opensslPath) {
161 // If path exists, test that command runs and can produce output
162 $test = CommandUtility::exec($this->opensslPath . ' version');
163 $result = substr($test, 0, 8) === 'OpenSSL ';
164 }
165 return $result;
166 }
167 }