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