int proxy_ftp_sess_send_auth_tls(pool *p, struct proxy_session *proxy_sess) { int uri_tls, use_tls, xerrno; char *auth_feat; array_header *auth_feats = NULL; pool *tmp_pool; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; use_tls = proxy_tls_use_tls(); if (use_tls == PROXY_TLS_ENGINE_OFF) { pr_trace_msg(trace_channel, 19, "TLS support not enabled/desired, skipping"); return 0; } /* Check for any per-URI scheme-based TLS requirements. */ uri_tls = proxy_conn_get_tls(proxy_sess->dst_pconn); auth_feat = pr_table_get(proxy_sess->backend_features, C_AUTH, NULL); if (auth_feat == NULL) { /* Backend server does not indicate that it supports AUTH via FEAT. */ /* If TLS is required, then fail now. */ if (uri_tls == PROXY_TLS_ENGINE_ON || use_tls == PROXY_TLS_ENGINE_ON) { const char *ip_str; ip_str = pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr); if (uri_tls == PROXY_TLS_ENGINE_ON) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "backend server %s does not support AUTH TLS (see FEAT response) but " "URI '%.100s' requires TLS, failing connection", ip_str, proxy_conn_get_uri(proxy_sess->dst_pconn)); } else if (use_tls == PROXY_TLS_ENGINE_ON) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "backend server %s does not support AUTH TLS (see FEAT response) but " "ProxyTLSEngine requires TLS, failing connection", ip_str); } errno = EPERM; return -1; } /* Tell the Proxy NetIO API to NOT try to use our TLS NetIO. */ proxy_netio_use(PR_NETIO_STRM_CTRL, NULL); pr_trace_msg(trace_channel, 9, "backend server does not support AUTH TLS (via FEAT), skipping"); return 0; } tmp_pool = make_sub_pool(p); /* Note: the FEAT response against IIS servers shows e.g.: * * 211-Extended features supported: * LANG EN* * UTF8 * AUTH TLS;TLS-C;SSL;TLS-P; * PBSZ * PROT C;P; * CCC * HOST * SIZE * MDTM * REST STREAM * 211 END * * Note how the AUTH and PROT values are not exactly as specified * in RFC 4217. This means we'll need to deal with as is. There will * be other servers with other FEAT response formats, too. */ if (parse_feat(tmp_pool, auth_feat, &auth_feats) > 0) { register unsigned int i; pr_trace_msg(trace_channel, 9, "parsed FEAT value '%s' into %d values", auth_feat, auth_feats->nelts); for (i = 0; i < auth_feats->nelts; i++) { char *val; val = ((char **) auth_feats->elts)[i]; pr_trace_msg(trace_channel, 9, " %s", val); } } cmd = pr_cmd_alloc(tmp_pool, 2, C_AUTH, "TLS"); cmd->arg = pstrdup(tmp_pool, "TLS"); resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines); if (resp == NULL) { xerrno = errno; proxy_netio_use(PR_NETIO_STRM_CTRL, NULL); destroy_pool(tmp_pool); errno = xerrno; return -1; } if (resp->num[0] != '2') { pr_trace_msg(trace_channel, 4, "received unexpected %s response code %s from backend", (char *) cmd->argv[0], resp->num); proxy_netio_use(PR_NETIO_STRM_CTRL, NULL); destroy_pool(tmp_pool); errno = EPERM; return -1; } destroy_pool(tmp_pool); return 0; }
int proxy_ftp_sess_send_auth_tls(pool *p, struct proxy_session *proxy_sess) { int uri_tls, use_tls, xerrno; char *auth_feat; array_header *auth_feats = NULL; pool *tmp_pool; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; use_tls = proxy_tls_use_tls(); if (use_tls == PROXY_TLS_ENGINE_OFF) { pr_trace_msg(trace_channel, 19, "TLS support not enabled/desired, skipping"); return 0; } /* Check for any per-URI scheme-based TLS requirements. */ uri_tls = proxy_conn_get_tls(proxy_sess->dst_pconn); auth_feat = pr_table_get(proxy_sess->backend_features, C_AUTH, NULL); if (auth_feat == NULL) { /* Backend server does not indicate that it supports AUTH via FEAT. * * Even though this is the case, we will still try to send the AUTH * command. A malicious attacker could be modifying the plaintext * FEAT listing, to make us think that TLS is not supported, and thus * prevent us from encrypting the session (a la "SSL stripping"). */ /* If TLS is required, then complain loudly. */ if (uri_tls == PROXY_TLS_ENGINE_ON || use_tls == PROXY_TLS_ENGINE_ON) { const char *ip_str; ip_str = pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr); if (uri_tls == PROXY_TLS_ENGINE_ON) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "backend server %s does not support AUTH TLS (see FEAT response) but " "URI '%.100s' requires TLS, attempting anyway", ip_str, proxy_conn_get_uri(proxy_sess->dst_pconn)); } else if (use_tls == PROXY_TLS_ENGINE_ON) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "backend server %s does not support AUTH TLS (see FEAT response) but " "ProxyTLSEngine requires TLS, attempting anyway", ip_str); } } pr_trace_msg(trace_channel, 9, "backend server does not support AUTH TLS (via FEAT)"); } tmp_pool = make_sub_pool(p); /* Note: the FEAT response against IIS servers shows e.g.: * * 211-Extended features supported: * LANG EN* * UTF8 * AUTH TLS;TLS-C;SSL;TLS-P; * PBSZ * PROT C;P; * CCC * HOST * SIZE * MDTM * REST STREAM * 211 END * * Note how the AUTH and PROT values are not exactly as specified * in RFC 4217. This means we'll need to deal with it as is. There will * be other servers with other FEAT response formats, too. */ if (parse_feat(tmp_pool, auth_feat, &auth_feats) > 0) { register unsigned int i; pr_trace_msg(trace_channel, 9, "parsed FEAT value '%s' into %d values", auth_feat, auth_feats->nelts); for (i = 0; i < auth_feats->nelts; i++) { char *val; val = ((char **) auth_feats->elts)[i]; pr_trace_msg(trace_channel, 9, " %s", val); } } /* XXX How should we interoperate with servers that support/want the * older formats, e.g.: * * AUTH SSL (which automatically assumes PBSZ 0, PROT P) * AUTH TLS-P (synonym for AUTH SSL) * AUTH TLS-C (synonym for AUTH TLS) */ cmd = pr_cmd_alloc(tmp_pool, 2, C_AUTH, "TLS"); cmd->arg = pstrdup(tmp_pool, "TLS"); resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines); if (resp == NULL) { xerrno = errno; proxy_netio_use(PR_NETIO_STRM_CTRL, NULL); destroy_pool(tmp_pool); errno = xerrno; return -1; } if (resp->num[0] != '2') { /* XXX Some older servers might respond with a 334 response code, per * RFC 2228. See, for example: * http://serverfault.com/questions/640978/ftp-alter-server-response-in-transit */ pr_trace_msg(trace_channel, 4, "received unexpected %s response code %s from backend", (char *) cmd->argv[0], resp->num); proxy_netio_use(PR_NETIO_STRM_CTRL, NULL); destroy_pool(tmp_pool); errno = EPERM; return -1; } destroy_pool(tmp_pool); 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; pr_netaddr_t *dst_addr; array_header *other_addrs = NULL; dst_addr = proxy_sess->dst_addr; other_addrs = proxy_sess->other_addrs; server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn == NULL) { xerrno = errno; if (other_addrs != NULL) { register unsigned int i; /* Try the other IP addresses for the requested name (if any) as well. */ for (i = 0; i < other_addrs->nelts; i++) { dst_addr = ((pr_netaddr_t **) other_addrs->elts)[i]; pr_trace_msg(trace_channel, 8, "attempting to connect to other address #%u (%s) for requested " "URI '%.100s'", i+1, pr_netaddr_get_ipstr(dst_addr), proxy_conn_get_uri(proxy_sess->dst_pconn)); server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn != NULL) { proxy_sess->dst_addr = dst_addr; break; } } } if (server_conn == NULL) { xerrno = errno; /* EINVALs lead to strange-looking error responses; change them to * EPERM. */ if (xerrno == EINVAL) { xerrno = EPERM; } } errno = xerrno; 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); if (*resp == NULL) { xerrno = errno; pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to read banner from server %s:%u: %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), strerror(xerrno)); errno = EPERM; return -1; } if ((*resp)->num[0] != '2') { banner_ok = FALSE; } (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received banner from backend %s:%u%s: %s %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), banner_ok ? "" : ", DISCONNECTING", (*resp)->num, (*resp)->msg); if (banner_ok == FALSE) { pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; errno = EPERM; return -1; } /* Get the features supported by the backend server */ if (proxy_ftp_sess_get_feat(p, proxy_sess) < 0) { if (errno != EPERM) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to determine features of backend server: %s", strerror(errno)); } } use_tls = proxy_tls_use_tls(); if (use_tls != PROXY_TLS_ENGINE_OFF) { if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error enabling TLS on control connection to backend server: %s", strerror(xerrno)); pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } } 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) { if (proxy_sess_state & PROXY_SESS_STATE_BACKEND_HAS_CTRL_TLS) { /* NOTE: should this be a fatal error? */ (void) proxy_ftp_sess_send_pbsz_prot(p, proxy_sess); } } (void) proxy_ftp_sess_send_host(p, proxy_sess); proxy_sess_state |= PROXY_SESS_STATE_CONNECTED; return 0; }