[Git][debian-proftpd-team/proftpd-mod-proxy][upstream] New upstream version 0.7
Hilmar Preuße
gitlab at salsa.debian.org
Sat Oct 31 23:10:17 GMT 2020
Hilmar Preuße pushed to branch upstream at Debian ProFTPD Team / proftpd-mod-proxy
Commits:
21d27214 by Hilmar Preusse at 2020-10-31T23:49:45+01:00
New upstream version 0.7
- - - - -
19 changed files:
- .gitignore
- include/proxy/ftp/ctrl.h
- include/proxy/tls.h
- lib/proxy/conn.c
- lib/proxy/forward.c
- lib/proxy/ftp/ctrl.c
- lib/proxy/ftp/sess.c
- lib/proxy/inet.c
- lib/proxy/reverse.c
- lib/proxy/tls.c
- lib/proxy/uri.c
- mod_proxy.c
- mod_proxy.h.in
- mod_proxy.html
- t/api/conn.c
- t/api/tls.c
- t/lib/ProFTPD/Tests/Modules/mod_proxy.pm
- t/lib/ProFTPD/Tests/Modules/mod_proxy/sql.pm
- t/lib/ProFTPD/Tests/Modules/mod_proxy/tls.pm
Changes:
=====================================
.gitignore
=====================================
@@ -5,6 +5,8 @@ config.status
autom4te.cache
mod_proxy.h
tests.log
+t/api-tests
+t/api-tests.log
.libs
*.a
*.sw?
=====================================
include/proxy/ftp/ctrl.h
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy FTP control conn API
- * Copyright (c) 2012-2016 TJ Saunders
+ * Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@ int proxy_ftp_ctrl_handle_async(pool *p, conn_t *backend_conn,
pr_response_t *proxy_ftp_ctrl_recv_resp(pool *p, conn_t *ctrl_conn,
unsigned int *resp_nlines, int flags);
+int proxy_ftp_ctrl_send_abort(pool *p, conn_t *ctrl_conn, cmd_rec *cmd);
int proxy_ftp_ctrl_send_cmd(pool *p, conn_t *ctrl_conn, cmd_rec *cmd);
int proxy_ftp_ctrl_send_resp(pool *p, conn_t *ctrl_conn, pr_response_t *resp,
unsigned int resp_nlines);
=====================================
include/proxy/tls.h
=====================================
@@ -51,6 +51,9 @@
#define PROXY_TLS_ENGINE_ON 1
#define PROXY_TLS_ENGINE_OFF 2
#define PROXY_TLS_ENGINE_AUTO 3
+#define PROXY_TLS_ENGINE_IMPLICIT 4
+
+#define PROXY_TLS_IMPLICIT_FTPS_PORT 990
/* ProxyTLSOptions values */
#define PROXY_TLS_OPT_ENABLE_DIAGS 0x0001
=====================================
lib/proxy/conn.c
=====================================
@@ -143,13 +143,18 @@ const struct proxy_conn *proxy_conn_create(pool *p, const char *uri) {
return NULL;
}
- if (strncmp(proto, "ftps", 5) == 0) {
+ if (strcmp(proto, "ftps") == 0) {
/* If the 'ftps' scheme is used, then FTPS is REQUIRED for connections
* to this server.
*/
use_tls = PROXY_TLS_ENGINE_ON;
- } else if (strncmp(proto, "sftp", 5) == 0) {
+ /* We automatically (and only) use implicit FTPS for port 990. */
+ if (remote_port == PROXY_TLS_IMPLICIT_FTPS_PORT) {
+ use_tls = PROXY_TLS_ENGINE_IMPLICIT;
+ }
+
+ } else if (strcmp(proto, "sftp") == 0) {
/* As might be obvious, do not try to use TLS against an SSH2/SFTP
* server.
*/
@@ -469,7 +474,7 @@ conn_t *proxy_conn_get_server_conn(pool *p, struct proxy_session *proxy_sess,
if (res == 0) {
pr_netio_stream_t *nstrm;
- int connected = FALSE, nstrm_mode = PR_NETIO_IO_RD;
+ int connected = FALSE, nstrm_mode = PR_NETIO_IO_RD, use_tls;
if ((proxy_opts & PROXY_OPT_USE_PROXY_PROTOCOL_V1) ||
(proxy_opts & PROXY_OPT_USE_PROXY_PROTOCOL_V2)) {
@@ -480,6 +485,14 @@ conn_t *proxy_conn_get_server_conn(pool *p, struct proxy_session *proxy_sess,
nstrm_mode = PR_NETIO_IO_WR;
}
+ use_tls = proxy_tls_using_tls();
+ if (use_tls == PROXY_TLS_ENGINE_IMPLICIT) {
+ /* For implicit FTPS connections, we will be initiating the TLS
+ * handshake, and thus we need to wait for the stream to be writable.
+ */
+ nstrm_mode = PR_NETIO_IO_WR;
+ }
+
/* Not yet connected. */
nstrm = proxy_netio_open(p, PR_NETIO_STRM_OTHR, server_conn->listen_fd,
nstrm_mode);
@@ -499,7 +512,7 @@ conn_t *proxy_conn_get_server_conn(pool *p, struct proxy_session *proxy_sess,
proxy_netio_set_poll_interval(nstrm, 1);
- while (!connected) {
+ while (connected == FALSE) {
int polled;
pr_signals_handle();
=====================================
lib/proxy/forward.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy forward proxy implementation
- * Copyright (c) 2012-2016 TJ Saunders
+ * Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -152,16 +152,60 @@ int proxy_forward_have_authenticated(cmd_rec *cmd) {
return authd;
}
+static int forward_tls_postopen(pool *p, struct proxy_session *proxy_sess,
+ conn_t *server_conn, pr_response_t **resp) {
+ int xerrno;
+
+ if (proxy_netio_postopen(server_conn->instrm) < 0) {
+ xerrno = errno;
+
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "postopen error for backend control connection input stream: %s",
+ strerror(xerrno));
+ proxy_inet_close(session.pool, server_conn);
+ proxy_sess->backend_ctrl_conn = NULL;
+
+ *resp = NULL;
+ errno = xerrno;
+ return -1;
+ }
+
+ if (proxy_netio_postopen(server_conn->outstrm) < 0) {
+ xerrno = errno;
+
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "postopen error for backend control connection output stream: %s",
+ strerror(xerrno));
+ proxy_inet_close(session.pool, server_conn);
+ proxy_sess->backend_ctrl_conn = NULL;
+
+ *resp = NULL;
+ errno = xerrno;
+ return -1;
+ }
+
+ return 0;
+}
+
static int forward_connect(pool *p, struct proxy_session *proxy_sess,
pr_response_t **resp, unsigned int *resp_nlines) {
conn_t *server_conn = NULL;
int banner_ok = TRUE, use_tls, xerrno = 0;
const pr_netaddr_t *dst_addr;
array_header *other_addrs = NULL;
+ char port_text[32];
dst_addr = proxy_sess->dst_addr;
other_addrs = proxy_sess->other_addrs;
+ /* If the destination port is 990, assume implicit FTPS. */
+ if (ntohs(pr_netaddr_get_port(dst_addr)) == PROXY_TLS_IMPLICIT_FTPS_PORT) {
+ pr_trace_msg(trace_channel, 9, "%s#%u requesting, using implicit FTPS",
+ pr_netaddr_get_ipstr(dst_addr),
+ (unsigned int) ntohs(pr_netaddr_get_port(dst_addr)));
+ proxy_tls_set_tls(PROXY_TLS_ENGINE_IMPLICIT);
+ }
+
server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
if (server_conn == NULL) {
xerrno = errno;
@@ -200,13 +244,22 @@ static int forward_connect(pool *p, struct proxy_session *proxy_sess,
return -1;
}
+ proxy_sess->frontend_ctrl_conn = session.c;
+ proxy_sess->backend_ctrl_conn = server_conn;
+
+ use_tls = proxy_tls_using_tls();
+
+ /* Handle implicit FTPS connects. */
+ if (use_tls == PROXY_TLS_ENGINE_IMPLICIT) {
+ if (forward_tls_postopen(p, proxy_sess, server_conn, resp) < 0) {
+ return -1;
+ }
+ }
+
/* XXX Support/send a CLNT command of our own? Configurable via e.g.
* "UserAgent" string?
*/
- proxy_sess->frontend_ctrl_conn = session.c;
- proxy_sess->backend_ctrl_conn = server_conn;
-
/* Read the response from the backend server. */
*resp = proxy_ftp_ctrl_recv_resp(p, proxy_sess->backend_ctrl_conn,
resp_nlines, 0);
@@ -250,7 +303,8 @@ static int forward_connect(pool *p, struct proxy_session *proxy_sess,
}
use_tls = proxy_tls_using_tls();
- if (use_tls != PROXY_TLS_ENGINE_OFF) {
+ if (use_tls != PROXY_TLS_ENGINE_OFF &&
+ use_tls != PROXY_TLS_ENGINE_IMPLICIT) {
if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0 &&
errno != ENOSYS) {
xerrno = errno;
@@ -269,32 +323,11 @@ static int forward_connect(pool *p, struct proxy_session *proxy_sess,
use_tls = proxy_tls_using_tls();
}
- if (proxy_netio_postopen(server_conn->instrm) < 0) {
- xerrno = errno;
-
- (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
- "postopen error for backend control connection input stream: %s",
- strerror(xerrno));
- proxy_inet_close(session.pool, server_conn);
- proxy_sess->backend_ctrl_conn = NULL;
-
- *resp = NULL;
- errno = xerrno;
- return -1;
- }
-
- if (proxy_netio_postopen(server_conn->outstrm) < 0) {
- xerrno = errno;
-
- (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
- "postopen error for backend control connection output stream: %s",
- strerror(xerrno));
- proxy_inet_close(session.pool, server_conn);
- proxy_sess->backend_ctrl_conn = NULL;
-
- *resp = NULL;
- errno = xerrno;
- return -1;
+ if (use_tls != PROXY_TLS_ENGINE_OFF &&
+ use_tls != PROXY_TLS_ENGINE_IMPLICIT) {
+ if (forward_tls_postopen(p, proxy_sess, server_conn, resp) < 0) {
+ return -1;
+ }
}
if (use_tls != PROXY_TLS_ENGINE_OFF) {
@@ -306,6 +339,18 @@ static int forward_connect(pool *p, struct proxy_session *proxy_sess,
(void) proxy_ftp_sess_send_host(p, proxy_sess);
+ /* Populate the session notes about this connection. */
+ memset(port_text, '\0', sizeof(port_text));
+ pr_snprintf(port_text, sizeof(port_text)-1, "%d",
+ proxy_conn_get_port(proxy_sess->dst_pconn));
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-ip",
+ pr_netaddr_get_ipstr(dst_addr), 0);
+ (void) pr_table_remove(session.notes, "mod_proxy.backend-port", NULL);
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-port",
+ port_text, 0);
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-url",
+ proxy_conn_get_uri(proxy_sess->dst_pconn), 0);
+
proxy_sess_state |= PROXY_SESS_STATE_CONNECTED;
return 0;
}
=====================================
lib/proxy/ftp/ctrl.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy FTP control conn routines
- * Copyright (c) 2012-2016 TJ Saunders
+ * Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,6 +26,7 @@
#include "proxy/netio.h"
#include "proxy/ftp/ctrl.h"
+#include "proxy/tls.h"
static const char *trace_channel = "proxy.ftp.ctrl";
@@ -366,6 +367,84 @@ pr_response_t *proxy_ftp_ctrl_recv_resp(pool *p, conn_t *ctrl_conn,
return resp;
}
+#ifndef TELNET_DM
+# define TELNET_DM 242
+#endif /* TELNET_DM */
+
+#ifndef TELNET_IAC
+# define TELNET_IAC 255
+#endif /* TELNET_IAC */
+
+#ifndef TELNET_IP
+# define TELNET_IP 244
+#endif /* TELNET_IP */
+
+int proxy_ftp_ctrl_send_abort(pool *p, conn_t *ctrl_conn, cmd_rec *cmd) {
+ int fd, res, use_tls, xerrno;
+ unsigned char buf[7];
+
+ if (p == NULL ||
+ ctrl_conn == NULL ||
+ cmd == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* If we are proxying the ABOR command, preface it with the Telnet "Sync"
+ * mechanism, using OOB data. If the receiving server supports this, it can
+ * generate a signal to interrupt any IO occurring on the backend server
+ * (such as when sendfile(2) is used).
+ *
+ * Note that such Telnet codes can only be used if we are NOT using TLS
+ * on the backend control connection.
+ */
+ use_tls = proxy_tls_using_tls();
+ if (use_tls != PROXY_TLS_ENGINE_OFF) {
+ return proxy_ftp_ctrl_send_cmd(p, ctrl_conn, cmd);
+ }
+
+ fd = PR_NETIO_FD(ctrl_conn->outstrm);
+
+ buf[0] = TELNET_IAC;
+ buf[1] = TELNET_IP;
+ buf[2] = TELNET_IAC;
+
+ pr_trace_msg(trace_channel, 9,
+ "sending Telnet abort code out-of-band to backend");
+ res = send(fd, &buf, 3, MSG_OOB);
+ xerrno = errno;
+
+ if (res < 0) {
+ pr_trace_msg(trace_channel, 1,
+ "error sending Telnet abort code out-of-band to backend: %s",
+ strerror(xerrno));
+ errno = xerrno;
+ return -1;
+ }
+
+ buf[0] = TELNET_DM;
+ buf[1] = 'A';
+ buf[2] = 'B';
+ buf[3] = 'O';
+ buf[4] = 'R';
+ buf[5] = '\r';
+ buf[6] = '\n';
+
+ pr_trace_msg(trace_channel, 9,
+ "proxied %s command from frontend to backend", (char *) cmd->argv[0]);
+ res = send(fd, &buf, 7, 0);
+ xerrno = errno;
+
+ if (res < 0) {
+ pr_trace_msg(trace_channel, 1,
+ "error sending Telnet DM code to backend: %s", strerror(xerrno));
+ errno = xerrno;
+ return -1;
+ }
+
+ return 0;
+}
+
int proxy_ftp_ctrl_send_cmd(pool *p, conn_t *ctrl_conn, cmd_rec *cmd) {
int res;
=====================================
lib/proxy/ftp/sess.c
=====================================
@@ -307,7 +307,13 @@ int proxy_ftp_sess_send_auth_tls(pool *p,
use_tls = proxy_tls_using_tls();
if (use_tls == PROXY_TLS_ENGINE_OFF) {
pr_trace_msg(trace_channel, 19,
- "TLS support not enabled/desired, skipping");
+ "TLS support not enabled/desired, skipping 'AUTH TLS' command");
+ return 0;
+ }
+
+ if (use_tls == PROXY_TLS_ENGINE_IMPLICIT) {
+ pr_trace_msg(trace_channel, 19,
+ "implicit FTPS support requested, skipping 'AUTH TLS' command");
return 0;
}
=====================================
lib/proxy/inet.c
=====================================
@@ -72,14 +72,21 @@ void proxy_inet_close(pool *p, conn_t *conn) {
* functions for closing, too.
*/
+ /* Shutdowns first, then closes. */
if (conn->instrm != NULL) {
proxy_netio_shutdown(conn->instrm, 0);
+ }
+
+ if (conn->outstrm != NULL) {
+ proxy_netio_shutdown(conn->outstrm, 1);
+ }
+
+ if (conn->instrm != NULL) {
proxy_netio_close(conn->instrm);
conn->instrm = NULL;
}
if (conn->outstrm != NULL) {
- proxy_netio_shutdown(conn->outstrm, 1);
proxy_netio_close(conn->outstrm);
conn->outstrm = NULL;
}
=====================================
lib/proxy/reverse.c
=====================================
@@ -698,9 +698,52 @@ static const struct proxy_conn *get_reverse_server_conn(pool *p,
return pconn;
}
+static int reverse_tls_postopen(pool *p, struct proxy_session *proxy_sess,
+ conn_t *server_conn) {
+ int xerrno;
+
+ if (proxy_netio_postopen(server_conn->instrm) < 0) {
+ xerrno = errno;
+
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "postopen error for backend control connection input stream: %s",
+ strerror(xerrno));
+ proxy_inet_close(session.pool, server_conn);
+ proxy_sess->backend_ctrl_conn = NULL;
+
+ pr_response_block(FALSE);
+
+ /* Note that we explicitly return EINVAL here, to indicate to the calling
+ * code in mod_proxy that it should return e.g. "Login incorrect."
+ */
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (proxy_netio_postopen(server_conn->outstrm) < 0) {
+ xerrno = errno;
+
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "postopen error for backend control connection output stream: %s",
+ strerror(xerrno));
+ proxy_inet_close(session.pool, server_conn);
+ proxy_sess->backend_ctrl_conn = NULL;
+
+ pr_response_block(FALSE);
+
+ /* Note that we explicitly return EINVAL here, to indicate to the calling
+ * code in mod_proxy that it should return e.g. "Login incorrect."
+ */
+ errno = EINVAL;
+ return -1;
+ }
+
+ return 0;
+}
+
static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
const void *connect_data) {
- int backend_id = -1, use_tls, xerrno = 0;
+ int backend_id = -1, uri_tls, use_tls, xerrno = 0;
conn_t *server_conn = NULL;
pr_response_t *resp = NULL;
unsigned int resp_nlines = 0;
@@ -708,6 +751,7 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
const pr_netaddr_t *dst_addr;
array_header *other_addrs = NULL;
uint64_t connecting_ms, connected_ms;
+ char port_text[32];
pconn = get_reverse_server_conn(p, proxy_sess, &backend_id, connect_data);
if (pconn == NULL) {
@@ -719,6 +763,14 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
proxy_sess->dst_pconn = pconn;
proxy_sess->other_addrs = other_addrs;
+ uri_tls = proxy_conn_get_tls(pconn);
+ if (uri_tls == PROXY_TLS_ENGINE_IMPLICIT) {
+ pr_trace_msg(trace_channel, 9, "%s#%u requesting, using implicit FTPS",
+ pr_netaddr_get_ipstr(dst_addr),
+ (unsigned int) ntohs(pr_netaddr_get_port(dst_addr)));
+ proxy_tls_set_tls(uri_tls);
+ }
+
pr_gettimeofday_millis(&connecting_ms);
server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr);
if (server_conn == NULL) {
@@ -794,15 +846,22 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
}
}
- /* XXX Support/send a CLNT command of our own? Configurable via e.g.
- * "UserAgent" string?
- */
-
proxy_sess->frontend_ctrl_conn = session.c;
proxy_sess->backend_ctrl_conn = server_conn;
use_tls = proxy_tls_using_tls();
+ /* Handle implicit FTPS connects. */
+ if (use_tls == PROXY_TLS_ENGINE_IMPLICIT) {
+ if (reverse_tls_postopen(p, proxy_sess, server_conn) < 0) {
+ return -1;
+ }
+ }
+
+ /* XXX Support/send a CLNT command of our own? Configurable via e.g.
+ * "UserAgent" string?
+ */
+
resp = proxy_ftp_ctrl_recv_resp(p, server_conn, &resp_nlines, 0);
if (resp == NULL) {
xerrno = errno;
@@ -860,7 +919,8 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
pr_response_block(TRUE);
- if (use_tls != PROXY_TLS_ENGINE_OFF) {
+ if (use_tls != PROXY_TLS_ENGINE_OFF &&
+ use_tls != PROXY_TLS_ENGINE_IMPLICIT) {
if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0 &&
errno != ENOSYS) {
xerrno = errno;
@@ -879,40 +939,11 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
use_tls = proxy_tls_using_tls();
}
- if (proxy_netio_postopen(server_conn->instrm) < 0) {
- xerrno = errno;
-
- (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
- "postopen error for backend control connection input stream: %s",
- strerror(xerrno));
- proxy_inet_close(session.pool, server_conn);
- proxy_sess->backend_ctrl_conn = NULL;
-
- pr_response_block(FALSE);
-
- /* Note that we explicitly return EINVAL here, to indicate to the calling
- * code in mod_proxy that it should return e.g. "Login incorrect."
- */
- errno = EINVAL;
- return -1;
- }
-
- if (proxy_netio_postopen(server_conn->outstrm) < 0) {
- xerrno = errno;
-
- (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
- "postopen error for backend control connection output stream: %s",
- strerror(xerrno));
- proxy_inet_close(session.pool, server_conn);
- proxy_sess->backend_ctrl_conn = NULL;
-
- pr_response_block(FALSE);
-
- /* Note that we explicitly return EINVAL here, to indicate to the calling
- * code in mod_proxy that it should return e.g. "Login incorrect."
- */
- errno = EINVAL;
- return -1;
+ if (use_tls != PROXY_TLS_ENGINE_OFF &&
+ use_tls != PROXY_TLS_ENGINE_IMPLICIT) {
+ if (reverse_tls_postopen(p, proxy_sess, server_conn) < 0) {
+ return -1;
+ }
}
if (use_tls != PROXY_TLS_ENGINE_OFF) {
@@ -937,6 +968,18 @@ static int reverse_try_connect(pool *p, struct proxy_session *proxy_sess,
(void) proxy_ftp_sess_send_host(p, proxy_sess);
+ /* Populate the session notes about this connection. */
+ memset(port_text, '\0', sizeof(port_text));
+ pr_snprintf(port_text, sizeof(port_text)-1, "%d",
+ proxy_conn_get_port(proxy_sess->dst_pconn));
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-ip",
+ pr_netaddr_get_ipstr(dst_addr), 0);
+ (void) pr_table_remove(session.notes, "mod_proxy.backend-port", NULL);
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-port",
+ port_text, 0);
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-url",
+ proxy_conn_get_uri(proxy_sess->dst_pconn), 0);
+
proxy_sess_state |= PROXY_SESS_STATE_CONNECTED;
return 0;
}
=====================================
lib/proxy/tls.c
=====================================
@@ -2387,7 +2387,8 @@ int proxy_tls_set_tls(int engine) {
#ifdef PR_USE_OPENSSL
if (engine != PROXY_TLS_ENGINE_ON &&
engine != PROXY_TLS_ENGINE_OFF &&
- engine != PROXY_TLS_ENGINE_AUTO) {
+ engine != PROXY_TLS_ENGINE_AUTO &&
+ engine != PROXY_TLS_ENGINE_IMPLICIT) {
errno = EINVAL;
return -1;
}
@@ -2504,6 +2505,9 @@ static int get_disabled_protocols(unsigned int supported_protocols) {
# ifdef SSL_OP_NO_TLSv1_2
disabled_protocols |= SSL_OP_NO_TLSv1_2;
# endif
+# ifdef SSL_OP_NO_TLSv1_3
+ disabled_protocols |= SSL_OP_NO_TLSv1_3;
+# endif
/* Now, based on the given bitset of supported protocols, clear the
* necessary bits.
@@ -3749,7 +3753,7 @@ static void tls_tlsext_cb(SSL *ssl, int server, int type,
case TLSEXT_TYPE_supported_versions: {
BIO *bio = NULL;
char *ext_info = NULL;
- long ext_infolen;
+ long ext_infolen = 0;
/* If we are the server responding, we only indicate the selected
* protocol version. Otherwise, we are a client indicating the range
@@ -3819,7 +3823,7 @@ static void tls_tlsext_cb(SSL *ssl, int server, int type,
case TLSEXT_TYPE_psk_kex_modes: {
BIO *bio = NULL;
char *ext_info = NULL;
- long ext_infolen;
+ long ext_infolen = 0;
extension_name = "PSK KEX modes";
=====================================
lib/proxy/uri.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy URI implementation
- * Copyright (c) 2012-2016 TJ Saunders
+ * Copyright (c) 2012-2020 TJ Saunders
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -286,11 +286,11 @@ int proxy_uri_parse(pool *p, const char *uri, char **scheme, char **host,
if (ptr2 == NULL) {
*host = uri_parse_host(p, uri, ptr, NULL);
- if (strncmp(*scheme, "ftp", 4) == 0 ||
- strncmp(*scheme, "ftps", 5) == 0) {
+ if (strcmp(*scheme, "ftp") == 0 ||
+ strcmp(*scheme, "ftps") == 0) {
*port = 21;
- } else if (strncmp(*scheme, "sftp", 5) == 0) {
+ } else if (strcmp(*scheme, "sftp") == 0) {
*port = 22;
} else {
@@ -312,11 +312,11 @@ int proxy_uri_parse(pool *p, const char *uri, char **scheme, char **host,
if (ptr2 == NULL) {
/* XXX How to configure "implicit" FTPS, if at all? */
- if (strncmp(*scheme, "ftp", 4) == 0 ||
- strncmp(*scheme, "ftps", 5) == 0) {
+ if (strcmp(*scheme, "ftp") == 0 ||
+ strcmp(*scheme, "ftps") == 0) {
*port = 21;
- } else if (strncmp(*scheme, "sftp", 5) == 0) {
+ } else if (strcmp(*scheme, "sftp") == 0) {
*port = 22;
} else {
=====================================
mod_proxy.c
=====================================
@@ -84,28 +84,12 @@ static void proxy_timeoutidle_ev(const void *, void *);
static void proxy_timeoutnoxfer_ev(const void *, void *);
static void proxy_timeoutstalled_ev(const void *, void *);
-MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
+static int recv_resp(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_t **rp) {
int res, xerrno = 0;
pr_response_t *resp;
unsigned int resp_nlines = 0;
- res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
- cmd);
- if (res < 0) {
- xerrno = errno;
- (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
- "error sending %s to backend: %s", (char *) cmd->argv[0],
- strerror(xerrno));
-
- pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
- strerror(xerrno));
- pr_response_flush(&resp_err_list);
-
- errno = xerrno;
- return PR_ERROR(cmd);
- }
-
resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
&resp_nlines, 0);
if (resp == NULL) {
@@ -133,7 +117,7 @@ MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
pr_response_flush(&resp_err_list);
errno = xerrno;
- return PR_ERROR(cmd);
+ return -1;
}
res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn,
@@ -142,16 +126,91 @@ MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
xerrno = errno;
pr_response_block(TRUE);
+ errno = xerrno;
+ return -1;
+ }
+
+ if (rp != NULL) {
+ *rp = resp;
+ }
+
+ return 0;
+}
+
+MODRET proxy_cmd(cmd_rec *cmd, struct proxy_session *proxy_sess,
+ pr_response_t **rp) {
+ int res, xerrno = 0;
+
+ res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn,
+ cmd);
+ xerrno = errno;
+
+ if (res < 0) {
+ xerrno = errno;
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "error sending %s to backend: %s", (char *) cmd->argv[0],
+ strerror(xerrno));
+
+ pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
+ strerror(xerrno));
+ pr_response_flush(&resp_err_list);
+
errno = xerrno;
return PR_ERROR(cmd);
}
+ if (recv_resp(cmd, proxy_sess, rp) < 0) {
+ return PR_ERROR(cmd);
+ }
+
pr_response_block(TRUE);
+ return PR_HANDLED(cmd);
+}
- if (rp != NULL) {
- *rp = resp;
+MODRET proxy_abort(cmd_rec *cmd, struct proxy_session *proxy_sess,
+ pr_response_t **rp) {
+ int res, xerrno = 0;
+
+ res = proxy_ftp_ctrl_send_abort(cmd->tmp_pool,
+ proxy_sess->backend_ctrl_conn, cmd);
+ xerrno = errno;
+
+ if (res < 0) {
+ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION,
+ "error sending %s to backend: %s", (char *) cmd->argv[0],
+ strerror(xerrno));
+
+ pr_response_add_err(R_500, _("%s: %s"), (char *) cmd->argv[0],
+ strerror(xerrno));
+ pr_response_flush(&resp_err_list);
+
+ errno = xerrno;
+ return PR_ERROR(cmd);
+ }
+
+ if (proxy_sess->backend_data_conn != NULL) {
+ pr_trace_msg(trace_channel, 19, "received ABOR on frontend connection, "
+ "closing backend data connection");
+ proxy_inet_close(session.pool, proxy_sess->backend_data_conn);
+ proxy_sess->backend_data_conn = NULL;
+ }
+
+ if (recv_resp(cmd, proxy_sess, rp) < 0) {
+ return PR_ERROR(cmd);
+ }
+
+ /* The ABOR command might have two responses, as when there is a data
+ * transfer in progress: one response for the data transfer, and one for
+ * handling the ABOR command itself.
+ */
+ if ((proxy_sess->frontend_sess_flags & SF_XFER) ||
+ (proxy_sess->backend_sess_flags & SF_XFER)) {
+ if (recv_resp(cmd, proxy_sess, rp) < 0) {
+ return PR_ERROR(cmd);
+ }
}
+ pr_response_block(TRUE);
return PR_HANDLED(cmd);
}
@@ -2311,12 +2370,22 @@ MODRET proxy_data(struct proxy_session *proxy_sess, cmd_rec *cmd) {
xfer_direction = PR_NETIO_IO_WR;
session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
+ if (session.xfer.path == NULL) {
+ /* Add this note for the Jot API, and resolving %F/%f variables. */
+ (void) pr_table_add_dup(cmd->notes, "mod_xfer.store-path", cmd->arg, 0);
+ session.xfer.path = cmd->arg;
+ }
} else {
/* Downloading, i.e. reading from backend data conn.*/
xfer_direction = PR_NETIO_IO_RD;
session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
+ if (session.xfer.path == NULL) {
+ /* Add this note for the Jot API, and resolving %F/%f variables. */
+ (void) pr_table_add_dup(cmd->notes, "mod_xfer.retr-path", cmd->arg, 0);
+ session.xfer.path = cmd->arg;
+ }
}
res = proxy_data_prepare_conns(proxy_sess, cmd, &frontend_conn,
@@ -2515,19 +2584,28 @@ MODRET proxy_data(struct proxy_session *proxy_sess, cmd_rec *cmd) {
continue;
}
-#if 0
/* Any commands from the frontend client take priority */
-
- /* NOTE: This is temporarily disabled, until I can better handle an
- * ABOR command on the frontend control connection whilst in the middle
- * of a data transfer.
- */
if (frontend_ctrlfd >= 0 &&
FD_ISSET(frontend_ctrlfd, &rfds)) {
proxy_process_cmd();
pr_response_block(FALSE);
+
+ /* Check for closed frontend/backend data connections, as from an ABOR
+ * command.
+ */
+ if (session.d == NULL ||
+ proxy_sess->backend_data_conn == NULL) {
+ if (pr_data_get_timeout(PR_DATA_TIMEOUT_STALLED) > 0) {
+ pr_timer_remove(PR_TIMER_STALLED, ANY_MODULE);
+ }
+
+ pr_throttle_pause(bytes_transferred, TRUE);
+ pr_response_clear(&resp_list);
+ pr_response_clear(&resp_err_list);
+
+ return PR_HANDLED(cmd);
+ }
}
-#endif
if (src_data_conn != NULL &&
datafd >= 0 &&
@@ -4347,6 +4425,23 @@ MODRET proxy_any(cmd_rec *cmd) {
return PR_DECLINED(cmd);
}
break;
+
+ case PR_CMD_ABOR_ID:
+ mr = proxy_abort(cmd, proxy_sess, NULL);
+ if ((proxy_sess->frontend_sess_flags & SF_XFER) ||
+ (proxy_sess->backend_sess_flags & SF_XFER)) {
+ pr_trace_msg(trace_channel, 19, "received ABOR on frontend connection, "
+ "closing frontend data connection");
+
+ if (session.d != NULL) {
+ pr_inet_close(session.pool, proxy_sess->frontend_data_conn);
+ proxy_sess->frontend_data_conn = session.d = NULL;
+ }
+
+ proxy_sess->frontend_sess_flags &= ~SF_XFER;
+ proxy_sess->backend_sess_flags &= ~SF_XFER;
+ }
+ return mr;
}
/* If we are not connected to a backend server, then don't try to proxy
@@ -4422,8 +4517,11 @@ static void proxy_exit_ev(const void *event_data, void *user_data) {
proxy_sess = (struct proxy_session *) pr_table_get(session.notes,
"mod_proxy.proxy-session", NULL);
if (proxy_sess != NULL) {
+ /* proxy_sess->frontend_ctrl_conn is session.c; let the core engine
+ * close that connection. If we try to close it here via pr_inet_close(),
+ * we risk segfaults due to double-free of the memory, stale pointers, etc.
+ */
if (proxy_sess->frontend_ctrl_conn != NULL) {
- pr_inet_close(proxy_sess->pool, proxy_sess->frontend_ctrl_conn);
proxy_sess->frontend_ctrl_conn = NULL;
}
@@ -4821,6 +4919,9 @@ static int proxy_sess_init(void) {
return -1;
}
+ /* Provide default note values. */
+ (void) pr_table_add_dup(session.notes, "mod_proxy.backend-port", "0", 0);
+
c = find_config(main_server->conf, CONF_PARAM, "ProxySourceAddress", FALSE);
if (c != NULL) {
proxy_sess->src_addr = c->argv[0];
=====================================
mod_proxy.h.in
=====================================
@@ -58,11 +58,11 @@
/* Define if you have the strnstr(3) function. */
#undef HAVE_STRNSTR
-#define MOD_PROXY_VERSION "mod_proxy/0.6"
+#define MOD_PROXY_VERSION "mod_proxy/0.7"
/* Make sure the version of proftpd is as necessary. */
-#if PROFTPD_VERSION_NUMBER < 0x0001030605
-# error "ProFTPD 1.3.6rc5 or later required"
+#if PROFTPD_VERSION_NUMBER < 0x0001030706
+# error "ProFTPD 1.3.7a or later required"
#endif
/* mod_proxy option flags */
=====================================
mod_proxy.html
=====================================
@@ -648,18 +648,20 @@ The <code>ProxyReverseServers</code> directive configures the list of servers
to be used as the backend servers for reverse proxying.
<p>
-Each server <b>must</b> be configured as a <i>URL</i>. Only the "ftp" scheme
-is currently supported. If not specified, the port will be 21. IPv6 addresses
-<b>must</b> be enclosed within square brackets. Thus, for example, the
-following are all valid URLs:
+Each server <b>must</b> be configured as a <i>URL</i>. Only the "ftp" and
+"ftps" schemes are currently supported. If not specified, the port will be
+21. IPv6 addresses <b>must</b> be enclosed within square brackets. Thus, for
+example, the following are all valid URLs:
<pre>
ftp://<em>ftp1.example.com:2121</em>
ftp://<em>1.2.3.4</em>
ftp://<em>[::ffff:6.7.8.9]:2121</em>
+ ftps://<em>ftp2.example.com</em>
+ ftps://<em>ftp3.example.com:990</em>
</pre>
And using them all in the configuration would look like:
<pre>
- ProxyReverseServers ftp://ftp1.example.com:2121 ftp://1.2.3.4 ftp://[::ffff:6.7.8.9]:2121
+ ProxyReverseServers ftp://ftp1.example.com:2121 ftps://1.2.3.4 ftp://[::ffff:6.7.8.9]:2121
</pre>
<p>
@@ -1391,9 +1393,14 @@ for supporting forward proxying:
ProxyRole forward
ProxyForwardMethod user at host
- ProxyForwardTo ^ftp\.example\.com [NC]
+ ProxyForwardTo ^ftp\.example\.com\:21$ [NC]
</IfModule>
</pre>
+If the configured <code>ProxyForwardTo</code> pattern is <em>not</em> met,
+the following will be logged in the <code>ProxyLog</code>:
+<pre>
+mod_proxy/0.7[16151]: host/port 'server.example.org:2121' did not match ProxyForwardTo ^ftp\.example\.com\:21$, rejecting
+</pre>
<p>
<b>Reverse Proxy Configuration</b><br>
@@ -1549,6 +1556,27 @@ The <em>sticky</em> policies are:
<li><code>PerUser</code>
</ul>
+<p>
+<b>Implicit FTPS Support</b><br>
+The <code>mod_proxy</code> module includes support for using <a href="https://en.wikipedia.org/wiki/FTPS#Implicit">implicit FTPS</a> with backend servers,
+both when forward and reverse proxying.
+
+<p>
+In order to use implicit FTPS for a reverse proxy server, the URI syntax
+must be used in a <code>ProxyReverseServers</code> directive; the scheme
+<b>must</b> be "ftps" <i>and</b> the port must be explicitly specified as 990,
+thus:
+<code>
+ ProxyReverseServers ftps://ftp.example.com:990
+</code>
+
+<p>
+When forward proxying, the client must request the destination server <i>and</i>
+specify a port of 990, <i>e.g.</i>:
+<code>
+ USER user at ftp.example.com:990
+</code>
+
<p>
<b>SFTP/SCP Support</b><br>
The <code>mod_proxy</code> module only works for FTP/FTPS sessions; it does
@@ -1601,7 +1629,6 @@ The following lists the features I hope to add to <code>mod_proxy</code>,
according to need, demand, inclination, and time:
<ul>
<li><code>MODE Z</code> support
- <li>Directory format translation (<i>e.g.</i> <code>LIST</code> to <code>MLSD</code>)
<li>SFTP/SCP support
</ul>
=====================================
t/api/conn.c
=====================================
@@ -311,6 +311,7 @@ START_TEST (conn_get_tls_test) {
const char *url;
const struct proxy_conn *pconn;
+ mark_point();
tls = proxy_conn_get_tls(NULL);
fail_unless(tls < 0, "Got TLS from null pconn unexpectedly");
fail_unless(errno == EINVAL, "Expected EINVAL (%d), got '%s' (%d)", EINVAL,
@@ -321,10 +322,12 @@ START_TEST (conn_get_tls_test) {
fail_if(pconn == NULL, "Failed to create pconn for URL '%s' as expected",
url);
+ mark_point();
tls = proxy_conn_get_tls(pconn);
fail_unless(tls == PROXY_TLS_ENGINE_AUTO, "Expected TLS auto, got %d", tls);
proxy_conn_free(pconn);
+ mark_point();
url = "ftps://127.0.0.1:21";
pconn = proxy_conn_create(p, url);
fail_if(pconn == NULL, "Failed to create pconn for URL '%s' as expected",
@@ -332,7 +335,17 @@ START_TEST (conn_get_tls_test) {
tls = proxy_conn_get_tls(pconn);
fail_unless(tls == PROXY_TLS_ENGINE_ON, "Expected TLS on, got %d", tls);
+ proxy_conn_free(pconn);
+
+ mark_point();
+ url = "ftps://127.0.0.1:990";
+ pconn = proxy_conn_create(p, url);
+ fail_if(pconn == NULL, "Failed to create pconn for URL '%s' as expected",
+ url);
+ tls = proxy_conn_get_tls(pconn);
+ fail_unless(tls == PROXY_TLS_ENGINE_IMPLICIT,
+ "Expected TLS implicit, got %d", tls);
proxy_conn_free(pconn);
}
END_TEST
=====================================
t/api/tls.c
=====================================
@@ -1,6 +1,6 @@
/*
* ProFTPD - mod_proxy testsuite
- * Copyright (c) 2015-2017 TJ Saunders <tj at castaglia.org>
+ * Copyright (c) 2015-2020 TJ Saunders <tj at castaglia.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -249,6 +249,10 @@ START_TEST (tls_using_tls_test) {
res = proxy_tls_set_tls(PROXY_TLS_ENGINE_AUTO);
tls = proxy_tls_using_tls();
fail_unless(tls == PROXY_TLS_ENGINE_AUTO, "Expected TLS auto, got %d", tls);
+
+ res = proxy_tls_set_tls(PROXY_TLS_ENGINE_IMPLICIT);
+ tls = proxy_tls_using_tls();
+ fail_unless(tls == PROXY_TLS_ENGINE_IMPLICIT, "Expected TLS implicit, got %d", tls);
#endif /* PR_USE_OPENSSL */
}
END_TEST
=====================================
t/lib/ProFTPD/Tests/Modules/mod_proxy.pm
=====================================
@@ -91,6 +91,11 @@ my $TESTS = {
test_class => [qw(forking reverse)],
},
+ proxy_reverse_abort => {
+ order => ++$order,
+ test_class => [qw(forking reverse)],
+ },
+
proxy_reverse_list_pasv => {
order => ++$order,
test_class => [qw(forking reverse)],
@@ -3011,6 +3016,121 @@ EOC
unlink($log_file);
}
+sub proxy_reverse_abort {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ WtmpLog off
+ TransferLog none
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ $client->quote('ABOR');
+ my $resp_code = $client->response_code();
+ my $resp_msg = $client->response_msg();
+
+ my $expected = 226;
+ $self->assert($expected == $resp_code,
+ test_msg("Expected response code $expected, got $resp_code"));
+
+ $expected = 'Abort successful';
+ $self->assert($expected eq $resp_msg,
+ test_msg("Expected response message '$expected', got '$resp_msg'"));
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
sub proxy_reverse_list_pasv {
my $self = shift;
my $tmpdir = $self->{tmpdir};
@@ -4966,38 +5086,7 @@ EOC
sub proxy_reverse_retr_abort {
my $self = shift;
my $tmpdir = $self->{tmpdir};
-
- my $config_file = "$tmpdir/proxy.conf";
- my $pid_file = File::Spec->rel2abs("$tmpdir/proxy.pid");
- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/proxy.scoreboard");
-
- my $log_file = test_get_logfile();
-
- my $auth_user_file = File::Spec->rel2abs("$tmpdir/proxy.passwd");
- my $auth_group_file = File::Spec->rel2abs("$tmpdir/proxy.group");
-
- my $user = 'proftpd';
- my $passwd = 'test';
- my $group = 'ftpd';
- my $home_dir = File::Spec->rel2abs($tmpdir);
- my $uid = 500;
- my $gid = 500;
-
- # Make sure that, if we're running as root, that the home directory has
- # permissions/privs set for the account we create
- if ($< == 0) {
- unless (chmod(0755, $home_dir)) {
- die("Can't set perms on $home_dir to 0755: $!");
- }
-
- unless (chown($uid, $gid, $home_dir)) {
- die("Can't set owner of $home_dir to $uid/$gid: $!");
- }
- }
-
- auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
- '/bin/bash');
- auth_group_write($auth_group_file, $group, $gid, $user);
+ my $setup = test_setup($tmpdir, 'proxy');
my $test_datalen = (4 * 1024 * 1024);
my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
@@ -5015,22 +5104,23 @@ sub proxy_reverse_retr_abort {
my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
$vhost_port += 12;
- my $proxy_config = get_reverse_proxy_config($tmpdir, $log_file, $vhost_port);
-
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
my $timeout_idle = 10;
my $config = {
- PidFile => $pid_file,
- ScoreboardFile => $scoreboard_file,
- SystemLog => $log_file,
- TraceLog => $log_file,
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20',
- AuthUserFile => $auth_user_file,
- AuthGroupFile => $auth_group_file,
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
SocketBindTight => 'on',
TimeoutIdle => $timeout_idle,
- UseIPv6 => 'on',
+ TimeoutLinger => 1,
+ UseIPv6 => 'off',
IfModules => {
'mod_proxy.c' => $proxy_config,
@@ -5042,37 +5132,38 @@ sub proxy_reverse_retr_abort {
Limit => {
LOGIN => {
- DenyUser => $user,
+ DenyUser => $setup->{user},
},
},
-
};
- my ($port, $config_user, $config_group) = config_write($config_file, $config);
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
- if (open(my $fh, ">> $config_file")) {
+ if (open(my $fh, ">> $setup->{config_file}")) {
print $fh <<EOC;
<VirtualHost 127.0.0.1>
Port $vhost_port
ServerName "Real Server"
- AuthUserFile $auth_user_file
- AuthGroupFile $auth_group_file
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
AuthOrder mod_auth_file.c
AllowOverride off
TimeoutIdle $timeout_idle
+ TimeoutLinger 1
TransferLog none
WtmpLog off
</VirtualHost>
EOC
unless (close($fh)) {
- die("Can't write $config_file: $!");
+ die("Can't write $setup->{config_file}: $!");
}
} else {
- die("Can't open $config_file: $!");
+ die("Can't open $setup->{config_file}: $!");
}
# Open pipes, for use between the parent and child processes. Specifically,
@@ -5091,8 +5182,9 @@ EOC
if ($pid) {
eval {
sleep(1);
+
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
- $client->login($user, $passwd);
+ $client->login($setup->{user}, $setup->{passwd});
$client->type('binary');
my $conn = $client->retr_raw($test_file);
@@ -5103,7 +5195,7 @@ EOC
my $buf;
$conn->read($buf, 8192, 30);
- eval { $conn->close() };
+ eval { $client->quote('ABOR') };
my $resp_code = $client->response_code();
my $resp_msg = $client->response_msg();
@@ -5111,7 +5203,6 @@ EOC
$client->quit();
};
-
if ($@) {
$ex = $@;
}
@@ -5120,7 +5211,7 @@ EOC
$wfh->flush();
} else {
- eval { server_wait($config_file, $rfh, $timeout_idle + 2) };
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
if ($@) {
warn($@);
exit 1;
@@ -5130,18 +5221,10 @@ EOC
}
# Stop server
- server_stop($pid_file);
-
+ server_stop($setup->{pid_file});
$self->assert_child_ok($pid);
- if ($ex) {
- test_append_logfile($log_file, $ex);
- unlink($log_file);
-
- die($ex);
- }
-
- unlink($log_file);
+ test_cleanup($setup->{log_file}, $ex);
}
sub proxy_reverse_stor_pasv {
@@ -6230,38 +6313,7 @@ EOC
sub proxy_reverse_stor_abort {
my $self = shift;
my $tmpdir = $self->{tmpdir};
-
- my $config_file = "$tmpdir/proxy.conf";
- my $pid_file = File::Spec->rel2abs("$tmpdir/proxy.pid");
- my $scoreboard_file = File::Spec->rel2abs("$tmpdir/proxy.scoreboard");
-
- my $log_file = test_get_logfile();
-
- my $auth_user_file = File::Spec->rel2abs("$tmpdir/proxy.passwd");
- my $auth_group_file = File::Spec->rel2abs("$tmpdir/proxy.group");
-
- my $user = 'proftpd';
- my $passwd = 'test';
- my $group = 'ftpd';
- my $home_dir = File::Spec->rel2abs($tmpdir);
- my $uid = 500;
- my $gid = 500;
-
- # Make sure that, if we're running as root, that the home directory has
- # permissions/privs set for the account we create
- if ($< == 0) {
- unless (chmod(0755, $home_dir)) {
- die("Can't set perms on $home_dir to 0755: $!");
- }
-
- unless (chown($uid, $gid, $home_dir)) {
- die("Can't set owner of $home_dir to $uid/$gid: $!");
- }
- }
-
- auth_user_write($auth_user_file, $user, $passwd, $uid, $gid, $home_dir,
- '/bin/bash');
- auth_group_write($auth_group_file, $group, $gid, $user);
+ my $setup = test_setup($tmpdir, 'proxy');
my $test_datalen = (4 * 1024 * 1024);
my $test_file = File::Spec->rel2abs("$tmpdir/test.txt");
@@ -6269,22 +6321,24 @@ sub proxy_reverse_stor_abort {
my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
$vhost_port += 12;
- my $proxy_config = get_reverse_proxy_config($tmpdir, $log_file, $vhost_port);
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
my $timeout_idle = 10;
my $config = {
- PidFile => $pid_file,
- ScoreboardFile => $scoreboard_file,
- SystemLog => $log_file,
- TraceLog => $log_file,
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20',
- AuthUserFile => $auth_user_file,
- AuthGroupFile => $auth_group_file,
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
SocketBindTight => 'on',
TimeoutIdle => $timeout_idle,
- UseIPv6 => 'on',
+ TimeoutLinger => 1,
+ UseIPv6 => 'off',
IfModules => {
'mod_proxy.c' => $proxy_config,
@@ -6296,37 +6350,37 @@ sub proxy_reverse_stor_abort {
Limit => {
LOGIN => {
- DenyUser => $user,
+ DenyUser => $setup->{user},
},
},
-
};
- my ($port, $config_user, $config_group) = config_write($config_file, $config);
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
- if (open(my $fh, ">> $config_file")) {
+ if (open(my $fh, ">> $setup->{config_file}")) {
print $fh <<EOC;
<VirtualHost 127.0.0.1>
Port $vhost_port
ServerName "Real Server"
- AuthUserFile $auth_user_file
- AuthGroupFile $auth_group_file
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
AuthOrder mod_auth_file.c
AllowOverride off
TimeoutIdle $timeout_idle
-
+ TimeoutLinger 1
TransferLog none
WtmpLog off
</VirtualHost>
EOC
unless (close($fh)) {
- die("Can't write $config_file: $!");
+ die("Can't write $setup->{config_file}: $!");
}
} else {
- die("Can't open $config_file: $!");
+ die("Can't open $setup->{config_file}: $!");
}
# Open pipes, for use between the parent and child processes. Specifically,
@@ -6345,8 +6399,9 @@ EOC
if ($pid) {
eval {
sleep(1);
+
my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 1);
- $client->login($user, $passwd);
+ $client->login($setup->{user}, $setup->{passwd});
$client->type('binary');
my $conn = $client->stor_raw($test_file);
@@ -6365,7 +6420,6 @@ EOC
$client->quit();
};
-
if ($@) {
$ex = $@;
}
@@ -6374,7 +6428,7 @@ EOC
$wfh->flush();
} else {
- eval { server_wait($config_file, $rfh, $timeout_idle + 2) };
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
if ($@) {
warn($@);
exit 1;
@@ -6384,18 +6438,10 @@ EOC
}
# Stop server
- server_stop($pid_file);
-
+ server_stop($setup->{pid_file});
$self->assert_child_ok($pid);
- if ($ex) {
- test_append_logfile($log_file, $ex);
- unlink($log_file);
-
- die($ex);
- }
-
- unlink($log_file);
+ test_cleanup($setup->{log_file}, $ex);
}
sub proxy_reverse_rest_retr {
=====================================
t/lib/ProFTPD/Tests/Modules/mod_proxy/sql.pm
=====================================
@@ -46,6 +46,26 @@ my $TESTS = {
test_class => [qw(forking mod_sql_sqlite reverse)],
},
+ proxy_sql_sqllog_forward_proxied_address_note_issue175 => {
+ order => ++$order,
+ test_class => [qw(forking forward mod_sql_sqlite)],
+ },
+
+ proxy_sql_sqllog_forward_proxied_file_xfer_issue175 => {
+ order => ++$order,
+ test_class => [qw(forking forward mod_sql_sqlite)],
+ },
+
+ proxy_sql_sqllog_reverse_proxied_address_note_issue175 => {
+ order => ++$order,
+ test_class => [qw(forking mod_sql_sqlite reverse)],
+ },
+
+ proxy_sql_sqllog_reverse_proxied_file_xfer_issue175 => {
+ order => ++$order,
+ test_class => [qw(forking mod_sql_sqlite reverse)],
+ },
+
};
sub new {
@@ -107,6 +127,31 @@ sub get_redis_config {
return $config;
}
+sub get_forward_proxy_config {
+ my $tmpdir = shift;
+ my $log_file = shift;
+ my $vhost_port = shift;
+
+ my $table_dir = File::Spec->rel2abs("$tmpdir/var/proxy");
+
+ my $config = {
+ ProxyEngine => 'on',
+ ProxyLog => $log_file,
+ ProxyRole => 'forward',
+ ProxyTables => $table_dir,
+ ProxyTimeoutConnect => '1sec',
+
+ Class => {
+ 'forward-proxy' => {
+ From => '127.0.0.1',
+ ProxyForwardEnabled => 'on',
+ },
+ },
+ };
+
+ return $config;
+}
+
sub get_reverse_proxy_config {
my $tmpdir = shift;
my $log_file = shift;
@@ -1257,4 +1302,843 @@ EOC
test_cleanup($setup->{log_file}, $ex);
}
+sub get_sessions {
+ my $db_file = shift;
+ my $where = shift;
+
+ my $sql = "SELECT user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, timestamp FROM proxy_sessions";
+ if ($where) {
+ $sql .= " WHERE $where";
+ }
+
+ my $cmd = "sqlite3 $db_file \"$sql\"";
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "Executing sqlite3: $cmd\n";
+ }
+
+ my $res = join('', `$cmd`);
+ chomp($res);
+
+ # The default sqlite3 delimiter is '|'
+ return split(/\|/, $res);
+}
+
+sub get_transfers {
+ my $db_file = shift;
+ my $where = shift;
+
+ my $sql = "SELECT user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, file, timestamp FROM proxy_transfers";
+ if ($where) {
+ $sql .= " WHERE $where";
+ }
+
+ my $cmd = "sqlite3 $db_file \"$sql\"";
+
+ if ($ENV{TEST_VERBOSE}) {
+ print STDERR "Executing sqlite3: $cmd\n";
+ }
+
+ my $res = join('', `$cmd`);
+ chomp($res);
+
+ # The default sqlite3 delimiter is '|'
+ return split(/\|/, $res);
+}
+
+sub proxy_sql_sqllog_forward_proxied_address_note_issue175 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+ my $vhost_port2 = $vhost_port - 7;
+
+ my $proxy_config = get_forward_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+ $proxy_config->{ProxyForwardMethod} = 'proxyuser at host,user';
+
+ my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
+
+ # Build up the sqlite3 command to create tables and populate them
+ my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
+ if (open(my $fh, "> $db_script")) {
+ print $fh <<EOS;
+CREATE TABLE proxy_sessions (
+ user TEXT,
+ protocol TEXT,
+ frontend_ipaddr TEXT,
+ local_ipaddr TEXT,
+ backend_ipaddr TEXT,
+ backend_port INTEGER,
+ timestamp TEXT
+);
+EOS
+ unless (close($fh)) {
+ die("Can't write $db_script: $!");
+ }
+
+ } else {
+ die("Can't open $db_script: $!");
+ }
+
+ my $cmd = "sqlite3 $db_file < $db_script";
+ build_db($cmd, $db_script);
+
+ # Make sure that, if we're running as root, the database file has
+ # the permissions/privs set for use by proftpd
+ if ($< == 0) {
+ unless (chmod(0666, $db_file)) {
+ die("Can't set perms on $db_file to 0666: $!");
+ }
+ }
+
+ my $timeout_idle = 10;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 auth:0 event:0 jot:20 lock:0 scoreboard:0 signal:0 proxy:20 proxy.db:20 proxy.forward:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 sql:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_sql.c' => {
+ SQLEngine => 'log',
+ SQLBackend => 'sqlite3',
+ SQLConnectInfo => $db_file,
+ SQLLogFile => $setup->{log_file},
+ SQLNamedQuery => 'session_start FREEFORM "INSERT INTO proxy_sessions (user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, timestamp) VALUES (\'%u\', \'%{protocol}\', \'%a\', \'%L\', \'%{note:mod_proxy.backend-ip}\', %{note:mod_proxy.backend-port}, \'%{iso8601}\')"',
+ SQLLog => 'PASS session_start',
+ }
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ TimeoutIdle $timeout_idle
+
+ TransferLog none
+ WtmpLog off
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 0);
+ $client->login("$setup->{user}\@127.0.0.1:$vhost_port", $setup->{passwd});
+ $client->login($setup->{user}, $setup->{passwd});
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex) if $ex;
+
+ eval {
+ my ($login, $protocol, $frontend_ip, $local_ip, $backend_ip, $backend_port, $timestamp) = get_sessions($db_file, "user = \'$setup->{user}\'");
+
+ my $expected = $setup->{user};
+ $self->assert($expected eq $login, "Expected '$expected', got '$login'");
+
+ my $expected = 'ftp';
+ $self->assert($expected eq $protocol, "Expected '$expected', got '$protocol'");
+
+ $expected = '127.0.0.1';
+ $self->assert($expected eq $frontend_ip,
+ "Expected frontend IP '$expected', got '$frontend_ip'");
+
+ $self->assert($expected eq $local_ip,
+ "Expected local IP '$expected', got '$local_ip'");
+
+ $self->assert($expected eq $backend_ip,
+ "Expected backend IP '$expected', got '$backend_ip'");
+
+ $expected = $vhost_port;
+ $self->assert($expected == $backend_port,
+ "Expected backend port $expected, got $backend_port");
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
+sub proxy_sql_sqllog_forward_proxied_file_xfer_issue175 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+ my $vhost_port2 = $vhost_port - 7;
+
+ my $proxy_config = get_forward_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+ $proxy_config->{ProxyForwardMethod} = 'proxyuser at host,user';
+
+ my $test_file = File::Spec->rel2abs("$tmpdir/test.dat");
+ if (open(my $fh, "> $test_file")) {
+ print $fh "Hello, World!\n";
+ unless (close($fh)) {
+ die("Can't write $test_file: $!");
+ }
+
+ } else {
+ die("Can't open $test_file: $!");
+ }
+
+ my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
+
+ # Build up the sqlite3 command to create tables and populate them
+ my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
+ if (open(my $fh, "> $db_script")) {
+ print $fh <<EOS;
+CREATE TABLE proxy_transfers (
+ user TEXT,
+ protocol TEXT,
+ frontend_ipaddr TEXT,
+ local_ipaddr TEXT,
+ backend_ipaddr TEXT,
+ backend_port INTEGER,
+ timestamp TEXT,
+ file TEXT
+);
+EOS
+ unless (close($fh)) {
+ die("Can't write $db_script: $!");
+ }
+
+ } else {
+ die("Can't open $db_script: $!");
+ }
+
+ my $cmd = "sqlite3 $db_file < $db_script";
+ build_db($cmd, $db_script);
+
+ # Make sure that, if we're running as root, the database file has
+ # the permissions/privs set for use by proftpd
+ if ($< == 0) {
+ unless (chmod(0666, $db_file)) {
+ die("Can't set perms on $db_file to 0666: $!");
+ }
+ }
+
+ my $timeout_idle = 10;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 auth:0 event:0 jot:20 lock:0 scoreboard:0 signal:0 proxy:20 proxy.db:20 proxy.forward:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 sql:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_sql.c' => {
+ SQLEngine => 'log',
+ SQLBackend => 'sqlite3',
+ SQLConnectInfo => $db_file,
+ SQLLogFile => $setup->{log_file},
+ SQLNamedQuery => 'file_xfer FREEFORM "INSERT INTO proxy_transfers (user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, file, timestamp) VALUES (\'%u\', \'%{protocol}\', \'%a\', \'%L\', \'%{note:mod_proxy.backend-ip}\', %{note:mod_proxy.backend-port}, \'%F\', \'%{iso8601}\')"',
+ SQLLog => 'RETR,STOR file_xfer',
+ }
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ TimeoutIdle $timeout_idle
+
+ TransferLog none
+ WtmpLog off
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 0);
+ $client->login("$setup->{user}\@127.0.0.1:$vhost_port", $setup->{passwd});
+ $client->login($setup->{user}, $setup->{passwd});
+
+ my $conn = $client->retr_raw('test.dat');
+ unless ($conn) {
+ die("RETR failed: " . $client->response_code() . ' ' .
+ $client->response_msg());
+ }
+
+ my $buf = '';
+ $conn->read($buf, 1024, 10);
+ eval { $conn->close() };
+
+ my $resp_code = $client->response_code();
+ my $resp_msg = $client->response_msg();
+ $self->assert_transfer_ok($resp_code, $resp_msg);
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex) if $ex;
+
+ eval {
+ my ($login, $protocol, $frontend_ip, $local_ip, $backend_ip, $backend_port, $path, $timestamp) = get_transfers($db_file, "user = \'$setup->{user}\'");
+
+ my $expected = $setup->{user};
+ $self->assert($expected eq $login, "Expected '$expected', got '$login'");
+
+ my $expected = 'ftp';
+ $self->assert($expected eq $protocol, "Expected '$expected', got '$protocol'");
+
+ $expected = '127.0.0.1';
+ $self->assert($expected eq $frontend_ip,
+ "Expected frontend IP '$expected', got '$frontend_ip'");
+
+ $self->assert($expected eq $local_ip,
+ "Expected local IP '$expected', got '$local_ip'");
+
+ $self->assert($expected eq $backend_ip,
+ "Expected backend IP '$expected', got '$backend_ip'");
+
+ $expected = $vhost_port;
+ $self->assert($expected == $backend_port,
+ "Expected backend port $expected, got $backend_port");
+
+ $expected = 'test.dat';
+ $self->assert($expected eq $path,
+ "Expected transfer path '$expected', got '$path'");
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
+sub proxy_sql_sqllog_reverse_proxied_address_note_issue175 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+ my $vhost_port2 = $vhost_port - 7;
+
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+ $proxy_config->{ProxyTimeoutConnect} = '1sec';
+ $proxy_config->{ProxyReverseConnectPolicy} = 'RoundRobin';
+
+ my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
+
+ # Build up the sqlite3 command to create tables and populate them
+ my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
+ if (open(my $fh, "> $db_script")) {
+ print $fh <<EOS;
+CREATE TABLE proxy_sessions (
+ user TEXT,
+ protocol TEXT,
+ frontend_ipaddr TEXT,
+ local_ipaddr TEXT,
+ backend_ipaddr TEXT,
+ backend_port INTEGER,
+ timestamp TEXT
+);
+EOS
+ unless (close($fh)) {
+ die("Can't write $db_script: $!");
+ }
+
+ } else {
+ die("Can't open $db_script: $!");
+ }
+
+ my $cmd = "sqlite3 $db_file < $db_script";
+ build_db($cmd, $db_script);
+
+ # Make sure that, if we're running as root, the database file has
+ # the permissions/privs set for use by proftpd
+ if ($< == 0) {
+ unless (chmod(0666, $db_file)) {
+ die("Can't set perms on $db_file to 0666: $!");
+ }
+ }
+
+ my $timeout_idle = 10;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 auth:0 event:0 jot:20 lock:0 scoreboard:0 signal:0 proxy:20 proxy.db:20 proxy.reverse:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 sql:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_sql.c' => {
+ SQLEngine => 'log',
+ SQLBackend => 'sqlite3',
+ SQLConnectInfo => $db_file,
+ SQLLogFile => $setup->{log_file},
+ SQLNamedQuery => 'session_start FREEFORM "INSERT INTO proxy_sessions (user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, timestamp) VALUES (\'%u\', \'%{protocol}\', \'%a\', \'%L\', \'%{note:mod_proxy.backend-ip}\', %{note:mod_proxy.backend-port}, \'%{iso8601}\')"',
+ SQLLog => 'PASS session_start',
+ }
+ },
+
+ Limit => {
+ LOGIN => {
+ DenyUser => $setup->{user},
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ TimeoutIdle $timeout_idle
+
+ TransferLog none
+ WtmpLog off
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 0);
+ $client->login($setup->{user}, $setup->{passwd});
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex) if $ex;
+
+ eval {
+ my ($login, $protocol, $frontend_ip, $local_ip, $backend_ip, $backend_port, $timestamp) = get_sessions($db_file, "user = \'$setup->{user}\'");
+
+ my $expected = $setup->{user};
+ $self->assert($expected eq $login, "Expected '$expected', got '$login'");
+
+ my $expected = 'ftp';
+ $self->assert($expected eq $protocol, "Expected '$expected', got '$protocol'");
+
+ $expected = '127.0.0.1';
+ $self->assert($expected eq $frontend_ip,
+ "Expected frontend IP '$expected', got '$frontend_ip'");
+
+ $self->assert($expected eq $local_ip,
+ "Expected local IP '$expected', got '$local_ip'");
+
+ $self->assert($expected eq $backend_ip,
+ "Expected backend IP '$expected', got '$backend_ip'");
+
+ $expected = $vhost_port;
+ $self->assert($expected == $backend_port,
+ "Expected backend port $expected, got $backend_port");
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
+sub proxy_sql_sqllog_reverse_proxied_file_xfer_issue175 {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+ my $vhost_port2 = $vhost_port - 7;
+
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+ $proxy_config->{ProxyReverseConnectPolicy} = 'Shuffle';
+
+ my $test_file = File::Spec->rel2abs("$tmpdir/test.dat");
+ if (open(my $fh, "> $test_file")) {
+ print $fh "Hello, World!\n";
+ unless (close($fh)) {
+ die("Can't write $test_file: $!");
+ }
+
+ } else {
+ die("Can't open $test_file: $!");
+ }
+
+ my $db_file = File::Spec->rel2abs("$tmpdir/proftpd.db");
+
+ # Build up the sqlite3 command to create tables and populate them
+ my $db_script = File::Spec->rel2abs("$tmpdir/proftpd.sql");
+ if (open(my $fh, "> $db_script")) {
+ print $fh <<EOS;
+CREATE TABLE proxy_transfers (
+ user TEXT,
+ protocol TEXT,
+ frontend_ipaddr TEXT,
+ local_ipaddr TEXT,
+ backend_ipaddr TEXT,
+ backend_port INTEGER,
+ timestamp TEXT,
+ file TEXT
+);
+EOS
+ unless (close($fh)) {
+ die("Can't write $db_script: $!");
+ }
+
+ } else {
+ die("Can't open $db_script: $!");
+ }
+
+ my $cmd = "sqlite3 $db_file < $db_script";
+ build_db($cmd, $db_script);
+
+ # Make sure that, if we're running as root, the database file has
+ # the permissions/privs set for use by proftpd
+ if ($< == 0) {
+ unless (chmod(0666, $db_file)) {
+ die("Can't set perms on $db_file to 0666: $!");
+ }
+ }
+
+ my $timeout_idle = 10;
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 auth:0 event:0 jot:20 lock:0 scoreboard:0 signal:0 proxy:20 proxy.db:20 proxy.reverse:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 sql:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_sql.c' => {
+ SQLEngine => 'log',
+ SQLBackend => 'sqlite3',
+ SQLConnectInfo => $db_file,
+ SQLLogFile => $setup->{log_file},
+ SQLNamedQuery => 'file_xfer FREEFORM "INSERT INTO proxy_transfers (user, protocol, frontend_ipaddr, local_ipaddr, backend_ipaddr, backend_port, file, timestamp) VALUES (\'%u\', \'%{protocol}\', \'%a\', \'%L\', \'%{note:mod_proxy.backend-ip}\', %{note:mod_proxy.backend-port}, \'%F\', \'%{iso8601}\')"',
+ SQLLog => 'RETR,STOR file_xfer',
+ }
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ TimeoutIdle $timeout_idle
+
+ TransferLog none
+ WtmpLog off
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ sleep(1);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port, 0, 0);
+ $client->login($setup->{user}, $setup->{passwd});
+
+ my $conn = $client->retr_raw('test.dat');
+ unless ($conn) {
+ die("RETR failed: " . $client->response_code() . ' ' .
+ $client->response_msg());
+ }
+
+ my $buf = '';
+ $conn->read($buf, 1024, 10);
+ eval { $conn->close() };
+
+ my $resp_code = $client->response_code();
+ my $resp_msg = $client->response_msg();
+ $self->assert_transfer_ok($resp_code, $resp_msg);
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, $timeout_idle + 2) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex) if $ex;
+
+ eval {
+ my ($login, $protocol, $frontend_ip, $local_ip, $backend_ip, $backend_port, $path, $timestamp) = get_transfers($db_file, "user = \'$setup->{user}\'");
+
+ my $expected = $setup->{user};
+ $self->assert($expected eq $login, "Expected '$expected', got '$login'");
+
+ my $expected = 'ftp';
+ $self->assert($expected eq $protocol, "Expected '$expected', got '$protocol'");
+
+ $expected = '127.0.0.1';
+ $self->assert($expected eq $frontend_ip,
+ "Expected frontend IP '$expected', got '$frontend_ip'");
+
+ $self->assert($expected eq $local_ip,
+ "Expected local IP '$expected', got '$local_ip'");
+
+ $self->assert($expected eq $backend_ip,
+ "Expected backend IP '$expected', got '$backend_ip'");
+
+ $expected = $vhost_port;
+ $self->assert($expected == $backend_port,
+ "Expected backend port $expected, got $backend_port");
+
+ $expected = 'test.dat';
+ $self->assert($expected eq $path,
+ "Expected transfer path '$expected', got '$path'");
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
1;
=====================================
t/lib/ProFTPD/Tests/Modules/mod_proxy/tls.pm
=====================================
@@ -45,6 +45,11 @@ my $TESTS = {
test_class => [qw(forking mod_tls reverse)],
},
+ proxy_reverse_backend_tls_implicit_login => {
+ order => ++$order,
+ test_class => [qw(forking mod_tls reverse)],
+ },
+
proxy_reverse_backend_tls_login_cached_session => {
order => ++$order,
test_class => [qw(forking mod_tls mod_tls_shmcache reverse)],
@@ -95,6 +100,11 @@ my $TESTS = {
test_class => [qw(forking mod_tls reverse)],
},
+ proxy_reverse_frontend_backend_tls_abort => {
+ order => ++$order,
+ test_class => [qw(forking mod_tls reverse)],
+ },
+
proxy_reverse_frontend_tls_json_peruser => {
order => ++$order,
test_class => [qw(forking mod_tls reverse)],
@@ -155,6 +165,11 @@ my $TESTS = {
test_class => [qw(forking mod_tls forward)],
},
+ proxy_forward_backend_tls_implicit_login => {
+ order => ++$order,
+ test_class => [qw(forking mod_tls forward)],
+ },
+
proxy_forward_backend_tls_login_failed_unknown_ca => {
order => ++$order,
test_class => [qw(forking mod_tls forward)],
@@ -1197,6 +1212,100 @@ EOC
unlink($log_file);
}
+sub proxy_reverse_backend_tls_implicit_login {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
+ my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
+
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file}, 990);
+ $proxy_config->{ProxyReverseServers} = 'ftps://demo:password@test.rebex.net:990';
+ $proxy_config->{ProxyTimeoutConnect} = '10s';
+ $proxy_config->{ProxyTLSEngine} = 'auto';
+ $proxy_config->{ProxyTLSCACertificateFile} = $ca_file;
+ $proxy_config->{ProxyTLSVerifyServer} = 'off';
+
+ if ($ENV{TEST_VERBOSE}) {
+ $proxy_config->{ProxyTLSOptions} = 'EnableDiags';
+ }
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.conn:20 proxy.db:20 proxy.netio:20 proxy.tls:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 proxy.ftp.sess:20 proxy.netio:20 proxy.tls:20 netio:20 tls:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+
+ Limit => {
+ LOGIN => {
+ DenyUser => $setup->{user},
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ # Give the server a chance to start up
+ sleep(2);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+ $client->login($setup->{user}, $setup->{passwd});
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
# TODO: Note that this test is used for manually reviewing the generated logs;
# it does NOT currently fail if session caching fails (although it should).
sub proxy_reverse_backend_tls_login_cached_session {
@@ -3219,6 +3328,186 @@ EOC
unlink($log_file);
}
+sub proxy_reverse_frontend_backend_tls_abort {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
+ my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
+
+ my $vhost_port = ProFTPD::TestSuite::Utils::get_high_numbered_port();
+ $vhost_port += 12;
+
+ my $proxy_config = get_reverse_proxy_config($tmpdir, $setup->{log_file},
+ $vhost_port);
+ $proxy_config->{ProxyTLSEngine} = 'auto';
+ $proxy_config->{ProxyTLSCACertificateFile} = $ca_file;
+ $proxy_config->{ProxyTLSVerifyServer} = 'off';
+
+ if ($ENV{TEST_VERBOSE}) {
+ $proxy_config->{ProxyTLSOptions} = 'EnableDiags';
+ }
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 event:0 lock:0 netio:20 scoreboard:0 signal:0 proxy:20 proxy.netio:20 proxy.db:20 proxy.reverse:20 proxy.tls:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 proxy.ftp.sess:20 tls:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_tls.c' => {
+ TLSEngine => 'on',
+ TLSLog => $setup->{log_file},
+ TLSProtocol => 'SSLv3 TLSv1',
+ TLSRequired => 'on',
+ TLSRSACertificateFile => $cert_file,
+ TLSCACertificateFile => $ca_file,
+ TLSOptions => 'NoSessionReuseRequired EnableDiags',
+ TLSTimeoutHandshake => 5,
+ TLSVerifyClient => 'off',
+ TLSVerifyServer => 'off',
+ },
+
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+
+ Limit => {
+ LOGIN => {
+ DenyUser => $setup->{user},
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ if (open(my $fh, ">> $setup->{config_file}")) {
+ print $fh <<EOC;
+<VirtualHost 127.0.0.1>
+ Port $vhost_port
+ ServerName "Real Server"
+
+ AuthUserFile $setup->{auth_user_file}
+ AuthGroupFile $setup->{auth_group_file}
+ AuthOrder mod_auth_file.c
+
+ AllowOverride off
+ WtmpLog off
+ TransferLog none
+
+ <IfModule mod_tls.c>
+ TLSEngine on
+ TLSLog $setup->{log_file}
+ TLSProtocol SSLv3 TLSv1
+ TLSRequired on
+ TLSRSACertificateFile $cert_file
+ TLSCACertificateFile $ca_file
+
+ TLSVerifyClient off
+ TLSVerifyServer off
+ TLSOptions EnableDiags NoSessionReuseRequired
+ </IfModule>
+</VirtualHost>
+EOC
+ unless (close($fh)) {
+ die("Can't write $setup->{config_file}: $!");
+ }
+
+ } else {
+ die("Can't open $setup->{config_file}: $!");
+ }
+
+ require Net::FTPSSL;
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ # Give the server a chance to start up
+ sleep(2);
+
+ my $ssl_opts = {
+ SSL_verify_mode => $IO::Socket::SSL::SSL_VERIFY_NONE,
+ };
+
+ my $client_opts = {
+ Encryption => 'E',
+ Port => $port,
+ SSL_Client_Certificate => $ssl_opts,
+ };
+
+ if ($ENV{TEST_VERBOSE}) {
+ $client_opts->{Debug} = 1;
+ }
+
+ my $client = Net::FTPSSL->new('127.0.0.1', %$client_opts);
+
+ unless ($client) {
+ die("Can't connect to FTPS server: " . IO::Socket::SSL::errstr());
+ }
+
+ unless ($client->login($setup->{user}, $setup->{passwd})) {
+ die("Can't login: " . $client->last_message());
+ }
+
+ my $res = $client->_abort();
+ unless ($res) {
+ die("ABOR failed unexpectedly: " . $client->last_message() .
+ "(" . IO::Socket::SSL::errstr() . ")");
+ }
+
+ my $resp_msg = $client->last_message();
+ my $expected = '226 Abort successful';
+ $self->assert($expected eq $resp_msg,
+ test_msg("Expected response '$expected', got '$resp_msg'"));
+
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh, 20) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
sub proxy_reverse_frontend_tls_json_peruser {
my $self = shift;
my $tmpdir = $self->{tmpdir};
@@ -5402,6 +5691,96 @@ EOC
unlink($log_file);
}
+sub proxy_forward_backend_tls_implicit_login {
+ my $self = shift;
+ my $tmpdir = $self->{tmpdir};
+ my $setup = test_setup($tmpdir, 'proxy');
+
+ my $cert_file = File::Spec->rel2abs('t/etc/modules/mod_tls/server-cert.pem');
+ my $ca_file = File::Spec->rel2abs('t/etc/modules/mod_tls/ca-cert.pem');
+
+ my $proxy_config = get_forward_proxy_config($tmpdir, $setup->{log_file}, 990);
+ $proxy_config->{ProxyForwardMethod} = 'user at host';
+ $proxy_config->{ProxyTLSEngine} = 'on';
+ $proxy_config->{ProxyTLSCACertificateFile} = $ca_file;
+ $proxy_config->{ProxyTLSVerifyServer} = 'off';
+ $proxy_config->{ProxyRetryCount} = 1;
+
+ if ($ENV{TEST_VERBOSE}) {
+ $proxy_config->{ProxyTLSOptions} = 'EnableDiags';
+ }
+
+ my $config = {
+ PidFile => $setup->{pid_file},
+ ScoreboardFile => $setup->{scoreboard_file},
+ SystemLog => $setup->{log_file},
+ TraceLog => $setup->{log_file},
+ Trace => 'DEFAULT:10 event:0 lock:0 scoreboard:0 signal:0 proxy:20 proxy.conn:20 proxy.forward:20 proxy.netio:20 proxy.tls:20 proxy.ftp.conn:20 proxy.ftp.ctrl:20 proxy.ftp.data:20 proxy.ftp.msg:20 netio:20 tls:20',
+
+ AuthUserFile => $setup->{auth_user_file},
+ AuthGroupFile => $setup->{auth_group_file},
+ SocketBindTight => 'on',
+
+ IfModules => {
+ 'mod_proxy.c' => $proxy_config,
+
+ 'mod_delay.c' => {
+ DelayEngine => 'off',
+ },
+ },
+ };
+
+ my ($port, $config_user, $config_group) = config_write($setup->{config_file},
+ $config);
+
+ require Net::FTPSSL;
+
+ # Open pipes, for use between the parent and child processes. Specifically,
+ # the child will indicate when it's done with its test by writing a message
+ # to the parent.
+ my ($rfh, $wfh);
+ unless (pipe($rfh, $wfh)) {
+ die("Can't open pipe: $!");
+ }
+
+ my $ex;
+
+ # Fork child
+ $self->handle_sigchld();
+ defined(my $pid = fork()) or die("Can't fork: $!");
+ if ($pid) {
+ eval {
+ # Give the server a chance to start up
+ sleep(2);
+
+ my $client = ProFTPD::TestSuite::FTP->new('127.0.0.1', $port);
+ $client->login('demo at test.rebex.net:990', 'password');
+ $client->quit();
+ };
+ if ($@) {
+ $ex = $@;
+ }
+
+ $wfh->print("done\n");
+ $wfh->flush();
+
+ } else {
+ eval { server_wait($setup->{config_file}, $rfh) };
+ if ($@) {
+ warn($@);
+ exit 1;
+ }
+
+ exit 0;
+ }
+
+ # Stop server
+ server_stop($setup->{pid_file});
+ $self->assert_child_ok($pid);
+
+ test_cleanup($setup->{log_file}, $ex);
+}
+
sub proxy_forward_backend_tls_login_failed_unknown_ca {
my $self = shift;
my $tmpdir = $self->{tmpdir};
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-proxy/-/commit/21d2721455e883ba788688902d94ec18ae1e86de
--
View it on GitLab: https://salsa.debian.org/debian-proftpd-team/proftpd-mod-proxy/-/commit/21d2721455e883ba788688902d94ec18ae1e86de
You're receiving this email because of your account on salsa.debian.org.
More information about the Pkg-proftpd-maintainers
mailing list