Skip to content

Commit da9a678

Browse files
committed
openssl: wip xp_common 3
1 parent 5324973 commit da9a678

4 files changed

Lines changed: 195 additions & 360 deletions

File tree

ext/openssl/xp_common.c

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,149 @@ void php_openssl_add_crypto_cipher(zval *crypto, SSL *ssl)
219219
}
220220
}
221221

222+
int php_openssl_get_ctx_session_callbacks_index(void)
223+
{
224+
static int index = -1;
225+
if (index < 0) {
226+
index = SSL_CTX_get_ex_new_index(0, "PHP openssl session callbacks index", NULL, NULL, NULL);
227+
}
228+
return index;
229+
}
230+
231+
int php_openssl_get_ctx_stream_index(void)
232+
{
233+
static int index = -1;
234+
if (index < 0) {
235+
index = SSL_CTX_get_ex_new_index(0, "PHP openssl ctx stream index", NULL, NULL, NULL);
236+
}
237+
return index;
238+
}
239+
240+
static php_openssl_session_callbacks_t *php_openssl_ctx_session_callbacks(SSL_CTX *ctx)
241+
{
242+
return (php_openssl_session_callbacks_t *)SSL_CTX_get_ex_data(ctx,
243+
php_openssl_get_ctx_session_callbacks_index());
244+
}
245+
246+
int php_openssl_session_new_cb(SSL *ssl, SSL_SESSION *session)
247+
{
248+
php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, php_openssl_get_ssl_stream_data_index());
249+
php_openssl_session_callbacks_t *cbs = php_openssl_ctx_session_callbacks(SSL_get_SSL_CTX(ssl));
250+
if (!stream || !cbs) {
251+
return 0;
252+
}
253+
254+
/* Increment reference - we're giving ownership to the PHP object */
255+
SSL_SESSION_up_ref(session);
256+
257+
zval args[2];
258+
ZVAL_RES(&args[0], stream->res);
259+
php_openssl_session_object_init(&args[1], session);
260+
261+
zend_call_known_fcc(&cbs->new_cb, NULL, 2, args, NULL);
262+
263+
zval_ptr_dtor(&args[1]);
264+
return 0;
265+
}
266+
267+
SSL_SESSION *php_openssl_session_get_cb(SSL *ssl, const unsigned char *session_id, int session_id_len, int *copy)
268+
{
269+
php_stream *stream = (php_stream *)SSL_get_ex_data(ssl, php_openssl_get_ssl_stream_data_index());
270+
php_openssl_session_callbacks_t *cbs = php_openssl_ctx_session_callbacks(SSL_get_SSL_CTX(ssl));
271+
if (!stream || !cbs) {
272+
*copy = 0;
273+
return NULL;
274+
}
275+
276+
zval args[2], retval;
277+
ZVAL_RES(&args[0], stream->res);
278+
ZVAL_STRINGL(&args[1], (char *)session_id, session_id_len);
279+
280+
SSL_SESSION *session = NULL;
281+
zend_call_known_fcc(&cbs->get_cb, &retval, 2, args, NULL);
282+
zval_ptr_dtor(&args[1]);
283+
284+
if (php_openssl_is_session_ce(&retval)) {
285+
SSL_SESSION *found = php_openssl_session_from_zval(&retval);
286+
if (found != NULL) {
287+
/* OpenSSL takes ownership of the returned session. */
288+
SSL_SESSION_up_ref(found);
289+
session = found;
290+
}
291+
} else if (Z_TYPE(retval) != IS_NULL) {
292+
zend_type_error("session_get_cb return type must be null or Openssl\\Session");
293+
}
294+
295+
zval_ptr_dtor(&retval);
296+
*copy = 0;
297+
return session;
298+
}
299+
300+
void php_openssl_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *session)
301+
{
302+
php_stream *stream = (php_stream *)SSL_CTX_get_ex_data(ctx, php_openssl_get_ctx_stream_index());
303+
php_openssl_session_callbacks_t *cbs = php_openssl_ctx_session_callbacks(ctx);
304+
if (!stream || !cbs) {
305+
return;
306+
}
307+
308+
unsigned int session_id_len = 0;
309+
const unsigned char *session_id = SSL_SESSION_get_id(session, &session_id_len);
310+
311+
zval args[2];
312+
ZVAL_RES(&args[0], stream->res);
313+
ZVAL_STRINGL(&args[1], (char *)session_id, session_id_len);
314+
315+
zend_call_known_fcc(&cbs->remove_cb, NULL, 2, args, NULL);
316+
zval_ptr_dtor(&args[1]);
317+
}
318+
319+
zend_result php_openssl_validate_and_allocate_session_callback(php_stream *stream,
320+
php_openssl_session_callbacks_t **callbacks, const zval *callable,
321+
enum php_openssl_session_callback_type cb_type, bool is_persistent)
322+
{
323+
const char *callback_name;
324+
switch (cb_type) {
325+
case PHP_OPENSSL_NEW_CB: callback_name = "session_new_cb"; break;
326+
case PHP_OPENSSL_GET_CB: callback_name = "session_get_cb"; break;
327+
case PHP_OPENSSL_REMOVE_CB: callback_name = "session_remove_cb"; break;
328+
}
329+
330+
/* Callbacks not supported for persistent streams */
331+
if (is_persistent) {
332+
php_stream_warn(stream, PersistentNotSupported,
333+
"%s is not supported for persistent streams", callback_name);
334+
return FAILURE;
335+
}
336+
337+
char *is_callable_error = NULL;
338+
zend_fcall_info_cache fcc;
339+
if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, &is_callable_error)) {
340+
if (is_callable_error) {
341+
zend_type_error("%s must be a valid callback, %s", callback_name, is_callable_error);
342+
efree(is_callable_error);
343+
} else {
344+
zend_type_error("%s must be a valid callback", callback_name);
345+
}
346+
return FAILURE;
347+
}
348+
349+
if (!*callbacks) {
350+
*callbacks = (php_openssl_session_callbacks_t *)pecalloc(
351+
1, sizeof(php_openssl_session_callbacks_t), is_persistent);
352+
(*callbacks)->refcount = 1;
353+
}
354+
355+
zend_fcc_addref(&fcc);
356+
switch (cb_type) {
357+
case PHP_OPENSSL_NEW_CB: (*callbacks)->new_cb = fcc; break;
358+
case PHP_OPENSSL_GET_CB: (*callbacks)->get_cb = fcc; break;
359+
case PHP_OPENSSL_REMOVE_CB: (*callbacks)->remove_cb = fcc; break;
360+
}
361+
362+
return SUCCESS;
363+
}
364+
222365
zend_result php_openssl_set_local_cert(SSL_CTX *ctx, php_stream *stream)
223366
{
224367
zval *val;

ext/openssl/xp_common.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,34 @@ void php_openssl_disable_peer_verification(SSL_CTX *ctx, php_stream *stream);
5757
/* Add the negotiated cipher_name/bits/version to a crypto metadata array. */
5858
void php_openssl_add_crypto_cipher(zval *crypto, SSL *ssl);
5959

60+
/* Userland session-resumption callbacks, shared (refcounted) by a listener and
61+
* its accepted connections. */
62+
typedef struct _php_openssl_session_callbacks_t {
63+
int refcount;
64+
zend_fcall_info_cache new_cb;
65+
zend_fcall_info_cache get_cb;
66+
zend_fcall_info_cache remove_cb;
67+
} php_openssl_session_callbacks_t;
68+
69+
enum php_openssl_session_callback_type {
70+
PHP_OPENSSL_NEW_CB,
71+
PHP_OPENSSL_GET_CB,
72+
PHP_OPENSSL_REMOVE_CB,
73+
};
74+
75+
/* ex-data indices on the SSL_CTX so the shared session callbacks can reach the
76+
* stream and the callbacks struct without knowing the transport's netstream type. */
77+
int php_openssl_get_ctx_session_callbacks_index(void);
78+
int php_openssl_get_ctx_stream_index(void);
79+
80+
/* The OpenSSL session cache callbacks (data comes from the CTX ex-data). */
81+
int php_openssl_session_new_cb(SSL *ssl, SSL_SESSION *session);
82+
SSL_SESSION *php_openssl_session_get_cb(SSL *ssl, const unsigned char *session_id, int session_id_len, int *copy);
83+
void php_openssl_session_remove_cb(SSL_CTX *ctx, SSL_SESSION *session);
84+
85+
/* Validate a session_*_cb callable and store it in *callbacks (allocating it). */
86+
zend_result php_openssl_validate_and_allocate_session_callback(php_stream *stream,
87+
php_openssl_session_callbacks_t **callbacks, const zval *callable,
88+
enum php_openssl_session_callback_type cb_type, bool is_persistent);
89+
6090
#endif /* PHP_OPENSSL_XP_COMMON_H */

0 commit comments

Comments
 (0)