diff --git a/customer_domains.php b/customer_domains.php index 8cc3bf50..c97f6b07 100644 --- a/customer_domains.php +++ b/customer_domains.php @@ -447,87 +447,16 @@ if ($page == 'overview') { if ($action == '' || $action == 'view') { if (isset($_POST['send']) && $_POST['send'] == 'send') { - - $ssl_cert_file = isset($_POST['ssl_cert_file']) ? $_POST['ssl_cert_file'] : ''; - $ssl_key_file = isset($_POST['ssl_key_file']) ? $_POST['ssl_key_file'] : ''; - $ssl_ca_file = isset($_POST['ssl_ca_file']) ? $_POST['ssl_ca_file'] : ''; - $ssl_cert_chainfile = isset($_POST['ssl_cert_chainfile']) ? $_POST['ssl_cert_chainfile'] : ''; $do_insert = isset($_POST['do_insert']) ? (($_POST['do_insert'] == 1) ? true : false) : false; - - if ($ssl_cert_file != '' && $ssl_key_file == '') { - standard_error('sslcertificateismissingprivatekey'); - } - - $do_verify = true; - - // no cert-file given -> forget everything - if ($ssl_cert_file == '') { - $ssl_key_file = ''; - $ssl_ca_file = ''; - $ssl_cert_chainfile = ''; - $do_verify = false; - } - - // verify certificate content - if ($do_verify) { - // array openssl_x509_parse ( mixed $x509cert [, bool $shortnames = true ] ) - // openssl_x509_parse() returns information about the supplied x509cert, including fields such as - // subject name, issuer name, purposes, valid from and valid to dates etc. - $cert_content = openssl_x509_parse($ssl_cert_file); - - if (is_array($cert_content) && isset($cert_content['subject']) && isset($cert_content['subject']['CN'])) { - // bool openssl_x509_check_private_key ( mixed $cert , mixed $key ) - // Checks whether the given key is the private key that corresponds to cert. - if (openssl_x509_check_private_key($ssl_cert_file, $ssl_key_file) === false) { - standard_error('sslcertificateinvalidcertkeypair'); - } - - // check optional stuff - if ($ssl_ca_file != '') { - $ca_content = openssl_x509_parse($ssl_ca_file); - if (!is_array($ca_content)) { - // invalid - standard_error('sslcertificateinvalidca'); - } - } - if ($ssl_cert_chainfile != '') { - $chain_content = openssl_x509_parse($ssl_cert_chainfile); - if (!is_array($chain_content)) { - // invalid - standard_error('sslcertificateinvalidchain'); - } - } + try { + if ($do_insert) { + Certificates::getLocal($userinfo, $_POST)->add(); } else { - standard_error('sslcertificateinvalidcert'); + Certificates::getLocal($userinfo, $_POST)->update(); } + } catch (Exception $e) { + dynamic_error($e->getMessage()); } - - // Add/Update database entry - $qrystart = "UPDATE "; - $qrywhere = "WHERE "; - if ($do_insert) { - $qrystart = "INSERT INTO "; - $qrywhere = ", "; - } - $stmt = Database::prepare($qrystart." `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` SET - `ssl_cert_file` = :ssl_cert_file, - `ssl_key_file` = :ssl_key_file, - `ssl_ca_file` = :ssl_ca_file, - `ssl_cert_chainfile` = :ssl_cert_chainfile - ".$qrywhere." `domainid`= :domainid" - ); - $params = array( - "ssl_cert_file" => $ssl_cert_file, - "ssl_key_file" => $ssl_key_file, - "ssl_ca_file" => $ssl_ca_file, - "ssl_cert_chainfile" => $ssl_cert_chainfile, - "domainid" => $id - ); - Database::pexecute($stmt, $params); - - // insert task to re-generate webserver-configs (#1260) - inserttask('1'); - // back to domain overview redirectTo($filename, array('page' => 'domains', 's' => $s)); } @@ -535,8 +464,7 @@ if ($page == 'overview') { $stmt = Database::prepare("SELECT * FROM `".TABLE_PANEL_DOMAIN_SSL_SETTINGS."` WHERE `domainid`= :domainid" ); - Database::pexecute($stmt, array("domainid" => $id)); - $result = $stmt->fetch(PDO::FETCH_ASSOC); + $result = Database::pexecute_first($stmt, array("domainid" => $id)); $do_insert = false; // if no entry can be found, behave like we have empty values diff --git a/lib/classes/api/commands/class.Certificates.php b/lib/classes/api/commands/class.Certificates.php new file mode 100644 index 00000000..fb17a709 --- /dev/null +++ b/lib/classes/api/commands/class.Certificates.php @@ -0,0 +1,325 @@ + (2010-) + * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt + * @package API + * @since 0.10.0 + * + */ +class Certificates extends ApiCommand implements ResourceEntity +{ + + /** + * add new ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * @param string $ssl_cert_file + * @param string $ssl_key_file + * @param string $ssl_ca_file + * optional + * @param string $ssl_cert_chainfile + * optional + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function add() + { + $domainid = $this->getParam('domainid', true, 0); + $dn_optional = ($domainid <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $domainid, + 'domainname' => $domainname + )); + // parameters + $ssl_cert_file = $this->getParam('ssl_cert_file'); + $ssl_key_file = $this->getParam('ssl_key_file'); + $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); + $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, true); + $idna_convert = new idna_convert_wrapper(); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] added ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + + /** + * return ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function get() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + $domainid = $domain['id']; + + $stmt = Database::prepare("SELECT * FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE `domainid`= :domainid"); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] get ssl-certificate for '" . $domain['domain'] . "'"); + $result = Database::pexecute_first($stmt, array( + "domainid" => $domainid + )); + return $this->response(200, "successfull", $result); + } + + /** + * update ssl-certificate entry for given domain by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ + public function update() + { + $id = $this->getParam('id', true, 0); + $dn_optional = ($id <= 0 ? false : true); + $domainname = $this->getParam('domainname', $dn_optional, ''); + + if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { + throw new Exception("You cannot access this resource", 405); + } + + $domain = $this->apiCall('SubDomains.get', array( + 'id' => $id, + 'domainname' => $domainname + )); + + // parameters + $ssl_cert_file = $this->getParam('ssl_cert_file'); + $ssl_key_file = $this->getParam('ssl_key_file'); + $ssl_ca_file = $this->getParam('ssl_ca_file', true, ''); + $ssl_cert_chainfile = $this->getParam('ssl_cert_chainfile', true, ''); + $this->addOrUpdateCertificate($domain['id'], $ssl_cert_file, $ssl_key_file, $ssl_ca_file, $ssl_cert_chainfile, false); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] updated ssl-certificate for '" . $domain['domain'] . "'"); + $result = $this->apiCall('Certificates.get', array( + 'id' => $domain['id'] + )); + return $this->response(200, "successfull", $result); + } + + /** + * lists all certificate entries + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ + public function listing() + { + // select all my (accessable) certificates + $certs_stmt_query = "SELECT s.*, d.domain, d.letsencrypt, c.customerid, c.loginname + FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s + LEFT JOIN `" . TABLE_PANEL_DOMAINS . "` d ON `d`.`id` = `s`.`domainid` + LEFT JOIN `" . TABLE_PANEL_CUSTOMERS . "` c ON `c`.`customerid` = `d`.`customerid` + WHERE "; + + $qry_params = array(); + + if ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') { + // admin with only customer-specific permissions + $certs_stmt_query .= "d.adminid = :adminid "; + $qry_params['adminid'] = $this->getUserDetail('adminid'); + } elseif ($this->isAdmin() == false) { + // customer-area + $certs_stmt_query .= "d.customerid = :cid "; + $qry_params['cid'] = $this->getUserDetail('customerid'); + } else { + $certs_stmt_query .= "1 "; + } + $certs_stmt = Database::prepare($certs_stmt_query); + Database::pexecute($certs_stmt, $qry_params, true, true); + $result = array(); + while ($cert = $certs_stmt->fetch(PDO::FETCH_ASSOC)) { + // respect froxlor-hostname + if ($cert['domainid'] == 0) { + $cert['domain'] = Settings::Get('system.hostname'); + $cert['letsencrypt'] = Settings::Get('system.le_froxlor_enabled'); + $cert['loginname'] = 'froxlor.panel'; + } + $result[] = $cert; + } + return $this->response(200, "successfull", array( + 'count' => count($result), + 'list' => $result + )); + } + + /** + * delete certificates entry by id + * + * @param int $id + * + * @return array + */ + public function delete() + { + $id = $this->getParam('id'); + + $chk = ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '1') ? true : false; + if ($this->isAdmin() == false) { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`customerid` = :cid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'cid' => $this->getUserDetail('customerid') + )); + } elseif ($this->isAdmin() && $this->getUserDetail('customers_see_all') == '0') { + $chk_stmt = Database::prepare(" + SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d + LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id + WHERE s.`id` = :id AND d.`adminid` = :aid + "); + $chk = Database::pexecute_first($chk_stmt, array( + 'id' => $id, + 'aid' => $this->getUserDetail('adminid') + )); + } + if ($chk !== false) { + // additional access check by trying to get the certificate + $result = $this->apiCall('Certificates.get', array( + 'domainname' => $chk['domain'] + )); + $del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); + Database::pexecute($del_stmt, array( + 'id' => $id + )); + $this->logger()->logAction($this->isAdmin() ? ADM_ACTION : USR_ACTION, LOG_INFO, "[API] removed ssl-certificate for '" . $chk['domain'] . "'"); + return $this->response(200, "successfull", $result); + } + } + + /** + * insert or update certificates entry + * + * @param int $domainid + * @param string $ssl_cert_file + * @param string $ssl_key_file + * @param string $ssl_ca_file + * @param string $ssl_cert_chainfile + * @param boolean $do_insert + * optional default false + * + * @return boolean + */ + private function addOrUpdateCertificate($domainid = 0, $ssl_cert_file = '', $ssl_key_file = '', $ssl_ca_file = '', $ssl_cert_chainfile = '', $do_insert = false) + { + if ($ssl_cert_file != '' && $ssl_key_file == '') { + standard_error('sslcertificateismissingprivatekey', '', true); + } + + $do_verify = true; + // no cert-file given -> forget everything + if ($ssl_cert_file == '') { + $ssl_key_file = ''; + $ssl_ca_file = ''; + $ssl_cert_chainfile = ''; + $do_verify = false; + } + + // verify certificate content + if ($do_verify) { + // array openssl_x509_parse ( mixed $x509cert [, bool $shortnames = true ] ) + // openssl_x509_parse() returns information about the supplied x509cert, including fields such as + // subject name, issuer name, purposes, valid from and valid to dates etc. + $cert_content = openssl_x509_parse($ssl_cert_file); + + if (is_array($cert_content) && isset($cert_content['subject']) && isset($cert_content['subject']['CN'])) { + // bool openssl_x509_check_private_key ( mixed $cert , mixed $key ) + // Checks whether the given key is the private key that corresponds to cert. + if (openssl_x509_check_private_key($ssl_cert_file, $ssl_key_file) === false) { + standard_error('sslcertificateinvalidcertkeypair', '', true); + } + + // check optional stuff + if ($ssl_ca_file != '') { + $ca_content = openssl_x509_parse($ssl_ca_file); + if (! is_array($ca_content)) { + // invalid + standard_error('sslcertificateinvalidca', '', true); + } + } + if ($ssl_cert_chainfile != '') { + $chain_content = openssl_x509_parse($ssl_cert_chainfile); + if (! is_array($chain_content)) { + // invalid + standard_error('sslcertificateinvalidchain', '', true); + } + } + } else { + standard_error('sslcertificateinvalidcert', '', true); + } + } + + // Add/Update database entry + $qrystart = "UPDATE "; + $qrywhere = "WHERE "; + if ($do_insert) { + $qrystart = "INSERT INTO "; + $qrywhere = ", "; + } + $stmt = Database::prepare($qrystart . " `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` SET + `ssl_cert_file` = :ssl_cert_file, + `ssl_key_file` = :ssl_key_file, + `ssl_ca_file` = :ssl_ca_file, + `ssl_cert_chainfile` = :ssl_cert_chainfile + " . $qrywhere . " `domainid`= :domainid + "); + $params = array( + "ssl_cert_file" => $ssl_cert_file, + "ssl_key_file" => $ssl_key_file, + "ssl_ca_file" => $ssl_ca_file, + "ssl_cert_chainfile" => $ssl_cert_chainfile, + "domainid" => $domainid + ); + Database::pexecute($stmt, $params, true, true); + // insert task to re-generate webserver-configs (#1260) + inserttask('1'); + return true; + } +} diff --git a/lib/classes/api/commands/class.SubDomains.php b/lib/classes/api/commands/class.SubDomains.php index 6ef6592a..1e2e1cbf 100644 --- a/lib/classes/api/commands/class.SubDomains.php +++ b/lib/classes/api/commands/class.SubDomains.php @@ -405,6 +405,18 @@ class SubDomains extends ApiCommand implements ResourceEntity throw new Exception("Subdomain with " . $key . " could not be found", 404); } + /** + * update subdomain entry by either id or domainname + * + * @param int $id + * optional, the domain-id + * @param string $domainname + * optional, the domainname + * + * @access admin, customer + * @throws Exception + * @return array + */ public function update() { $id = $this->getParam('id', true, 0); @@ -414,7 +426,7 @@ class SubDomains extends ApiCommand implements ResourceEntity if ($this->isAdmin() == false && Settings::IsInList('panel.customer_hide_options', 'domains')) { throw new Exception("You cannot access this resource", 405); } - + $result = $this->apiCall('SubDomains.get', array( 'id' => $id, 'domainname' => $domainname @@ -628,6 +640,13 @@ class SubDomains extends ApiCommand implements ResourceEntity return $this->response(200, "successfull", $result); } + /** + * lists all subdomain entries + * + * @access admin, customer + * @throws Exception + * @return array count|list + */ public function listing() { if ($this->isAdmin()) { diff --git a/ssl_certificates.php b/ssl_certificates.php index dd0bba10..eb08f534 100644 --- a/ssl_certificates.php +++ b/ssl_certificates.php @@ -22,40 +22,19 @@ if (! defined('AREA')) { // This file is being included in admin_domains and customer_domains // and therefore does not need to require lib/init.php -$del_stmt = Database::prepare("DELETE FROM `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` WHERE id = :id"); $success_message = ""; // do the delete and then just show a success-message and the certificates list again if ($action == 'delete') { $id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($id > 0) { - $chk = (AREA == 'admin' && $userinfo['customers_see_all'] == '1') ? true : false; - if (AREA == 'customer') { - $chk_stmt = Database::prepare(" - SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d - LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id - WHERE s.`id` = :id AND d.`customerid` = :cid - "); - $chk = Database::pexecute_first($chk_stmt, array( - 'id' => $id, - 'cid' => $userinfo['customerid'] - )); - } elseif (AREA == 'admin' && $userinfo['customers_see_all'] == '0') { - $chk_stmt = Database::prepare(" - SELECT d.domain FROM `" . TABLE_PANEL_DOMAINS . "` d - LEFT JOIN `" . TABLE_PANEL_DOMAIN_SSL_SETTINGS . "` s ON s.domainid = d.id - WHERE s.`id` = :id AND d.`adminid` = :aid - "); - $chk = Database::pexecute_first($chk_stmt, array( - 'id' => $id, - 'aid' => $userinfo['adminid'] - )); - } - if ($chk !== false) { - Database::pexecute($del_stmt, array( + try { + $json_result = Certificates::getLocal($userinfo, array( 'id' => $id - )); + ))->delete(); $success_message = sprintf($lng['domains']['ssl_certificate_removed'], $id); + } catch (Exception $e) { + dynamic_error($e->getMessage()); } } }