@@ -56,6 +56,10 @@ ZEND_DECLARE_MODULE_GLOBALS(openssl)
5656
5757#include "openssl_arginfo.h"
5858
59+ /* OpenSSLException class */
60+
61+ zend_class_entry * php_openssl_exception_ce ;
62+
5963/* OpenSSLCertificate class */
6064
6165zend_class_entry * php_openssl_certificate_ce ;
@@ -165,6 +169,302 @@ static void php_openssl_pkey_free_obj(zend_object *object)
165169 zend_object_std_dtor (& key_object -> std );
166170}
167171
172+ /* OpenSSLSession class */
173+
174+ zend_class_entry * php_openssl_session_ce ;
175+
176+ static zend_object_handlers php_openssl_session_object_handlers ;
177+
178+ bool php_openssl_is_session_ce (zval * val )
179+ {
180+ return Z_TYPE_P (val ) == IS_OBJECT && Z_OBJCE_P (val ) == php_openssl_session_ce ;
181+ }
182+
183+ SSL_SESSION * php_openssl_session_from_zval (zval * zv )
184+ {
185+ if (!php_openssl_is_session_ce (zv )) {
186+ return NULL ;
187+ }
188+ return Z_OPENSSL_SESSION_P (zv )-> session ;
189+ }
190+
191+ void php_openssl_session_object_init (zval * zv , SSL_SESSION * session )
192+ {
193+ object_init_ex (zv , php_openssl_session_ce );
194+ php_openssl_session_object * obj = Z_OPENSSL_SESSION_P (zv );
195+ obj -> session = session ;
196+
197+ unsigned int id_len = 0 ;
198+ const unsigned char * id = SSL_SESSION_get_id (session , & id_len );
199+ zend_update_property_stringl (php_openssl_session_ce , Z_OBJ_P (zv ),
200+ ZEND_STRL ("id" ), (char * )id , id_len );
201+ }
202+
203+ static zend_object * php_openssl_session_create_object (zend_class_entry * class_type )
204+ {
205+ php_openssl_session_object * intern = zend_object_alloc (sizeof (php_openssl_session_object ), class_type );
206+
207+ zend_object_std_init (& intern -> std , class_type );
208+ object_properties_init (& intern -> std , class_type );
209+
210+ return & intern -> std ;
211+ }
212+
213+ static zend_function * php_openssl_session_get_constructor (zend_object * object )
214+ {
215+ zend_throw_error (NULL ,
216+ "Cannot directly construct OpenSSLSession, use OpenSSLSession::import() or TLS session callbacks" );
217+ return NULL ;
218+ }
219+
220+ static void php_openssl_session_free_obj (zend_object * object )
221+ {
222+ php_openssl_session_object * session_object = php_openssl_session_from_obj (object );
223+
224+ if (session_object -> session ) {
225+ SSL_SESSION_free (session_object -> session );
226+ session_object -> session = NULL ;
227+ }
228+ zend_object_std_dtor (& session_object -> std );
229+ }
230+
231+ #define PHP_OPENSSL_SESSION_CHECK () \
232+ php_openssl_session_object *obj = Z_OPENSSL_SESSION_P(ZEND_THIS); \
233+ if (!obj->session) { \
234+ zend_throw_exception(php_openssl_exception_ce, "Session is not valid", 0); \
235+ RETURN_THROWS(); \
236+ }
237+
238+ PHP_METHOD (Openssl_Session , export )
239+ {
240+ zend_long format = ENCODING_PEM ;
241+
242+ ZEND_PARSE_PARAMETERS_START (0 , 1 )
243+ Z_PARAM_OPTIONAL
244+ Z_PARAM_LONG (format )
245+ ZEND_PARSE_PARAMETERS_END ();
246+
247+ PHP_OPENSSL_SESSION_CHECK ();
248+
249+ if (format == ENCODING_DER ) {
250+ int len = i2d_SSL_SESSION (obj -> session , NULL );
251+ if (len <= 0 ) {
252+ zend_throw_exception (php_openssl_exception_ce , "Failed to export session" , 0 );
253+ RETURN_THROWS ();
254+ }
255+
256+ zend_string * result = zend_string_alloc (len , 0 );
257+ unsigned char * p = (unsigned char * )ZSTR_VAL (result );
258+ i2d_SSL_SESSION (obj -> session , & p );
259+ ZSTR_VAL (result )[len ] = '\0' ;
260+
261+ RETURN_NEW_STR (result );
262+ }
263+
264+ if (format == ENCODING_PEM ) {
265+ BIO * bio = BIO_new (BIO_s_mem ());
266+ if (!bio ) {
267+ zend_throw_exception (php_openssl_exception_ce , "Failed to create BIO" , 0 );
268+ RETURN_THROWS ();
269+ }
270+
271+ if (!PEM_write_bio_SSL_SESSION (bio , obj -> session )) {
272+ BIO_free (bio );
273+ zend_throw_exception (php_openssl_exception_ce , "Failed to export session as PEM" , 0 );
274+ RETURN_THROWS ();
275+ }
276+
277+ char * data ;
278+ long len = BIO_get_mem_data (bio , & data );
279+ zend_string * result = zend_string_init (data , len , 0 );
280+ BIO_free (bio );
281+
282+ RETURN_NEW_STR (result );
283+ }
284+
285+ zend_argument_value_error (1 , "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM" );
286+ RETURN_THROWS ();
287+ }
288+
289+ PHP_METHOD (Openssl_Session , import )
290+ {
291+ zend_string * data ;
292+ zend_long format = ENCODING_PEM ;
293+
294+ ZEND_PARSE_PARAMETERS_START (1 , 2 )
295+ Z_PARAM_STR (data )
296+ Z_PARAM_OPTIONAL
297+ Z_PARAM_LONG (format )
298+ ZEND_PARSE_PARAMETERS_END ();
299+
300+ SSL_SESSION * session = NULL ;
301+
302+ if (format == ENCODING_DER ) {
303+ const unsigned char * p = (const unsigned char * )ZSTR_VAL (data );
304+ session = d2i_SSL_SESSION (NULL , & p , ZSTR_LEN (data ));
305+ } else if (format == ENCODING_PEM ) {
306+ BIO * bio = BIO_new_mem_buf (ZSTR_VAL (data ), ZSTR_LEN (data ));
307+ if (bio ) {
308+ session = PEM_read_bio_SSL_SESSION (bio , NULL , NULL , NULL );
309+ BIO_free (bio );
310+ }
311+ } else {
312+ zend_argument_value_error (2 , "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM" );
313+ RETURN_THROWS ();
314+ }
315+
316+ if (!session ) {
317+ zend_throw_exception (php_openssl_exception_ce , "Failed to import session data" , 0 );
318+ RETURN_THROWS ();
319+ }
320+
321+ php_openssl_session_object_init (return_value , session );
322+ }
323+
324+ PHP_METHOD (Openssl_Session , isResumable )
325+ {
326+ ZEND_PARSE_PARAMETERS_NONE ();
327+ PHP_OPENSSL_SESSION_CHECK ();
328+
329+ RETURN_BOOL (SSL_SESSION_is_resumable (obj -> session ));
330+ }
331+
332+ PHP_METHOD (Openssl_Session , getTimeout )
333+ {
334+ ZEND_PARSE_PARAMETERS_NONE ();
335+ PHP_OPENSSL_SESSION_CHECK ();
336+ RETURN_LONG ((zend_long )SSL_SESSION_get_timeout (obj -> session ));
337+ }
338+
339+ PHP_METHOD (Openssl_Session , getCreatedAt )
340+ {
341+ ZEND_PARSE_PARAMETERS_NONE ();
342+ PHP_OPENSSL_SESSION_CHECK ();
343+ #if PHP_OPENSSL_API_VERSION >= 0x30300
344+ RETURN_LONG ((zend_long )SSL_SESSION_get_time_ex (obj -> session ));
345+ #else
346+ RETURN_LONG ((zend_long )SSL_SESSION_get_time (obj -> session ));
347+ #endif
348+ }
349+
350+ PHP_METHOD (Openssl_Session , getProtocol )
351+ {
352+ ZEND_PARSE_PARAMETERS_NONE ();
353+ PHP_OPENSSL_SESSION_CHECK ();
354+
355+ int version = SSL_SESSION_get_protocol_version (obj -> session );
356+
357+ switch (version ) {
358+ case TLS1_3_VERSION :
359+ RETURN_STRING ("TLSv1.3" );
360+ case TLS1_2_VERSION :
361+ RETURN_STRING ("TLSv1.2" );
362+ case TLS1_1_VERSION :
363+ RETURN_STRING ("TLSv1.1" );
364+ case TLS1_VERSION :
365+ RETURN_STRING ("TLSv1.0" );
366+ default :
367+ RETURN_NULL ();
368+ }
369+ }
370+
371+ PHP_METHOD (Openssl_Session , getCipher )
372+ {
373+ ZEND_PARSE_PARAMETERS_NONE ();
374+ PHP_OPENSSL_SESSION_CHECK ();
375+
376+ const SSL_CIPHER * cipher = SSL_SESSION_get0_cipher (obj -> session );
377+ if (!cipher ) {
378+ RETURN_NULL ();
379+ }
380+
381+ RETURN_STRING (SSL_CIPHER_get_name (cipher ));
382+ }
383+
384+ PHP_METHOD (Openssl_Session , hasTicket )
385+ {
386+ ZEND_PARSE_PARAMETERS_NONE ();
387+ PHP_OPENSSL_SESSION_CHECK ();
388+
389+ RETURN_BOOL (SSL_SESSION_has_ticket (obj -> session ));
390+ }
391+
392+ PHP_METHOD (Openssl_Session , getTicketLifetimeHint )
393+ {
394+ ZEND_PARSE_PARAMETERS_NONE ();
395+ PHP_OPENSSL_SESSION_CHECK ();
396+
397+ if (!SSL_SESSION_has_ticket (obj -> session )) {
398+ RETURN_NULL ();
399+ }
400+
401+ RETURN_LONG ((zend_long )SSL_SESSION_get_ticket_lifetime_hint (obj -> session ));
402+ }
403+ PHP_METHOD (Openssl_Session , __serialize )
404+ {
405+ ZEND_PARSE_PARAMETERS_NONE ();
406+
407+ PHP_OPENSSL_SESSION_CHECK ();
408+
409+ BIO * bio = BIO_new (BIO_s_mem ());
410+ if (!bio ) {
411+ zend_throw_exception (php_openssl_exception_ce , "Failed to serialize session" , 0 );
412+ RETURN_THROWS ();
413+ }
414+
415+ if (!PEM_write_bio_SSL_SESSION (bio , obj -> session )) {
416+ BIO_free (bio );
417+ zend_throw_exception (php_openssl_exception_ce , "Failed to serialize session" , 0 );
418+ RETURN_THROWS ();
419+ }
420+
421+ char * data ;
422+ long len = BIO_get_mem_data (bio , & data );
423+ zend_string * pem = zend_string_init (data , len , 0 );
424+ BIO_free (bio );
425+
426+ array_init (return_value );
427+ add_assoc_str (return_value , "pem" , pem );
428+ }
429+
430+ PHP_METHOD (Openssl_Session , __unserialize )
431+ {
432+ HashTable * data ;
433+
434+ ZEND_PARSE_PARAMETERS_START (1 , 1 )
435+ Z_PARAM_ARRAY_HT (data )
436+ ZEND_PARSE_PARAMETERS_END ();
437+
438+ zval * pem_zv = zend_hash_str_find (data , ZEND_STRL ("pem" ));
439+ if (!pem_zv || Z_TYPE_P (pem_zv ) != IS_STRING ) {
440+ zend_throw_exception (php_openssl_exception_ce , "Invalid serialization data" , 0 );
441+ RETURN_THROWS ();
442+ }
443+
444+ BIO * bio = BIO_new_mem_buf (Z_STRVAL_P (pem_zv ), Z_STRLEN_P (pem_zv ));
445+ if (!bio ) {
446+ zend_throw_exception (php_openssl_exception_ce , "Failed to unserialize session" , 0 );
447+ RETURN_THROWS ();
448+ }
449+
450+ SSL_SESSION * session = PEM_read_bio_SSL_SESSION (bio , NULL , NULL , NULL );
451+ BIO_free (bio );
452+
453+ if (!session ) {
454+ zend_throw_exception (php_openssl_exception_ce , "Failed to unserialize session" , 0 );
455+ RETURN_THROWS ();
456+ }
457+
458+ php_openssl_session_object * obj = Z_OPENSSL_SESSION_P (ZEND_THIS );
459+ obj -> session = session ;
460+
461+ /* Populate id property */
462+ unsigned int id_len = 0 ;
463+ const unsigned char * id = SSL_SESSION_get_id (session , & id_len );
464+ zend_update_property_stringl (php_openssl_session_ce , Z_OBJ_P (ZEND_THIS ),
465+ ZEND_STRL ("id" ), (char * )id , id_len );
466+ }
467+
168468#if defined(HAVE_OPENSSL_ARGON2 )
169469static const zend_module_dep openssl_deps [] = {
170470 ZEND_MOD_REQUIRED ("standard" )
@@ -381,6 +681,8 @@ PHP_INI_END()
381681/* {{{ PHP_MINIT_FUNCTION */
382682PHP_MINIT_FUNCTION (openssl )
383683{
684+ php_openssl_exception_ce = register_class_Openssl_OpensslException (zend_ce_exception );
685+
384686 php_openssl_certificate_ce = register_class_OpenSSLCertificate ();
385687 php_openssl_certificate_ce -> create_object = php_openssl_certificate_create_object ;
386688 php_openssl_certificate_ce -> default_object_handlers = & php_openssl_certificate_object_handlers ;
@@ -414,6 +716,17 @@ PHP_MINIT_FUNCTION(openssl)
414716 php_openssl_pkey_object_handlers .clone_obj = NULL ;
415717 php_openssl_pkey_object_handlers .compare = zend_objects_not_comparable ;
416718
719+ php_openssl_session_ce = register_class_Openssl_Session ();
720+ php_openssl_session_ce -> create_object = php_openssl_session_create_object ;
721+ php_openssl_session_ce -> default_object_handlers = & php_openssl_session_object_handlers ;
722+
723+ memcpy (& php_openssl_session_object_handlers , & std_object_handlers , sizeof (zend_object_handlers ));
724+ php_openssl_session_object_handlers .offset = offsetof(php_openssl_session_object , std );
725+ php_openssl_session_object_handlers .free_obj = php_openssl_session_free_obj ;
726+ php_openssl_session_object_handlers .get_constructor = php_openssl_session_get_constructor ;
727+ php_openssl_session_object_handlers .clone_obj = NULL ;
728+ php_openssl_session_object_handlers .compare = zend_objects_not_comparable ;
729+
417730 register_openssl_symbols (module_number );
418731
419732 php_openssl_backend_init ();
0 commit comments