571 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			571 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			PHP
		
	
	
	
| <?php
 | |
| /*
 | |
|   ----------------------------------------------------------------------
 | |
|   AlternC - Web Hosting System
 | |
|   Copyright (C) 2000-2014 by the AlternC Development Team.
 | |
|   https://alternc.org/
 | |
|   ----------------------------------------------------------------------
 | |
|   LICENSE
 | |
|   
 | |
|   This program is free software; you can redistribute it and/or
 | |
|   modify it under the terms of the GNU General Public License (GPL)
 | |
|   as published by the Free Software Foundation; either version 2
 | |
|   of the License, or (at your option) any later version.
 | |
|   
 | |
|   This program is distributed in the hope that it will be useful,
 | |
|   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|   GNU General Public License for more details.
 | |
|   
 | |
|   To read the license please visit http://www.gnu.org/copyleft/gpl.html
 | |
|   ----------------------------------------------------------------------
 | |
|   Purpose of file: Manage SSL Certificates and HTTPS Hosting
 | |
|   ----------------------------------------------------------------------
 | |
| */
 | |
| 
 | |
| 
 | |
| /* ----------------------------------------------------------------- */
 | |
| /**
 | |
|  * SSL Certificates management class
 | |
|  */
 | |
| class m_ssl {
 | |
| 
 | |
|   const STATUS_PENDING = 0; // we have a key / csr, but no CRT 
 | |
|   const STATUS_OK = 1; // we have the key, csr, crt, chain
 | |
|   const STATUS_EXPIRED = 99; // The certificate is now expired.
 | |
|   public $error="";
 | |
| 
 | |
|   // Includes one or more of those flags to see only those certificates 
 | |
|   // when listing them: 
 | |
|   const FILTER_PENDING = 1;
 | |
|   const FILTER_OK = 2;
 | |
|   const FILTER_EXPIRED = 4;
 | |
|   const FILTER_SHARED = 8;
 | |
| 
 | |
|   const SSL_INCRON_FILE = "/var/run/alternc/generate_certif_alias";
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /**
 | |
|    * Constructor
 | |
|    */
 | |
|   function m_ssl() {
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /**
 | |
|    * Hook to add the "ssl certificate" menu in the Panel
 | |
|    */
 | |
|   function hook_menu() {
 | |
|     global $quota,$db,$cuid;
 | |
|     $q = $quota->getquota("ssl");
 | |
|     $obj=null;
 | |
|     if ($q['t']>0) {
 | |
|       $obj = array( 
 | |
| 		   'title'       => _("SSL Certificates"),
 | |
| 		   'ico'         => 'images/ssl.png',
 | |
| 		   'link'        => 'toggle',
 | |
| 		   'pos'         => 130,
 | |
| 		   'links'       => array(),
 | |
| 		    ) ;
 | |
| 
 | |
|       if ( $quota->cancreate("ssl") ) {
 | |
| 	$obj['links'][] =
 | |
| 	  array (
 | |
| 		 'ico' => 'images/new.png',
 | |
| 		 'txt' => _("New SSL certificate"),
 | |
| 		 'url' => "ssl_new.php",
 | |
| 		 'class' => '',
 | |
| 		 );
 | |
|       }
 | |
| 
 | |
|       // or admin shared >0 !
 | |
|       $db->query("SELECT COUNT(*) AS cnt FROM certificates WHERE uid='$cuid' OR shared=1");
 | |
|       $used=$q['u'];
 | |
|       if ($db->next_record()) {
 | |
| 	$used=$db->f("cnt");
 | |
|       }
 | |
|       if ( $used > 0 ) { // if there are some SSL certificates
 | |
| 	$obj['links'][] =
 | |
| 	  array (
 | |
| 		 'txt' => _("List SSL Certificates"),
 | |
| 		 'url' => "ssl_list.php"
 | |
| 		 );
 | |
|       }
 | |
|     }
 | |
|     return $obj;
 | |
|   }
 | |
|   
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Return all the SSL certificates for an account (or the searched one)
 | |
|    * @param $filter an integer telling which certificate we want to see (see FILTER_* constants above)
 | |
|    * the default is showing all certificate, but only Pending and OK certificates, not expired or shared one 
 | |
|    * when there is more than 10.
 | |
|    * @return array all the ssl certificate this user can use 
 | |
|    * (each array is the content of the certificates table)
 | |
|    */
 | |
|   function get_list(&$filter=null) {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","get_list");
 | |
|     $r=array();
 | |
|     // If we have no filter, we filter by default on pending and ok certificates if there is more than 10 of them for the same user.
 | |
|     if (is_null($filter)) {
 | |
|       $db->query("SELECT count(*) AS cnt FROM certificates WHERE uid='$cuid' OR shared=1;");
 | |
|       $db->next_record();
 | |
|       if ($db->f("cnt")>10) {
 | |
| 	$filter=(self::FILTER_PENDING | self::FILTER_OK);
 | |
|       } else {
 | |
| 	$filter=(self::FILTER_PENDING | self::FILTER_OK | self::FILTER_EXPIRED | self::FILTER_SHARED);
 | |
|       }
 | |
|     }
 | |
|     // filter the filter values :) 
 | |
|     $filter=($filter & (self::FILTER_PENDING | self::FILTER_OK | self::FILTER_EXPIRED | self::FILTER_SHARED));
 | |
|     // Here filter can't be null (and will be returned to the caller !)
 | |
|     $sql="";
 | |
|     if ($filter & self::FILTER_SHARED) {
 | |
|       $sql=" (uid='$cuid' OR shared=1) ";
 | |
|     } else {
 | |
|       $sql=" uid='$cuid' ";
 | |
|     }
 | |
|     $sql.=" AND status IN (-1";
 | |
|     if ($filter & self::FILTER_PENDING) $sql.=",".self::STATUS_PENDING;
 | |
|     if ($filter & self::FILTER_OK) $sql.=",".self::STATUS_OK;
 | |
|     if ($filter & self::FILTER_EXPIRED) $sql.=",".self::STATUS_EXPIRED;
 | |
|     $sql.=") ";
 | |
|     $db->query("SELECT *, UNIX_TIMESTAMP(validstart) AS validstartts, UNIX_TIMESTAMP(validend) AS validendts FROM certificates WHERE $sql ORDER BY shared, fqdn;");
 | |
|     if ($db->num_rows()) {
 | |
|       while ($db->next_record()) {
 | |
| 	$r[]=$db->Record;
 | |
|       }
 | |
|       return $r;
 | |
|     } else {
 | |
|       $err->raise("ssl",_("No SSL certificates available"));
 | |
|       return array();
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Generate a new CSR, a new Private RSA Key, for FQDN.
 | |
|    * @param $fqdn string the FQDN of the domain name for which we want a CSR.
 | |
|    * a wildcard certificate must start by *.
 | |
|    * @return integer the Certificate ID created in the MySQL database
 | |
|    * or false if an error occurred
 | |
|    */
 | |
|   function new_csr($fqdn) {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","new_csr");
 | |
|     if (substr($fqdn,0,2)=="*.") {
 | |
|       $f=substr($fqdn,2);
 | |
|     } else {
 | |
|       $f=$fqdn;
 | |
|     }
 | |
|     if (checkfqdn($f)) {
 | |
|       $err->raise("ssl",_("Bad FQDN domain name"));
 | |
|       return false;
 | |
|     }
 | |
|     putenv("OPENSSL_CONF=/etc/alternc/openssl.cnf");
 | |
|     $pkey=openssl_pkey_new();
 | |
|     if (!$pkey) {
 | |
|       $err->raise("ssl",_("Can't generate a private key (1)"));
 | |
|       return false;		  
 | |
|     }
 | |
|     if (!openssl_pkey_export($pkey, $privKey)) {
 | |
|       $err->raise("ssl",_("Can't generate a private key (2)"));
 | |
|       return false;		        
 | |
|     }
 | |
|     $dn=array("commonName" => $fqdn);
 | |
|     // override the (not taken from openssl.cnf) digest to use SHA-2 / SHA256 and not SHA-1 or MD5 :
 | |
|     $config=array("digest_alg"=>"sha256");
 | |
|     $csr=openssl_csr_new($dn, $pkey,$config);
 | |
|     openssl_csr_export($csr, $csrout);
 | |
|     $db->query("INSERT INTO certificates SET uid='$cuid', status=".self::STATUS_PENDING.", shared=0, fqdn='".addslashes($fqdn)."', altnames='', validstart=NOW(), sslcsr='".addslashes($csrout)."', sslkey='".addslashes($privKey)."';");
 | |
|     if (!($id=$db->lastid())) {
 | |
|       $err->raise("ssl",_("Can't generate a CSR"));
 | |
|       return false;
 | |
|     }
 | |
|     return $id;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Return all informations of a given certificate for the current user.
 | |
|    * @return array all the informations of the current certificate as a hash.
 | |
|    */
 | |
|   function get_certificate($id) {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","get_certificate");
 | |
|     $id=intval($id);
 | |
|     $db->query("SELECT *, UNIX_TIMESTAMP(validstart) AS validstartts, UNIX_TIMESTAMP(validend) AS validendts FROM certificates WHERE (uid='$cuid' OR (shared=1 AND status=".self::STATUS_OK.") ) AND id='$id';");
 | |
|     if (!$db->next_record()) {
 | |
|       $err->raise("ssl",_("Can't find this Certifcate"));
 | |
|       return false;
 | |
|     }
 | |
|     return $db->Record;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Share (or unshare) an ssl certificate
 | |
|    * @param $id integer the id of the certificate in the table.
 | |
|    * @param $action integer share (1) or unshare (0) this certificate
 | |
|    * @return boolean
 | |
|    */
 | |
|   function share($id,$action=1) {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","share");
 | |
|     $id=intval($id);
 | |
|     $db->query("SELECT id FROM certificates WHERE uid='$cuid' AND status=".self::STATUS_OK." AND id='$id';");
 | |
|     if (!$db->next_record()) {
 | |
|       $err->raise("ssl",_("Can't find this Certifcate"));
 | |
|       return false;
 | |
|     }
 | |
|     if ($action) $action=1; else $action=0;
 | |
|     $db->query("UPDATE certificates SET shared=$action WHERE id='$id';");
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Return all the subdomains that can be ssl-enabled for the current account.
 | |
|    * @return array of strings : all the subdomains. 
 | |
|    * Excludes the one for which a cert is already available
 | |
|    */
 | |
|   function get_new_advice() {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","get_new_advice");
 | |
|     $r=array();
 | |
|     // my certificates, either OK or PENDING (not expired) or the SHARED one (only OK then)
 | |
|     $db->query("SELECT fqdn FROM certificates WHERE
 | |
|       (uid='$cuid' AND status IN (".self::STATUS_PENDING.",".self::STATUS_OK.") ) 
 | |
|    OR (shared=1 AND status=".self::STATUS_OK.") 
 | |
|    ORDER BY shared, fqdn;");
 | |
|     $r=array();
 | |
|     while ($db->next_record()) {
 | |
|       $r[]=$db->f("fqdn"); 
 | |
|     }
 | |
|     // Now we get all our subdomains for certain domaines_types
 | |
|     $db->query("SELECT sub,domaine FROM sub_domaines WHERE compte='$cuid' AND type IN ('vhost', 'url', 'roundcube', 'squirrelmail', 'panel', 'php52');");
 | |
|     $advice=array();
 | |
|     while ($db->next_record()) {
 | |
|       $me=$db->f("sub");
 | |
|       if ($me) $me.=".";
 | |
|       $me.=$db->f("domaine");
 | |
|       if (!in_array($me,$r) && !in_array($me,$advice)) {
 | |
| 	$advice[]=$me;
 | |
|       }
 | |
|       if (!in_array("*.".$db->f("domaine"),$r) && !in_array("*.".$db->f("domaine"),$advice)) {
 | |
| 	$advice[]="*.".$db->f("domaine");
 | |
|       }
 | |
|     }
 | |
|     sort($advice);
 | |
|     return($advice);
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Import an existing ssl Key, Certificate and (maybe) a Chained Cert
 | |
|    * @param $key string the X.509 PEM-encoded RSA key
 | |
|    * @param $crt string the X.509  PEM-encoded certificate, which *must* 
 | |
|    * be the one signinf the private RSA key in $key
 | |
|    * @param $chain string the X.509 PEM-encoded list of SSL Certificate chain if intermediate authorities
 | |
|    * @return integer the ID of the newly created certificate in the table
 | |
|    * or false if an error occurred
 | |
|    */
 | |
|   function import_cert($key,$crt,$chain) {
 | |
|     global $cuid, $err, $db;
 | |
|     $err->log("ssl","import_cert");
 | |
|     
 | |
|     $result=$this->check_cert($crt,$chain,$key);    
 | |
|     if ($result===false) {
 | |
|       $err->raise("ssl",$this->error);
 | |
|       return false;
 | |
|     }
 | |
|     list($crt,$chain,$key,$crtdata)=$result;
 | |
| 
 | |
|     $validstart=$crtdata['validFrom_time_t'];
 | |
|     $validend=$crtdata['validTo_time_t'];
 | |
|     $fqdn=$crtdata["subject"]["CN"];
 | |
|     $altnames=$this->parseAltNames($crtdata["extensions"]["subjectAltName"]);
 | |
| 
 | |
|     // Everything is PERFECT and has been thoroughly checked, let's insert those in the DB !
 | |
|     $sql="INSERT INTO certificates SET uid='$cuid', status=".self::STATUS_OK.", shared=0, fqdn='".addslashes($fqdn)."', altnames='".addslashes($altnames)."', validstart=FROM_UNIXTIME(".intval($validstart)."), validend=FROM_UNIXTIME(".intval($validend)."), sslkey='".addslashes($key)."', sslcrt='".addslashes($crt)."', sslchain='".addslashes($chain)."';";
 | |
|     $db->query($sql);
 | |
|     if (!($id=$db->lastid())) {
 | |
|       $err->raise("ssl",_("Can't save the Key/Crt/Chain now. Please try later."));
 | |
|       return false;
 | |
|     }
 | |
|     return $id;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Import an ssl certificate into an existing certificate entry in the DB.
 | |
|    * (finalize an enrollment process)
 | |
|    * @param $certid integer the ID in the database of the SSL Certificate
 | |
|    * @param $crt string the X.509  PEM-encoded certificate, which *must* 
 | |
|    * be the one signinf the private RSA key in certificate $certid
 | |
|    * @param $chain string the X.509 PEM-encoded list of SSL Certificate chain if intermediate authorities
 | |
|    * @return integer the ID of the updated certificate in the table
 | |
|    * or false if an error occurred
 | |
|    */
 | |
|   function finalize($certid,$crt,$chain) {
 | |
|     global $cuid, $err, $db;
 | |
|     $err->log("ssl","finalize");
 | |
|     
 | |
|     $certid=intval($certid);
 | |
|     $result=$this->check_cert($crt,$chain,"",$certid);    
 | |
|     if ($result===false) {
 | |
|       $err->raise("ssl",$this->error);
 | |
|       return false;
 | |
|     }
 | |
|     list($crt,$chain,$key,$crtdata)=$result;
 | |
| 
 | |
|     $validstart=$crtdata['validFrom_time_t'];
 | |
|     $validend=$crtdata['validTo_time_t'];
 | |
|     $fqdn=$crtdata["subject"]["CN"];
 | |
|     $altnames=$this->parseAltNames($crtdata["extensions"]["subjectAltName"]);
 | |
| 
 | |
|     // Everything is PERFECT and has been thoroughly checked, let's insert those in the DB !
 | |
|     $sql="UPDATE certificates SET status=".self::STATUS_OK.", shared=0, fqdn='".addslashes($fqdn)."', altnames='".addslashes($altnames)."', validstart=FROM_UNIXTIME(".intval($validstart)."), validend=FROM_UNIXTIME(".intval($validend)."), sslcrt='".addslashes($crt)."', sslchain='".addslashes($chain)."' WHERE id='$certid' ;";
 | |
|     if (!$db->query($sql)) {
 | |
|       $err->raise("ssl",_("Can't save the Crt/Chain now. Please try later."));
 | |
|       return false;
 | |
|     }
 | |
|     return $certid;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Function called by a hook when an AlternC member is deleted.
 | |
|    * @access private
 | |
|    * TODO: delete unused ssl certificates ?? > do this in the crontab.
 | |
|    */
 | |
|   function alternc_del_member() {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","alternc_del_member");
 | |
|     $db->query("UPDATE certificates SET ssl_action='DELETE' WHERE uid='$cuid'");
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Hook which returns the used quota for the $name service for the current user.
 | |
|    * @param $name string name of the quota 
 | |
|    * @return integer the number of service used or false if an error occured
 | |
|    * @access private
 | |
|    */
 | |
|   function hook_quota_get() {
 | |
|     global $db,$err,$cuid;
 | |
|     $err->log("ssl","getquota");
 | |
|     $q=Array("name"=>"ssl", "description"=>_("SSL Certificates"), "used"=>0);
 | |
|     $db->query("SELECT COUNT(*) AS cnt FROM certificates WHERE uid='$cuid' AND status!=".self::STATUS_EXPIRED);
 | |
|     if ($db->next_record()) {
 | |
|       $q['used']=$db->f("cnt");
 | |
|     }
 | |
|     return $q;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Export every information for an AlternC's account
 | |
|    * @access private
 | |
|    * EXPERIMENTAL 'sid' function ;) 
 | |
|    */
 | |
|   function alternc_export_conf() {
 | |
|     global $db,$err;
 | |
|     $err->log("ssl","export");
 | |
|     $f=$this->get_list();
 | |
|     $str="  <ssl>";
 | |
|     $db->query("SELECT COUNT(*) AS cnt FROM certificates WHERE uid='$cuid' AND status!=".self::STATUS_EXPIRED);
 | |
|     while ($db->next_record()) {
 | |
|       $str.="   <id>".($db->Record["id"])."</id>\n";
 | |
|       $str.="   <csr>".($db->Record["sslcsr"])."</key>\n";
 | |
|       $str.="   <key>".($db->Record["sslkey"])."<key>\n";
 | |
|       $str.="   <crt>".($db->Record["sslcrt"])."</crt>\n";
 | |
|       $str.="   <chain>".($db->Record["sslchain"])."<chain>\n";
 | |
|     }
 | |
|     $str.=" </ssl>\n";
 | |
|     return $str;
 | |
|   }
 | |
|   
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Returns the list of alternate names of an X.509 SSL Certificate 
 | |
|    * from the attribute list.
 | |
|    * @param $str string the $crtdata["extensions"]["subjectAltName"] from openssl
 | |
|    * @return array an array of FQDNs
 | |
|    */
 | |
|   function parseAltNames($str) {
 | |
|     if (preg_match_all("#DNS:([^,]*),#",$str,$mat, PREG_PATTERN_ORDER)) {
 | |
|       return implode("\n",$mat[1]);
 | |
|     } else {
 | |
|       return "";
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Add (immediately) a global alias to the HTTP 
 | |
|    * certif_alias table and add it to apache configuration
 | |
|    * by launching a incron action. 
 | |
|    * name is the name of the alias, starting by /
 | |
|    * value is the value of the filename stored at this location
 | |
|    * If an alias with the same name already exists, return false.
 | |
|    * if the alias has been properly defined, return true.
 | |
|    * @return boolean
 | |
|    */
 | |
|   function alias_add($name,$value) {
 | |
|     global $err,$cuid,$db;
 | |
|     $db->query("SELECT name FROM certif_alias WHERE name='".addslashes($name)."';");
 | |
|     if ($db->next_record()) {
 | |
|       $err->raise("ssl",_("Alias already exists"));
 | |
|       return false;
 | |
|     }
 | |
|     $db->query("INSERT INTO certif_alias SET name='".addslashes($name)."', value='".addslashes($value)."', uid=".intval($cuid).";");
 | |
|     touch(self::SSL_INCRON_FILE);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Removes (immediately) a global alias to the HTTP 
 | |
|    * certif_alias table and add it to apache configuration
 | |
|    * by launching a incron action. 
 | |
|    * name is the name of the alias, starting by /
 | |
|    * @return boolean
 | |
|    */
 | |
|   function alias_del($name) {
 | |
|     global $err,$cuid,$db;
 | |
|     $db->query("SELECT name FROM certif_alias WHERE name='".addslashes($name)."' AND uid=".intval($cuid).";");
 | |
|     if (!$db->next_record()) {
 | |
|       $err->raise("ssl",_("Alias not found"));
 | |
|       return false;
 | |
|     }
 | |
|     $db->query("DELETE FROM certif_alias WHERE name='".addslashes($name)."' AND uid=".intval($cuid).";");
 | |
|     touch(self::SSL_INCRON_FILE);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /* ----------------------------------------------------------------- */
 | |
|   /** Check that a crt is a proper certificate
 | |
|    * @param $crt string an SSL Certificate
 | |
|    * @param $chain string is a list of certificates
 | |
|    * @param $key string  is a rsa key associated with certificate 
 | |
|    * @param $certid if no key is specified, use it from this certificate ID in the table
 | |
|    * @return array the crt, chain, key, crtdata(array) after a proper reformatting  
 | |
|    * or false if an error occurred (in that case $this->error is filled)
 | |
|    */
 | |
|   function check_cert($crt,$chain,$key="",$certid=null) {
 | |
|     global $err,$cuid,$db;
 | |
|     // Check that the key crt and chain are really SSL certificates and keys
 | |
|     $crt=trim(str_replace("\r\n","\n",$crt))."\n";
 | |
|     $key=trim(str_replace("\r\n","\n",$key))."\n";
 | |
|     $chain=trim(str_replace("\r\n","\n",$chain))."\n";
 | |
| 
 | |
|     $this->error="";
 | |
|     if (trim($key)=="" && !is_null($certid)) {
 | |
|       // find it in the DB : 
 | |
|       $db->query("SELECT sslkey FROM certificates WHERE id=".intval($certid).";");
 | |
|       if (!$db->next_record()) {
 | |
| 	$this->error.=_("Can't find the private key in the certificate table, please check your form.");
 | |
| 	return false;
 | |
|       }
 | |
|       $key=$db->f("sslkey");
 | |
|       $key=trim(str_replace("\r\n","\n",$key))."\n";
 | |
|     } 
 | |
| 
 | |
|     if (substr($crt,0,28)!="-----BEGIN CERTIFICATE-----\n" ||
 | |
| 	substr($crt,-26,26)!="-----END CERTIFICATE-----\n") {
 | |
|       $this->error.=_("The certificate must begin by BEGIN CERTIFICATE and end by END CERTIFICATE lines. Please check you pasted it in PEM form.")."\n";
 | |
|     }
 | |
|     if ($chain && 
 | |
| 	(substr($chain,0,28)!="-----BEGIN CERTIFICATE-----\n" ||
 | |
| 	 substr($chain,-26,26)!="-----END CERTIFICATE-----\n")) {
 | |
|       $this->error.=_("The chained certificate must begin by BEGIN CERTIFICATE and end by END CERTIFICATE lines. Please check you pasted it in PEM form.")."\n";
 | |
|     }
 | |
|     if (substr($key,0,32)!="-----BEGIN RSA PRIVATE KEY-----\n" ||
 | |
| 	substr($key,-30,30)!="-----END RSA PRIVATE KEY-----\n") {
 | |
|       $this->error.=_("The private key must begin by BEGIN RSA PRIVATE KEY and end by END RSA PRIVATE KEY lines. Please check you pasted it in PEM form.")."\n";
 | |
|     }
 | |
|     if ($this->error) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // We split the chained certificates in individuals certificates : 
 | |
|     $chains=array();
 | |
|     $status=0; $new="";
 | |
|     $lines=explode("\n",$chain);
 | |
|     foreach($lines as $line) {
 | |
|       if ($line=="-----BEGIN CERTIFICATE-----" && $status==0) {
 | |
| 	$status=1;
 | |
| 	$new=$line."\n";
 | |
| 	continue;
 | |
|       }
 | |
|       if ($line=="-----END CERTIFICATE-----" && $status==1) {
 | |
| 	$status=0;
 | |
| 	$new.=$line."\n";
 | |
| 	$chains[]=$new;
 | |
| 	$new="";
 | |
| 	continue;
 | |
|       }
 | |
|       if ($status==1) {
 | |
| 	$new.=$line."\n";
 | |
|       }
 | |
|     }
 | |
|     // here chains contains all the ssl certificates in the chained certs.
 | |
|     // Now we check those using Openssl functions (real check :) ) 
 | |
|     $rchains=array(); $i=0;
 | |
|     foreach($chains as $tmpcert) {
 | |
|       $i++;
 | |
|       $tmpr=openssl_x509_read($tmpcert);
 | |
|       if ($tmpr===false) {
 | |
| 	$this->error.=sprintf(_("The %d-th certificate in the chain is invalid"),$i)."\n";
 | |
|       } else {
 | |
| 	$rchains[]=$tmpr;
 | |
|       }
 | |
|     }
 | |
|     $validstart=0; $validend=0;
 | |
|     $rcrt=openssl_x509_read($crt);
 | |
|     $crtdata = openssl_x509_parse($crt);
 | |
|     if ($rcrt===false || $crtdata===false) {
 | |
|       $this->error.=_("The certificate is invalid.")."\n";
 | |
|     }
 | |
| 
 | |
|     $rkey=openssl_pkey_get_private($key);    
 | |
|     if ($rkey===false) {
 | |
|       $this->error.=_("The private key is invalid.")."\n";
 | |
|     }
 | |
|     if (!$this->error) {
 | |
|       // check that the private key and the certificates are matching :
 | |
|       if (!openssl_x509_check_private_key($rcrt,$rkey)) {
 | |
| 	$this->error.=_("The private key is not the one signed inside the certificate.")."\n";
 | |
|       }
 | |
|     }
 | |
|     if (!$this->error) {
 | |
|       // Everything is fine, let's recreate crt, chain, key from our internal OpenSSL structures:
 | |
|       if (!openssl_x509_export($rcrt,$crt)) {
 | |
| 	$this->error.=_("Can't export your certificate as a string, please check its syntax.")."\n";
 | |
|       }
 | |
|       $chain="";
 | |
|       foreach($rchains as $r) {
 | |
| 	if (!openssl_x509_export($r,$tmp)) {
 | |
| 	  $this->error.=_("Can't export one of your chained certificates as a string, please check its syntax.")."\n";
 | |
| 	} else {
 | |
| 	  $chain.=$tmp;
 | |
| 	}
 | |
|       }
 | |
|       if (!openssl_pkey_export($rkey,$key)) {
 | |
| 	$this->error.=_("Can't export your private key as a string, please check its syntax.")."\n";
 | |
|       }
 | |
|     }
 | |
|     return array($crt,$chain,$key,$crtdata);
 | |
|   } // check_cert
 | |
| 
 | |
|   
 | |
| } /* Class m_ssl */
 | |
| 
 |