really-simple-ssl
Last commit date
assets
4 weeks ago
core
4 weeks ago
languages
4 weeks ago
lets-encrypt
4 weeks ago
lib
4 weeks ago
mailer
4 weeks ago
modal
4 weeks ago
placeholders
4 weeks ago
progress
4 weeks ago
security
4 weeks ago
settings
4 weeks ago
testssl
4 weeks ago
upgrade
4 weeks ago
.wp-env.json
4 weeks ago
SECURITY.md
4 weeks ago
class-admin.php
4 weeks ago
class-cache.php
4 weeks ago
class-certificate.php
4 weeks ago
class-front-end.php
4 weeks ago
class-installer.php
4 weeks ago
class-mixed-content-fixer.php
4 weeks ago
class-multisite.php
4 weeks ago
class-server.php
4 weeks ago
class-site-health.php
4 weeks ago
class-wp-cli.php
4 weeks ago
compatibility.php
4 weeks ago
force-deactivate.txt
4 weeks ago
functions.php
4 weeks ago
index.php
4 weeks ago
readme.txt
4 weeks ago
rector.php
4 weeks ago
rlrsssl-really-simple-ssl.php
4 weeks ago
rsssl-auto-loader.php
4 weeks ago
sbom.json.gz
4 weeks ago
ssl-test-page.php
4 weeks ago
system-status.php
4 weeks ago
uninstall.php
4 weeks ago
upgrade.php
4 weeks ago
class-certificate.php
279 lines
| 1 | <?php defined( 'ABSPATH' ) or die(); |
| 2 | |
| 3 | if ( ! class_exists( 'rsssl_certificate' ) ) { |
| 4 | class rsssl_certificate { |
| 5 | |
| 6 | private static $_this; |
| 7 | public function __construct() { |
| 8 | if ( isset( self::$_this ) ) { |
| 9 | wp_die( 'you cannot create a second instance.' ); |
| 10 | } |
| 11 | self::$_this = $this; |
| 12 | } |
| 13 | |
| 14 | public static function this() { |
| 15 | return self::$_this; |
| 16 | } |
| 17 | |
| 18 | /** |
| 19 | * |
| 20 | * @since 3.0 |
| 21 | * |
| 22 | * Check if the certificate is valid |
| 23 | * |
| 24 | * @return bool |
| 25 | * |
| 26 | */ |
| 27 | |
| 28 | public function is_valid(): bool { |
| 29 | $domain = $this->get_domain(); |
| 30 | if ( ! $domain || ! function_exists( 'stream_context_get_params' ) ) { |
| 31 | set_transient( 'rsssl_certinfo', 'no-response', DAY_IN_SECONDS ); |
| 32 | } else { |
| 33 | $certinfo = $this->get_certinfo( $domain ); |
| 34 | if ( ! $certinfo ) { |
| 35 | return false; |
| 36 | } |
| 37 | |
| 38 | $domain_valid = $this->is_domain_valid( $certinfo, $domain ); |
| 39 | $date_valid = $this->is_date_valid( $certinfo ); |
| 40 | if ( $domain_valid && $date_valid ) { |
| 41 | return true; |
| 42 | } |
| 43 | } |
| 44 | return false; |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * get domain |
| 49 | * @return string |
| 50 | */ |
| 51 | public function get_domain() { |
| 52 | $domain = site_url(); |
| 53 | //Parse to strip off any /subfolder/ |
| 54 | $parse = parse_url( $domain ); |
| 55 | return isset( $parse['host'] ) ? $parse['host'] : false; |
| 56 | } |
| 57 | |
| 58 | /** |
| 59 | * |
| 60 | * Check common name(s) and alternative name(s) on certificate and match them to the site_url ($domain) |
| 61 | * |
| 62 | * @since 3.0 |
| 63 | * |
| 64 | * @access public |
| 65 | * @param $certinfo |
| 66 | * @param $domain |
| 67 | * @return bool |
| 68 | * |
| 69 | */ |
| 70 | |
| 71 | public function is_domain_valid( $certinfo, $domain ): bool { |
| 72 | //first check standard situation |
| 73 | //Get both the common name(s) and the alternative names from the certificate |
| 74 | $certificate_common_names = isset( $certinfo['subject']['CN'] ) ? $certinfo['subject']['CN'] : ''; |
| 75 | $certificate_alternative_names = isset( $certinfo['extensions']['subjectAltName'] ) ? $certinfo['extensions']['subjectAltName'] : ''; |
| 76 | //Check if the domain is found in either the certificate common name(s) (CN) or alternative name(s) (AN) |
| 77 | $pos_cn = strpos( $certificate_common_names, $domain ); |
| 78 | $pos_an = strpos( $certificate_alternative_names, $domain ); |
| 79 | |
| 80 | //If the domain is found, return true |
| 81 | if ( false !== $pos_cn || false !== $pos_an ) { |
| 82 | return true; |
| 83 | } |
| 84 | |
| 85 | //if nothing found, we check for wildcard |
| 86 | //strip of asterisk, and check if the wildcard domain is part of current domain |
| 87 | $cert_domains = array(); |
| 88 | if ( $this->is_wildcard() ) { |
| 89 | $certificate_alternative_names = explode( ', ', $certificate_alternative_names ); |
| 90 | $cert_domains[] = trim( str_replace( '*', '', $certificate_common_names ) ); |
| 91 | foreach ( $certificate_alternative_names as $subject_alt_name ) { |
| 92 | $cert_domains[] = trim( str_replace( '*', '', $subject_alt_name ) ); |
| 93 | } |
| 94 | |
| 95 | foreach ( $cert_domains as $cert_domain ) { |
| 96 | //If the wildcard domain is found, return true |
| 97 | if ( ( strpos( $domain, $cert_domain ) !== false ) ) { |
| 98 | return true; |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | return false; |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Check if detection failed |
| 108 | * @return bool |
| 109 | */ |
| 110 | public function detection_failed() { |
| 111 | $certinfo = get_transient( 'rsssl_certinfo' ); |
| 112 | if ( $certinfo && 'no-response' === $certinfo ) { |
| 113 | return true; |
| 114 | } |
| 115 | |
| 116 | return false; |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * |
| 121 | * Check if the date is valid by looking at the validFrom and validTo times |
| 122 | * |
| 123 | * @since 3.0 |
| 124 | * |
| 125 | * @access public |
| 126 | * |
| 127 | * @return bool |
| 128 | * |
| 129 | */ |
| 130 | |
| 131 | public function is_date_valid( $certinfo ) { |
| 132 | |
| 133 | //Get the start date and end date from the certificate |
| 134 | $start_date = isset( $certinfo['validFrom_time_t'] ) ? $certinfo['validFrom_time_t'] : false; |
| 135 | $end_date = isset( $certinfo['validTo_time_t'] ) ? $certinfo['validTo_time_t'] : false; |
| 136 | $current_date = time(); |
| 137 | |
| 138 | //Check if the current date is between the start date and end date. If so, return true |
| 139 | if ( $current_date > $start_date && ( $current_date < $end_date ) ) { |
| 140 | return true; |
| 141 | } |
| 142 | |
| 143 | return false; |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Check if the certificate is valid, but about to expire. |
| 148 | * @return bool |
| 149 | */ |
| 150 | public function about_to_expire() { |
| 151 | //if not valid, it's already expired |
| 152 | if ( ! $this->is_valid() ) { |
| 153 | return true; |
| 154 | } |
| 155 | |
| 156 | //we have now renewed the cert info transient |
| 157 | $certinfo = get_transient( 'rsssl_certinfo' ); |
| 158 | $end_date = isset( $certinfo['validTo_time_t'] ) ? $certinfo['validTo_time_t'] : false; |
| 159 | $expiry_days_time = strtotime( '+' . rsssl_le_manual_generation_renewal_check . ' days' ); |
| 160 | if ( $expiry_days_time < $end_date ) { |
| 161 | return false; |
| 162 | } else { |
| 163 | return true; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | /** |
| 168 | * |
| 169 | * Check if the certificate is a wildcard certificate |
| 170 | * Function is used in class-multisite.php to determine whether to show a notice for multisite subfolder installations without a wildcard certificate |
| 171 | * |
| 172 | * @since 3.0 |
| 173 | * |
| 174 | * @access public |
| 175 | * |
| 176 | * @return bool |
| 177 | * |
| 178 | */ |
| 179 | |
| 180 | public function is_wildcard() { |
| 181 | $domain = network_site_url(); |
| 182 | $certinfo = $this->get_certinfo( $domain ); |
| 183 | //Get the certificate common name |
| 184 | $certificate_common_name = isset( $certinfo['subject']['CN'] ) ? $certinfo['subject']['CN'] : false; |
| 185 | $subject_alt_names = isset( $certinfo['extensions']['subjectAltName'] ) ? explode( ', ', $certinfo['extensions']['subjectAltName'] ) : false; |
| 186 | |
| 187 | //Check if the common name(s) contain an * |
| 188 | if ( strpos( $certificate_common_name, '*' ) ) { |
| 189 | return true; |
| 190 | } |
| 191 | |
| 192 | if ( is_array( $subject_alt_names ) ) { |
| 193 | foreach ( $subject_alt_names as $subject_alt_name ) { |
| 194 | if ( strpos( $subject_alt_name, '*' ) !== false ) { |
| 195 | return true; |
| 196 | } |
| 197 | } |
| 198 | } |
| 199 | return false; |
| 200 | } |
| 201 | |
| 202 | /** |
| 203 | * |
| 204 | * Get the certificate info |
| 205 | * |
| 206 | * @since 3.0 |
| 207 | * @param string $url |
| 208 | * @return string|bool |
| 209 | * @access public |
| 210 | * |
| 211 | */ |
| 212 | |
| 213 | public function get_certinfo( $url ) { |
| 214 | $certinfo = get_transient( 'rsssl_certinfo' ); |
| 215 | //if the last check resulted in a "no response", we skip this check for a day. |
| 216 | if ( 'no-response' === $certinfo ) { |
| 217 | return false; |
| 218 | } |
| 219 | |
| 220 | if ( ! $certinfo || RSSSL()->admin->is_settings_page() ) { |
| 221 | $url = 'https://' . str_replace( array( 'https://', 'http://' ), '', $url ); |
| 222 | $original_parse = parse_url( $url, PHP_URL_HOST ); |
| 223 | if ( $original_parse ) { |
| 224 | $get = stream_context_create( array( 'ssl' => array( 'capture_peer_cert' => true ) ) ); |
| 225 | if ( $get ) { |
| 226 | set_error_handler( array( $this, 'custom_error_handling' ) ); |
| 227 | $read = stream_socket_client( 'ssl://' . $original_parse . ':443', $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $get ); |
| 228 | restore_error_handler(); |
| 229 | |
| 230 | if ( ! $read ) { |
| 231 | $certinfo = 'no-response'; |
| 232 | } |
| 233 | |
| 234 | if ( 0 === $errno && $read ) { |
| 235 | $cert = stream_context_get_params( $read ); |
| 236 | if ( isset( $cert['options']['ssl']['peer_certificate'] ) ) { |
| 237 | $certinfo = openssl_x509_parse( $cert['options']['ssl']['peer_certificate'] ); |
| 238 | } else { |
| 239 | $certinfo = 'no-response'; |
| 240 | } |
| 241 | } |
| 242 | } |
| 243 | } |
| 244 | set_transient( 'rsssl_certinfo', $certinfo, DAY_IN_SECONDS ); |
| 245 | } |
| 246 | if ( 'not-valid' === $certinfo ) { |
| 247 | return false; |
| 248 | } |
| 249 | if ( ! empty( $certinfo ) ) { |
| 250 | return $certinfo; |
| 251 | } |
| 252 | |
| 253 | return false; |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * Catch errors |
| 258 | * |
| 259 | * @since 3.0 |
| 260 | * |
| 261 | * @access public |
| 262 | * @param $errno |
| 263 | * @param $errstr |
| 264 | * @param $errfile |
| 265 | * @param $errline |
| 266 | * @param array $errcontext |
| 267 | * |
| 268 | * @return bool |
| 269 | */ |
| 270 | |
| 271 | public function custom_error_handling( $errno, $errstr, $errfile, $errline, $errcontext = array() ) { |
| 272 | return true; |
| 273 | } |
| 274 | |
| 275 | |
| 276 | //class closure |
| 277 | } |
| 278 | } |
| 279 |