Total Complexity | 56 |
Total Lines | 341 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like RFC6614Tests often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use RFC6614Tests, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
40 | class RFC6614Tests extends AbstractTest { |
||
41 | |||
42 | /** |
||
43 | * dictionary of translatable texts around the certificates we check |
||
44 | * |
||
45 | * @var array |
||
46 | */ |
||
47 | private $TLS_certkeys = []; |
||
48 | |||
49 | /** |
||
50 | * list of IP addresses which are candidates for dynamic discovery targets |
||
51 | * |
||
52 | * @var array |
||
53 | */ |
||
54 | private $candidateIPs; |
||
55 | |||
56 | /** |
||
57 | * the hostname which should show up in the certificate when establishing |
||
58 | * a connection to the RADIUS/TLS server (hostname is an intermediary result |
||
59 | * of the RFC7585 DNS resolution algorithm, in SRV response) |
||
60 | * |
||
61 | * @var string |
||
62 | */ |
||
63 | private $expectedName; |
||
64 | |||
65 | /** |
||
66 | * associative array holding the server-side cert test results for a given IP (IP is the key) |
||
67 | * |
||
68 | * @var array |
||
69 | */ |
||
70 | public $TLS_CA_checks_result; |
||
71 | |||
72 | /** |
||
73 | * associative array holding the client-side cert test results for a given IP (IP is the key) |
||
74 | * |
||
75 | * @var array |
||
76 | */ |
||
77 | public $TLS_clients_checks_result; |
||
78 | |||
79 | /** |
||
80 | * Sets up the instance for testing of a number of candidate IPs |
||
81 | * |
||
82 | * @param array $listOfIPs candidates to test |
||
83 | */ |
||
84 | public function __construct($listOfIPs, $expectedName) { |
||
85 | parent::__construct(); |
||
86 | \core\common\Entity::intoThePotatoes(); |
||
87 | $this->TLS_certkeys = [ |
||
88 | 'eduPKI' => _('eduPKI'), |
||
89 | 'NCU' => _('Nicolaus Copernicus University'), |
||
90 | 'ACCREDITED' => _('accredited'), |
||
91 | 'NONACCREDITED' => _('non-accredited'), |
||
92 | 'CORRECT' => _('correct certificate'), |
||
93 | 'WRONGPOLICY' => _('certificate with wrong policy OID'), |
||
94 | 'EXPIRED' => _('expired certificate'), |
||
95 | 'REVOKED' => _('revoked certificate'), |
||
96 | 'PASS' => _('pass'), |
||
97 | 'FAIL' => _('fail'), |
||
98 | 'non-eduPKI-accredited' => _("eduroam-accredited CA (now only for tests)"), |
||
99 | ]; |
||
100 | $this->TLS_CA_checks_result = []; |
||
101 | $this->TLS_clients_checks_result = []; |
||
102 | |||
103 | $this->candidateIPs = $listOfIPs; |
||
104 | $this->expectedName = $expectedName; |
||
105 | \core\common\Entity::outOfThePotatoes(); |
||
106 | } |
||
107 | |||
108 | /** |
||
109 | * run all checks on all candidates |
||
110 | * |
||
111 | * @return void |
||
112 | */ |
||
113 | public function allChecks() { |
||
114 | foreach ($this->candidateIPs as $oneIP) { |
||
115 | $this->cApathCheck($oneIP); |
||
116 | $this->tlsClientSideCheck($oneIP); |
||
117 | } |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * This function executes openssl s_clientends command to check if a server accepts a CA |
||
122 | * |
||
123 | * @param string $host IP:port |
||
124 | * @return int returncode |
||
125 | */ |
||
126 | public function cApathCheck(string $host) { |
||
127 | if (!isset($this->TLS_CA_checks_result[$host])) { |
||
128 | $this->TLS_CA_checks_result[$host] = []; |
||
129 | } |
||
130 | $opensslbabble = $this->execOpensslClient($host, '', $this->TLS_CA_checks_result[$host]); |
||
131 | $overallRetval = $this->opensslCAResult($host, $opensslbabble); |
||
132 | if ($overallRetval == AbstractTest::RETVAL_OK) { |
||
133 | $this->checkServerName($host); |
||
134 | } |
||
135 | return $overallRetval; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * checks whether the received servername matches the expected server name |
||
140 | * |
||
141 | * @return bool yes or no |
||
142 | */ |
||
143 | private function checkServerName($host) { |
||
144 | // it could match CN or sAN:DNS, we don't care which |
||
145 | if (isset($this->TLS_CA_checks_result[$host]['certdata']['subject']['CN'])) { |
||
146 | $testNames = $this->TLS_CA_checks_result[$host]['certdata']['subject']['CN']; |
||
147 | if (!is_array($testNames)) { |
||
148 | $testNames = [$testNames]; |
||
149 | } |
||
150 | if (array_search($this->expectedName, $testNames) !== FALSE) { |
||
151 | return TRUE; |
||
152 | } |
||
153 | } |
||
154 | if (isset($this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'])) { |
||
155 | $testNames = $this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname']; |
||
156 | if (!is_array($testNames)) { |
||
157 | $testNames = [$testNames]; |
||
158 | } |
||
159 | foreach ($testNames as $oneName) { |
||
160 | if (preg_match("/".$this->expectedName."/", $oneName) === 1) { |
||
161 | return TRUE; |
||
162 | } |
||
163 | } |
||
164 | } |
||
165 | $this->TLS_CA_checks_result[$host]['cert_oddity'] = RADIUSTests::CERTPROB_DYN_SERVER_NAME_MISMATCH; |
||
166 | return FALSE; |
||
167 | |||
168 | } |
||
169 | |||
170 | /** |
||
171 | * This function executes openssl s_client command to check if a server accepts a client certificate |
||
172 | * |
||
173 | * @param string $host IP:port |
||
174 | * @return int returncode |
||
175 | */ |
||
176 | public function tlsClientSideCheck(string $host) { |
||
177 | $res = RADIUSTests::RETVAL_OK; |
||
178 | if (!is_array(CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts']) || count(CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts']) == 0) { |
||
179 | return RADIUSTests::RETVAL_SKIPPED; |
||
180 | } |
||
181 | if (preg_match("/\[/", $host)) { |
||
182 | return RADIUSTests::RETVAL_INVALID; |
||
183 | } |
||
184 | foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-clientcerts'] as $type => $tlsclient) { |
||
185 | $this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['from'] = $type; |
||
186 | $this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['status'] = $tlsclient['status']; |
||
187 | $this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['message'] = $this->TLS_certkeys[$tlsclient['status']]; |
||
188 | $this->TLS_clients_checks_result[$host]['ca'][$type]['clientcertinfo']['issuer'] = $tlsclient['issuerCA']; |
||
189 | foreach ($tlsclient['certificates'] as $k => $cert) { |
||
190 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['status'] = $cert['status']; |
||
191 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['message'] = $this->TLS_certkeys[$cert['status']]; |
||
192 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['expected'] = $cert['expected']; |
||
193 | $add = ' -cert ' . ROOT . '/config/cli-certs/' . $cert['public'] . ' -key ' . ROOT . '/config/cli-certs/' . $cert['private']; |
||
194 | if (!isset($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k])) { |
||
195 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k] = []; |
||
196 | } |
||
197 | $opensslbabble = $this->execOpensslClient($host, $add, $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]); |
||
198 | $res = $this->opensslClientsResult($host, $opensslbabble, $this->TLS_clients_checks_result, $type, $k); |
||
199 | if ($cert['expected'] == 'PASS') { |
||
200 | if (!$this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['connected']) { |
||
201 | if (($tlsclient['status'] == 'ACCREDITED') && ($cert['status'] == 'CORRECT')) { |
||
202 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['returncode'] = RADIUSTests::CERTPROB_NOT_ACCEPTED; |
||
203 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['finalerror'] = 1; |
||
204 | break; |
||
205 | } |
||
206 | } |
||
207 | } else { |
||
208 | if ($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['connected']) { |
||
209 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['returncode'] = RADIUSTests::CERTPROB_WRONGLY_ACCEPTED; |
||
210 | } |
||
211 | |||
212 | if (($this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['reason'] == RADIUSTests::CERTPROB_UNKNOWN_CA) && ($tlsclient['status'] == 'ACCREDITED') && ($cert['status'] == 'CORRECT')) { |
||
213 | $this->TLS_clients_checks_result[$host]['ca'][$type]['certificate'][$k]['finalerror'] = 1; |
||
214 | echo "koniec zabawy2<br>"; |
||
215 | break; |
||
216 | } |
||
217 | } |
||
218 | } |
||
219 | } |
||
220 | return $res; |
||
221 | } |
||
222 | |||
223 | /** |
||
224 | * This function executes openssl s_client command |
||
225 | * |
||
226 | * @param string $host IP address |
||
227 | * @param string $arg arguments to add to the openssl command |
||
228 | * @param array $testresults by-reference: the testresults array we are writing into |
||
229 | * @return array result of openssl s_client ... |
||
230 | */ |
||
231 | private function execOpensslClient($host, $arg, &$testresults) { |
||
232 | // we got the IP address either from DNS (guaranteeing well-formedness) |
||
233 | // or from filter_var'ed user input. So it is always safe as an argument |
||
234 | // but code analysers want this more explicit, so here is this extra |
||
235 | // call to escapeshellarg() |
||
236 | $escapedHost = escapeshellarg($host); |
||
237 | $this->loggerInstance->debug(4, CONFIG['PATHS']['openssl'] . " s_client -connect " . $escapedHost . " -tls1 -CApath " . ROOT . "/config/ca-certs/ $arg 2>&1\n"); |
||
238 | $time_start = microtime(true); |
||
239 | $opensslbabble = []; |
||
240 | $result = 999; // likely to become zero by openssl; don't want to initialise to zero, could cover up exec failures |
||
241 | exec(CONFIG['PATHS']['openssl'] . " s_client -connect " . $escapedHost . " -no_ssl2 -no_ssl3 -CApath " . ROOT . "/config/ca-certs/ $arg 2>&1", $opensslbabble, $result); |
||
242 | $time_stop = microtime(true); |
||
243 | $testresults['time_millisec'] = floor(($time_stop - $time_start) * 1000); |
||
244 | $testresults['returncode'] = $result; |
||
245 | return $opensslbabble; |
||
246 | } |
||
247 | |||
248 | /** |
||
249 | * This function parses openssl s_client result |
||
250 | * |
||
251 | * @param string $host IP:port |
||
252 | * @param array $opensslbabble openssl command output |
||
253 | * @return int return code |
||
254 | */ |
||
255 | private function opensslCAResult($host, $opensslbabble) { |
||
256 | $res = RADIUSTests::RETVAL_OK; |
||
257 | if (preg_match('/connect: Connection refused/', implode($opensslbabble))) { |
||
258 | $this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_CONNECTION_REFUSED; |
||
259 | $res = RADIUSTests::RETVAL_INVALID; |
||
260 | } |
||
261 | if (preg_match('/verify error:num=19/', implode($opensslbabble))) { |
||
262 | $this->TLS_CA_checks_result[$host]['cert_oddity'] = RADIUSTests::CERTPROB_UNKNOWN_CA; |
||
263 | $this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_INVALID; |
||
264 | $res = RADIUSTests::RETVAL_INVALID; |
||
265 | } |
||
266 | if (preg_match('/verify return:1/', implode($opensslbabble))) { |
||
267 | $this->TLS_CA_checks_result[$host]['status'] = RADIUSTests::RETVAL_OK; |
||
268 | $servercertStage1 = implode("\n", $opensslbabble); |
||
269 | $servercert = preg_replace("/.*(-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----\n).*/s", "$1", $servercertStage1); |
||
270 | $data = openssl_x509_parse($servercert); |
||
271 | $this->TLS_CA_checks_result[$host]['certdata']['subject'] = $data['name']; |
||
272 | $this->TLS_CA_checks_result[$host]['certdata']['issuer'] = $this->getCertificateIssuer($data); |
||
273 | if (($altname = $this->getCertificatePropertyField($data, 'subjectAltName'))) { |
||
274 | $this->TLS_CA_checks_result[$host]['certdata']['extensions']['subjectaltname'] = $altname; |
||
275 | } |
||
276 | |||
277 | $oids = $this->propertyCheckPolicy($data); |
||
278 | if (!empty($oids)) { |
||
279 | foreach ($oids as $resultArrayKey => $o) { |
||
280 | $this->TLS_CA_checks_result[$host]['certdata']['extensions']['policyoid'][] = " $o ($resultArrayKey)"; |
||
281 | } |
||
282 | } |
||
283 | if (($crl = $this->getCertificatePropertyField($data, 'crlDistributionPoints'))) { |
||
284 | $this->TLS_CA_checks_result[$host]['certdata']['extensions']['crlDistributionPoint'] = $crl; |
||
285 | } |
||
286 | if (($ocsp = $this->getCertificatePropertyField($data, 'authorityInfoAccess'))) { |
||
287 | $this->TLS_CA_checks_result[$host]['certdata']['extensions']['authorityInfoAccess'] = $ocsp; |
||
288 | } |
||
289 | } |
||
290 | return $res; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * This function parses openssl s_client result |
||
295 | * |
||
296 | * @param string $host IP:port |
||
297 | * @param array $opensslbabble openssl command output |
||
298 | * @param array $testresults by-reference: pointer to results array we write into |
||
299 | * @param string $type type of certificate |
||
300 | * @param int $resultArrayKey results array key |
||
301 | * @return int return code |
||
302 | */ |
||
303 | private function opensslClientsResult($host, $opensslbabble, &$testresults, $type = '', $resultArrayKey = 0) { |
||
304 | \core\common\Entity::intoThePotatoes(); |
||
305 | $res = RADIUSTests::RETVAL_OK; |
||
306 | $ret = $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['returncode']; |
||
307 | $output = implode($opensslbabble); |
||
308 | if ($ret == 0) { |
||
309 | $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['connected'] = 1; |
||
310 | } else { |
||
311 | $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['connected'] = 0; |
||
312 | if (preg_match('/connect: Connection refused/', implode($opensslbabble))) { |
||
313 | $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['returncode'] = RADIUSTests::RETVAL_CONNECTION_REFUSED; |
||
314 | $resComment = _("No TLS connection established: Connection refused"); |
||
315 | } elseif (preg_match('/sslv3 alert certificate expired/', $output)) { |
||
316 | $resComment = _("certificate expired"); |
||
317 | } elseif (preg_match('/sslv3 alert certificate revoked/', $output)) { |
||
318 | $resComment = _("certificate was revoked"); |
||
319 | } elseif (preg_match('/SSL alert number 46/', $output)) { |
||
320 | $resComment = _("bad policy"); |
||
321 | } elseif (preg_match('/tlsv1 alert unknown ca/', $output)) { |
||
322 | $resComment = _("unknown authority"); |
||
323 | $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['reason'] = RADIUSTests::CERTPROB_UNKNOWN_CA; |
||
324 | } else { |
||
325 | $resComment = _("unknown authority or no certificate policy or another problem"); |
||
326 | } |
||
327 | $testresults[$host]['ca'][$type]['certificate'][$resultArrayKey]['resultcomment'] = $resComment; |
||
328 | } |
||
329 | \core\common\Entity::outOfThePotatoes(); |
||
330 | return $res; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * This function parses a X.509 cert and returns all certificatePolicies OIDs |
||
335 | * |
||
336 | * @param array $cert (returned from openssl_x509_parse) |
||
337 | * @return array of OIDs |
||
338 | */ |
||
339 | private function propertyCheckPolicy($cert) { |
||
340 | $oids = []; |
||
341 | if ($cert['extensions']['certificatePolicies']) { |
||
342 | foreach (CONFIG_DIAGNOSTICS['RADIUSTESTS']['TLS-acceptableOIDs'] as $key => $oid) { |
||
343 | if (preg_match("/Policy: $oid/", $cert['extensions']['certificatePolicies'])) { |
||
344 | $oids[$key] = $oid; |
||
345 | } |
||
346 | } |
||
347 | } |
||
348 | return $oids; |
||
349 | } |
||
350 | /** |
||
351 | * This function parses a X.509 cert and returns the value of $field |
||
352 | * |
||
353 | * @param array $cert (returned from openssl_x509_parse) |
||
354 | * @return string value of the issuer field or '' |
||
355 | */ |
||
356 | private function getCertificateIssuer($cert) { |
||
368 | } |
||
369 | /** |
||
370 | * This function parses a X.509 cert and returns the value of $field |
||
371 | * |
||
372 | * @param array $cert (returned from openssl_x509_parse) |
||
373 | * @param string $field the field to search for |
||
374 | * @return string value of the extention named $field or '' |
||
375 | */ |
||
376 | private function getCertificatePropertyField($cert, $field) { |
||
381 | } |
||
382 | |||
383 | } |
||
384 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.