summaryrefslogblamecommitdiffstats
path: root/modules-available/webinterface/inc/webinterface.inc.php
blob: 035b94e6664cf9807db05bf8fd12d015817c4f4f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11










                                                                              

                                                           




















































                                                                                                                         




                                                                     

























                                                                                                   

                                                        

                                                                 
                                                                       




                                                              



                                                                     
                                                                                                      


                                           
                                                                                                                       
         


                                                               


                                                              

                                                                     
                                                                  













                                                                     












                                                                             
 
<?php

class WebInterface
{
	public const PROP_TYPE = 'webinterface.https-type';
	public const PROP_HSTS = 'webinterface.https-hsts';
	public const PROP_REDIRECT = 'webinterface.https-redirect';
	public const PROP_CURRENT_CERT_DOMAINS = 'webinterface.https-domains';

	public const PROP_REDIRECT_DOMAIN = 'webinterface.redirect-domain';

	public const PROP_API_KEY = 'webinterface.api-key';

	/**
	 * Read data all handled domains from current certificate.
	 * SAN takes precedence, if empty, we fall back to CN.
	 * @param string[] $certDomains
	 * @return bool success reading?
	 */
	public static function extractCurrentCertData(array &$certDomains, int &$expireTimestamp, string &$issuer): bool
	{
		if (!is_readable('/etc/lighttpd/pub-cert.pem'))
		return false;
		$cert = openssl_x509_parse(file_get_contents('/etc/lighttpd/pub-cert.pem'));
		if ($cert === false)
			return false;
		// Domains
		$certDomains = [];
		if (isset($cert['extensions']['subjectAltName'])) {
			$doms = preg_split('/[,\s]+/', $cert['extensions']['subjectAltName'], -1, PREG_SPLIT_NO_EMPTY);
			foreach ($doms as $d) {
				if (substr_compare($d, 'DNS:', 0, 4, true) !== 0)
					continue;
				$d = substr($d, 4);
				if (preg_match('/^([a-z0-9_-]|\*\.)[a-z0-9_.-]+$/', $d) && !in_array($d, $certDomains)) {
					$certDomains[] = $d;
				}
			}
		}
		if (empty($certDomains) && isset($cert['subject']['CN'])
				&& preg_match('/^([a-z0-9_-]|\*\.)[a-z0-9_.-]+$/', $cert['subject']['CN'])) {
			$certDomains[] = $cert['subject']['CN'];
		}
		foreach ($certDomains as &$d) {
			if ($d[-1] === '.') {
				$d = substr($d, 0, -1);
			}
		}
		Property::set(self::PROP_CURRENT_CERT_DOMAINS, implode(' ', $certDomains));
		// Expire time
		$expireTimestamp = $cert['validTo_time_t'] ?? 0;
		// Issuer
		$issuer = $cert['issuer']['CN'] ?? 'Unknown';
		return true;
	}

	public static function setDomainRedirect(bool $enable): void
	{
		Property::set(self::PROP_REDIRECT_DOMAIN, $enable ? '1' : false);
	}

	public static function getDomainRedirect(): bool
	{
		return !empty(Property::get(self::PROP_REDIRECT_DOMAIN, false));
	}

	public static function isHttpsRedirectEnabled(): bool
	{
		return Property::get(self::PROP_REDIRECT) === 'True';
	}

	private static function registerCallback($task, string $newState, string $logMessage): void
	{
		if (!Taskmanager::isTask($task))
			return;
		TaskmanagerCallback::addCallback($task, 'webifCert', [
			'state' => $newState,
			'message' => $logMessage . ' by ' . (User::getLogin() ?? 'system'),
		]);
	}

	public static function certTaskFinishedCallback(array $task, $data): void
	{
		if (!Taskmanager::isFinished($task))
			return;
		if (!isset($data['state']) || !isset($data['message'])) {
			error_log('Invalid certTaskFinishedCallback: Missing fields');
			return;
		}
		if (Taskmanager::isFailed($task)) {
			EventLog::failure($data['message'], json_encode($task, JSON_PRETTY_PRINT));
			return;
		}
		EventLog::info($data['message']);
		Property::set(self::PROP_TYPE, $data['state']);
	}

	public static function tmDisableHttps(): ?string
	{
		Property::set(WebInterface::PROP_HSTS, 'off');
		$task = Taskmanager::submit('LighttpdHttps', []);
		self::registerCallback($task, 'off', 'HTTPS disabled');
		return $task['id'] ?? null;
	}

	public static function tmGenerateRandomCert(): ?string
	{
		$task = Taskmanager::submit('LighttpdHttps', [
			'proxyip' => Property::getServerIp(),
			'redirect' => self::isHttpsRedirectEnabled(),
		]);
		self::registerCallback($task, 'generated', 'Self-signed HTTPS certificate generated');
		return $task['id'] ?? null;
	}

	public static function tmImportCustomCert(string $key, string $cert, string $type, string $logMessage): ?string
	{
		$key = preg_replace('/[\r\n]+/', "\n", $key);
		$cert = preg_replace('/[\r\n]+/', "\n", $cert);
		Property::set(WebInterface::PROP_TYPE, $type);
		$task = Taskmanager::submit('LighttpdHttps', [
			'importcert' => $cert,
			'importkey' => $key,
			'redirect' => self::isHttpsRedirectEnabled(),
		]);
		self::registerCallback($task, $type, $logMessage);
		return $task['id'] ?? null;
	}

	public static function tmSetHttpRedirectMode(): ?string
	{
		if (Property::get(WebInterface::PROP_TYPE) === 'off')
			return null;
		$task = Taskmanager::submit('LighttpdHttps', array(
			'redirectOnly' => true,
			'redirect' => self::isHttpsRedirectEnabled(),
		));
		return $task['id'] ?? null;
	}

	public static function getApiKey(): ?string
	{
		$key = Property::get(self::PROP_API_KEY, null);
		if (empty($key))
			return null;
		return $key;
	}

	public static function setApiKey(?string $key): void
	{
		Property::set(self::PROP_API_KEY, empty($key) ? null : $key);
	}

}