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 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 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 314 if (sslsock->ssl_handle) { 315 if (sslsock->s.is_blocked) { 316 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS already set-up for this stream"); 317 return -1; 318 } else { 319 return 0; 320 } 321 } 322 323 /* need to do slightly different things, based on client/server method, 324 * so lets remember which method was selected */ 325 326 switch (cparam->inputs.method) { 327 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: 328 sslsock->is_client = 1; 329 method = SSLv23_client_method(); 330 break; 331 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: 332#ifdef OPENSSL_NO_SSL2 333 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 334 return -1; 335#else 336 sslsock->is_client = 1; 337 method = SSLv2_client_method(); 338 break; 339#endif 340 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: 341 sslsock->is_client = 1; 342 method = SSLv3_client_method(); 343 break; 344 case STREAM_CRYPTO_METHOD_TLS_CLIENT: 345 sslsock->is_client = 1; 346 method = TLSv1_client_method(); 347 break; 348 case STREAM_CRYPTO_METHOD_SSLv23_SERVER: 349 sslsock->is_client = 0; 350 method = SSLv23_server_method(); 351 break; 352 case STREAM_CRYPTO_METHOD_SSLv3_SERVER: 353 sslsock->is_client = 0; 354 method = SSLv3_server_method(); 355 break; 356 case STREAM_CRYPTO_METHOD_SSLv2_SERVER: 357#ifdef OPENSSL_NO_SSL2 358 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 359 return -1; 360#else 361 sslsock->is_client = 0; 362 method = SSLv2_server_method(); 363 break; 364#endif 365 case STREAM_CRYPTO_METHOD_TLS_SERVER: 366 sslsock->is_client = 0; 367 method = TLSv1_server_method(); 368 break; 369 default: 370 return -1; 371 372 } 373 374 sslsock->ctx = SSL_CTX_new(method); 375 if (sslsock->ctx == NULL) { 376 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL context"); 377 return -1; 378 } 379 380 SSL_CTX_set_options(sslsock->ctx, SSL_OP_ALL); 381 382#if OPENSSL_VERSION_NUMBER >= 0x0090806fL 383 { 384 zval **val; 385 386 if (stream->context && SUCCESS == php_stream_context_get_option( 387 stream->context, "ssl", "no_ticket", &val) && 388 zval_is_true(*val)) { 389 SSL_CTX_set_options(sslsock->ctx, SSL_OP_NO_TICKET); 390 } 391 } 392#endif 393 394 sslsock->ssl_handle = php_SSL_new_from_context(sslsock->ctx, stream TSRMLS_CC); 395 if (sslsock->ssl_handle == NULL) { 396 php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create an SSL handle"); 397 SSL_CTX_free(sslsock->ctx); 398 sslsock->ctx = NULL; 399 return -1; 400 } 401 402 if (!SSL_set_fd(sslsock->ssl_handle, sslsock->s.socket)) { 403 handle_ssl_error(stream, 0, 1 TSRMLS_CC); 404 } 405 406 if (cparam->inputs.session) { 407 if (cparam->inputs.session->ops != &php_openssl_socket_ops) { 408 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied session stream must be an SSL enabled stream"); 409 } else if (((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle == NULL) { 410 php_error_docref(NULL TSRMLS_CC, E_WARNING, "supplied SSL session stream is not initialized"); 411 } else { 412 SSL_copy_session_id(sslsock->ssl_handle, ((php_openssl_netstream_data_t*)cparam->inputs.session->abstract)->ssl_handle); 413 } 414 } 415 return 0; 416} 417 418static inline int php_openssl_enable_crypto(php_stream *stream, 419 php_openssl_netstream_data_t *sslsock, 420 php_stream_xport_crypto_param *cparam 421 TSRMLS_DC) 422{ 423 int n, retry = 1; 424 425 if (cparam->inputs.activate && !sslsock->ssl_active) { 426 struct timeval start_time, 427 *timeout; 428 int blocked = sslsock->s.is_blocked, 429 has_timeout = 0; 430 431#if OPENSSL_VERSION_NUMBER >= 0x0090806fL && !defined(OPENSSL_NO_TLSEXT) 432 if (sslsock->is_client && sslsock->sni) { 433 SSL_set_tlsext_host_name(sslsock->ssl_handle, sslsock->sni); 434 } 435#endif 436 437 if (!sslsock->state_set) { 438 if (sslsock->is_client) { 439 SSL_set_connect_state(sslsock->ssl_handle); 440 } else { 441 SSL_set_accept_state(sslsock->ssl_handle); 442 } 443 sslsock->state_set = 1; 444 } 445 446 if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) { 447 sslsock->s.is_blocked = 0; 448 } 449 450 timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout; 451 has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec); 452 /* gettimeofday is not monotonic; using it here is not strictly correct */ 453 if (has_timeout) { 454 gettimeofday(&start_time, NULL); 455 } 456 457 do { 458 struct timeval cur_time, 459 elapsed_time; 460 461 if (sslsock->is_client) { 462 n = SSL_connect(sslsock->ssl_handle); 463 } else { 464 n = SSL_accept(sslsock->ssl_handle); 465 } 466 467 if (has_timeout) { 468 gettimeofday(&cur_time, NULL); 469 elapsed_time.tv_sec = cur_time.tv_sec - start_time.tv_sec; 470 elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec; 471 if (cur_time.tv_usec < start_time.tv_usec) { 472 elapsed_time.tv_sec -= 1L; 473 elapsed_time.tv_usec += 1000000L; 474 } 475 476 if (elapsed_time.tv_sec > timeout->tv_sec || 477 (elapsed_time.tv_sec == timeout->tv_sec && 478 elapsed_time.tv_usec > timeout->tv_usec)) { 479 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: crypto enabling timeout"); 480 return -1; 481 } 482 } 483 484 if (n <= 0) { 485 /* in case of SSL_ERROR_WANT_READ/WRITE, do not retry in non-blocking mode */ 486 retry = handle_ssl_error(stream, n, blocked TSRMLS_CC); 487 if (retry) { 488 /* wait until something interesting happens in the socket. It may be a 489 * timeout. Also consider the unlikely of possibility of a write block */ 490 int err = SSL_get_error(sslsock->ssl_handle, n); 491 struct timeval left_time; 492 493 if (has_timeout) { 494 left_time.tv_sec = timeout->tv_sec - elapsed_time.tv_sec; 495 left_time.tv_usec = timeout->tv_usec - elapsed_time.tv_usec; 496 if (timeout->tv_usec < elapsed_time.tv_usec) { 497 left_time.tv_sec -= 1L; 498 left_time.tv_usec += 1000000L; 499 } 500 } 501 php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ? 502 (POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL); 503 } 504 } else { 505 retry = 0; 506 } 507 } while (retry); 508 509 if (sslsock->s.is_blocked != blocked && SUCCESS == php_set_sock_blocking(sslsock->s.socket, blocked TSRMLS_CC)) { 510 sslsock->s.is_blocked = blocked; 511 } 512 513 if (n == 1) { 514 X509 *peer_cert; 515 516 peer_cert = SSL_get_peer_certificate(sslsock->ssl_handle); 517 518 if (FAILURE == php_openssl_apply_verification_policy(sslsock->ssl_handle, peer_cert, stream TSRMLS_CC)) { 519 SSL_shutdown(sslsock->ssl_handle); 520 n = -1; 521 } else { 522 sslsock->ssl_active = 1; 523 524 /* allow the script to capture the peer cert 525 * and/or the certificate chain */ 526 if (stream->context) { 527 zval **val, *zcert; 528 529 if (SUCCESS == php_stream_context_get_option( 530 stream->context, "ssl", 531 "capture_peer_cert", &val) && 532 zval_is_true(*val)) { 533 MAKE_STD_ZVAL(zcert); 534 ZVAL_RESOURCE(zcert, zend_list_insert(peer_cert, 535 php_openssl_get_x509_list_id())); 536 php_stream_context_set_option(stream->context, 537 "ssl", "peer_certificate", 538 zcert); 539 peer_cert = NULL; 540 FREE_ZVAL(zcert); 541 } 542 543 if (SUCCESS == php_stream_context_get_option( 544 stream->context, "ssl", 545 "capture_peer_cert_chain", &val) && 546 zval_is_true(*val)) { 547 zval *arr; 548 STACK_OF(X509) *chain; 549 550 MAKE_STD_ZVAL(arr); 551 chain = SSL_get_peer_cert_chain( 552 sslsock->ssl_handle); 553 554 if (chain && sk_X509_num(chain) > 0) { 555 int i; 556 array_init(arr); 557 558 for (i = 0; i < sk_X509_num(chain); i++) { 559 X509 *mycert = X509_dup( 560 sk_X509_value(chain, i)); 561 MAKE_STD_ZVAL(zcert); 562 ZVAL_RESOURCE(zcert, 563 zend_list_insert(mycert, 564 php_openssl_get_x509_list_id())); 565 add_next_index_zval(arr, zcert); 566 } 567 568 } else { 569 ZVAL_NULL(arr); 570 } 571 572 php_stream_context_set_option(stream->context, 573 "ssl", "peer_certificate_chain", 574 arr); 575 zval_dtor(arr); 576 efree(arr); 577 } 578 } 579 } 580 581 if (peer_cert) { 582 X509_free(peer_cert); 583 } 584 } else { 585 n = errno == EAGAIN ? 0 : -1; 586 } 587 588 return n; 589 590 } else if (!cparam->inputs.activate && sslsock->ssl_active) { 591 /* deactivate - common for server/client */ 592 SSL_shutdown(sslsock->ssl_handle); 593 sslsock->ssl_active = 0; 594 } 595 return -1; 596} 597 598static inline int php_openssl_tcp_sockop_accept(php_stream *stream, php_openssl_netstream_data_t *sock, 599 php_stream_xport_param *xparam STREAMS_DC TSRMLS_DC) 600{ 601 int clisock; 602 603 xparam->outputs.client = NULL; 604 605 clisock = php_network_accept_incoming(sock->s.socket, 606 xparam->want_textaddr ? &xparam->outputs.textaddr : NULL, 607 xparam->want_textaddr ? &xparam->outputs.textaddrlen : NULL, 608 xparam->want_addr ? &xparam->outputs.addr : NULL, 609 xparam->want_addr ? &xparam->outputs.addrlen : NULL, 610 xparam->inputs.timeout, 611 xparam->want_errortext ? &xparam->outputs.error_text : NULL, 612 &xparam->outputs.error_code 613 TSRMLS_CC); 614 615 if (clisock >= 0) { 616 php_openssl_netstream_data_t *clisockdata; 617 618 clisockdata = emalloc(sizeof(*clisockdata)); 619 620 if (clisockdata == NULL) { 621 closesocket(clisock); 622 /* technically a fatal error */ 623 } else { 624 /* copy underlying tcp fields */ 625 memset(clisockdata, 0, sizeof(*clisockdata)); 626 memcpy(clisockdata, sock, sizeof(clisockdata->s)); 627 628 clisockdata->s.socket = clisock; 629 630 xparam->outputs.client = php_stream_alloc_rel(stream->ops, clisockdata, NULL, "r+"); 631 if (xparam->outputs.client) { 632 xparam->outputs.client->context = stream->context; 633 if (stream->context) { 634 zend_list_addref(stream->context->rsrc_id); 635 } 636 } 637 } 638 639 if (xparam->outputs.client && sock->enable_on_connect) { 640 /* apply crypto */ 641 switch (sock->method) { 642 case STREAM_CRYPTO_METHOD_SSLv23_CLIENT: 643 sock->method = STREAM_CRYPTO_METHOD_SSLv23_SERVER; 644 break; 645 case STREAM_CRYPTO_METHOD_SSLv2_CLIENT: 646 sock->method = STREAM_CRYPTO_METHOD_SSLv2_SERVER; 647 break; 648 case STREAM_CRYPTO_METHOD_SSLv3_CLIENT: 649 sock->method = STREAM_CRYPTO_METHOD_SSLv3_SERVER; 650 break; 651 case STREAM_CRYPTO_METHOD_TLS_CLIENT: 652 sock->method = STREAM_CRYPTO_METHOD_TLS_SERVER; 653 break; 654 default: 655 break; 656 } 657 658 clisockdata->method = sock->method; 659 660 if (php_stream_xport_crypto_setup(xparam->outputs.client, clisockdata->method, 661 NULL TSRMLS_CC) < 0 || php_stream_xport_crypto_enable( 662 xparam->outputs.client, 1 TSRMLS_CC) < 0) { 663 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); 664 665 php_stream_close(xparam->outputs.client); 666 xparam->outputs.client = NULL; 667 xparam->outputs.returncode = -1; 668 } 669 } 670 } 671 672 return xparam->outputs.client == NULL ? -1 : 0; 673} 674static int php_openssl_sockop_set_option(php_stream *stream, int option, int value, void *ptrparam TSRMLS_DC) 675{ 676 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 677 php_stream_xport_crypto_param *cparam = (php_stream_xport_crypto_param *)ptrparam; 678 php_stream_xport_param *xparam = (php_stream_xport_param *)ptrparam; 679 680 switch (option) { 681 case PHP_STREAM_OPTION_CHECK_LIVENESS: 682 { 683 struct timeval tv; 684 char buf; 685 int alive = 1; 686 687 if (value == -1) { 688 if (sslsock->s.timeout.tv_sec == -1) { 689 tv.tv_sec = FG(default_socket_timeout); 690 tv.tv_usec = 0; 691 } else { 692 tv = sslsock->connect_timeout; 693 } 694 } else { 695 tv.tv_sec = value; 696 tv.tv_usec = 0; 697 } 698 699 if (sslsock->s.socket == -1) { 700 alive = 0; 701 } else if (php_pollfd_for(sslsock->s.socket, PHP_POLLREADABLE|POLLPRI, &tv) > 0) { 702 if (sslsock->ssl_active) { 703 int n; 704 705 do { 706 n = SSL_peek(sslsock->ssl_handle, &buf, sizeof(buf)); 707 if (n <= 0) { 708 int err = SSL_get_error(sslsock->ssl_handle, n); 709 710 if (err == SSL_ERROR_SYSCALL) { 711 alive = php_socket_errno() == EAGAIN; 712 break; 713 } 714 715 if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { 716 /* re-negotiate */ 717 continue; 718 } 719 720 /* any other problem is a fatal error */ 721 alive = 0; 722 } 723 /* either peek succeeded or there was an error; we 724 * have set the alive flag appropriately */ 725 break; 726 } while (1); 727 } else if (0 == recv(sslsock->s.socket, &buf, sizeof(buf), MSG_PEEK) && php_socket_errno() != EAGAIN) { 728 alive = 0; 729 } 730 } 731 return alive ? PHP_STREAM_OPTION_RETURN_OK : PHP_STREAM_OPTION_RETURN_ERR; 732 } 733 734 case PHP_STREAM_OPTION_CRYPTO_API: 735 736 switch(cparam->op) { 737 738 case STREAM_XPORT_CRYPTO_OP_SETUP: 739 cparam->outputs.returncode = php_openssl_setup_crypto(stream, sslsock, cparam TSRMLS_CC); 740 return PHP_STREAM_OPTION_RETURN_OK; 741 break; 742 case STREAM_XPORT_CRYPTO_OP_ENABLE: 743 cparam->outputs.returncode = php_openssl_enable_crypto(stream, sslsock, cparam TSRMLS_CC); 744 return PHP_STREAM_OPTION_RETURN_OK; 745 break; 746 default: 747 /* fall through */ 748 break; 749 } 750 751 break; 752 753 case PHP_STREAM_OPTION_XPORT_API: 754 switch(xparam->op) { 755 756 case STREAM_XPORT_OP_CONNECT: 757 case STREAM_XPORT_OP_CONNECT_ASYNC: 758 /* TODO: Async connects need to check the enable_on_connect option when 759 * we notice that the connect has actually been established */ 760 php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); 761 762 if ((sslsock->enable_on_connect) && 763 ((xparam->outputs.returncode == 0) || 764 (xparam->op == STREAM_XPORT_OP_CONNECT_ASYNC && 765 xparam->outputs.returncode == 1 && xparam->outputs.error_code == EINPROGRESS))) 766 { 767 if (php_stream_xport_crypto_setup(stream, sslsock->method, NULL TSRMLS_CC) < 0 || 768 php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) { 769 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to enable crypto"); 770 xparam->outputs.returncode = -1; 771 } 772 } 773 return PHP_STREAM_OPTION_RETURN_OK; 774 775 case STREAM_XPORT_OP_ACCEPT: 776 /* we need to copy the additional fields that the underlying tcp transport 777 * doesn't know about */ 778 xparam->outputs.returncode = php_openssl_tcp_sockop_accept(stream, sslsock, xparam STREAMS_CC TSRMLS_CC); 779 780 781 return PHP_STREAM_OPTION_RETURN_OK; 782 783 default: 784 /* fall through */ 785 break; 786 } 787 } 788 789 return php_stream_socket_ops.set_option(stream, option, value, ptrparam TSRMLS_CC); 790} 791 792static int php_openssl_sockop_cast(php_stream *stream, int castas, void **ret TSRMLS_DC) 793{ 794 php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract; 795 796 switch(castas) { 797 case PHP_STREAM_AS_STDIO: 798 if (sslsock->ssl_active) { 799 return FAILURE; 800 } 801 if (ret) { 802 *ret = fdopen(sslsock->s.socket, stream->mode); 803 if (*ret) { 804 return SUCCESS; 805 } 806 return FAILURE; 807 } 808 return SUCCESS; 809 810 case PHP_STREAM_AS_FD_FOR_SELECT: 811 if (ret) { 812 *(int *)ret = sslsock->s.socket; 813 } 814 return SUCCESS; 815 816 case PHP_STREAM_AS_FD: 817 case PHP_STREAM_AS_SOCKETD: 818 if (sslsock->ssl_active) { 819 return FAILURE; 820 } 821 if (ret) { 822 *(int *)ret = sslsock->s.socket; 823 } 824 return SUCCESS; 825 default: 826 return FAILURE; 827 } 828} 829 830php_stream_ops php_openssl_socket_ops = { 831 php_openssl_sockop_write, php_openssl_sockop_read, 832 php_openssl_sockop_close, php_openssl_sockop_flush, 833 "tcp_socket/ssl", 834 NULL, /* seek */ 835 php_openssl_sockop_cast, 836 php_openssl_sockop_stat, 837 php_openssl_sockop_set_option, 838}; 839 840static char * get_sni(php_stream_context *ctx, char *resourcename, long resourcenamelen, int is_persistent TSRMLS_DC) { 841 842 php_url *url; 843 844 if (ctx) { 845 zval **val = NULL; 846 847 if (php_stream_context_get_option(ctx, "ssl", "SNI_enabled", &val) == SUCCESS && !zend_is_true(*val)) { 848 return NULL; 849 } 850 if (php_stream_context_get_option(ctx, "ssl", "SNI_server_name", &val) == SUCCESS) { 851 convert_to_string_ex(val); 852 return pestrdup(Z_STRVAL_PP(val), is_persistent); 853 } 854 } 855 856 if (!resourcename) { 857 return NULL; 858 } 859 860 url = php_url_parse_ex(resourcename, resourcenamelen); 861 if (!url) { 862 return NULL; 863 } 864 865 if (url->host) { 866 const char * host = url->host; 867 char * sni = NULL; 868 size_t len = strlen(host); 869 870 /* skip trailing dots */ 871 while (len && host[len-1] == '.') { 872 --len; 873 } 874 875 if (len) { 876 sni = pestrndup(host, len, is_persistent); 877 } 878 879 php_url_free(url); 880 return sni; 881 } 882 883 php_url_free(url); 884 return NULL; 885} 886 887php_stream *php_openssl_ssl_socket_factory(const char *proto, long protolen, 888 char *resourcename, long resourcenamelen, 889 const char *persistent_id, int options, int flags, 890 struct timeval *timeout, 891 php_stream_context *context STREAMS_DC TSRMLS_DC) 892{ 893 php_stream *stream = NULL; 894 php_openssl_netstream_data_t *sslsock = NULL; 895 896 sslsock = pemalloc(sizeof(php_openssl_netstream_data_t), persistent_id ? 1 : 0); 897 memset(sslsock, 0, sizeof(*sslsock)); 898 899 sslsock->s.is_blocked = 1; 900 /* this timeout is used by standard stream funcs, therefor it should use the default value */ 901 sslsock->s.timeout.tv_sec = FG(default_socket_timeout); 902 sslsock->s.timeout.tv_usec = 0; 903 904 /* use separate timeout for our private funcs */ 905 sslsock->connect_timeout.tv_sec = timeout->tv_sec; 906 sslsock->connect_timeout.tv_usec = timeout->tv_usec; 907 908 /* we don't know the socket until we have determined if we are binding or 909 * connecting */ 910 sslsock->s.socket = -1; 911 912 /* Initialize context as NULL */ 913 sslsock->ctx = NULL; 914 915 stream = php_stream_alloc_rel(&php_openssl_socket_ops, sslsock, persistent_id, "r+"); 916 917 if (stream == NULL) { 918 pefree(sslsock, persistent_id ? 1 : 0); 919 return NULL; 920 } 921 922 sslsock->sni = get_sni(context, resourcename, resourcenamelen, !!persistent_id TSRMLS_CC); 923 924 if (strncmp(proto, "ssl", protolen) == 0) { 925 sslsock->enable_on_connect = 1; 926 sslsock->method = STREAM_CRYPTO_METHOD_SSLv23_CLIENT; 927 } else if (strncmp(proto, "sslv2", protolen) == 0) { 928#ifdef OPENSSL_NO_SSL2 929 php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSLv2 support is not compiled into the OpenSSL library PHP is linked against"); 930 return NULL; 931#else 932 sslsock->enable_on_connect = 1; 933 sslsock->method = STREAM_CRYPTO_METHOD_SSLv2_CLIENT; 934#endif 935 } else if (strncmp(proto, "sslv3", protolen) == 0) { 936 sslsock->enable_on_connect = 1; 937 sslsock->method = STREAM_CRYPTO_METHOD_SSLv3_CLIENT; 938 } else if (strncmp(proto, "tls", protolen) == 0) { 939 sslsock->enable_on_connect = 1; 940 sslsock->method = STREAM_CRYPTO_METHOD_TLS_CLIENT; 941 } 942 943 return stream; 944} 945 946 947 948/* 949 * Local variables: 950 * tab-width: 4 951 * c-basic-offset: 4 952 * End: 953 * vim600: noet sw=4 ts=4 fdm=marker 954 * vim<600: noet sw=4 ts=4 955 */ 956