From 1e12a26a047e35139c468b434b672e697ad79d4d Mon Sep 17 00:00:00 2001 From: sld-admin Date: Sat, 14 Feb 2026 10:42:43 +0000 Subject: [PATCH] added the ssl-cn and ssl-org --- functions/main.php | 6 +- generate-report.php | 172 ++++++++++++++++++++------------------------ web_check.json | 3 +- 3 files changed, 85 insertions(+), 96 deletions(-) diff --git a/functions/main.php b/functions/main.php index a4ecf5e..d640f74 100644 --- a/functions/main.php +++ b/functions/main.php @@ -93,7 +93,8 @@ function get_status_class($ssl, $word, $expiry){ $wordtocheck=$check_element['wordtocheck']; $ssl_released=$check_element['ssl-released']; $ssl_expiry=$check_element['ssl-expiry']; - $ssl_company=$check_element['ssl-company']; + $ssl_cn=$check_element['ssl-cn']; + $ssl_org=$check_element['ssl-org']; $http_error=$check_element['content_http_code']; $content_error=$check_element['content_error']; $ssl_error=$check_element['ssl_error']; @@ -135,7 +136,8 @@ function get_status_class($ssl, $word, $expiry){
SSL RELEASED:
SSL EXPIRY: >
SSL DAYS LEFT: >
-
SSL COMPANY:
+
SSL COMMON NAME(CN):
+
SSL ORGANISATION(O):
PATH:
ERROR CODE:
diff --git a/generate-report.php b/generate-report.php index 98140f2..55ea22e 100644 --- a/generate-report.php +++ b/generate-report.php @@ -1,97 +1,75 @@ +#!/usr/bin/php diff($expiry); - $days = (int)$interval->format('%r%a'); // giorni con segno - - return $days; + return (int)$interval->format('%r%a'); // giorni con segno } function normalizeDomainHost(string $domain): string { - // rimuovi schema se presente if (preg_match('#^https?://#i', $domain)) { $p = parse_url($domain); return $p['host'] ?? rtrim($domain, '/'); } - // rimuovi eventuale slash e porta $d = preg_replace('#/.*$#', '', $domain); $d = preg_replace('/:\d+$/', '', $d); return $d; } -// Estrae nome "company" dall'issuer del certificato (controlla più chiavi possibili) -function extractIssuerName(array $cert_info): ?string { - if (empty($cert_info['issuer']) || !is_array($cert_info['issuer'])) return null; - $issuer = $cert_info['issuer']; - - // controlla le chiavi comunemente usate - $keys = ['O', 'commonName', 'o', 'OU', 'CN', 'organizationName']; - foreach ($keys as $k) { - if (isset($issuer[$k]) && !empty($issuer[$k])) { - return $issuer[$k]; +// Estrazione CN / O dall'issuer ottenuto via openssl_x509_parse +function extractIssuerFieldsFromParsed(array $cert_info): array { + $cn = null; + $org = null; + if (!empty($cert_info['issuer']) && is_array($cert_info['issuer'])) { + $issuer = $cert_info['issuer']; + // chiavi possibili: CN, commonName, O, organizationName + $cnKeys = ['CN', 'commonName', 'cn']; + $orgKeys = ['O', 'organizationName', 'o']; + foreach ($cnKeys as $k) { + if (isset($issuer[$k]) && $issuer[$k] !== '') { $cn = $issuer[$k]; break; } + } + foreach ($orgKeys as $k) { + if (isset($issuer[$k]) && $issuer[$k] !== '') { $org = $issuer[$k]; break; } } } - - // fallback: unisci i valori dell'issuer in una stringa - $vals = []; - foreach ($issuer as $k => $v) { - if (!is_numeric($k)) $vals[] = "$k=$v"; - else $vals[] = $v; - } - return $vals ? implode(', ', $vals) : null; + return ['cn' => $cn, 'org' => $org]; } -function extractIssuerCN(array $cert_info): ?string { - if (empty($cert_info['issuer']) || !is_array($cert_info['issuer'])) return null; - $issuer = $cert_info['issuer']; - - // prima prova CN (Common Name) - $keys = ['CN', 'commonName', 'O', 'o', 'OU', 'organizationName']; - foreach ($keys as $k) { - if (isset($issuer[$k]) && !empty($issuer[$k])) { - return $issuer[$k]; - } - } - - // fallback: unisci tutti - $vals = []; - foreach ($issuer as $k => $v) { - if (!is_numeric($k)) $vals[] = "$k=$v"; - else $vals[] = $v; - } - return $vals ? implode(', ', $vals) : null; -} - -// Ottieni informazioni certificato via stream_socket_client (SNI) +// Ottieni informazioni certificato via stream_socket_client (SNI), fallback a openssl s_client function getCertInfo(string $host, int $port = 443, int $timeout = 10): array { $result = [ 'status' => 0, 'issued' => null, 'expiry' => null, - 'company' => null, + 'cn' => null, + 'org' => null, 'error' => null ]; + // stream context con SNI $ctx = stream_context_create([ 'ssl' => [ 'capture_peer_cert' => true, - 'verify_peer' => false, // non forziamo la verifica CA per garantire il fetch del cert + 'verify_peer' => false, 'verify_peer_name' => false, 'peer_name' => $host, 'SNI_enabled' => true, @@ -109,16 +87,17 @@ function getCertInfo(string $host, int $port = 443, int $timeout = 10): array { if ($cert_info !== false) { $issued = isset($cert_info['validFrom_time_t']) ? date('d/m/Y', $cert_info['validFrom_time_t']) : null; $expiry = isset($cert_info['validTo_time_t']) ? date('d/m/Y', $cert_info['validTo_time_t']) : null; - $company = extractIssuerCN($cert_info) ?? null; + // preferiamo CN come "company" visibile, ma manteniamo CN e ORG separati + $fields = extractIssuerFieldsFromParsed($cert_info); + $company = $fields['cn'] ?? ($fields['org'] ?? null); $isValid = (isset($cert_info['validTo_time_t']) && time() < $cert_info['validTo_time_t']) ? 1 : 0; - $result = [ - 'status' => $isValid, - 'issued' => $issued, - 'expiry' => $expiry, - 'company' => $company, - 'error' => null - ]; + $result['status'] = $isValid; + $result['issued'] = $issued; + $result['expiry'] = $expiry; + $result['company'] = $company; + $result['cn'] = $fields['cn']; + $result['org'] = $fields['org']; return $result; } else { $result['error'] = 'openssl_x509_parse failed'; @@ -130,46 +109,49 @@ function getCertInfo(string $host, int $port = 443, int $timeout = 10): array { } } - // fallback: prova con openssl s_client se disponibile (solo se exec abilitato) + // fallback: openssl s_client (se exec è permesso) $escHost = escapeshellarg($host); - $cmd = sprintf("timeout %d openssl s_client -connect %s:443 -servername %s -showcerts /dev/null | openssl x509 -noout -dates -issuer", $timeout, $escHost, $escHost); + $cmd = sprintf("openssl s_client -connect %s:443 -servername %s -showcerts /dev/null | openssl x509 -noout -dates -issuer", $escHost, $escHost); + if (!empty($GLOBALS['opensslTimeoutBin'])) { + $cmd = $GLOBALS['opensslTimeoutBin'] . " {$timeout} " . $cmd; + } @exec($cmd, $out, $ret); if ($ret === 0 && !empty($out)) { $joined = implode("\n", $out); - // notBefore / notAfter + $issued = null; $expiry = null; $company = null; $cn = null; $org = null; if (preg_match('/notBefore=(.+)/i', $joined, $m1)) { $issuedRaw = trim($m1[1]); $issuedTs = strtotime($issuedRaw); $issued = $issuedTs ? date('d/m/Y', $issuedTs) : $issuedRaw; - } else { - $issued = null; } if (preg_match('/notAfter=(.+)/i', $joined, $m2)) { $expiryRaw = trim($m2[1]); $expiryTs = strtotime($expiryRaw); $expiry = $expiryTs ? date('d/m/Y', $expiryTs) : $expiryRaw; - } else { - $expiry = null; } - if (preg_match('/issuer=.*O=([^,\/]+)/i', $joined, $m3)) { - $company = trim($m3[1]); - } elseif (preg_match('/issuer=.*CN=([^,\/]+)/i', $joined, $m4)) { - $company = trim($m4[1]); - } else { - $company = null; + // extract CN and O from issuer string + if (preg_match('/issuer=.*CN=([^,\/]+)/i', $joined, $mCN)) { + $cn = trim($mCN[1]); } + if (preg_match('/issuer=.*O=([^,\/]+)/i', $joined, $mO)) { + $org = trim($mO[1]); + } + // company fallback: prefer CN then org + $company = $cn ?? $org ?? null; + $isValid = 0; if ($expiry) { $dt = DateTime::createFromFormat('d/m/Y', $expiry); if ($dt) $isValid = (time() < $dt->getTimestamp()) ? 1 : 0; } - return [ - 'status' => $isValid, - 'issued' => $issued, - 'expiry' => $expiry, - 'company' => $company, - 'error' => null - ]; + + $result['status'] = $isValid; + $result['issued'] = $issued; + $result['expiry'] = $expiry; + $result['company'] = $company; + $result['cn'] = $cn; + $result['org'] = $org; + return $result; } $result['error'] = "stream_socket_client failed: $errstr ($errno)"; @@ -187,10 +169,14 @@ function fetchWithCurl(string $url, ?string $user, ?string $pass, int $timeout = curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); - // se vuoi abilitare la verifica CA, metti true e assicurati che PHP abbia CA bundle + // non forziamo verifica CA per evitare falsi negativi in ambienti senza CA bundle curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + // browser-like headers per evitare 403 anti-bot + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117 Safari/537.36'); + curl_setopt($ch, CURLOPT_HTTPHEADER, ['Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8']); + if ($user !== null && $pass !== null) { curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); curl_setopt($ch, CURLOPT_USERPWD, $user . ':' . $pass); @@ -217,13 +203,13 @@ function checkContentMatch(string $url, string $match, ?string $user, ?string $p // --- Leggi input JSON --- if (!file_exists($inputFile)) { - echo "File $inputFile non trovato\n"; + fwrite(STDERR, "File $inputFile non trovato\n"); exit(1); } $raw = file_get_contents($inputFile); $domains = json_decode($raw, true); if ($domains === null) { - echo "Errore parsing JSON di input\n"; + fwrite(STDERR, "Errore parsing JSON di input\n"); exit(1); } @@ -233,12 +219,12 @@ $totalContentOk = 0; foreach ($domains as $entry) { $host = $entry['host'] ?? null; - $domain = $entry['domain'] ?? null; // es. wiki.sld-server.org - $path = $entry['path'] ?? ''; // es. /doku.php?id=start oppure "" + $domain = $entry['domain'] ?? null; + $path = $entry['path'] ?? ''; $word = $entry['word-to-check'] ?? ''; $basicAuth = $entry['basic_auth'] ?? false; - // supporta sia basic_auth_password che basic_auth_pass (tolleranza nomi) + // supporta diversi nomi per password in input $user = ($basicAuth && !empty($entry['basic_auth_name'])) ? $entry['basic_auth_name'] : null; $pass = null; if ($basicAuth) { @@ -248,12 +234,12 @@ foreach ($domains as $entry) { } // costruisci URL (https://domain + path). se path vuoto -> '/' - $domainClean = preg_replace('#^https?://#i', '', rtrim($domain, '/')); + $domainClean = preg_replace('#^https?://#i', '', rtrim($domain ?? '', '/')); $pathClean = $path === '' ? '/' : (strpos($path, '/') === 0 ? $path : '/' . $path); $url = 'https://' . $domainClean . $pathClean; // estrai host per SSL (rimuove eventuale port/path) - $sslHost = normalizeDomainHost($domain); + $sslHost = normalizeDomainHost($domain ?? ''); // --- SSL info --- $sslInfo = getCertInfo($sslHost, 443, $timeout); @@ -277,7 +263,8 @@ foreach ($domains as $entry) { 'sslcheck_ok' => $sslInfo['status'], 'ssl-released' => $sslInfo['issued'], 'ssl-expiry' => $sslInfo['expiry'], - 'ssl-company' => $sslInfo['company'], + 'ssl-cn' => $sslInfo['cn'] ?? null, + 'ssl-org' => $sslInfo['org'] ?? null, // campi di debug utili per capire perché auth/content falliscono 'content_http_code' => $http_code, 'content_error' => $content_error, @@ -291,4 +278,3 @@ file_put_contents($outputFile, json_encode($results, JSON_PRETTY_PRINT | JSON_UN // stampa sommario echo "Check completato. Risultati salvati in $outputFile\n"; echo "Totali: SSL ok = $totalSSLok, Content ok = $totalContentOk\n"; -?> diff --git a/web_check.json b/web_check.json index b915f90..63a6704 100644 --- a/web_check.json +++ b/web_check.json @@ -8,7 +8,8 @@ "sslcheck_ok": 0, "ssl-released": null, "ssl-expiry": null, - "ssl-company": null, + "ssl-cn": null, + "ssl-org": null, "content_http_code": 0, "content_error": "URL rejected: Malformed input to a URL function", "ssl_error": "stream_socket_client failed: php_network_getaddresses: getaddrinfo for domain failed: Name or service not known (0)"