1 : /*
2 : +----------------------------------------------------------------------+
3 : | PHP Version 6 |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) 1997-2009 The PHP Group |
6 : +----------------------------------------------------------------------+
7 : | This source file is subject to version 3.01 of the PHP license, |
8 : | that is bundled with this package in the file LICENSE, and is |
9 : | available through the world-wide-web at the following url: |
10 : | http://www.php.net/license/3_01.txt |
11 : | If you did not receive a copy of the PHP license and are unable to |
12 : | obtain it through the world-wide-web, please send a note to |
13 : | license@php.net so we can mail you a copy immediately. |
14 : +----------------------------------------------------------------------+
15 : | Author: Wez Furlong <wez@thebrainroom.com> |
16 : +----------------------------------------------------------------------+
17 : */
18 :
19 : /* $Id: xp_ssl.c 289860 2009-10-22 18:48:38Z lbarnaud $ */
20 :
21 : #include "php.h"
22 : #include "ext/standard/file.h"
23 : #include "ext/standard/url.h"
24 : #include "streams/php_streams_int.h"
25 : #include "ext/standard/php_smart_str.h"
26 : #include "php_network.h"
27 : #include "php_openssl.h"
28 : #include <openssl/ssl.h>
29 : #include <openssl/x509.h>
30 : #include <openssl/err.h>
31 :
32 : #ifdef PHP_WIN32
33 : #include "win32/time.h"
34 : #endif
35 :
36 : #ifdef NETWARE
37 : #include <sys/select.h>
38 : #endif
39 :
40 : int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC);
41 : SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC);
42 : int php_openssl_get_x509_list_id(void);
43 :
44 : /* This implementation is very closely tied to the that of the native
45 : * sockets implemented in the core.
46 : * Don't try this technique in other extensions!
47 : * */
48 :
49 : typedef struct _php_openssl_netstream_data_t {
50 : php_netstream_data_t s;
51 : SSL *ssl_handle;
52 : SSL_CTX *ctx;
53 : struct timeval connect_timeout;
54 : int enable_on_connect;
55 : int is_client;
56 : int ssl_active;
57 : php_stream_xport_crypt_method_t method;
58 : char *sni;
59 : unsigned state_set:1;
60 : unsigned _spare:31;
61 : } php_openssl_netstream_data_t;
62 :
63 : php_stream_ops php_openssl_socket_ops;
64 :
65 : /* it doesn't matter that we do some hash traversal here, since it is done only
66 : * in an error condition arising from a network connection problem */
67 : static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC)
68 0 : {
69 0 : if (stream->wrapperdata && stream->wrapper && strcasecmp(stream->wrapper->wops->label, "HTTP") == 0) {
70 : /* the wrapperdata is an array zval containing the headers */
71 : zval **tmp;
72 :
73 : #define SERVER_MICROSOFT_IIS "Server: Microsoft-IIS"
74 : #define SERVER_GOOGLE "Server: GFE/"
75 :
76 0 : zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata));
77 0 : while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) {
78 :
79 0 : if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) {
80 0 : return 1;
81 0 : } else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) {
82 0 : return 1;
83 : }
84 :
85 0 : zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata));
86 : }
87 : }
88 0 : return 0;
89 : }
90 :
91 : static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC)
92 10085 : {
93 10085 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
94 10085 : int err = SSL_get_error(sslsock->ssl_handle, nr_bytes);
95 : char esbuf[512];
96 10085 : smart_str ebuf = {0};
97 : unsigned long ecode;
98 10085 : int retry = 1;
99 :
100 10085 : switch(err) {
101 : case SSL_ERROR_ZERO_RETURN:
102 : /* SSL terminated (but socket may still be active) */
103 0 : retry = 0;
104 0 : break;
105 : case SSL_ERROR_WANT_READ:
106 : case SSL_ERROR_WANT_WRITE:
107 : /* re-negotiation, or perhaps the SSL layer needs more
108 : * packets: retry in next iteration */
109 10085 : errno = EAGAIN;
110 10085 : retry = is_init ? 1 : sslsock->s.is_blocked;
111 10085 : break;
112 : case SSL_ERROR_SYSCALL:
113 0 : if (ERR_peek_error() == 0) {
114 0 : if (nr_bytes == 0) {
115 0 : if (!is_http_stream_talking_to_iis(stream TSRMLS_CC)) {
116 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
117 : "SSL: fatal protocol error");
118 : }
119 0 : SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN);
120 0 : stream->eof = 1;
121 0 : retry = 0;
122 : } else {
123 0 : char *estr = php_socket_strerror(php_socket_errno(), NULL, 0);
124 :
125 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
126 : "SSL: %s", estr);
127 :
128 0 : efree(estr);
129 0 : retry = 0;
130 : }
131 0 : break;
132 : }
133 :
134 :
135 : /* fall through */
136 : default:
137 : /* some other error */
138 0 : ecode = ERR_get_error();
139 :
140 0 : switch (ERR_GET_REASON(ecode)) {
141 : case SSL_R_NO_SHARED_CIPHER:
142 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL_R_NO_SHARED_CIPHER: no suitable shared cipher could be used. This could be because the server is missing an SSL certificate (local_cert context option)");
143 0 : retry = 0;
144 0 : break;
145 :
146 : default:
147 : do {
148 : // NULL is automatically added
149 0 : ERR_error_string_n(ecode, esbuf, sizeof(esbuf));
150 0 : if (ebuf.c) {
151 0 : smart_str_appendc(&ebuf, '\n');
152 : }
153 0 : smart_str_appends(&ebuf, esbuf);
154 0 : } while ((ecode = ERR_get_error()) != 0);
155 :
156 0 : smart_str_0(&ebuf);
157 :
158 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING,
159 : "SSL operation failed with code %d. %s%s",
160 : err,
161 : ebuf.c ? "OpenSSL Error messages:\n" : "",
162 : ebuf.c ? ebuf.c : "");
163 0 : if (ebuf.c) {
164 0 : smart_str_free(&ebuf);
165 : }
166 : }
167 :
168 0 : retry = 0;
169 0 : errno = 0;
170 : }
171 10085 : return retry;
172 : }
173 :
174 :
175 : static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC)
176 143 : {
177 143 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
178 : int didwrite;
179 :
180 143 : if (sslsock->ssl_active) {
181 3 : int retry = 1;
182 :
183 : do {
184 3 : didwrite = SSL_write(sslsock->ssl_handle, buf, count);
185 :
186 3 : if (didwrite <= 0) {
187 0 : retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC);
188 : } else {
189 3 : break;
190 : }
191 0 : } while(retry);
192 :
193 3 : if (didwrite > 0) {
194 3 : php_stream_notify_progress_increment(stream->context, didwrite, 0);
195 : }
196 : } else {
197 140 : didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
198 : }
199 :
200 143 : if (didwrite < 0) {
201 0 : didwrite = 0;
202 : }
203 :
204 143 : return didwrite;
205 : }
206 :
207 : static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
208 129 : {
209 129 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
210 129 : int nr_bytes = 0;
211 :
212 129 : if (sslsock->ssl_active) {
213 3 : int retry = 1;
214 :
215 : do {
216 3 : nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
217 :
218 3 : if (nr_bytes <= 0) {
219 0 : retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
220 0 : stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
221 :
222 : } else {
223 : /* we got the data */
224 3 : break;
225 : }
226 0 : } while (retry);
227 :
228 3 : if (nr_bytes > 0) {
229 3 : php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
230 : }
231 : }
232 : else
233 : {
234 126 : nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
235 : }
236 :
237 129 : if (nr_bytes < 0) {
238 0 : nr_bytes = 0;
239 : }
240 :
241 129 : return nr_bytes;
242 : }
243 :
244 :
245 : static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC)
246 174 : {
247 174 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
248 : #ifdef PHP_WIN32
249 : int n;
250 : #endif
251 174 : if (close_handle) {
252 174 : if (sslsock->ssl_active) {
253 4 : SSL_shutdown(sslsock->ssl_handle);
254 4 : sslsock->ssl_active = 0;
255 : }
256 174 : if (sslsock->ssl_handle) {
257 4 : SSL_free(sslsock->ssl_handle);
258 4 : sslsock->ssl_handle = NULL;
259 : }
260 174 : if (sslsock->ctx) {
261 4 : SSL_CTX_free(sslsock->ctx);
262 4 : sslsock->ctx = NULL;
263 : }
264 : #ifdef PHP_WIN32
265 : if (sslsock->s.socket == -1)
266 : sslsock->s.socket = SOCK_ERR;
267 : #endif
268 174 : if (sslsock->s.socket != SOCK_ERR) {
269 : #ifdef PHP_WIN32
270 : /* prevent more data from coming in */
271 : shutdown(sslsock->s.socket, SHUT_RD);
272 :
273 : /* try to make sure that the OS sends all data before we close the connection.
274 : * Essentially, we are waiting for the socket to become writeable, which means
275 : * that all pending data has been sent.
276 : * We use a small timeout which should encourage the OS to send the data,
277 : * but at the same time avoid hanging indefintely.
278 : * */
279 : do {
280 : n = php_pollfd_for_ms(sslsock->s.socket, POLLOUT, 500);
281 : } while (n == -1 && php_socket_errno() == EINTR);
282 : #endif
283 155 : closesocket(sslsock->s.socket);
284 155 : sslsock->s.socket = SOCK_ERR;
285 : }
286 : }
287 :
288 174 : if (sslsock->sni) {
289 120 : pefree(sslsock->sni, php_stream_is_persistent(stream));
290 : }
291 174 : pefree(sslsock, php_stream_is_persistent(stream));
292 :
293 174 : return 0;
294 : }
295 :
296 : static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC)
297 174 : {
298 174 : return php_stream_socket_ops.flush(stream TSRMLS_CC);
299 : }
300 :
301 : static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC)
302 0 : {
303 0 : return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC);
304 : }
305 :
306 :
307 : static inline int php_openssl_setup_crypto(php_stream *stream,
308 : php_openssl_netstream_data_t *sslsock,
309 : php_stream_xport_crypto_param *cparam
310 : TSRMLS_DC)
311 4 : {
312 : SSL_METHOD *method;
313 :
314 4 : if (sslsock->ssl_handle) {
315 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream");
316 0 : return -1;
317 : }
318 :
319 : /* need to do slightly different things, based on client/server method,
320 : * so lets remember which method was selected */
321 :
322 4 : switch (cparam->inputs.method) {
323 : case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
324 2 : sslsock->is_client = 1;
325 2 : method = SSLv23_client_method();
326 2 : break;
327 : case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
328 0 : sslsock->is_client = 1;
329 0 : method = SSLv2_client_method();
330 0 : break;
331 : case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
332 0 : sslsock->is_client = 1;
333 0 : method = SSLv3_client_method();
334 0 : break;
335 : case STREAM_CRYPTO_METHOD_TLS_CLIENT:
336 0 : sslsock->is_client = 1;
337 0 : method = TLSv1_client_method();
338 0 : break;
339 : case STREAM_CRYPTO_METHOD_SSLv23_SERVER:
340 2 : sslsock->is_client = 0;
341 2 : method = SSLv23_server_method();
342 2 : break;
343 : case STREAM_CRYPTO_METHOD_SSLv3_SERVER:
344 0 : sslsock->is_client = 0;
345 0 : method = SSLv3_server_method();
346 0 : break;
347 : case STREAM_CRYPTO_METHOD_SSLv2_SERVER:
348 0 : sslsock->is_client = 0;
349 0 : method = SSLv2_server_method();
350 0 : break;
351 : case STREAM_CRYPTO_METHOD_TLS_SERVER:
352 0 : sslsock->is_client = 0;
353 0 : method = TLSv1_server_method();
354 0 : break;
355 : default:
356 0 : return -1;
357 :
358 : }
359 :
360 4 : sslsock->ctx = SSL_CTX_new(method);
361 4 : if (sslsock->ctx == NULL) {
362 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context");
363 0 : return -1;
364 : }
365 :
366 4 : SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL);
367 :
368 4 : sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC);
369 4 : if (sslsock->ssl_handle == NULL) {
370 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle");
371 0 : SSL_CTX_free(sslsock->ctx);
372 0 : sslsock->ctx = NULL;
373 0 : return -1;
374 : }
375 :
376 4 : if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) {
377 0 : handle_ssl_error(stream, 0, 1 TSRMLS_CC);
378 : }
379 :
380 4 : if (cparam->inputs.session) {
381 0 : if (cparam->inputs.session->ops != &php_openssl_socket_ops) {
382 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream");
383 : } else {
384 0 : SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle);
385 : }
386 : }
387 4 : return 0;
388 : }
389 :
390 : static inline int php_openssl_enable_crypto(php_stream *stream,
391 : php_openssl_netstream_data_t *sslsock,
392 : php_stream_xport_crypto_param *cparam
393 : TSRMLS_DC)
394 4 : {
395 4 : int n, retry = 1;
396 :
397 4 : if (cparam->inputs.activate && !sslsock->ssl_active) {
398 4 : float timeout = sslsock->connect_timeout.tv_sec + sslsock->connect_timeout.tv_usec / 1000000;
399 4 : int blocked = sslsock->s.is_blocked;
400 :
401 : #if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT)
402 : if (sslsock->is_client && sslsock->sni) {
403 : SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni);
404 : }
405 : #endif
406 :
407 4 : if (!sslsock->state_set) {
408 4 : if (sslsock->is_client) {
409 2 : SSL_set_connect_state(sslsock->ssl_handle);
410 : } else {
411 2 : SSL_set_accept_state(sslsock->ssl_handle);
412 : }
413 4 : sslsock->state_set = 1;
414 : }
415 :
416 4 : if (sslsock->is_client && SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
417 2 : sslsock->s.is_blocked = 0;
418 : }
419 : do {
420 10089 : if (sslsock->is_client) {
421 : struct timeval tvs, tve;
422 : struct timezone tz;
423 :
424 10087 : gettimeofday(&tvs, &tz);
425 10087 : n = SSL_connect(sslsock->ssl_handle);
426 10087 : gettimeofday(&tve, &tz);
427 :
428 10087 : timeout -= (tve.tv_sec + (float) tve.tv_usec / 1000000) - (tvs.tv_sec + (float) tvs.tv_usec / 1000000);
429 10087 : if (timeout < 0) {
430 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: connection timeout");
431 0 : return -1;
432 : }
433 : } else {
434 2 : n = SSL_accept(sslsock->ssl_handle);
435 : }
436 :
437 10089 : if (n <= 0) {
438 10085 : retry = handle_ssl_error(stream, n, 1 TSRMLS_CC);
439 : } else {
440 4 : break;
441 : }
442 10085 : } while (retry);
443 :
444 4 : if (sslsock->is_client && sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) {
445 2 : sslsock->s.is_blocked = blocked;
446 : }
447 :
448 4 : if (n == 1) {
449 : X509 *peer_cert;
450 :
451 4 : peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle);
452 :
453 4 : if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) {
454 0 : SSL_shutdown(sslsock->ssl_handle);
455 : } else {
456 4 : sslsock->ssl_active = 1;
457 :
458 : /* allow the script to capture the peer cert
459 : * and/or the certificate chain */
460 4 : if (stream->context) {
461 : zval **val, *zcert;
462 :
463 3 : if (SUCCESS == php_stream_context_get_option(
464 : stream->context, "ssl",
465 : "capture_peer_cert", &val) &&
466 : zval_is_true(*val)) {
467 0 : MAKE_STD_ZVAL(zcert);
468 0 : ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert,
469 : php_openssl_get_x509_list_id()));
470 0 : php_stream_context_set_option(stream->context,
471 : "ssl", "peer_certificate",
472 : zcert);
473 0 : peer_cert = NULL;
474 0 : FREE_ZVAL(zcert);
475 : }
476 :
477 3 : if (SUCCESS == php_stream_context_get_option(
478 : stream->context, "ssl",
479 : "capture_peer_cert_chain", &val) &&
480 : zval_is_true(*val)) {
481 : zval *arr;
482 : STACK_OF(X509) *chain;
483 :
484 0 : MAKE_STD_ZVAL(arr);
485 0 : chain = SSL_get_peer_cert_chain(
486 : sslsock->ssl_handle);
487 :
488 0 : if (chain && sk_X509_num(chain) > 0) {
489 : int i;
490 0 : array_init(arr);
491 :
492 0 : for (i = 0; i < sk_X509_num(chain); i++) {
493 : X509 *mycert = X509_dup(
494 0 : sk_X509_value(chain, i));
495 0 : MAKE_STD_ZVAL(zcert);
496 0 : ZVAL_RESOURCE(zcert,
497 : zend_list_insert(mycert,
498 : php_openssl_get_x509_list_id()));
499 0 : add_next_index_zval(arr, zcert);
500 0 : FREE_ZVAL(zcert);
501 : }
502 : } else {
503 0 : ZVAL_NULL(arr);
504 : }
505 :
506 0 : php_stream_context_set_option(stream->context,
507 : "ssl", "peer_certificate_chain",
508 : arr);
509 0 : zval_dtor(arr);
510 0 : efree(arr);
511 : }
512 : }
513 : }
514 :
515 4 : if (peer_cert) {
516 2 : X509_free(peer_cert);
517 : }
518 : } else {
519 0 : n = errno == EAGAIN ? 0 : -1;
520 : }
521 :
522 4 : return n;
523 :
524 0 : } else if (!cparam->inputs.activate && sslsock->ssl_active) {
525 : /* deactivate - common for server/client */
526 0 : SSL_shutdown(sslsock->ssl_handle);
527 0 : sslsock->ssl_active = 0;
528 : }
529 0 : return -1;
530 : }
531 :
532 : static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock,
533 : php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC)
534 40 : {
535 : int clisock;
536 :
537 40 : xparam->outputs.client = NULL;
538 :
539 40 : clisock = php_network_accept_incoming(sock->s.socket,
540 : xparam->want_textaddr ? &xparam->outputs.textaddr : NULL,
541 : xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL,
542 : xparam->want_addr ? &xparam->outputs.addr : NULL,
543 : xparam->want_addr ? &xparam->outputs.addrlen : NULL,
544 : xparam->inputs.timeout,
545 : xparam->want_errortext ? &xparam->outputs.error_text : NULL,
546 : &xparam->outputs.error_code
547 : TSRMLS_CC);
548 :
549 40 : if (clisock >= 0) {
550 : php_openssl_netstream_data_t *clisockdata;
551 :
552 40 : clisockdata = emalloc(sizeof(*clisockdata));
553 :
554 40 : if (clisockdata == NULL) {
555 0 : closesocket(clisock);
556 : /* technically a fatal error */
557 : } else {
558 : /* copy underlying tcp fields */
559 40 : memset(clisockdata, 0, sizeof(*clisockdata));
560 40 : memcpy(clisockdata, sock, sizeof(clisockdata->s));
561 :
562 40 : clisockdata->s.socket = clisock;
563 :
564 40 : xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+");
565 40 : if (xparam->outputs.client) {
566 40 : xparam->outputs.client->context = stream->context;
567 40 : if (stream->context) {
568 40 : zend_list_addref(stream->context->rsrc_id);
569 : }
570 : }
571 : }
572 :
573 40 : if (xparam->outputs.client && sock->enable_on_connect) {
574 : /* apply crypto */
575 2 : switch (sock->method) {
576 : case STREAM_CRYPTO_METHOD_SSLv23_CLIENT:
577 2 : sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER;
578 2 : break;
579 : case STREAM_CRYPTO_METHOD_SSLv2_CLIENT:
580 0 : sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER;
581 0 : break;
582 : case STREAM_CRYPTO_METHOD_SSLv3_CLIENT:
583 0 : sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER;
584 0 : break;
585 : case STREAM_CRYPTO_METHOD_TLS_CLIENT:
586 0 : sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER;
587 : break;
588 : }
589 :
590 2 : clisockdata->method = sock->method;
591 :
592 2 : if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method,
593 : NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable(
594 : xparam->outputs.client, 1 TSRMLS_CC) < 0) {
595 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
596 :
597 0 : php_stream_close(xparam->outputs.client);
598 0 : xparam->outputs.client = NULL;
599 0 : xparam->outputs.returncode = -1;
600 : }
601 : }
602 : }
603 :
604 40 : return xparam->outputs.client == NULL ? -1 : 0;
605 : }
606 : static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC)
607 243 : {
608 243 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
609 243 : php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam;
610 243 : php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam;
611 :
612 243 : switch (option) {
613 : case PHP_STREAM_OPTION_CHECK_LIVENESS:
614 : {
615 : struct timeval tv;
616 : char buf;
617 3 : int alive = 1;
618 :
619 3 : if (value == -1) {
620 0 : if (sslsock->s.timeout.tv_sec == -1) {
621 0 : tv.tv_sec = FG(default_socket_timeout);
622 0 : tv.tv_usec = 0;
623 : } else {
624 0 : tv = sslsock->connect_timeout;
625 : }
626 : } else {
627 3 : tv.tv_sec = value;
628 3 : tv.tv_usec = 0;
629 : }
630 :
631 3 : if (sslsock->s.socket == -1) {
632 0 : alive = 0;
633 3 : } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) {
634 3 : if (sslsock->ssl_active) {
635 : int n;
636 :
637 : do {
638 0 : n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf));
639 0 : if (n <= 0) {
640 0 : int err = SSL_get_error(sslsock->ssl_handle, n);
641 :
642 0 : if (err == SSL_ERROR_SYSCALL) {
643 0 : alive = php_socket_errno() == EAGAIN;
644 0 : break;
645 : }
646 :
647 0 : if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
648 : /* re-negotiate */
649 : continue;
650 : }
651 :
652 : /* any other problem is a fatal error */
653 0 : alive = 0;
654 : }
655 : /* either peek succeeded or there was an error; we
656 : * have set the alive flag appropriately */
657 0 : break;
658 0 : } while (1);
659 3 : } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) {
660 3 : alive = 0;
661 : }
662 : }
663 3 : return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR;
664 : }
665 :
666 : case PHP_STREAM_OPTION_CRYPTO_API:
667 :
668 8 : switch(cparam->op) {
669 :
670 : case STREAM_XPORT_CRYPTO_OP_SETUP:
671 4 : cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC);
672 4 : return PHP_STREAM_OPTION_RETURN_OK;
673 : break;
674 : case STREAM_XPORT_CRYPTO_OP_ENABLE:
675 4 : cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC);
676 4 : return PHP_STREAM_OPTION_RETURN_OK;
677 : break;
678 : default:
679 : /* fall through */
680 : break;
681 : }
682 :
683 0 : break;
684 :
685 : case PHP_STREAM_OPTION_XPORT_API:
686 211 : switch(xparam->op) {
687 :
688 : case STREAM_XPORT_OP_CONNECT:
689 : case STREAM_XPORT_OP_CONNECT_ASYNC:
690 : /* TODO: Async connects need to check the enable_on_connect option when
691 : * we notice that the connect has actually been established */
692 43 : php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
693 :
694 43 : if ((sslsock->enable_on_connect) &&
695 : ((xparam->outputs.returncode == 0) ||
696 : (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC &&
697 : xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS)))
698 : {
699 2 : if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 ||
700 : php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
701 0 : php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto");
702 0 : xparam->outputs.returncode = -1;
703 : }
704 : }
705 43 : return PHP_STREAM_OPTION_RETURN_OK;
706 :
707 : case STREAM_XPORT_OP_ACCEPT:
708 : /* we need to copy the additional fields that the underlying tcp transport
709 : * doesn't know about */
710 40 : xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC);
711 :
712 :
713 40 : return PHP_STREAM_OPTION_RETURN_OK;
714 :
715 : default:
716 : /* fall through */
717 : break;
718 : }
719 : }
720 :
721 149 : return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC);
722 : }
723 :
724 : static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC)
725 6 : {
726 6 : php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
727 :
728 6 : switch(castas) {
729 : case PHP_STREAM_AS_STDIO:
730 0 : if (sslsock->ssl_active) {
731 0 : return FAILURE;
732 : }
733 0 : if (ret) {
734 0 : *ret = fdopen(sslsock->s.socket, stream->mode);
735 0 : if (*ret) {
736 0 : return SUCCESS;
737 : }
738 0 : return FAILURE;
739 : }
740 0 : return SUCCESS;
741 :
742 : case PHP_STREAM_AS_FD_FOR_SELECT:
743 6 : if (ret) {
744 6 : *(int *)ret = sslsock->s.socket;
745 : }
746 6 : return SUCCESS;
747 :
748 : case PHP_STREAM_AS_FD:
749 : case PHP_STREAM_AS_SOCKETD:
750 0 : if (sslsock->ssl_active) {
751 0 : return FAILURE;
752 : }
753 0 : if (ret) {
754 0 : *(int *)ret = sslsock->s.socket;
755 : }
756 0 : return SUCCESS;
757 : default:
758 0 : return FAILURE;
759 : }
760 : }
761 :
762 : php_stream_ops php_openssl_socket_ops = {
763 : php_openssl_sockop_write, php_openssl_sockop_read,
764 : php_openssl_sockop_close, php_openssl_sockop_flush,
765 : "tcp_socket/ssl",
766 : NULL, /* seek */
767 : php_openssl_sockop_cast,
768 : php_openssl_sockop_stat,
769 : php_openssl_sockop_set_option,
770 : };
771 :
772 106 : static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) {
773 :
774 : php_url *url;
775 :
776 106 : if (ctx) {
777 77 : zval **val = NULL;
778 :
779 77 : if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) {
780 0 : return NULL;
781 : }
782 77 : if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) {
783 0 : convert_to_string_with_converter_ex(val, UG(utf8_conv));
784 0 : return pestrdup(Z_STRVAL_PP(val), is_persistent);
785 : }
786 : }
787 :
788 106 : if (!resourcename) {
789 0 : return NULL;
790 : }
791 :
792 106 : url = php_url_parse_ex(resourcename, resourcenamelen);
793 106 : if (!url) {
794 0 : return NULL;
795 : }
796 :
797 106 : if (url->host) {
798 92 : const char * host = url->host;
799 92 : char * sni = NULL;
800 92 : size_t len = strlen(host);
801 :
802 : /* skip trailing dots */
803 184 : while (len && host[len-1] == '.') {
804 0 : --len;
805 : }
806 :
807 92 : if (len) {
808 92 : sni = pestrndup(host, len, is_persistent);
809 : }
810 :
811 92 : php_url_free(url);
812 92 : return sni;
813 : }
814 :
815 14 : php_url_free(url);
816 14 : return NULL;
817 : }
818 :
819 : php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen,
820 : char *resourcename, long resourcenamelen,
821 : const char *persistent_id, int options, int flags,
822 : struct timeval *timeout,
823 : php_stream_context *context STREAMS_DC TSRMLS_DC)
824 106 : {
825 106 : php_stream *stream = NULL;
826 106 : php_openssl_netstream_data_t *sslsock = NULL;
827 :
828 106 : sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0);
829 106 : memset(sslsock, 0, sizeof(*sslsock));
830 :
831 106 : sslsock->s.is_blocked = 1;
832 : /* this timeout is used by standard stream funcs, therefor it should use the default value */
833 106 : sslsock->s.timeout.tv_sec = FG(default_socket_timeout);
834 106 : sslsock->s.timeout.tv_usec = 0;
835 :
836 : /* use separate timeout for our private funcs */
837 106 : sslsock->connect_timeout.tv_sec = timeout->tv_sec;
838 106 : sslsock->connect_timeout.tv_usec = timeout->tv_usec;
839 :
840 : /* we don't know the socket until we have determined if we are binding or
841 : * connecting */
842 106 : sslsock->s.socket = -1;
843 :
844 : /* Initialize context as NULL */
845 106 : sslsock->ctx = NULL;
846 :
847 106 : stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+");
848 :
849 106 : if (stream == NULL) {
850 0 : pefree(sslsock, persistent_id ? 1 : 0);
851 0 : return NULL;
852 : }
853 :
854 106 : sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC);
855 :
856 106 : if (strncmp(proto, "ssl", protolen) == 0) {
857 4 : sslsock->enable_on_connect = 1;
858 4 : sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT;
859 102 : } else if (strncmp(proto, "sslv2", protolen) == 0) {
860 0 : sslsock->enable_on_connect = 1;
861 0 : sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT;
862 102 : } else if (strncmp(proto, "sslv3", protolen) == 0) {
863 0 : sslsock->enable_on_connect = 1;
864 0 : sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT;
865 102 : } else if (strncmp(proto, "tls", protolen) == 0) {
866 0 : sslsock->enable_on_connect = 1;
867 0 : sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
868 : }
869 :
870 106 : return stream;
871 : }
872 :
873 :
874 :
875 : /*
876 : * Local variables:
877 : * tab-width: 4
878 : * c-basic-offset: 4
879 : * End:
880 : * vim600: noet sw=4 ts=4 fdm=marker
881 : * vim<600: noet sw=4 ts=4
882 : */
|