Skip to content

Commit a3268f8

Browse files
committed
Use PEM by default for import, export, serialize and test it
1 parent 26ae4b8 commit a3268f8

File tree

4 files changed

+209
-16
lines changed

4 files changed

+209
-16
lines changed

ext/openssl/openssl.c

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ static void php_openssl_session_free_obj(zend_object *object)
239239

240240
PHP_METHOD(OpenSSLSession, export)
241241
{
242-
zend_long format = ENCODING_DER;
242+
zend_long format = ENCODING_PEM;
243243

244244
ZEND_PARSE_PARAMETERS_START(0, 1)
245245
Z_PARAM_OPTIONAL
@@ -291,7 +291,7 @@ PHP_METHOD(OpenSSLSession, export)
291291
PHP_METHOD(OpenSSLSession, import)
292292
{
293293
zend_string *data;
294-
zend_long format = ENCODING_DER;
294+
zend_long format = ENCODING_PEM;
295295

296296
ZEND_PARSE_PARAMETERS_START(1, 2)
297297
Z_PARAM_STR(data)
@@ -311,7 +311,7 @@ PHP_METHOD(OpenSSLSession, import)
311311
BIO_free(bio);
312312
}
313313
} else {
314-
zend_argument_value_error(2, "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM");
314+
zend_argument_value_error(2, "must be OPENSSL_ENCODING_DER or OPENSSL_ENCODING_PEM");
315315
RETURN_THROWS();
316316
}
317317

@@ -402,26 +402,31 @@ PHP_METHOD(OpenSSLSession, getTicketLifetimeHint)
402402

403403
RETURN_LONG((zend_long)SSL_SESSION_get_ticket_lifetime_hint(obj->session));
404404
}
405-
406405
PHP_METHOD(OpenSSLSession, __serialize)
407406
{
408407
ZEND_PARSE_PARAMETERS_NONE();
409408

410409
PHP_OPENSSL_SESSION_CHECK();
411410

412-
int len = i2d_SSL_SESSION(obj->session, NULL);
413-
if (len <= 0) {
411+
BIO *bio = BIO_new(BIO_s_mem());
412+
if (!bio) {
414413
zend_throw_exception(php_openssl_exception_ce, "Failed to serialize session", 0);
415414
RETURN_THROWS();
416415
}
417416

418-
zend_string *der = zend_string_alloc(len, 0);
419-
unsigned char *p = (unsigned char *)ZSTR_VAL(der);
420-
i2d_SSL_SESSION(obj->session, &p);
421-
ZSTR_VAL(der)[len] = '\0';
417+
if (!PEM_write_bio_SSL_SESSION(bio, obj->session)) {
418+
BIO_free(bio);
419+
zend_throw_exception(php_openssl_exception_ce, "Failed to serialize session", 0);
420+
RETURN_THROWS();
421+
}
422+
423+
char *data;
424+
long len = BIO_get_mem_data(bio, &data);
425+
zend_string *pem = zend_string_init(data, len, 0);
426+
BIO_free(bio);
422427

423428
array_init(return_value);
424-
add_assoc_str(return_value, "der", der);
429+
add_assoc_str(return_value, "pem", pem);
425430
}
426431

427432
PHP_METHOD(OpenSSLSession, __unserialize)
@@ -432,14 +437,20 @@ PHP_METHOD(OpenSSLSession, __unserialize)
432437
Z_PARAM_ARRAY_HT(data)
433438
ZEND_PARSE_PARAMETERS_END();
434439

435-
zval *der_zv = zend_hash_str_find(data, ZEND_STRL("der"));
436-
if (!der_zv || Z_TYPE_P(der_zv) != IS_STRING) {
440+
zval *pem_zv = zend_hash_str_find(data, ZEND_STRL("pem"));
441+
if (!pem_zv || Z_TYPE_P(pem_zv) != IS_STRING) {
437442
zend_throw_exception(php_openssl_exception_ce, "Invalid serialization data", 0);
438443
RETURN_THROWS();
439444
}
440445

441-
const unsigned char *p = (const unsigned char *)Z_STRVAL_P(der_zv);
442-
SSL_SESSION *session = d2i_SSL_SESSION(NULL, &p, Z_STRLEN_P(der_zv));
446+
BIO *bio = BIO_new_mem_buf(Z_STRVAL_P(pem_zv), Z_STRLEN_P(pem_zv));
447+
if (!bio) {
448+
zend_throw_exception(php_openssl_exception_ce, "Failed to unserialize session", 0);
449+
RETURN_THROWS();
450+
}
451+
452+
SSL_SESSION *session = PEM_read_bio_SSL_SESSION(bio, NULL, NULL, NULL);
453+
BIO_free(bio);
443454

444455
if (!session) {
445456
zend_throw_exception(php_openssl_exception_ce, "Failed to unserialize session", 0);
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
--TEST--
2+
TLS session resumption - import and export session
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
?>
9+
--FILE--
10+
<?php
11+
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_resumption_serialize.pem.tmp';
12+
13+
$serverCode = <<<'CODE'
14+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
15+
$ctx = stream_context_create(['ssl' => [
16+
'local_cert' => '%s',
17+
'session_cache' => true,
18+
'session_id_context' => 'test-basic',
19+
]]);
20+
21+
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx);
22+
phpt_notify_server_start($server);
23+
24+
/* Accept single connections */
25+
$client = @stream_socket_accept($server, 30);
26+
if ($client) {
27+
fwrite($client, "Hello from server\n");
28+
fclose($client);
29+
}
30+
CODE;
31+
$serverCode = sprintf($serverCode, $certFile);
32+
33+
$clientCode = <<<'CODE'
34+
$sessionData = '';
35+
36+
$flags = STREAM_CLIENT_CONNECT;
37+
$ctx = stream_context_create(['ssl' => [
38+
'verify_peer' => false,
39+
'verify_peer_name' => false,
40+
'session_new_cb' => function($stream, $session) use (&$sessionData) {
41+
if (empty($sessionData)) {
42+
// default should be pem
43+
$pemSessionData = $session->export();
44+
var_dump($pemSessionData);
45+
$session = OpenSSLSession::import($pemSessionData);
46+
$pemSessionData = $session->export(OPENSSL_ENCODING_PEM);
47+
var_dump($pemSessionData);
48+
$session = OpenSSLSession::import($pemSessionData, OPENSSL_ENCODING_PEM);
49+
$derSessionData = $session->export(OPENSSL_ENCODING_DER);
50+
var_dump(strlen($derSessionData) > 0);
51+
var_dump(strpos($derSessionData, 'BEGIN SSL SESSION PARAMETERS') === false);
52+
$session = OpenSSLSession::import($derSessionData, OPENSSL_ENCODING_DER);
53+
var_dump($session);
54+
}
55+
$sessionData = $session;
56+
}
57+
]]);
58+
59+
/* First connection - full handshake */
60+
$client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx);
61+
if ($client1) {
62+
echo trim(fgets($client1)) . "\n";
63+
$meta1 = stream_get_meta_data($client1);
64+
echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n";
65+
echo "Session data received: " . (!empty($sessionData) ? "yes" : "no") . "\n";
66+
fclose($client1);
67+
}
68+
CODE;
69+
70+
include 'CertificateGenerator.inc';
71+
$certificateGenerator = new CertificateGenerator();
72+
$certificateGenerator->saveNewCertAsFileWithKey('session_resumption_test', $certFile);
73+
74+
include 'ServerClientTestCase.inc';
75+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
76+
?>
77+
--CLEAN--
78+
<?php
79+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'session_resumption_serialize.pem.tmp');
80+
?>
81+
--EXPECTF--
82+
string(%d) "-----BEGIN SSL SESSION PARAMETERS-----
83+
%a
84+
-----END SSL SESSION PARAMETERS-----
85+
"
86+
string(%d) "-----BEGIN SSL SESSION PARAMETERS-----
87+
%a
88+
-----END SSL SESSION PARAMETERS-----
89+
"
90+
bool(true)
91+
bool(true)
92+
object(OpenSSLSession)#%d (1) {
93+
["id"]=>
94+
string(32) "%s"
95+
}
96+
Hello from server
97+
First connection resumed: no
98+
Session data received: yes

ext/openssl/tests/session_resumption_invalid_session_import.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
TLS session resumption - server with cache disabled
2+
TLS session resumption - invalid session import
33
--EXTENSIONS--
44
openssl
55
--SKIPIF--
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
TLS session resumption - serialize session
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
if (!function_exists("proc_open")) die("skip no proc_open");
8+
?>
9+
--FILE--
10+
<?php
11+
$certFile = __DIR__ . DIRECTORY_SEPARATOR . 'session_resumption_serialize.pem.tmp';
12+
13+
$serverCode = <<<'CODE'
14+
$flags = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN;
15+
$ctx = stream_context_create(['ssl' => [
16+
'local_cert' => '%s',
17+
'session_cache' => true,
18+
'session_id_context' => 'test-basic',
19+
]]);
20+
21+
$server = stream_socket_server('tls://127.0.0.1:0', $errno, $errstr, $flags, $ctx);
22+
phpt_notify_server_start($server);
23+
24+
/* Accept single connections */
25+
$client = @stream_socket_accept($server, 30);
26+
if ($client) {
27+
fwrite($client, "Hello from server\n");
28+
fclose($client);
29+
}
30+
CODE;
31+
$serverCode = sprintf($serverCode, $certFile);
32+
33+
$clientCode = <<<'CODE'
34+
$sessionData = '';
35+
36+
$flags = STREAM_CLIENT_CONNECT;
37+
$ctx = stream_context_create(['ssl' => [
38+
'verify_peer' => false,
39+
'verify_peer_name' => false,
40+
'session_new_cb' => function($stream, $session) use (&$sessionData) {
41+
if (empty($sessionData)) {
42+
$serializedSessionData = serialize($session);
43+
var_dump($serializedSessionData);
44+
$session = unserialize($serializedSessionData);
45+
var_dump($session);
46+
}
47+
$sessionData = $session;
48+
}
49+
]]);
50+
51+
/* First connection - full handshake */
52+
$client1 = stream_socket_client("tls://{{ ADDR }}", $errno, $errstr, 30, $flags, $ctx);
53+
if ($client1) {
54+
echo trim(fgets($client1)) . "\n";
55+
$meta1 = stream_get_meta_data($client1);
56+
echo "First connection resumed: " . ($meta1['crypto']['session_reused'] ? "yes" : "no") . "\n";
57+
echo "Session data received: " . (!empty($sessionData) ? "yes" : "no") . "\n";
58+
fclose($client1);
59+
}
60+
CODE;
61+
62+
include 'CertificateGenerator.inc';
63+
$certificateGenerator = new CertificateGenerator();
64+
$certificateGenerator->saveNewCertAsFileWithKey('session_resumption_test', $certFile);
65+
66+
include 'ServerClientTestCase.inc';
67+
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
68+
?>
69+
--CLEAN--
70+
<?php
71+
@unlink(__DIR__ . DIRECTORY_SEPARATOR . 'session_resumption_serialize.pem.tmp');
72+
?>
73+
--EXPECTF--
74+
string(%d) "O:14:"OpenSSLSession":1:{s:3:"pem";s:%d:"-----BEGIN SSL SESSION PARAMETERS-----
75+
%a
76+
-----END SSL SESSION PARAMETERS-----
77+
";}"
78+
object(OpenSSLSession)#9 (1) {
79+
["id"]=>
80+
%a
81+
}
82+
Hello from server
83+
First connection resumed: no
84+
Session data received: yes

0 commit comments

Comments
 (0)