@@ -1666,6 +1666,84 @@ PHP_FUNCTION(openssl_csr_export)
16661666}
16671667/* }}} */
16681668
1669+ /* {{{ parse_time_range */
1670+ /* convert an array of either integers or strings to a pair of time_t values
1671+ * representing the notBefore and notAfter times for a certificate.
1672+ * If the array values are strings, they must either be a valid numeric string
1673+ * representing the unix timestamp, or they must be an ASN.1 timestamp.
1674+ */
1675+ static int parse_time_range (zval * validity , time_t * from_time , time_t * to_time ) {
1676+ zval * tmp ;
1677+ long lval ;
1678+ double dval ;
1679+ ASN1_TIME * t ;
1680+ time_t from = -1 ;
1681+ time_t to = -1 ;
1682+
1683+ if ((tmp = zend_hash_index_find (Z_ARRVAL_P (validity ), 2 )) != NULL ) {
1684+ php_error_docref (NULL , E_WARNING , "Too many timestamps" );
1685+ return FAILURE ;
1686+ }
1687+ if ((tmp = zend_hash_index_find (Z_ARRVAL_P (validity ), 1 )) == NULL ) {
1688+ php_error_docref (NULL , E_WARNING , "Too few timestamps" );
1689+ return FAILURE ;
1690+ }
1691+ if ((tmp = zend_hash_index_find (Z_ARRVAL_P (validity ), 0 )) != NULL &&
1692+ ((Z_TYPE_P (tmp ) == IS_LONG ) || (Z_TYPE_P (tmp ) == IS_STRING ))) {
1693+ if (Z_TYPE_P (tmp ) == IS_LONG ) {
1694+ from = Z_LVAL_P (tmp );
1695+ } else if (Z_TYPE_P (tmp ) == IS_STRING ) {
1696+ switch (is_numeric_string (Z_STRVAL_P (tmp ), Z_STRLEN_P (tmp ), & lval , & dval , 0 )) {
1697+ case IS_LONG :
1698+ from = lval ;
1699+ break ;
1700+ case IS_DOUBLE :
1701+ from = (int ) dval ;
1702+ break ;
1703+ default :
1704+ t = ASN1_UTCTIME_new ();
1705+ if (ASN1_TIME_set_string (t , Z_STRVAL_P (tmp ))) {
1706+ from = php_openssl_asn1_time_to_time_t (t );
1707+ }
1708+ ASN1_UTCTIME_free (t );
1709+ }
1710+ }
1711+ }
1712+ if (from == -1 ) {
1713+ php_error_docref (NULL , E_WARNING , "Invalid certificate start timestamp" );
1714+ return FAILURE ;
1715+ }
1716+ if ((tmp = zend_hash_index_find (Z_ARRVAL_P (validity ), 1 )) != NULL &&
1717+ ((Z_TYPE_P (tmp ) == IS_LONG ) || (Z_TYPE_P (tmp ) == IS_STRING ))) {
1718+ if (Z_TYPE_P (tmp ) == IS_LONG ) {
1719+ to = Z_LVAL_P (tmp );
1720+ } else {
1721+ switch (is_numeric_string (Z_STRVAL_P (tmp ), Z_STRLEN_P (tmp ), & lval , & dval , 0 )) {
1722+ case IS_LONG :
1723+ to = lval ;
1724+ break ;
1725+ case IS_DOUBLE :
1726+ to = (int ) dval ;
1727+ break ;
1728+ default :
1729+ t = ASN1_UTCTIME_new ();
1730+ if (ASN1_TIME_set_string (t , Z_STRVAL_P (tmp ))) {
1731+ to = php_openssl_asn1_time_to_time_t (t );
1732+ }
1733+ ASN1_UTCTIME_free (t );
1734+ }
1735+ }
1736+ }
1737+ if (to == -1 ) {
1738+ php_error_docref (NULL , E_WARNING , "Invalid certificate end timestamp" );
1739+ return FAILURE ;
1740+ }
1741+ * from_time = from ;
1742+ * to_time = to ;
1743+ return SUCCESS ;
1744+ }
1745+ /* }}} */
1746+
16691747/* {{{ Signs a cert with another CERT */
16701748PHP_FUNCTION (openssl_csr_sign )
16711749{
@@ -1677,20 +1755,22 @@ PHP_FUNCTION(openssl_csr_sign)
16771755 zend_object * cert_obj ;
16781756 zend_string * cert_str ;
16791757 zval * zpkey , * args = NULL ;
1680- zend_long num_days ;
1758+ zend_long num_days = -1 ;
1759+ zval * validity ;
16811760 zend_long serial = Z_L (0 );
16821761 zend_string * serial_hex = NULL ;
16831762 X509 * cert = NULL , * new_cert = NULL ;
16841763 EVP_PKEY * key = NULL , * priv_key = NULL ;
16851764 int i ;
16861765 bool new_cert_used = false;
16871766 struct php_x509_request req ;
1767+ time_t from_time = -1 , to_time = -1 ;
16881768
16891769 ZEND_PARSE_PARAMETERS_START (4 , 7 )
16901770 Z_PARAM_OBJ_OF_CLASS_OR_STR (csr_obj , php_openssl_request_ce , csr_str )
16911771 Z_PARAM_OBJ_OF_CLASS_OR_STR_OR_NULL (cert_obj , php_openssl_certificate_ce , cert_str )
16921772 Z_PARAM_ZVAL (zpkey )
1693- Z_PARAM_LONG ( num_days )
1773+ Z_PARAM_ZVAL ( validity )
16941774 Z_PARAM_OPTIONAL
16951775 Z_PARAM_ARRAY_OR_NULL (args )
16961776 Z_PARAM_LONG (serial )
@@ -1728,8 +1808,25 @@ PHP_FUNCTION(openssl_csr_sign)
17281808 goto cleanup ;
17291809 }
17301810
1731- if (num_days < 0 || num_days > LONG_MAX / 86400 ) {
1732- php_error_docref (NULL , E_WARNING , "Days must be between 0 and %ld" , LONG_MAX / 86400 );
1811+ /* If 'validity' is an integer, it is the number of days the certificate
1812+ * will be valid for, starting from right now.
1813+ * If it is an array, it is expected to contain two values, the
1814+ * starting time and the ending time for the validity period. Each of
1815+ * the values are expected to be either a numeric value representing a
1816+ * unix timestamp, or a string containing an ASN.1 timestamp.
1817+ */
1818+ if (Z_TYPE_P (validity ) == IS_LONG ) {
1819+ num_days = Z_LVAL_P (validity );
1820+ if (num_days < 0 || num_days > LONG_MAX / 86400 ) {
1821+ php_error_docref (NULL , E_WARNING , "Days must be between 0 and %ld" , LONG_MAX / 86400 );
1822+ goto cleanup ;
1823+ }
1824+ } else if (Z_TYPE_P (validity ) == IS_ARRAY ) {
1825+ if (parse_time_range (validity , & from_time , & to_time ) != SUCCESS ) {
1826+ goto cleanup ;
1827+ }
1828+ } else {
1829+ php_error_docref (NULL , E_WARNING , "Fourth parameter must be integer or array" );
17331830 goto cleanup ;
17341831 }
17351832
@@ -1800,8 +1897,13 @@ PHP_FUNCTION(openssl_csr_sign)
18001897 php_openssl_store_errors ();
18011898 goto cleanup ;
18021899 }
1803- X509_gmtime_adj (X509_getm_notBefore (new_cert ), 0 );
1804- X509_gmtime_adj (X509_getm_notAfter (new_cert ), 60 * 60 * 24 * num_days );
1900+ if (num_days == -1 ) {
1901+ ASN1_TIME_set (X509_getm_notBefore (new_cert ), from_time );
1902+ ASN1_TIME_set (X509_getm_notAfter (new_cert ), to_time );
1903+ } else {
1904+ X509_gmtime_adj (X509_getm_notBefore (new_cert ), 0 );
1905+ X509_gmtime_adj (X509_getm_notAfter (new_cert ), 60 * 60 * 24 * num_days );
1906+ }
18051907 i = X509_set_pubkey (new_cert , key );
18061908 if (!i ) {
18071909 php_openssl_store_errors ();
0 commit comments