1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | Copyright (c) 1997-2013 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$ */ 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 40int php_openssl_apply_verification_policy(SSL *ssl, X509 *peer, php_stream *stream TSRMLS_DC); 41SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC); 42int 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 49typedef 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 63php_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 */ 67static int is_http_stream_talking_to_iis(php_stream *stream TSRMLS_DC) 68{ 69 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 zend_hash_internal_pointer_reset(Z_ARRVAL_P(stream->wrapperdata)); 77 while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(stream->wrapperdata), (void**)&tmp)) { 78 79 if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_MICROSOFT_IIS, sizeof(SERVER_MICROSOFT_IIS)-1) == 0) { 80 return 1; 81 } else if (strncasecmp(Z_STRVAL_PP(tmp), SERVER_GOOGLE, sizeof(SERVER_GOOGLE)-1) == 0) { 82 return 1; 83 } 84 85 zend_hash_move_forward(Z_ARRVAL_P(stream->wrapperdata)); 86 } 87 } 88 return 0; 89} 90 91static int handle_ssl_error(php_stream *stream, int nr_bytes, zend_bool is_init TSRMLS_DC) 92{ 93 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 94 int err = SSL_get_error(sslsock->ssl_handle, nr_bytes); 95 char esbuf[512]; 96 smart_str ebuf = {0}; 97 unsigned long ecode; 98 int retry = 1; 99 100 switch(err) { 101 case SSL_ERROR_ZERO_RETURN: 102 /* SSL terminated (but socket may still be active) */ 103 retry = 0; 104 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 errno = EAGAIN; 110 retry = is_init ? 1 : sslsock->s.is_blocked; 111 break; 112 case SSL_ERROR_SYSCALL: 113 if (ERR_peek_error() == 0) { 114 if (nr_bytes == 0) { 115 if (!is_http_stream_talking_to_iis(stream TSRMLS_CC) && ERR_get_error() != 0) { 116 php_error_docref(NULL TSRMLS_CC, E_WARNING, 117 "SSL: fatal protocol error"); 118 } 119 SSL_set_shutdown(sslsock->ssl_handle, SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN); 120 stream->eof = 1; 121 retry = 0; 122 } else { 123 char *estr = php_socket_strerror(php_socket_errno(), NULL, 0); 124 125 php_error_docref(NULL TSRMLS_CC, E_WARNING, 126 "SSL: %s", estr); 127 128 efree(estr); 129 retry = 0; 130 } 131 break; 132 } 133 134 135 /* fall through */ 136 default: 137 /* some other error */ 138 ecode = ERR_get_error(); 139 140 switch (ERR_GET_REASON(ecode)) { 141 case SSL_R_NO_SHARED_CIPHER: 142 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 retry = 0; 144 break; 145 146 default: 147 do { 148 /* NULL is automatically added */ 149 ERR_error_string_n(ecode, esbuf, sizeof(esbuf)); 150 if (ebuf.c) { 151 smart_str_appendc(&ebuf, '\n'); 152 } 153 smart_str_appends(&ebuf, esbuf); 154 } while ((ecode = ERR_get_error()) != 0); 155 156 smart_str_0(&ebuf); 157 158 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 if (ebuf.c) { 164 smart_str_free(&ebuf); 165 } 166 } 167 168 retry = 0; 169 errno = 0; 170 } 171 return retry; 172} 173 174 175static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) 176{ 177 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 178 int didwrite; 179 180 if (sslsock->ssl_active) { 181 int retry = 1; 182 183 do { 184 didwrite = SSL_write(sslsock->ssl_handle, buf, count); 185 186 if (didwrite <= 0) { 187 retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC); 188 } else { 189 break; 190 } 191 } while(retry); 192 193 if (didwrite > 0) { 194 php_stream_notify_progress_increment(stream->context, didwrite, 0); 195 } 196 } else { 197 didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC); 198 } 199 200 if (didwrite < 0) { 201 didwrite = 0; 202 } 203 204 return didwrite; 205} 206 207static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) 208{ 209 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 210 int nr_bytes = 0; 211 212 if (sslsock->ssl_active) { 213 int retry = 1; 214 215 do { 216 nr_bytes = SSL_read(sslsock->ssl_handle, buf, count); 217 218 if (nr_bytes <= 0) { 219 retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC); 220 stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle)); 221 222 } else { 223 /* we got the data */ 224 break; 225 } 226 } while (retry); 227 228 if (nr_bytes > 0) { 229 php_stream_notify_progress_increment(stream->context, nr_bytes, 0); 230 } 231 } 232 else 233 { 234 nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC); 235 } 236 237 if (nr_bytes < 0) { 238 nr_bytes = 0; 239 } 240 241 return nr_bytes; 242} 243 244 245static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC) 246{ 247 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 248#ifdef PHP_WIN32 249 int n; 250#endif 251 if (close_handle) { 252 if (sslsock->ssl_active) { 253 SSL_shutdown(sslsock->ssl_handle); 254 sslsock->ssl_active = 0; 255 } 256 if (sslsock->ssl_handle) { 257 SSL_free(sslsock->ssl_handle); 258 sslsock->ssl_handle = NULL; 259 } 260 if (sslsock->ctx) { 261 SSL_CTX_free(sslsock->ctx); 262 sslsock->ctx = NULL; 263 } 264#ifdef PHP_WIN32 265 if (sslsock->s.socket == -1) 266 sslsock->s.socket = SOCK_ERR; 267#endif 268 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 indefinitely. 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 closesocket(sslsock->s.socket); 284 sslsock->s.socket = SOCK_ERR; 285 } 286 } 287 288 if (sslsock->sni) { 289 pefree(sslsock->sni, php_stream_is_persistent(stream)); 290 } 291 pefree(sslsock, php_stream_is_persistent(stream)); 292 293 return 0; 294} 295 296static int php_openssl_sockop_flush(php_stream *stream TSRMLS_DC) 297{ 298 return php_stream_socket_ops.flush(stream TSRMLS_CC); 299} 300 301static int php_openssl_sockop_stat(php_stream *stream, php_stream_statbuf *ssb TSRMLS_DC) 302{ 303 return php_stream_socket_ops.stat(stream, ssb TSRMLS_CC); 304} 305 306 307static 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{ 312 SSL_METHOD *method; 313 long ssl_ctx_options = SSL_OP_ALL; 314 315 if (sslsock->ssl_handle) { 316 if (sslsock->s.is_blocked) { 317 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream"); 318 return -1; 319 } else { 320 return 0; 321 } 322 } 323 324 /* need to do slightly different things, based on client/server method, 325 * so lets remember which method was selected */ 326 327 switch (cparam->inputs.method) { 328 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: 329 sslsock->is_client = 1; 330 method = SSLv23_client_method(); 331 break; 332 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: 333#ifdef OPENSSL_NO_SSL2 334 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 335 return -1; 336#else 337 sslsock->is_client = 1; 338 method = SSLv2_client_method(); 339 break; 340#endif 341 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: 342 sslsock->is_client = 1; 343 method = SSLv3_client_method(); 344 break; 345 case STREAM_CRYPTO_METHOD_TLS_CLIENT: 346 sslsock->is_client = 1; 347 method = TLSv1_client_method(); 348 break; 349 case STREAM_CRYPTO_METHOD_SSLv23_SERVER: 350 sslsock->is_client = 0; 351 method = SSLv23_server_method(); 352 break; 353 case STREAM_CRYPTO_METHOD_SSLv3_SERVER: 354 sslsock->is_client = 0; 355 method = SSLv3_server_method(); 356 break; 357 case STREAM_CRYPTO_METHOD_SSLv2_SERVER: 358#ifdef OPENSSL_NO_SSL2 359 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 360 return -1; 361#else 362 sslsock->is_client = 0; 363 method = SSLv2_server_method(); 364 break; 365#endif 366 case STREAM_CRYPTO_METHOD_TLS_SERVER: 367 sslsock->is_client = 0; 368 method = TLSv1_server_method(); 369 break; 370 default: 371 return -1; 372 373 } 374 375 sslsock->ctx = SSL_CTX_new(method); 376 if (sslsock->ctx == NULL) { 377 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context"); 378 return -1; 379 } 380 381#if OPENSSL_VERSION_NUMBER >= 0x0090605fL 382 ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; 383#endif 384 SSL_CTX_set_options(sslsock->ctx, ssl_ctx_options); 385 386#if OPENSSL_VERSION_NUMBER >= 0x0090806fL 387 { 388 zval **val; 389 390 if (stream->context && SUCCESS == php_stream_context_get_option( 391 stream->context, "ssl", "no_ticket", &val) && 392 zval_is_true(*val)) { 393 SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET); 394 } 395 } 396#endif 397 398#if OPENSSL_VERSION_NUMBER >= 0x10000000L 399 { 400 zval **val; 401 402 if (stream->context && SUCCESS == php_stream_context_get_option( 403 stream->context, "ssl", "disable_compression", &val) && 404 zval_is_true(*val)) { 405 SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_COMPRESSION); 406 } 407 } 408#endif 409 410 sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC); 411 if (sslsock->ssl_handle == NULL) { 412 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle"); 413 SSL_CTX_free(sslsock->ctx); 414 sslsock->ctx = NULL; 415 return -1; 416 } 417 418 if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { 419 handle_ssl_error(stream, 0, 1 TSRMLS_CC); 420 } 421 422 if (cparam->inputs.session) { 423 if (cparam->inputs.session->ops != &php_openssl_socket_ops) { 424 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream"); 425 } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) { 426 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized"); 427 } else { 428 SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle); 429 } 430 } 431 return 0; 432} 433 434static inline int php_openssl_enable_crypto(php_stream *stream, 435 php_openssl_netstream_data_t *sslsock, 436 php_stream_xport_crypto_param *cparam 437 TSRMLS_DC) 438{ 439 int n, retry = 1; 440 441 if (cparam->inputs.activate && !sslsock->ssl_active) { 442 struct timeval start_time, 443 *timeout; 444 int blocked = sslsock->s.is_blocked, 445 has_timeout = 0; 446 447#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) 448 if (sslsock->is_client && sslsock->sni) { 449 SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni); 450 } 451#endif 452 453 if (!sslsock->state_set) { 454 if (sslsock->is_client) { 455 SSL_set_connect_state(sslsock->ssl_handle); 456 } else { 457 SSL_set_accept_state(sslsock->ssl_handle); 458 } 459 sslsock->state_set = 1; 460 } 461 462 if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) { 463 sslsock->s.is_blocked = 0; 464 } 465 466 timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout; 467 has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec); 468 /* gettimeofday is not monotonic; using it here is not strictly correct */ 469 if (has_timeout) { 470 gettimeofday(&start_time, NULL); 471 } 472 473 do { 474 struct timeval cur_time, 475 elapsed_time; 476 477 if (sslsock->is_client) { 478 n = SSL_connect(sslsock->ssl_handle); 479 } else { 480 n = SSL_accept(sslsock->ssl_handle); 481 } 482 483 if (has_timeout) { 484 gettimeofday(&cur_time, NULL); 485 elapsed_time.tv_sec = cur_time.tv_sec - start_time.tv_sec; 486 elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec; 487 if (cur_time.tv_usec < start_time.tv_usec) { 488 elapsed_time.tv_sec -= 1L; 489 elapsed_time.tv_usec += 1000000L; 490 } 491 492 if (elapsed_time.tv_sec > timeout->tv_sec || 493 (elapsed_time.tv_sec == timeout->tv_sec && 494 elapsed_time.tv_usec > timeout->tv_usec)) { 495 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout"); 496 return -1; 497 } 498 } 499 500 if (n <= 0) { 501 /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */ 502 retry = handle_ssl_error(stream, n, blocked TSRMLS_CC); 503 if (retry) { 504 /* wait until something interesting happens in the socket. It may be a 505 * timeout. Also consider the unlikely of possibility of a write block */ 506 int err = SSL_get_error(sslsock->ssl_handle, n); 507 struct timeval left_time; 508 509 if (has_timeout) { 510 left_time.tv_sec = timeout->tv_sec - elapsed_time.tv_sec; 511 left_time.tv_usec = timeout->tv_usec - elapsed_time.tv_usec; 512 if (timeout->tv_usec < elapsed_time.tv_usec) { 513 left_time.tv_sec -= 1L; 514 left_time.tv_usec += 1000000L; 515 } 516 } 517 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ? 518 (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL); 519 } 520 } else { 521 retry = 0; 522 } 523 } while (retry); 524 525 if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) { 526 sslsock->s.is_blocked = blocked; 527 } 528 529 if (n == 1) { 530 X509 *peer_cert; 531 532 peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle); 533 534 if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) { 535 SSL_shutdown(sslsock->ssl_handle); 536 n = -1; 537 } else { 538 sslsock->ssl_active = 1; 539 540 /* allow the script to capture the peer cert 541 * and/or the certificate chain */ 542 if (stream->context) { 543 zval **val, *zcert; 544 545 if (SUCCESS == php_stream_context_get_option( 546 stream->context, "ssl", 547 "capture_peer_cert", &val) && 548 zval_is_true(*val)) { 549 MAKE_STD_ZVAL(zcert); 550 ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 551 php_openssl_get_x509_list_id() TSRMLS_CC)); 552 php_stream_context_set_option(stream->context, 553 "ssl", "peer_certificate", 554 zcert); 555 peer_cert = NULL; 556 FREE_ZVAL(zcert); 557 } 558 559 if (SUCCESS == php_stream_context_get_option( 560 stream->context, "ssl", 561 "capture_peer_cert_chain", &val) && 562 zval_is_true(*val)) { 563 zval *arr; 564 STACK_OF(X509) *chain; 565 566 MAKE_STD_ZVAL(arr); 567 chain = SSL_get_peer_cert_chain( 568 sslsock->ssl_handle); 569 570 if (chain && sk_X509_num(chain) > 0) { 571 int i; 572 array_init(arr); 573 574 for (i = 0; i < sk_X509_num(chain); i++) { 575 X509 *mycert = X509_dup( 576 sk_X509_value(chain, i)); 577 MAKE_STD_ZVAL(zcert); 578 ZVAL_RESOURCE(zcert, 579 zend_list_insert(mycert, 580 php_openssl_get_x509_list_id() TSRMLS_CC)); 581 add_next_index_zval(arr, zcert); 582 } 583 584 } else { 585 ZVAL_NULL(arr); 586 } 587 588 php_stream_context_set_option(stream->context, 589 "ssl", "peer_certificate_chain", 590 arr); 591 zval_dtor(arr); 592 efree(arr); 593 } 594 } 595 } 596 597 if (peer_cert) { 598 X509_free(peer_cert); 599 } 600 } else { 601 n = errno == EAGAIN ? 0 : -1; 602 } 603 604 return n; 605 606 } else if (!cparam->inputs.activate && sslsock->ssl_active) { 607 /* deactivate - common for server/client */ 608 SSL_shutdown(sslsock->ssl_handle); 609 sslsock->ssl_active = 0; 610 } 611 return -1; 612} 613 614static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock, 615 php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC) 616{ 617 int clisock; 618 619 xparam->outputs.client = NULL; 620 621 clisock = php_network_accept_incoming(sock->s.socket, 622 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, 623 xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, 624 xparam->want_addr ? &xparam->outputs.addr : NULL, 625 xparam->want_addr ? &xparam->outputs.addrlen : NULL, 626 xparam->inputs.timeout, 627 xparam->want_errortext ? &xparam->outputs.error_text : NULL, 628 &xparam->outputs.error_code 629 TSRMLS_CC); 630 631 if (clisock >= 0) { 632 php_openssl_netstream_data_t *clisockdata; 633 634 clisockdata = emalloc(sizeof(*clisockdata)); 635 636 if (clisockdata == NULL) { 637 closesocket(clisock); 638 /* technically a fatal error */ 639 } else { 640 /* copy underlying tcp fields */ 641 memset(clisockdata, 0, sizeof(*clisockdata)); 642 memcpy(clisockdata, sock, sizeof(clisockdata->s)); 643 644 clisockdata->s.socket = clisock; 645 646 xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+"); 647 if (xparam->outputs.client) { 648 xparam->outputs.client->context = stream->context; 649 if (stream->context) { 650 zend_list_addref(stream->context->rsrc_id); 651 } 652 } 653 } 654 655 if (xparam->outputs.client && sock->enable_on_connect) { 656 /* apply crypto */ 657 switch (sock->method) { 658 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: 659 sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER; 660 break; 661 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: 662 sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER; 663 break; 664 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: 665 sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER; 666 break; 667 case STREAM_CRYPTO_METHOD_TLS_CLIENT: 668 sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER; 669 break; 670 default: 671 break; 672 } 673 674 clisockdata->method = sock->method; 675 676 if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method, 677 NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable( 678 xparam->outputs.client, 1 TSRMLS_CC) < 0) { 679 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); 680 681 php_stream_close(xparam->outputs.client); 682 xparam->outputs.client = NULL; 683 xparam->outputs.returncode = -1; 684 } 685 } 686 } 687 688 return xparam->outputs.client == NULL ? -1 : 0; 689} 690static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) 691{ 692 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 693 php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam; 694 php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam; 695 696 switch (option) { 697 case PHP_STREAM_OPTION_CHECK_LIVENESS: 698 { 699 struct timeval tv; 700 char buf; 701 int alive = 1; 702 703 if (value == -1) { 704 if (sslsock->s.timeout.tv_sec == -1) { 705 tv.tv_sec = FG(default_socket_timeout); 706 tv.tv_usec = 0; 707 } else { 708 tv = sslsock->connect_timeout; 709 } 710 } else { 711 tv.tv_sec = value; 712 tv.tv_usec = 0; 713 } 714 715 if (sslsock->s.socket == -1) { 716 alive = 0; 717 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) { 718 if (sslsock->ssl_active) { 719 int n; 720 721 do { 722 n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf)); 723 if (n <= 0) { 724 int err = SSL_get_error(sslsock->ssl_handle, n); 725 726 if (err == SSL_ERROR_SYSCALL) { 727 alive = php_socket_errno() == EAGAIN; 728 break; 729 } 730 731 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 732 /* re-negotiate */ 733 continue; 734 } 735 736 /* any other problem is a fatal error */ 737 alive = 0; 738 } 739 /* either peek succeeded or there was an error; we 740 * have set the alive flag appropriately */ 741 break; 742 } while (1); 743 } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) { 744 alive = 0; 745 } 746 } 747 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; 748 } 749 750 case PHP_STREAM_OPTION_CRYPTO_API: 751 752 switch(cparam->op) { 753 754 case STREAM_XPORT_CRYPTO_OP_SETUP: 755 cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC); 756 return PHP_STREAM_OPTION_RETURN_OK; 757 break; 758 case STREAM_XPORT_CRYPTO_OP_ENABLE: 759 cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC); 760 return PHP_STREAM_OPTION_RETURN_OK; 761 break; 762 default: 763 /* fall through */ 764 break; 765 } 766 767 break; 768 769 case PHP_STREAM_OPTION_XPORT_API: 770 switch(xparam->op) { 771 772 case STREAM_XPORT_OP_CONNECT: 773 case STREAM_XPORT_OP_CONNECT_ASYNC: 774 /* TODO: Async connects need to check the enable_on_connect option when 775 * we notice that the connect has actually been established */ 776 php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); 777 778 if ((sslsock->enable_on_connect) && 779 ((xparam->outputs.returncode == 0) || 780 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 781 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS))) 782 { 783 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 || 784 php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) { 785 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); 786 xparam->outputs.returncode = -1; 787 } 788 } 789 return PHP_STREAM_OPTION_RETURN_OK; 790 791 case STREAM_XPORT_OP_ACCEPT: 792 /* we need to copy the additional fields that the underlying tcp transport 793 * doesn't know about */ 794 xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC); 795 796 797 return PHP_STREAM_OPTION_RETURN_OK; 798 799 default: 800 /* fall through */ 801 break; 802 } 803 } 804 805 return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); 806} 807 808static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) 809{ 810 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 811 812 switch(castas) { 813 case PHP_STREAM_AS_STDIO: 814 if (sslsock->ssl_active) { 815 return FAILURE; 816 } 817 if (ret) { 818 *ret = fdopen(sslsock->s.socket, stream->mode); 819 if (*ret) { 820 return SUCCESS; 821 } 822 return FAILURE; 823 } 824 return SUCCESS; 825 826 case PHP_STREAM_AS_FD_FOR_SELECT: 827 if (ret) { 828 *(int *)ret = sslsock->s.socket; 829 } 830 return SUCCESS; 831 832 case PHP_STREAM_AS_FD: 833 case PHP_STREAM_AS_SOCKETD: 834 if (sslsock->ssl_active) { 835 return FAILURE; 836 } 837 if (ret) { 838 *(int *)ret = sslsock->s.socket; 839 } 840 return SUCCESS; 841 default: 842 return FAILURE; 843 } 844} 845 846php_stream_ops php_openssl_socket_ops = { 847 php_openssl_sockop_write, php_openssl_sockop_read, 848 php_openssl_sockop_close, php_openssl_sockop_flush, 849 "tcp_socket/ssl", 850 NULL, /* seek */ 851 php_openssl_sockop_cast, 852 php_openssl_sockop_stat, 853 php_openssl_sockop_set_option, 854}; 855 856static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) { 857 858 php_url *url; 859 860 if (ctx) { 861 zval **val = NULL; 862 863 if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) { 864 return NULL; 865 } 866 if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) { 867 convert_to_string_ex(val); 868 return pestrdup(Z_STRVAL_PP(val), is_persistent); 869 } 870 } 871 872 if (!resourcename) { 873 return NULL; 874 } 875 876 url = php_url_parse_ex(resourcename, resourcenamelen); 877 if (!url) { 878 return NULL; 879 } 880 881 if (url->host) { 882 const char * host = url->host; 883 char * sni = NULL; 884 size_t len = strlen(host); 885 886 /* skip trailing dots */ 887 while (len && host[len-1] == '.') { 888 --len; 889 } 890 891 if (len) { 892 sni = pestrndup(host, len, is_persistent); 893 } 894 895 php_url_free(url); 896 return sni; 897 } 898 899 php_url_free(url); 900 return NULL; 901} 902 903php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen, 904 char *resourcename, long resourcenamelen, 905 const char *persistent_id, int options, int flags, 906 struct timeval *timeout, 907 php_stream_context *context STREAMS_DC TSRMLS_DC) 908{ 909 php_stream *stream = NULL; 910 php_openssl_netstream_data_t *sslsock = NULL; 911 912 sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0); 913 memset(sslsock, 0, sizeof(*sslsock)); 914 915 sslsock->s.is_blocked = 1; 916 /* this timeout is used by standard stream funcs, therefor it should use the default value */ 917 sslsock->s.timeout.tv_sec = FG(default_socket_timeout); 918 sslsock->s.timeout.tv_usec = 0; 919 920 /* use separate timeout for our private funcs */ 921 sslsock->connect_timeout.tv_sec = timeout->tv_sec; 922 sslsock->connect_timeout.tv_usec = timeout->tv_usec; 923 924 /* we don't know the socket until we have determined if we are binding or 925 * connecting */ 926 sslsock->s.socket = -1; 927 928 /* Initialize context as NULL */ 929 sslsock->ctx = NULL; 930 931 stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+"); 932 933 if (stream == NULL) { 934 pefree(sslsock, persistent_id ? 1 : 0); 935 return NULL; 936 } 937 938 sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC); 939 940 if (strncmp(proto, "ssl", protolen) == 0) { 941 sslsock->enable_on_connect = 1; 942 sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT; 943 } else if (strncmp(proto, "sslv2", protolen) == 0) { 944#ifdef OPENSSL_NO_SSL2 945 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 946 return NULL; 947#else 948 sslsock->enable_on_connect = 1; 949 sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; 950#endif 951 } else if (strncmp(proto, "sslv3", protolen) == 0) { 952 sslsock->enable_on_connect = 1; 953 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; 954 } else if (strncmp(proto, "tls", protolen) == 0) { 955 sslsock->enable_on_connect = 1; 956 sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 957 } 958 959 return stream; 960} 961 962 963 964/* 965 * Local variables: 966 * tab-width: 4 967 * c-basic-offset: 4 968 * End: 969 * vim600: noet sw=4 ts=4 fdm=marker 970 * vim<600: noet sw=4 ts=4 971 */ 972