/* {{{ ftp_login */ int ftp_login(ftpbuf_t *ftp, const char *user, const size_t user_len, const char *pass, const size_t pass_len) { #ifdef HAVE_FTP_SSL SSL_CTX *ctx = NULL; long ssl_ctx_options = SSL_OP_ALL; int err, res; zend_bool retry; #endif if (ftp == NULL) { return 0; } #ifdef HAVE_FTP_SSL if (ftp->use_ssl && !ftp->ssl_active) { if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "TLS", sizeof("TLS")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } if (ftp->resp != 234) { if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "SSL", sizeof("SSL")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } if (ftp->resp != 334) { return 0; } else { ftp->old_ssl = 1; ftp->use_ssl_for_data = 1; } } ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { php_error_docref(NULL, E_WARNING, "failed to create the SSL context"); return 0; } #if OPENSSL_VERSION_NUMBER >= 0x0090605fL ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif SSL_CTX_set_options(ctx, ssl_ctx_options); /* allow SSL to re-use sessions */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH); ftp->ssl_handle = SSL_new(ctx); if (ftp->ssl_handle == NULL) { php_error_docref(NULL, E_WARNING, "failed to create the SSL handle"); SSL_CTX_free(ctx); return 0; } SSL_set_fd(ftp->ssl_handle, ftp->fd); do { res = SSL_connect(ftp->ssl_handle); err = SSL_get_error(ftp->ssl_handle, res); /* TODO check if handling other error codes would make sense */ switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(ftp->ssl_handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { php_pollfd p; int i; p.fd = ftp->fd; p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "SSL/TLS handshake failed"); SSL_shutdown(ftp->ssl_handle); SSL_free(ftp->ssl_handle); return 0; } } while (retry); ftp->ssl_active = 1; if (!ftp->old_ssl) { /* set protection buffersize to zero */ if (!ftp_putcmd(ftp, "PBSZ", sizeof("PBSZ")-1, "0", sizeof("0")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } /* enable data conn encryption */ if (!ftp_putcmd(ftp, "PROT", sizeof("PROT")-1, "P", sizeof("P")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299); } } #endif if (!ftp_putcmd(ftp, "USER", sizeof("USER")-1, user, user_len)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } if (ftp->resp == 230) { return 1; } if (ftp->resp != 331) { return 0; } if (!ftp_putcmd(ftp, "PASS", sizeof("PASS")-1, pass, pass_len)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } return (ftp->resp == 230); }
/* {{{ data_accept */ databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp) { php_sockaddr_storage addr; socklen_t size; #ifdef HAVE_FTP_SSL SSL_CTX *ctx; SSL_SESSION *session; int err, res; zend_bool retry; #endif if (data->fd != -1) { goto data_accepted; } size = sizeof(addr); data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size); closesocket(data->listener); data->listener = -1; if (data->fd == -1) { efree(data); return NULL; } data_accepted: #ifdef HAVE_FTP_SSL /* now enable ssl if we need to */ if (ftp->use_ssl && ftp->use_ssl_for_data) { ctx = SSL_get_SSL_CTX(ftp->ssl_handle); if (ctx == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL context"); return 0; } data->ssl_handle = SSL_new(ctx); if (data->ssl_handle == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle"); return 0; } SSL_set_fd(data->ssl_handle, data->fd); if (ftp->old_ssl) { SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle); } /* get the session from the control connection so we can re-use it */ session = SSL_get_session(ftp->ssl_handle); if (session == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL session"); SSL_free(data->ssl_handle); return 0; } /* and set it on the data connection */ res = SSL_set_session(data->ssl_handle, session); if (res == 0) { php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session"); SSL_free(data->ssl_handle); return 0; } do { res = SSL_connect(data->ssl_handle); err = SSL_get_error(data->ssl_handle, res); switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(data->ssl_handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { php_pollfd p; int i; p.fd = ftp->fd; p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed"); SSL_shutdown(data->ssl_handle); SSL_free(data->ssl_handle); return 0; } } while (retry); data->ssl_active = 1; } #endif return data; }
/* {{{ my_send */ int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) { zend_long size, sent; int n; #ifdef HAVE_FTP_SSL int err; zend_bool retry = 0; SSL *handle = NULL; php_socket_t fd; #endif size = len; while (size) { n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000); if (n < 1) { #ifdef PHP_WIN32 if (n == 0) { _set_errno(ETIMEDOUT); } #else if (n == 0) { errno = ETIMEDOUT; } #endif return -1; } #ifdef HAVE_FTP_SSL if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { handle = ftp->ssl_handle; fd = ftp->fd; } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { handle = ftp->data->ssl_handle; fd = ftp->data->fd; } if (handle) { do { sent = SSL_write(handle, buf, size); err = SSL_get_error(handle, sent); switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_CONNECT: { php_pollfd p; int i; p.fd = fd; p.events = POLLOUT; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "SSL write failed"); return -1; } } while (retry); } else { #endif sent = send(s, buf, size, 0); #ifdef HAVE_FTP_SSL } #endif if (sent == -1) { return -1; } buf = (char*) buf + sent; size -= sent; } return len; }
/* {{{ my_recv */ int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) { int n, nr_bytes; #ifdef HAVE_FTP_SSL int err; zend_bool retry = 0; SSL *handle = NULL; php_socket_t fd; #endif n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000); if (n < 1) { #ifdef PHP_WIN32 if (n == 0) { _set_errno(ETIMEDOUT); } #else if (n == 0) { errno = ETIMEDOUT; } #endif return -1; } #ifdef HAVE_FTP_SSL if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) { handle = ftp->ssl_handle; fd = ftp->fd; } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) { handle = ftp->data->ssl_handle; fd = ftp->data->fd; } if (handle) { do { nr_bytes = SSL_read(handle, buf, len); err = SSL_get_error(handle, nr_bytes); switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_CONNECT: { php_pollfd p; int i; p.fd = fd; p.events = POLLIN|POLLPRI; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "SSL read failed"); return -1; } } while (retry); } else { #endif nr_bytes = recv(s, buf, len, 0); #ifdef HAVE_FTP_SSL } #endif return (nr_bytes); }
/* {{{ data_accept */ databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp) { php_sockaddr_storage addr; socklen_t size; #ifdef HAVE_FTP_SSL SSL_CTX *ctx; zend_long ssl_ctx_options = SSL_OP_ALL; int err, res; zend_bool retry; #endif if (data->fd != -1) { goto data_accepted; } size = sizeof(addr); data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size); closesocket(data->listener); data->listener = -1; if (data->fd == -1) { efree(data); return NULL; } data_accepted: #ifdef HAVE_FTP_SSL /* now enable ssl if we need to */ if (ftp->use_ssl && ftp->use_ssl_for_data) { ctx = SSL_CTX_new(SSLv23_client_method()); if (ctx == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL context"); return 0; } #if OPENSSL_VERSION_NUMBER >= 0x0090605fL ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif SSL_CTX_set_options(ctx, ssl_ctx_options); data->ssl_handle = SSL_new(ctx); if (data->ssl_handle == NULL) { php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle"); SSL_CTX_free(ctx); return 0; } SSL_set_fd(data->ssl_handle, data->fd); if (ftp->old_ssl) { SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle); } do { res = SSL_connect(data->ssl_handle); err = SSL_get_error(data->ssl_handle, res); switch (err) { case SSL_ERROR_NONE: retry = 0; break; case SSL_ERROR_ZERO_RETURN: retry = 0; SSL_shutdown(data->ssl_handle); break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: { php_pollfd p; int i; p.fd = ftp->fd; p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT; p.revents = 0; i = php_poll2(&p, 1, 300); retry = i > 0; } break; default: php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed"); SSL_shutdown(data->ssl_handle); SSL_free(data->ssl_handle); return 0; } } while (retry); data->ssl_active = 1; } #endif return data; }