Create New Item
×
Item Type
File
Folder
Item Name
×
Search file in folder and subfolders...
File Manager
/
wp-content
/
plugins
/
really-simple-ssl
/
lets-encrypt
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php defined('ABSPATH') or die("you do not have access to this page!"); require_once rsssl_le_path . 'vendor/autoload.php'; use LE_ACME2\Account; use LE_ACME2\Authorizer\AbstractDNSWriter; use LE_ACME2\Authorizer\DNS; use LE_ACME2\Authorizer\HTTP; use LE_ACME2\Connector\Connector; use LE_ACME2\Order; use LE_ACME2\Utilities\Certificate; use LE_ACME2\Utilities\Logger; class rsssl_letsencrypt_handler { private static $_this; public $account = false; public $challenge_directory = false; public $key_directory = false; public $certs_directory = false; public $subjects = array(); function __construct() { if (isset(self::$_this)) { wp_die(sprintf(__('%s is a singleton class and you cannot create a second instance.', 'really-simple-ssl'), get_class($this))); } if (rsssl_letsencrypt_generation_allowed(true)) { add_action('rsssl_before_save_lets-encrypt_option', array($this, 'before_save_wizard_option'), 10, 4); add_action('rsssl_le_activation', array($this, 'cleanup_on_ssl_activation')); add_action('rsssl_le_activation', array($this, 'plugin_activation_actions')); add_action('admin_init', array($this, 'maybe_add_htaccess_exclude')); add_action('admin_init', array($this, 'maybe_create_htaccess_directories')); $this->key_directory = $this->key_directory(); $this->challenge_directory = $this->challenge_directory(); $this->certs_directory = $this->certs_directory(); if ($this->key_directory) { Account::setCommonKeyDirectoryPath($this->key_directory); } if ($this->challenge_directory) { HTTP::setDirectoryPath($this->challenge_directory); } Connector::getInstance()->useStagingServer(false); Logger::getInstance()->setDesiredLevel(Logger::LEVEL_DISABLED); if (!get_option('rsssl_disable_ocsp')) { Certificate::enableFeatureOCSPMustStaple(); } Order::setPreferredChain('ISRG Root X1'); $this->subjects = $this->get_subjects(); $this->verify_dns(); } self::$_this = $this; } static function this() { return self::$_this; } public function maybe_add_htaccess_exclude() { if (!current_user_can('manage_options')) { return; } if (!RSSSL()->rsssl_server->uses_htaccess()) { return; } $htaccess_file = RSSSL()->really_simple_ssl->htaccess_file(); if (!file_exists($htaccess_file)) { return; } if (!is_writable($htaccess_file)) { return; } $htaccess = file_get_contents($htaccess_file); if (strpos($htaccess, 'Really Simple SSL LETS ENCRYPT') !== FALSE) { return; } $htaccess = preg_replace("/#\\s?BEGIN\\s?Really Simple SSL LETS ENCRYPT.*?#\\s?END\\s?Really Simple SSL LETS ENCRYPT/s", "", $htaccess); $htaccess = preg_replace("/\n+/", "\n", $htaccess); $rules = '#BEGIN Really Simple SSL LETS ENCRYPT' . "\n"; $rules .= 'RewriteRule ^.well-known/(.*)$ - [L]' . "\n"; $rules .= '#END Really Simple SSL LETS ENCRYPT' . "\n"; $htaccess = $rules . $htaccess; file_put_contents($htaccess_file, $htaccess); } public function installation_failed() { $installation_active = get_option("rsssl_le_start_installation"); $installation_failed = get_option("rsssl_installation_error"); return $installation_active && $installation_failed; } public function plugin_activation_actions() { if (get_option('rsssl_activated_plugin')) { delete_option('rsssl_activated_plugin'); } } public function cleanup_on_ssl_activation() { if (!current_user_can('manage_options')) { return; } $delete_credentials = !rsssl_get_value('store_credentials'); if (!$this->certificate_automatic_install_possible() || !$this->certificate_install_required() || $delete_credentials) { $fields = RSSSL_LE()->config->fields; $fields = array_filter($fields, function ($i) { return isset($i['type']) && $i['type'] === 'password'; }); $options = get_option('rsssl_options_lets-encrypt'); foreach ($fields as $fieldname => $field) { unset($options[$fieldname]); } update_option('rsssl_options_lets-encrypt', $options); } } public function before_save_wizard_option($fieldname, $fieldvalue, $prev_value, $type) { rsssl_progress_add('domain'); if ($fieldvalue === $prev_value) { return; } if ($fieldname === 'other_host_type') { if (!rsssl_do_local_lets_encrypt_generation()) { rsssl_progress_add('directories'); rsssl_progress_add('generation'); rsssl_progress_add('dns-verification'); } } if ($fieldname === 'email') { if (!is_email($fieldvalue)) { rsssl_progress_remove('domain'); } } } public function check_domain() { $details = parse_url(site_url()); $path = isset($details['path']) ? $details['path'] : ''; if (strpos(site_url(), 'localhost') !== false) { rsssl_progress_remove('system-status'); $action = 'stop'; $status = 'error'; $message = __("It is not possible to install Let's Encrypt on a localhost environment.", "really-simple-ssl"); } else { if (is_multisite() && get_current_blog_id() !== get_main_site_id()) { rsssl_progress_remove('system-status'); $action = 'stop'; $status = 'error'; $message = __("It is not possible to install Let's Encrypt on a subsite. Please go to the main site of your website.", "really-simple-ssl"); } else { if (strlen($path) > 0) { rsssl_progress_remove('system-status'); $action = 'stop'; $status = 'error'; $message = __("It is not possible to install Let's Encrypt on a subfolder configuration.", "really-simple-ssl") . rsssl_read_more('https://really-simple-ssl.com/install-ssl-on-subfolders'); } else { $action = 'continue'; $status = 'success'; $message = __("Your domain meets the requirements for Let's Encrypt.", "really-simple-ssl"); } } } return new RSSSL_RESPONSE($status, $action, $message); } public function search_ssl_installation_url() { $url = 'https://really-simple-ssl.com/install-ssl-certificate'; $host = 'enter-your-dashboard-url-here'; if (function_exists('wp_get_direct_update_https_url') && !empty(wp_get_direct_update_https_url())) { $url = wp_get_direct_update_https_url(); } if (rsssl_is_cpanel()) { $cpanel = new rsssl_cPanel(); $host = $cpanel->host; $url = $cpanel->ssl_installation_url; } else { if (rsssl_is_plesk()) { $plesk = new rsssl_plesk(); $host = $plesk->host; $url = $plesk->ssl_installation_url; } else { if (rsssl_is_directadmin()) { $directadmin = new rsssl_directadmin(); $host = $directadmin->host; $url = $directadmin->ssl_installation_url; } } } $hosting_company = rsssl_get_other_host(); if ($hosting_company && $hosting_company !== 'none') { $hosting_specific_link = RSSSL_LE()->config->hosts[$hosting_company]['ssl_installation_link']; if ($hosting_specific_link) { $site = trailingslashit(str_replace(array('https://', 'http://', 'www.'), '', site_url())); if (strpos($hosting_specific_link, '{host}') !== false && empty($host)) { $url = ''; } else { $url = str_replace(array('{host}', '{domain}'), array($host, $site), $hosting_specific_link); } } } $action = 'continue'; $status = 'warning'; $message = rsssl_get_manual_instructions_text($url); $output = $url; return new RSSSL_RESPONSE($status, $action, $message, $output); } public function certificate_status() { delete_transient('rsssl_certinfo'); if (RSSSL()->rsssl_certificate->is_valid()) { $certinfo = get_transient('rsssl_certinfo'); $end_date = isset($certinfo['validTo_time_t']) ? $certinfo['validTo_time_t'] : false; $grace_period = strtotime('+' . rsssl_le_manual_generation_renewal_check . ' days'); $expiry_date = !empty($end_date) ? date(get_option('date_format'), $end_date) : __("(unknown)", "really-simple-ssl"); if ($grace_period > $end_date) { $action = 'continue'; $status = 'success'; $message = sprintf(__("Your certificate will expire on %s.", "really-simple-ssl") . ' ' . __("Continue to renew.", "really-simple-ssl"), $expiry_date); } else { $action = 'continue'; $status = 'error'; $message = __("You already have a valid SSL certificate.", "really-simple-ssl"); } } else { $action = 'continue'; $status = 'success'; $message = __("SSL certificate should be generated and installed.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function certificate_about_to_expire() { $about_to_expire = RSSSL()->rsssl_certificate->about_to_expire(); if (!$about_to_expire) { delete_option('rsssl_le_start_renewal'); delete_option('rsssl_le_start_installation'); return false; } else { return true; } } public function server_software() { $action = 'continue'; $status = 'warning'; $message = __("The Hosting Panel software was not recognized. Depending on your hosting provider, the generated certificate may need to be installed manually.", "really-simple-ssl"); if (rsssl_is_cpanel()) { $status = 'success'; $message = __("CPanel recognized. Possibly the certificate can be installed automatically.", "really-simple-ssl"); } else { if (rsssl_is_plesk()) { $status = 'success'; $message = __("Plesk recognized. Possibly the certificate can be installed automatically.", "really-simple-ssl"); } else { if (rsssl_is_directadmin()) { $status = 'success'; $message = __("DirectAdmin recognized. Possibly the certificate can be installed automatically.", "really-simple-ssl"); } } } return new RSSSL_RESPONSE($status, $action, $message); } public function curl_exists() { if (function_exists('curl_init') === false) { $action = 'stop'; $status = 'error'; $message = __("The PHP function CURL is not available on your server, which is required. Please contact your hosting provider.", "really-simple-ssl"); } else { $action = 'continue'; $status = 'success'; $message = __("The PHP function CURL has successfully been detected.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function get_account() { $account_email = $this->account_email(); if (is_email($account_email)) { try { $this->account = !Account::exists($account_email) ? Account::create($account_email) : Account::get($account_email); $status = 'success'; $action = 'continue'; $message = __("Successfully retrieved account", "really-simple-ssl"); } catch (Exception $e) { error_log(print_r($e, true)); $response = $this->get_error($e); $status = 'error'; $action = 'retry'; if (strpos($response, 'invalid contact domain')) { $action = 'stop'; $response = __("The used domain for your email address is not allowed.", "really-simple-ssl") . ' ' . sprintf(__("Please change your email address %shere%s and try again.", "really-simple-ssl"), '<a href="' . rsssl_letsencrypt_wizard_url() . '&step=2' . '">', '</a>'); } $message = $response; } } else { error_log("no email set"); $status = 'error'; $action = 'stop'; $message = __("The email address was not set. Please set the email address", 'really-simple-ssl'); } return new RSSSL_RESPONSE($status, $action, $message); } public function get_dns_token() { if (rsssl_is_ready_for('dns-verification')) { $use_dns = rsssl_dns_verification_required(); $challenge_type = $use_dns ? Order::CHALLENGE_TYPE_DNS : Order::CHALLENGE_TYPE_HTTP; if ($use_dns) { try { $this->get_account(); $dnsWriter = new class extends AbstractDNSWriter { public function write(Order $order, string $identifier, string $digest) : bool { $tokens = get_option('rsssl_le_dns_tokens', array()); $tokens[$identifier] = $digest; update_option("rsssl_le_dns_tokens", $tokens); rsssl_progress_add('dns-verification'); return false; } }; DNS::setWriter($dnsWriter); $response = $this->get_order(); $order = $response->output; $response->output = false; if ($order) { try { if ($order->authorize($challenge_type)) { $response = new RSSSL_RESPONSE('success', 'continue', __("Token successfully retrieved.", 'really-simple-ssl'), json_encode(get_option('rsssl_le_dns_tokens'))); } else { if (get_option('rsssl_le_dns_tokens')) { $response = new RSSSL_RESPONSE('success', 'continue', __("Token successfully retrieved.", 'really-simple-ssl'), json_encode(get_option('rsssl_le_dns_tokens'))); } else { $response = new RSSSL_RESPONSE('error', 'retry', __("Token not received yet.", 'really-simple-ssl')); } } } catch (Exception $e) { error_log(print_r($e, true)); $error = $this->get_error($e); if (strpos($error, 'Order has status "invalid"') !== false) { $order->clear(); $error = __("The order is invalid, possibly due to too many failed authorization attempts. Please start at the previous step.", "really-simple-ssl"); } else { if (strpos($error, 'No order for ID ') !== FALSE) { $error .= ' ' . __("Order ID mismatch, regenerate order.", "really-simple-ssl"); $order->clear(); rsssl_progress_remove('dns-verification'); $error .= ' ' . __("If you entered your DNS records before, they need to be changed.", "really-simple-ssl"); } } $response = new RSSSL_RESPONSE('error', 'retry', $error); } } } catch (Exception $e) { rsssl_progress_remove('dns-verification'); $response = $this->get_error($e); error_log(print_r($e, true)); $response = new RSSSL_RESPONSE('error', 'retry', $response); } } else { $response = new RSSSL_RESPONSE('error', 'stop', __("Configured for HTTP challenge", 'really-simple-ssl')); } } else { rsssl_progress_remove('dns-verification'); $response = new RSSSL_RESPONSE('error', 'stop', $this->not_completed_steps_message('dns-verification')); } return $response; } public function verify_dns() { if (rsssl_is_ready_for('generation')) { update_option('rsssl_le_dns_records_verified', false); $tokens = get_option('rsssl_le_dns_tokens'); if (!$tokens) { $status = 'error'; $action = 'stop'; $message = __('Token not generated. Please complete the previous step.', "really-simple-ssl"); return new RSSSL_RESPONSE($status, $action, $message); } foreach ($tokens as $identifier => $token) { if (strpos($identifier, '*') !== false) { continue; } set_error_handler(array($this, 'custom_error_handling')); $response = dns_get_record("_acme-challenge.{$identifier}", DNS_TXT); restore_error_handler(); if (isset($response[0]['txt'])) { if ($response[0]['txt'] === $token) { $response = new RSSSL_RESPONSE('success', 'continue', sprintf(__('Successfully verified DNS records', "really-simple-ssl"), "_acme-challenge.{$identifier}")); update_option('rsssl_le_dns_records_verified', true); } else { $response = new RSSSL_RESPONSE('error', 'stop', sprintf(__('The DNS response for %s was %s, while it should be %s.', "really-simple-ssl"), "_acme-challenge.{$identifier}", $response[0]['txt'], $token)); break; } } else { $action = get_option('rsssl_skip_dns_check') ? 'continue' : 'stop'; $response = new RSSSL_RESPONSE('warning', $action, sprintf(__('Could not verify TXT record for domain %s', "really-simple-ssl"), "_acme-challenge.{$identifier}")); } } } else { $response = new RSSSL_RESPONSE('error', 'stop', $this->not_completed_steps_message('dns-verification')); } return $response; } public function clear_order() { $this->get_account(); if ($this->account) { $response = $this->get_order(); $order = $response->output; if ($order) { $order->clear(); } } } public function create_bundle_or_renew() { $bundle_completed = false; $use_dns = rsssl_dns_verification_required(); $attempt_count = intval(get_transient('rsssl_le_generate_attempt_count')); if ($attempt_count > 5) { delete_option("rsssl_le_start_renewal"); $message = __("The certificate generation was rate limited for 10 minutes because the authorization failed.", 'really-simple-ssl'); if ($use_dns) { $message .= ' ' . __("Please double check your DNS txt record.", 'really-simple-ssl'); } return new RSSSL_RESPONSE('error', 'stop', $message); } if (!get_option('rsssl_skip_dns_check')) { if ($use_dns && !get_option('rsssl_le_dns_records_verified')) { return new RSSSL_RESPONSE('error', 'stop', __("DNS records were not verified yet. Please complete the previous step.", 'really-simple-ssl')); } } if (rsssl_is_ready_for('generation')) { $this->get_account(); if ($use_dns) { $dnsWriter = new class extends AbstractDNSWriter { public function write(Order $order, string $identifier, string $digest) : bool { $status = false; if (get_option('rsssl_le_dns_tokens')) { $status = true; } return $status; } }; DNS::setWriter($dnsWriter); } $response = $this->get_order(); $order = $response->output; $response->output = false; if ($order) { if ($order->isCertificateBundleAvailable()) { try { $order->enableAutoRenewal(); $response = new RSSSL_RESPONSE('success', 'continue', __("Certificate already generated. It was renewed if required.", 'really-simple-ssl')); $bundle_completed = true; } catch (Exception $e) { error_log(print_r($e, true)); $response = new RSSSL_RESPONSE('error', 'retry', $this->get_error($e)); $bundle_completed = false; } } else { $finalized = false; $challenge_type = $use_dns ? Order::CHALLENGE_TYPE_DNS : Order::CHALLENGE_TYPE_HTTP; try { if ($order->authorize($challenge_type)) { $order->finalize(); $this->reset_attempt(); $finalized = true; } else { $this->count_attempt(); $response = new RSSSL_RESPONSE('error', 'retry', __('Authorization not completed yet.', "really-simple-ssl")); $bundle_completed = false; } } catch (Exception $e) { $this->count_attempt(); $message = $this->get_error($e); error_log(print_r($e, true)); $response = new RSSSL_RESPONSE('error', 'stop', $message); if (strpos($message, 'Order has status "invalid"') !== false) { $order->clear(); $response->message = __("The order is invalid, possibly due to too many failed authorization attempts. Please start at the previous step.", "really-simple-ssl"); if ($use_dns) { rsssl_progress_remove('dns-verification'); $response->message .= ' ' . __("As your order will be regenerated, you'll need to update your DNS text records.", "really-simple-ssl"); } } else { if (!get_option('rsssl_disable_ocsp')) { update_option('rsssl_disable_ocsp', true); $response->action = 'retry'; $response->status = 'warning'; $response->message = __("OCSP not supported, the certificate will be generated without OCSP.", "really-simple-ssl"); } } } if ($finalized) { try { if ($order->isCertificateBundleAvailable()) { error_log("cert bundle available"); $bundle_completed = true; $success_cert = $success_intermediate = $success_private = false; $bundle = $order->getCertificateBundle(); $pathToPrivateKey = $bundle->path . $bundle->private; $pathToCertificate = $bundle->path . $bundle->certificate; $pathToIntermediate = $bundle->path . $bundle->intermediate; if (file_exists($pathToPrivateKey)) { $success_private = true; update_option('rsssl_private_key_path', $pathToPrivateKey); } if (file_exists($pathToCertificate)) { $success_cert = true; update_option('rsssl_certificate_path', $pathToCertificate); } if (file_exists($pathToIntermediate)) { $success_intermediate = true; update_option('rsssl_intermediate_path', $pathToIntermediate); } if (!$success_cert || !$success_private || !$success_intermediate) { $bundle_completed = false; } if ($bundle_completed) { $response = new RSSSL_RESPONSE('success', 'continue', __("Successfully generated certificate.", 'really-simple-ssl')); } else { $response = new RSSSL_RESPONSE('error', 'retry', __("Files not created yet...", 'really-simple-ssl')); } } else { $response = new RSSSL_RESPONSE('error', 'retry', __("Bundle not available yet...", 'really-simple-ssl')); } } catch (Exception $e) { error_log(print_r($e, true)); $response = new RSSSL_RESPONSE('error', 'retry', $this->get_error($e)); } } } } } else { $response = new RSSSL_RESPONSE('error', 'stop', $this->not_completed_steps_message('generation')); } if ($bundle_completed) { rsssl_progress_add('generation'); update_option('rsssl_le_certificate_generated_by_rsssl', true); delete_option("rsssl_le_start_renewal"); } else { rsssl_progress_remove('generation'); } return $response; } public function get_order() { if (!Order::exists($this->account, $this->subjects)) { try { $response = new RSSSL_RESPONSE('success', 'continue', __("Order successfully created.", 'really-simple-ssl')); $response->output = Order::create($this->account, $this->subjects); } catch (Exception $e) { error_log(print_r($e, true)); $response = new RSSSL_RESPONSE('error', 'retry', $this->get_error($e)); } } else { $response = new RSSSL_RESPONSE('success', 'continue', __("Order successfully retrieved.", 'really-simple-ssl')); $response->output = Order::get($this->account, $this->subjects); } return $response; } public function count_attempt() { $attempt_count = intval(get_transient('rsssl_le_generate_attempt_count')); $attempt_count++; set_transient('rsssl_le_generate_attempt_count', $attempt_count, 10 * MINUTE_IN_SECONDS); } public function reset_attempt() { delete_transient('rsssl_le_generate_attempt_count'); } public function ssl_generation_can_auto_renew() { if (get_option('rsssl_verification_type') === 'DNS' && !get_option('rsssl_le_dns_configured_by_rsssl')) { return false; } else { return true; } } public function certificate_automatic_install_possible() { $install_method = get_option('rsssl_le_certificate_installed_by_rsssl'); if ($install_method === false) { return false; } else { return true; } } public function should_start_manual_installation_renewal() { if (!$this->should_start_manual_ssl_generation() && get_option("rsssl_le_start_installation")) { return true; } return false; } public function should_start_manual_ssl_generation() { return get_option("rsssl_le_start_renewal"); } public function certificate_renewal_status_notice() { if (!RSSSL_LE()->letsencrypt_handler->ssl_generation_can_auto_renew()) { return 'manual-generation'; } if ($this->certificate_install_required() && $this->certificate_automatic_install_possible() && $this->installation_failed()) { return 'automatic-installation-failed'; } if ($this->certificate_install_required() && !$this->certificate_automatic_install_possible()) { return 'manual-installation'; } return 'automatic'; } public function certificate_install_required() { $install_method = get_option('rsssl_le_certificate_installed_by_rsssl'); $hosting_company = rsssl_get_other_host(); if (in_array($install_method, RSSSL_LE()->config->no_installation_renewal_needed) || in_array($hosting_company, RSSSL_LE()->config->no_installation_renewal_needed)) { return false; } return true; } public function cron_certificate_needs_renewal() { $cert_file = get_option('rsssl_certificate_path'); if (empty($cert_file)) { return false; } $certificate = file_get_contents($cert_file); $certificateInfo = openssl_x509_parse($certificate); $valid_to = $certificateInfo['validTo_time_t']; $in_expiry_days = strtotime("+" . rsssl_le_cron_generation_renewal_check . " days"); if ($in_expiry_days > $valid_to) { return true; } else { return false; } } public function account_email() { return rsssl_get_value('email_address', false); } public function terms_accepted() { $accepted = rsssl_get_value('accept_le_terms', false); if ($accepted) { $status = 'success'; $action = 'continue'; $message = __("Terms & Conditions are accepted.", 'really-simple-ssl'); } else { $status = 'error'; $action = 'stop'; $message = __("The Terms & Conditions were not accepted. Please accept in the general settings.", 'really-simple-ssl'); } return new RSSSL_RESPONSE($status, $action, $message); } public function update_account($new_email) { if (!$this->account) { return; } try { $this->account->update($new_email); } catch (Exception $e) { error_log("Lets encrypt email update failed"); error_log(print_r($e, true)); } } public function get_subjects() { $subjects = array(); $domain = rsssl_get_domain(); $root = str_replace('www.', '', $domain); $subjects[] = $domain; if (!rsssl_is_subdomain()) { if (rsssl_get_value('include_alias')) { if (strpos($domain, 'www.') !== false) { $alias_domain = $root; } else { $alias_domain = 'www.' . $root; } $subjects[] = $alias_domain; } } if (rsssl_wildcard_certificate_required()) { $domain = rsssl_get_domain(); $domain = str_replace('www.', '', $domain); $subjects = array($domain, '*.' . $domain); } return apply_filters('rsssl_le_subjects', $subjects); } public function is_ready_for($item) { if (!rsssl_do_local_lets_encrypt_generation()) { rsssl_progress_add('directories'); rsssl_progress_add('generation'); rsssl_progress_add('dns-verification'); } if (!rsssl_dns_verification_required()) { rsssl_progress_add('dns-verification'); } if (empty(rsssl_get_not_completed_steps($item))) { return true; } else { return false; } } public function custom_error_handling($errno, $errstr, $errfile, $errline, $errcontext = array()) { return true; } public function not_completed_steps_message($step) { $not_completed_steps = rsssl_get_not_completed_steps($step); $nice_names = array(); foreach ($not_completed_steps as $not_completed_step) { $index = array_search($not_completed_step, array_column(RSSSL_LE()->config->steps['lets-encrypt'], 'id')); $nice_names[] = RSSSL_LE()->config->steps['lets-encrypt'][$index + 1]['title']; } return sprintf(__('Please complete the following step(s) first: %s', "really-simple-ssl"), implode(", ", $nice_names)); } public function check_writing_permissions() { $directories_without_permissions = $this->directories_without_writing_permissions(); $has_missing_permissions = count($directories_without_permissions) > 0; if ($has_missing_permissions) { rsssl_progress_remove('directories'); $action = 'stop'; $status = 'error'; $message = __("The following directories do not have the necessary writing permissions.", "really-simple-ssl") . " " . __("Set permissions to 644 to enable SSL generation.", "really-simple-ssl"); foreach ($directories_without_permissions as $directories_without_permission) { $message .= "<br> - " . $directories_without_permission; } } else { $action = 'continue'; $status = 'success'; $message = __("The required directories have the necessary writing permissions.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function check_challenge_directory() { if (!$this->challenge_directory()) { rsssl_progress_remove('directories'); $action = 'stop'; $status = 'error'; $message = __("The challenge directory is not created yet.", "really-simple-ssl"); } else { $action = 'continue'; $status = 'success'; $message = __("The challenge directory was successfully created.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function check_key_directory() { $action = 'stop'; $status = 'error'; $message = __("The key directory is not created yet.", "really-simple-ssl"); if (!get_option('rsssl_create_folders_in_root')) { $action = 'retry'; $message = __("Trying to create directory in root of website.", "really-simple-ssl"); } if (!$this->key_directory()) { rsssl_progress_remove('directories'); } else { $action = 'continue'; $status = 'success'; $message = __("The key directory was successfully created.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function check_certs_directory() { if (!$this->certs_directory()) { rsssl_progress_remove('directories'); $action = 'stop'; $status = 'error'; $message = __("The certs directory is not created yet.", "really-simple-ssl"); } else { $action = 'continue'; $status = 'success'; $message = __("The certs directory was successfully created.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function directories_without_writing_permissions() { $required_folders = array($this->key_directory, $this->certs_directory); if (!rsssl_dns_verification_required()) { $required_folders[] = $this->challenge_directory; } $no_writing_permissions = array(); foreach ($required_folders as $required_folder) { if (!$this->directory_has_writing_permissions($required_folder)) { $no_writing_permissions[] = $required_folder; } } return $no_writing_permissions; } public function directory_has_writing_permissions($directory) { set_error_handler(array($this, 'custom_error_handling')); $test_file = fopen($directory . "/really-simple-ssl-permissions-check.txt", "w"); if (!$test_file) { return false; } fwrite($test_file, 'file to test writing permissions for Really Simple SSL'); fclose($test_file); restore_error_handler(); if (!file_exists($directory . "/really-simple-ssl-permissions-check.txt")) { return false; } else { return true; } } public function challenge_directory_reachable() { $file_content = false; $status_code = __('no response', 'really-simple-ssl'); $url = str_replace('https://', 'http://', site_url('.well-known/acme-challenge/really-simple-ssl-permissions-check.txt')); $error_message = sprintf(__("Could not reach challenge directory over %s.", "really-simple-ssl"), '<a target="_blank" href="' . $url . '">' . $url . '</a>'); $test_string = 'Really Simple SSL'; $folders = $this->directories_without_writing_permissions(); if (!$this->challenge_directory() || count($folders) !== 0) { $status = 'error'; $action = 'stop'; $message = __("Challenge directory not writable.", "really-simple-ssl"); return new RSSSL_RESPONSE($status, $action, $message); } $response = wp_remote_get($url); if (is_array($response)) { $status_code = wp_remote_retrieve_response_code($response); $file_content = wp_remote_retrieve_body($response); } if ($status_code !== 200) { if (get_option('rsssl_skip_challenge_directory_request')) { $status = 'warning'; $action = 'continue'; $message = $error_message . ' ' . sprintf(__("Error code %s.", "really-simple-ssl"), $status_code); } else { $status = 'error'; $action = 'stop'; $message = $error_message . ' ' . sprintf(__("Error code %s.", "really-simple-ssl"), $status_code); rsssl_progress_remove('directories'); } } else { if (!is_wp_error($response) && strpos($file_content, $test_string) !== false) { $status = 'success'; $action = 'continue'; $message = __("Successfully verified alias domain.", "really-simple-ssl"); set_transient('rsssl_alias_domain_available', 'available', 30 * MINUTE_IN_SECONDS); } else { $status = 'error'; $action = 'stop'; $message = $error_message; rsssl_progress_remove('directories'); } } return new RSSSL_RESPONSE($status, $action, $message); } public function challenge_directory() { $root_directory = trailingslashit(ABSPATH); if (!file_exists($root_directory . '.well-known')) { mkdir($root_directory . '.well-known'); } if (!file_exists($root_directory . '.well-known/acme-challenge')) { mkdir($root_directory . '.well-known/acme-challenge'); } if (file_exists($root_directory . '.well-known/acme-challenge')) { return $root_directory . '.well-known/acme-challenge'; } else { return false; } } public function certs_directory() { $directory = $this->get_directory_path(); if (!file_exists($directory . 'ssl')) { mkdir($directory . 'ssl'); } if (!file_exists($directory . 'ssl/certs')) { mkdir($directory . 'ssl/certs'); } if (file_exists($directory . 'ssl/certs')) { return $directory . 'ssl/certs'; } else { return false; } } public function get_directory_path() { $root_directory = trailingslashit(ABSPATH); if (get_option('rsssl_create_folders_in_root')) { if (!get_option('rsssl_ssl_dirname')) { $token = str_shuffle(time()); update_option('rsssl_ssl_dirname', $token); } if (!file_exists($root_directory . get_option('rsssl_ssl_dirname'))) { mkdir($root_directory . get_option('rsssl_ssl_dirname')); } return $root_directory . trailingslashit(get_option('rsssl_ssl_dirname')); } else { return trailingslashit(dirname($root_directory)); } } public function key_directory() { $directory = $this->get_directory_path(); if (!file_exists($directory . 'ssl')) { mkdir($directory . 'ssl'); } if (!file_exists($directory . 'ssl/keys')) { mkdir($directory . 'ssl/keys'); } if (file_exists($directory . 'ssl/keys')) { return $directory . 'ssl/keys'; } else { $challenge_dir = $this->challenge_directory; $has_writing_permissions = $this->directory_has_writing_permissions($challenge_dir); if (RSSSL()->rsssl_server->uses_htaccess() && $has_writing_permissions) { update_option('rsssl_create_folders_in_root', true); } return false; } } public function clear_keys_directory() { if (!current_user_can('manage_options')) { return; } $dir = $this->key_directory(); $this->delete_files_directories_recursively($dir); } private function delete_files_directories_recursively($dir) { if (strpos($dir, 'ssl/keys') !== false) { foreach (glob($dir . '/*') as $file) { if (is_dir($file)) { $this->delete_files_directories_recursively($file); } else { unlink($file); } } rmdir($dir); } } public function maybe_create_htaccess_directories() { if (!current_user_can('manage_options')) { return; } if (!RSSSL()->rsssl_server->uses_htaccess()) { return; } if (!get_option('rsssl_create_folders_in_root')) { return; } if (!empty($this->get_directory_path())) { $this->write_htaccess_dir_file($this->get_directory_path() . 'ssl/.htaccess', 'ssl'); } if (!empty($this->key_directory())) { $this->write_htaccess_dir_file(trailingslashit($this->key_directory()) . '.htaccess', 'key'); } if (!empty($this->certs_directory())) { $this->write_htaccess_dir_file(trailingslashit($this->certs_directory()) . '.htaccess', 'certs'); } } public function write_htaccess_dir_file($path, $type) { $htaccess = '<ifModule mod_authz_core.c>' . "\n" . ' Require all denied' . "\n" . '</ifModule>' . "\n" . '<ifModule !mod_authz_core.c>' . "\n" . ' Deny from all' . "\n" . '</ifModule>'; insert_with_markers($path, 'Really Simple SSL LETS ENCRYPT', $htaccess); $htaccess = file_get_contents($path); if (strpos($htaccess, 'deny from all') !== FALSE) { update_option('rsssl_htaccess_file_set_' . $type, true); return; } } public function is_subdomain_setup() { if (!is_multisite()) { $is_subdomain = false; } else { if (defined('SUBDOMAIN_INSTALL') && SUBDOMAIN_INSTALL) { $is_subdomain = true; } else { $is_subdomain = false; } } if ($is_subdomain) { $status = 'error'; $action = 'stop'; $message = sprintf(__("This is a multisite configuration with subdomains, which requires a wildcard certificate. Wildcard certificates are part of the %spremium%s plan.", 'really-simple-ssl'), '<a href="https://really-simple-ssl.com/pro" target="_blank">', '</a>'); rsssl_progress_remove('system-status'); } else { $status = 'success'; $action = 'continue'; $message = __("No subdomain setup detected.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } public function is_wildcard() { $subjects = $this->get_subjects(); $is_wildcard = false; foreach ($subjects as $domain) { if (strpos($domain, '*') !== false) { $is_wildcard = true; } } return $is_wildcard; } public function alias_domain_available() { if (rsssl_is_subdomain()) { return new RSSSL_RESPONSE('success', 'continue', __("Alias domain check is not relevant for a subdomain", "really-simple-ssl")); } $uploads = wp_upload_dir(); $upload_dir = trailingslashit($uploads['basedir']); $upload_url = trailingslashit($uploads['baseurl']); $file_content = false; $status_code = __('no response', 'really-simple-ssl'); $domain = rsssl_get_domain(); if (strpos($domain, 'www.') !== false) { $is_www = true; $alias_domain = str_replace('www.', '', $domain); } else { $is_www = false; $alias_domain = 'www.' . $domain; } if ($is_www) { $message = __("Please check if the non www version of your site also points to this website.", "really-simple-ssl"); } else { $message = __("Please check if the www version of your site also points to this website.", "really-simple-ssl"); } $error_message = __("Could not verify alias domain.", "really-simple-ssl") . ' ' . $message . ' ' . __("If this is not the case, don't add this alias to your certificate.", "really-simple-ssl"); $cached_status = get_transient('rsssl_alias_domain_available'); if ($cached_status) { if ($cached_status === 'available') { $status = 'success'; $action = 'continue'; $message = __("Successfully verified alias domain.", "really-simple-ssl"); } else { $status = 'warning'; $action = 'continue'; $message = $error_message; } return new RSSSL_RESPONSE($status, $action, $message); } if (!file_exists($upload_dir . 'rsssl')) { mkdir($upload_dir . 'rsssl'); } $test_string = 'file to test alias domain existence'; $test_file = $upload_dir . 'rsssl/test.txt'; file_put_contents($test_file, $test_string); $test_url = $upload_url . 'rsssl/test.txt'; if (!file_exists($test_file)) { $status = 'error'; $action = 'stop'; $message = __("Could not create test folder and file.", "really-simple-ssl") . ' ' . __("Please create a folder 'rsssl' in the uploads directory, with 644 permissions.", "really-simple-ssl"); } else { set_transient('rsssl_alias_domain_available', 'not-available', 30 * MINUTE_IN_SECONDS); $alias_test_url = str_replace($domain, $alias_domain, $test_url); $alias_test_url = str_replace('https://', 'http://', $alias_test_url); $response = wp_remote_get($alias_test_url); if (is_array($response)) { $status_code = wp_remote_retrieve_response_code($response); $file_content = wp_remote_retrieve_body($response); } if ($status_code !== 200) { $status = 'warning'; $action = 'continue'; $message = $error_message; if (intval($status_code) != 0) { $message .= ' ' . sprintf(__("Error code %s", "really-simple-ssl"), $status_code); } } else { if (!is_wp_error($response) && strpos($file_content, $test_string) !== false) { if (!get_option('rsssl_initial_alias_domain_value_set')) { RSSSL_LE()->field->save_field('rsssl_include_alias', true); update_option('rsssl_initial_alias_domain_value_set', true); } $status = 'success'; $action = 'continue'; $message = __("Successfully verified alias domain.", "really-simple-ssl"); set_transient('rsssl_alias_domain_available', 'available', 30 * MINUTE_IN_SECONDS); } else { $status = 'warning'; $action = 'continue'; $message = $error_message; } } } return new RSSSL_RESPONSE($status, $action, $message); } private function get_error($e) { $is_raw_response = false; if (method_exists($e, 'getRawResponse') && isset($e->getRawResponse()->body['detail'])) { $is_raw_response = true; $error = $e->getRawResponse()->body['detail']; error_log($error); if (isset($e->getRawResponse()->body['subproblems'])) { $error .= '<ul>'; foreach ($e->getRawResponse()->body['subproblems'] as $index => $problem) { $error .= '<li>' . $this->cleanup_error_message($e->getRawResponse()->body['subproblems'][$index]['detail']) . '</li>'; } $error .= '</ul>'; } } else { $error = $e->getMessage(); } $max = strpos($error, 'CURL response'); if ($max === false) { $max = 200; } if (!$is_raw_response) { $error = substr($error, 0, $max); } return $error; } public function cron_renew_installation() { $install_method = get_option('rsssl_le_certificate_installed_by_rsssl'); $data = explode(':', $install_method); $server = isset($data[0]) ? $data[0] : false; $type = isset($data[1]) ? $data[1] : false; $attempt_count = intval(get_transient('rsssl_le_install_attempt_count')); $attempt_count++; set_transient('rsssl_le_install_attempt_count', $attempt_count, DAY_IN_SECONDS); if ($attempt_count > 10) { delete_option("rsssl_le_start_installation"); $status = 'error'; $action = 'stop'; $message = __("The certificate installation was rate limited. Please try again later.", 'really-simple-ssl'); return new RSSSL_RESPONSE($status, $action, $message); } if (rsssl_is_ready_for('installation')) { try { if ($server === 'cpanel') { if ($type === 'default') { $response = rsssl_install_cpanel_default(); } else { if (function_exists('rsssl_install_cpanel_shell')) { $response = rsssl_install_cpanel_shell(); } } if ($response->status === 'success') { delete_option("rsssl_le_start_installation"); } return $response; } else { if ($server === 'plesk') { $response = rsssl_plesk_install(); if ($response->status === 'success') { delete_option("rsssl_le_start_installation"); } return $response; } else { $status = 'error'; $action = 'stop'; $message = __("Not recognized server.", "really-simple-ssl"); } } } catch (Exception $e) { error_log(print_r($e, true)); $status = 'error'; $action = 'stop'; $message = __("Installation failed.", "really-simple-ssl"); } } else { $status = 'error'; $action = 'stop'; $message = __("The system is not ready for the installation yet. Please run the wizard again.", "really-simple-ssl"); } return new RSSSL_RESPONSE($status, $action, $message); } private function cleanup_error_message($msg) { return str_replace(array('Refer to sub-problems for more information.', 'Error creating new order ::'), '', $msg); } public function encode($string) { if (strlen(trim($string)) === 0) { return $string; } if (strpos($string, 'rsssl_') !== FALSE) { return $string; } $key = $this->get_key(); if (!$key) { $key = $this->set_key(); } $ivlength = openssl_cipher_iv_length('aes-256-cbc'); $iv = openssl_random_pseudo_bytes($ivlength); $ciphertext_raw = openssl_encrypt($string, 'aes-256-cbc', $key, 0, $iv); $key = base64_encode($iv . $ciphertext_raw); return 'rsssl_' . $key; } public function decode($string) { if (!wp_doing_cron() && !current_user_can('manage_options')) { return ''; } if (strpos($string, 'rsssl_') !== FALSE) { $key = $this->get_key(); $string = str_replace('rsssl_', '', $string); $ivlength = openssl_cipher_iv_length('aes-256-cbc'); $iv = substr(base64_decode($string), 0, $ivlength); $encrypted_data = substr(base64_decode($string), $ivlength); $decrypted = openssl_decrypt($encrypted_data, 'aes-256-cbc', $key, 0, $iv); return $decrypted; } return $string; } private function set_key() { update_site_option('rsssl_key', time()); return get_site_option('rsssl_key'); } private function get_key() { return get_site_option('rsssl_key'); } }