END_TEST START_TEST (cmd_cmp_test) { cmd_rec *cmd; int res; res = pr_cmd_cmp(NULL, 1); fail_unless(res == -1, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); cmd = pr_cmd_alloc(p, 1, "foo"); res = pr_cmd_cmp(cmd, 0); fail_unless(res == -1, "Failed to handle bad ID argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); res = pr_cmd_cmp(cmd, 1); fail_unless(res == 1, "Failed to handle empty cmd_rec argument"); cmd = pr_cmd_alloc(p, 1, C_RETR); res = pr_cmd_cmp(cmd, PR_CMD_ACCT_ID); fail_unless(res > 0, "Unexpected comparison result: %d", res); res = pr_cmd_cmp(cmd, PR_CMD_STOR_ID); fail_unless(res < 0, "Unexpected comparison result: %d", res); res = pr_cmd_cmp(cmd, PR_CMD_RETR_ID); fail_unless(res == 0, "Unexpected comparison result: %d", res); }
END_TEST START_TEST (cmd_strcmp_test) { cmd_rec *cmd; int res; res = pr_cmd_strcmp(NULL, NULL); fail_unless(res == -1, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); mark_point(); cmd = pr_cmd_alloc(p, 0); res = pr_cmd_strcmp(cmd, "a"); fail_unless(res == 1, "Failed to handle empty cmd_rec"); mark_point(); cmd = pr_cmd_alloc(p, 1, C_RETR); res = pr_cmd_strcmp(cmd, "a"); fail_unless(res > 0, "Unexpected comparison result: %d", res); mark_point(); cmd->cmd_id = 0; res = pr_cmd_strcmp(cmd, "S"); fail_unless(res > 0, "Unexpected comparison result: %d", res); mark_point(); cmd->cmd_id = 0; res = pr_cmd_strcmp(cmd, C_RETR); fail_unless(res == 0, "Unexpected comparison result: %d", res); }
END_TEST START_TEST (cmd_is_ssh2_test) { int res; cmd_rec *cmd; res = pr_cmd_is_ssh2(NULL); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); cmd = pr_cmd_alloc(p, 1, C_SYST); cmd->argv[0] = NULL; res = pr_cmd_is_ssh2(cmd); fail_unless(res < 0, "Failed to handle null name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); mark_point(); cmd->argv[0] = C_SYST; res = pr_cmd_is_ssh2(cmd); fail_unless(res == FALSE, "Expected FALSE (%d), got %d", FALSE, res); mark_point(); cmd = pr_cmd_alloc(p, 1, "SSH-2.0-OpenSSH_5.6p1"); res = pr_cmd_is_ssh2(cmd); fail_unless(res == TRUE, "Expected TRUE (%d), got %d", TRUE, res); mark_point(); cmd = pr_cmd_alloc(p, 1, "SSH-1.99-JSCH"); res = pr_cmd_is_ssh2(cmd); fail_unless(res == TRUE, "Expected TRUE (%d), got %d", TRUE, res); }
END_TEST START_TEST (module_create_ret_test) { cmd_rec *cmd; modret_t *mr; char *numeric, *msg; mr = mod_create_ret(NULL, 0, NULL, NULL); fail_unless(mr == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); cmd = pr_cmd_alloc(p, 1, "testsuite"); mr = mod_create_ret(cmd, 1, NULL, NULL); fail_unless(mr != NULL, "Failed to create modret: %s", strerror(errno)); fail_unless(mr->mr_error == 1, "Expected 1, got %d", mr->mr_error); fail_unless(mr->mr_numeric == NULL, "Expected null, got '%s'", mr->mr_numeric); fail_unless(mr->mr_message == NULL, "Expected null, got '%s'", mr->mr_message); numeric = "foo"; msg = "bar"; mr = mod_create_ret(cmd, 1, numeric, msg); fail_unless(mr != NULL, "Failed to create modret: %s", strerror(errno)); fail_unless(mr->mr_error == 1, "Expected 1, got %d", mr->mr_error); fail_unless(mr->mr_numeric != NULL, "Expected '%s', got null"); fail_unless(strcmp(mr->mr_numeric, numeric) == 0, "Expected '%s', got '%s'", numeric, mr->mr_numeric); fail_unless(mr->mr_message != NULL, "Expected '%s', got null"); fail_unless(strcmp(mr->mr_message, msg) == 0, "Expected '%s', got '%s'", msg, mr->mr_message); }
static int read_service_req(struct ssh2_packet *pkt, char **service) { unsigned char *buf; char *service_name; uint32_t buflen; cmd_rec *cmd; buf = pkt->payload; buflen = pkt->payload_len; service_name = sftp_msg_read_string(pkt->pool, &buf, &buflen); pr_trace_msg(trace_channel, 10, "'%s' service requested", service_name); cmd = pr_cmd_alloc(pkt->pool, 1, pstrdup(pkt->pool, "SERVICE_REQUEST")); cmd->arg = service_name; cmd->cmd_class = CL_MISC|CL_SSH; if (strncmp(service_name, "ssh-userauth", 13) == 0 || strncmp(service_name, "ssh-connection", 14) == 0) { if (service) *service = pstrdup(service_pool, service_name); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); return 0; } (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "client requested unsupported '%s' service", service_name); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); return -1; }
END_TEST START_TEST (cmd_get_errno_test) { int res, *xerrno = NULL; cmd_rec *cmd = NULL; res = pr_cmd_get_errno(NULL); fail_unless(res == -1, "Failed to handle null cmd_rec"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); cmd = pr_cmd_alloc(p, 1, "foo"); res = pr_cmd_get_errno(cmd); fail_unless(res == 0, "Expected errno 0, got %d", res); (void) pr_table_remove(cmd->notes, "errno", NULL); res = pr_cmd_get_errno(cmd); fail_unless(res < 0, "Failed to handle missing 'errno' note"); fail_unless(errno == ENOENT, "Expected ENOENT (%d), got %s (%d)", ENOENT, strerror(errno), errno); xerrno = pcalloc(cmd->pool, sizeof(int)); (void) pr_table_add(cmd->notes, "errno", xerrno, sizeof(int)); res = pr_cmd_set_errno(NULL, ENOENT); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); res = pr_cmd_set_errno(cmd, ENOENT); fail_unless(res == 0, "Failed to stash errno ENOENT: %s", strerror(errno)); res = pr_cmd_get_errno(cmd); fail_unless(res == ENOENT, "Expected errno ENOENT, got %s (%d)", strerror(res), res); }
END_TEST START_TEST (module_create_error_test) { cmd_rec *cmd; modret_t *mr; mr = mod_create_error(NULL, 0); fail_unless(mr == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); cmd = pr_cmd_alloc(p, 1, "testsuite"); mr = mod_create_error(cmd, 1); fail_unless(mr != NULL, "Failed to create modret: %s", strerror(errno)); fail_unless(mr->mr_error == 1, "Expected 1, got %d", mr->mr_error); }
END_TEST START_TEST (module_create_data_test) { cmd_rec *cmd; modret_t *mr; int data = 1; mr = mod_create_data(NULL, NULL); fail_unless(mr == NULL, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); cmd = pr_cmd_alloc(p, 1, "testsuite"); mr = mod_create_data(cmd, &data); fail_unless(mr != NULL, "Failed to create modret: %s", strerror(errno)); fail_unless(mr->data == &data, "Expected %p, got %p", &data, mr->data); }
END_TEST START_TEST (response_pool_bug3711_test) { int res; cmd_rec *cmd; pool *resp_pool, *cmd_pool; char *err_code = R_450, *err_msg = "Busy"; resp_pool = make_sub_pool(p); cmd_pool = make_sub_pool(p); cmd = pr_cmd_alloc(cmd_pool, 1, "foo"); pr_response_set_pool(cmd->pool); pr_response_add_err(err_code, "%s", err_msg); /* We expect segfaults here, so use the mark_point() function to get * more accurate reporting of the problematic line of code in the * error logs. */ mark_point(); /* We explicitly do NOT reset the Response API pool here, to emulate the * behavior of Bug#3711. * * In the future, we could address this by proving a Pool API function * that e.g. the Response API could use, to check whether the given * pool is still a valid pool. To do this, the Pool API would keep a * list of allocated pools, which would then be scanned. In practice such * a list is maintained, albeit in a tree form. And there is tracking * of the root trees for pools; permanent_pool is not the only root pool * which can be created/used. */ destroy_pool(cmd_pool); mark_point(); pr_response_add_err(err_code, "%s", err_msg); mark_point(); pr_response_add_err(err_code, "%s", err_msg); mark_point(); pr_response_add_err(err_code, "%s", err_msg); }
int proxy_ftp_sess_send_host(pool *p, struct proxy_session *proxy_sess) { pool *tmp_pool; int xerrno = 0; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; const char *host; if (pr_table_get(proxy_sess->backend_features, C_HOST, NULL) == NULL) { pr_trace_msg(trace_channel, 9, "HOST not supported by backend server, ignoring"); return 0; } tmp_pool = make_sub_pool(p); host = proxy_conn_get_host(proxy_sess->dst_pconn); cmd = pr_cmd_alloc(tmp_pool, 2, C_HOST, host); cmd->arg = pstrdup(tmp_pool, host); resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines); if (resp == NULL) { xerrno = errno; 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); destroy_pool(tmp_pool); errno = EPERM; return -1; } destroy_pool(tmp_pool); return 0; }
END_TEST START_TEST (cmd_set_name_test) { int res; cmd_rec *cmd; const char *name; res = pr_cmd_set_name(NULL, NULL); fail_unless(res < 0, "Failed to handle null arguments"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); cmd = pr_cmd_alloc(p, 1, "foo"); res = pr_cmd_set_name(cmd, NULL); fail_unless(res < 0, "Failed to handle null name"); fail_unless(errno == EINVAL, "Expected EINVAL (%d), got %s (%d)", EINVAL, strerror(errno), errno); name = "bar"; res = pr_cmd_set_name(cmd, name); fail_unless(res == 0, "Failed to command name to '%s': %s", name, strerror(errno)); }
END_TEST START_TEST (cmd_get_displayable_str_test) { char *ok, *res = NULL; cmd_rec *cmd = NULL; size_t len = 0; res = pr_cmd_get_displayable_str(NULL, NULL); fail_unless(res == NULL, "Failed to handle null cmd_rec"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); cmd = pr_cmd_alloc(p, 1, "foo"); res = pr_cmd_get_displayable_str(cmd, NULL); ok = "foo"; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); mark_point(); cmd->argc = 0; res = pr_cmd_get_displayable_str(cmd, NULL); fail_if(res == NULL, "Expected string, got null"); /* Note: We still expect the PREVIOUS ok value, since * pr_cmd_get_displayable_str() should cache the constructed string, * rather than creating it anew. */ fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); mark_point(); pr_cmd_clear_cache(cmd); res = pr_cmd_get_displayable_str(cmd, NULL); ok = ""; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); mark_point(); cmd = pr_cmd_alloc(p, 1, "bar"); cmd->arg = NULL; res = pr_cmd_get_displayable_str(cmd, NULL); ok = "bar"; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); mark_point(); cmd = pr_cmd_alloc(p, 1, "baz"); cmd->argv[0] = NULL; cmd->arg = pstrdup(p, "baz"); res = pr_cmd_get_displayable_str(cmd, NULL); /* cmd->argv[0] is the command name; without that, it does not matter * what cmd->arg is. Hence why if cmd->argv[0] is null, we expect the * empty string. */ ok = ""; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); mark_point(); cmd = pr_cmd_alloc(p, 3, "foo", "bar", "baz"); cmd->arg = NULL; res = pr_cmd_get_displayable_str(cmd, NULL); /* cmd->argv[0] is the command name; without that, it does not matter * what cmd->arg is. Hence why if cmd->argv[0] is null, we expect the * empty string. */ ok = "foo bar baz"; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); /* Make sure we can handle cases where cmd_rec->argv has been tampered * with. */ mark_point(); cmd = pr_cmd_alloc(p, 3, "foo", "bar", "baz"); cmd->argv[0] = NULL; res = pr_cmd_get_displayable_str(cmd, NULL); ok = " bar baz"; fail_if(res == NULL, "Expected string, got null"); fail_unless(strcmp(res, ok) == 0, "Expected '%s', got '%s'", ok, res); }
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; }
const pr_netaddr_t *proxy_ftp_xfer_prepare_passive(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int res, xerrno = 0; cmd_rec *pasv_cmd; const pr_netaddr_t *remote_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; unsigned short remote_port; char *passive_cmd, *passive_respcode = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return NULL; } /* Whether we send a PASV (and expect 227) or an EPSV (and expect 229) * need to depend on the policy_id. */ switch (policy_id) { case PR_CMD_PASV_ID: passive_cmd = C_PASV; break; case PR_CMD_EPSV_ID: /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ passive_cmd = C_EPSV; if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } break; default: /* In this case, the cmd we were given is the one we should send to * the backend server -- but only if it is either EPSV or PASV. */ if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP passive transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return NULL; } passive_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) == 0) { /* If the remote host does not mention EPSV in its features, fall back * to using PASV. */ if (pr_table_get(proxy_sess->backend_features, C_EPSV, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPSV not supported by backend server (via FEAT), using PASV"); if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } passive_cmd = C_PASV; policy_id = PR_CMD_PASV_ID; } } break; } pasv_cmd = pr_cmd_alloc(cmd->tmp_pool, 1, passive_cmd); switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: passive_respcode = R_227; break; case PR_CMD_EPSV_ID: passive_respcode = R_229; break; } res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, pasv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines, flags); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) pasv_cmd->argv[0], strerror(xerrno)); pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } /* We specifically expect a 227 or 229 response code here; anything else is * an error. Right? */ if (strncmp(resp->num, passive_respcode, 4) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received response code %s, but expected %s for %s command", resp->num, passive_respcode, (char *) pasv_cmd->argv[0]); if (policy_id == PR_CMD_EPSV_ID) { /* If using EPSV failed, try again using PASV, and switch the * DataTransferPolicy (if EPSV) to be PASV, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPSV_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPSV to PASV DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PASV_ID; } return proxy_ftp_xfer_prepare_passive(PR_CMD_PASV_ID, cmd, error_code, proxy_sess, flags); } res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); errno = EPERM; return NULL; } switch (pr_cmd_get_id(pasv_cmd->argv[0])) { case PR_CMD_PASV_ID: remote_addr = proxy_ftp_msg_parse_addr(cmd->tmp_pool, resp->msg, pr_netaddr_get_family(session.c->local_addr)); break; case PR_CMD_EPSV_ID: remote_addr = proxy_ftp_msg_parse_ext_addr(cmd->tmp_pool, resp->msg, session.c->remote_addr, PR_CMD_EPSV_ID, NULL); break; } if (remote_addr == NULL) { xerrno = errno; pr_trace_msg(trace_channel, 2, "error parsing %s response '%s': %s", (char *) pasv_cmd->argv[0], resp->msg, strerror(xerrno)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } remote_port = ntohs(pr_netaddr_get_port(remote_addr)); /* Make sure that the given address matches the address to which we * originally connected. */ if (pr_netaddr_cmp(remote_addr, proxy_sess->backend_ctrl_conn->remote_addr) != 0) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s address %s (address mismatch with %s)", (char *) pasv_cmd->argv[0], pr_netaddr_get_ipstr(remote_addr), pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr)); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } if (remote_port < 1024) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "Refused %s port %hu (below 1024)", (char *) pasv_cmd->argv[0], remote_port); xerrno = EPERM; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return NULL; } return remote_addr; }
int sftp_kbdint_recv_response(pool *p, unsigned int expected_count, unsigned int *rcvd_count, const char ***responses) { register unsigned int i; unsigned char *buf; cmd_rec *cmd; array_header *list; uint32_t buflen, resp_count; struct ssh2_packet *pkt; char mesg_type; int res; if (p == NULL || rcvd_count == NULL || responses == NULL) { errno = EINVAL; return -1; } pkt = sftp_ssh2_packet_create(kbdint_pool); res = sftp_ssh2_packet_read(sftp_conn->rfd, pkt); if (res < 0) { destroy_pool(pkt->pool); return res; } mesg_type = sftp_ssh2_packet_get_mesg_type(pkt); if (mesg_type != SFTP_SSH2_MSG_USER_AUTH_INFO_RESP) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "expecting USER_AUTH_INFO_RESP message, received %s (%d)", sftp_ssh2_packet_get_mesg_type_desc(mesg_type), mesg_type); destroy_pool(pkt->pool); errno = EPERM; return -1; } cmd = pr_cmd_alloc(pkt->pool, 2, pstrdup(pkt->pool, "USER_AUTH_INFO_RESP")); cmd->arg = "(data)"; pr_trace_msg(trace_channel, 9, "reading USER_AUTH_INFO_RESP message from client"); buf = pkt->payload; buflen = pkt->payload_len; resp_count = sftp_msg_read_int(pkt->pool, &buf, &buflen); /* Ensure that the number of responses sent by the client is the same * as the number of challenges sent, lest a malicious client attempt to * trick us into allocating too much memory (Bug#3973). */ if (resp_count != expected_count) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "sent %lu %s, but received %lu %s", (unsigned long) expected_count, expected_count != 1 ? "challenges" : "challenge", (unsigned long) resp_count, resp_count != 1 ? "responses" : "response"); destroy_pool(pkt->pool); errno = EPERM; return -1; } if (resp_count > SFTP_KBDINT_MAX_RESPONSES) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "received too many responses (%lu > max %lu), rejecting", (unsigned long) resp_count, (unsigned long) SFTP_KBDINT_MAX_RESPONSES); destroy_pool(pkt->pool); errno = EPERM; return -1; } list = make_array(p, resp_count, sizeof(char *)); for (i = 0; i < resp_count; i++) { char *resp; resp = sftp_msg_read_string(pkt->pool, &buf, &buflen); *((char **) push_array(list)) = pstrdup(p, sftp_utf8_decode_str(p, resp)); } *rcvd_count = (unsigned int) resp_count; *responses = ((const char **) list->elts); return 0; }
int proxy_ftp_sess_get_feat(pool *p, struct proxy_session *proxy_sess) { pool *tmp_pool; int res, xerrno = 0; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; char *feats, *token; size_t token_len = 0; tmp_pool = make_sub_pool(p); cmd = pr_cmd_alloc(tmp_pool, 1, C_FEAT); res = proxy_ftp_ctrl_send_cmd(tmp_pool, proxy_sess->backend_ctrl_conn, cmd); if (res < 0) { xerrno = errno; pr_trace_msg(trace_channel, 4, "error sending %s to backend: %s", (char *) cmd->argv[0], strerror(xerrno)); destroy_pool(tmp_pool); errno = xerrno; return -1; } resp = proxy_ftp_ctrl_recv_resp(tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines); if (resp == NULL) { xerrno = errno; pr_trace_msg(trace_channel, 4, "error receiving %s response from backend: %s", (char *) cmd->argv[0], strerror(xerrno)); 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); /* Note: If the UseProxyProtocol ProxyOption is enabled, AND if the * response message mentions a "PROXY" command, we might read an * error response here that is NOT actually for the FEAT command we just * sent. * * A backend FTP server which does not understand the PROXY protocol * will treat it as a normal FTP command, and respond. And that will * put us, the client, out of lockstep with the server, for how do we know * that we need to read that error response FIRST, then send another * command? */ destroy_pool(tmp_pool); errno = EPERM; return -1; } proxy_sess->backend_features = pr_table_nalloc(p, 0, 4); feats = resp->msg; token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len); while (token != NULL) { pr_signals_handle(); if (token_len > 0) { /* The FEAT response lines in which we are interested all start with * a single space, per RFC spec. Ignore any other lines. */ if (token[0] == ' ') { char *key, *val, *ptr; /* Find the next space in the string, to delimit our key/value pairs. */ ptr = strchr(token + 1, ' '); if (ptr != NULL) { key = pstrndup(p, token + 1, ptr - token - 1); val = pstrdup(p, ptr + 1); } else { key = pstrdup(p, token + 1); val = pstrdup(p, ""); } pr_table_add(proxy_sess->backend_features, key, val, 0); } } feats = token + token_len + 1; token = pr_str_get_token2(&feats, (char *) feat_crlf, &token_len); } destroy_pool(tmp_pool); return 0; }
int proxy_ftp_sess_send_pbsz_prot(pool *p, struct proxy_session *proxy_sess) { int use_tls; 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; } if (pr_table_get(proxy_sess->backend_features, C_PBSZ, NULL) != NULL) { int xerrno; pool *tmp_pool; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; tmp_pool = make_sub_pool(p); cmd = pr_cmd_alloc(tmp_pool, 2, C_PBSZ, "0"); cmd->arg = pstrdup(tmp_pool, "0"); resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines); if (resp == NULL) { xerrno = errno; 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); destroy_pool(tmp_pool); errno = EPERM; return -1; } destroy_pool(tmp_pool); } if (pr_table_get(proxy_sess->backend_features, C_PROT, NULL) != NULL) { int xerrno; pool *tmp_pool; cmd_rec *cmd; pr_response_t *resp; unsigned int resp_nlines = 0; tmp_pool = make_sub_pool(p); cmd = pr_cmd_alloc(tmp_pool, 2, C_PROT, "P"); cmd->arg = pstrdup(tmp_pool, "P"); resp = send_recv(tmp_pool, proxy_sess->backend_ctrl_conn, cmd, &resp_nlines); if (resp == NULL) { xerrno = errno; 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); 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; }
int proxy_ftp_xfer_prepare_active(int policy_id, cmd_rec *cmd, const char *error_code, struct proxy_session *proxy_sess, int flags) { int backend_family, bind_family, res, xerrno = 0; cmd_rec *actv_cmd; const pr_netaddr_t *bind_addr = NULL; pr_response_t *resp; unsigned int resp_nlines = 0; conn_t *data_conn = NULL; char *active_cmd; const char *resp_msg = NULL; if (cmd == NULL || error_code == NULL || proxy_sess == NULL || proxy_sess->backend_ctrl_conn == NULL) { errno = EINVAL; return -1; } switch (policy_id) { case PR_CMD_PORT_ID: active_cmd = C_PORT; break; case PR_CMD_EPRT_ID: /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ active_cmd = C_EPRT; if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_ID; } break; default: /* In this case, the cmd we were given is the one we should send to * the backend server -- but only if it is either EPRT or PORT. */ if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 && pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0) { pr_trace_msg(trace_channel, 9, "illegal FTP active transfer command '%s'", (char *) cmd->argv[0]); errno = EINVAL; return -1; } active_cmd = cmd->argv[0]; if (pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) == 0) { /* If the remote host does not mention EPRT in its features, fall back * to using PORT. */ if (pr_table_get(proxy_sess->backend_features, C_EPRT, NULL) == NULL) { pr_trace_msg(trace_channel, 19, "EPRT not supported by backend server (via FEAT), using PORT"); if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } active_cmd = C_PORT; policy_id = PR_CMD_PORT_ID; } } break; } bind_addr = proxy_sess->src_addr; if (bind_addr == NULL) { bind_addr = session.c->local_addr; } if (pr_netaddr_is_loopback(bind_addr) == TRUE && pr_netaddr_is_loopback(proxy_sess->backend_ctrl_conn->remote_addr) != TRUE) { const char *local_name; const pr_netaddr_t *local_addr; local_name = pr_netaddr_get_localaddr_str(cmd->pool); local_addr = pr_netaddr_get_addr(cmd->pool, local_name, NULL); if (local_addr != NULL) { pr_trace_msg(trace_channel, 14, "%s is a loopback address, using %s instead", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(local_addr)); bind_addr = local_addr; } } /* Need to check the family of the bind addr against the family of the * backend server. */ backend_family = pr_netaddr_get_family(proxy_sess->backend_ctrl_conn->remote_addr); bind_family = pr_netaddr_get_family(bind_addr); if (bind_family == backend_family) { #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { /* Make sure that the family is NOT IPv6, even though the local and * remote families match. The PORT command cannot be used for IPv6 * addresses -- but EPRT CAN be used for IPv6 addresses. */ if (bind_family == AF_INET6 && policy_id == PR_CMD_PORT_ID) { pr_netaddr_t *mapped_addr; mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer using PORT", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } #endif /* PR_USE_IPV6 */ } else { if (backend_family == AF_INET) { pr_netaddr_t *mapped_addr; /* In this scenario, the remote peer is an IPv4 (or IPv4-mapped IPv6) * peer, so make sure we use an IPv4 local address. */ mapped_addr = pr_netaddr_v6tov4(cmd->pool, bind_addr); if (mapped_addr != NULL) { pr_trace_msg(trace_channel, 9, "converting local IPv6 address '%s' to IPv4 address '%s' for " "active transfer with IPv4 peer", pr_netaddr_get_ipstr(bind_addr), pr_netaddr_get_ipstr(mapped_addr)); bind_addr = mapped_addr; } } } if (proxy_sess->backend_data_conn != NULL) { /* Make sure that we only have one backend data connection. */ proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; } data_conn = proxy_ftp_conn_listen(cmd->tmp_pool, bind_addr, FALSE); if (data_conn == NULL) { xerrno = errno; pr_response_add_err(error_code, _("Unable to build data connection: Internal error")); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } proxy_sess->backend_data_conn = data_conn; switch (pr_cmd_get_id(active_cmd)) { case PR_CMD_PORT_ID: resp_msg = proxy_ftp_msg_fmt_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, FALSE); break; case PR_CMD_EPRT_ID: resp_msg = proxy_ftp_msg_fmt_ext_addr(cmd->tmp_pool, data_conn->local_addr, data_conn->local_port, PR_CMD_EPRT_ID, FALSE); break; } actv_cmd = pr_cmd_alloc(cmd->tmp_pool, 2, active_cmd, resp_msg); actv_cmd->arg = (char *) resp_msg; pr_cmd_clear_cache(actv_cmd); res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, actv_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines, flags); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) actv_cmd->argv[0], strerror(xerrno)); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; pr_response_add_err(error_code, "%s: %s", (char *) cmd->argv[0], strerror(xerrno)); pr_response_flush(&resp_err_list); errno = xerrno; return -1; } if (resp->num[0] != '2') { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received non-2xx response from backend for %s: %s %s", (char *) actv_cmd->argv[0], resp->num, resp->msg); proxy_inet_close(session.pool, proxy_sess->backend_data_conn); proxy_sess->backend_data_conn = NULL; if (policy_id == PR_CMD_EPRT_ID) { /* If using EPRT failed, try again using PORT, and switch the * DataTransferPolicy (if EPRT) to be PORT, for future attempts. */ if (proxy_sess->dataxfer_policy == PR_CMD_EPRT_ID) { pr_trace_msg(trace_channel, 15, "falling back from EPRT to PORT DataTransferPolicy"); proxy_sess->dataxfer_policy = PR_CMD_PORT_ID; } return proxy_ftp_xfer_prepare_active(PR_CMD_PORT_ID, cmd, error_code, proxy_sess, flags); } pr_response_add_err(error_code, "%s", resp->msg); pr_response_flush(&resp_err_list); errno = EINVAL; return -1; } return 0; }
static int forward_handle_user_passthru(cmd_rec *cmd, struct proxy_session *proxy_sess, int *successful, int flags) { int res, xerrno; char *user = NULL; cmd_rec *user_cmd = NULL; pr_response_t *resp = NULL; unsigned int resp_nlines = 0; if (flags & PROXY_FORWARD_USER_PASSTHRU_FL_PARSE_DSTADDR) { struct proxy_conn *pconn = NULL; pr_netaddr_t *remote_addr = NULL; array_header *other_addrs = NULL; res = forward_cmd_parse_dst(cmd->tmp_pool, cmd->arg, &user, &pconn); if (res < 0) { errno = EINVAL; return -1; } remote_addr = proxy_conn_get_addr(pconn, &other_addrs); /* Ensure that the requested remote address is NOT (blatantly) ourselves, * i.e. the proxy itself. This prevents easy-to-detect proxy loops. */ if (pr_netaddr_cmp(remote_addr, session.c->local_addr) == 0 && pr_netaddr_get_port(remote_addr) == pr_netaddr_get_port(session.c->local_addr)) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "requested destination %s#%u is local address %s#%u, rejecting", pr_netaddr_get_ipstr(remote_addr), ntohs(pr_netaddr_get_port(remote_addr)), pr_netaddr_get_ipstr(session.c->local_addr), ntohs(pr_netaddr_get_port(session.c->local_addr))); pr_response_send(R_530, _("Unable to connect to %s: %s"), proxy_conn_get_hostport(pconn), strerror(EPERM)); return 1; } proxy_sess->dst_addr = remote_addr; proxy_sess->other_addrs = other_addrs; proxy_sess->dst_pconn = pconn; /* Change the command so that it no longer includes the proxy info. */ user_cmd = pr_cmd_alloc(cmd->pool, 2, C_USER, user); user_cmd->arg = user; } else { user_cmd = cmd; } if (flags & PROXY_FORWARD_USER_PASSTHRU_FL_CONNECT_DSTADDR) { pr_response_t *banner = NULL; unsigned int banner_nlines = 0; res = forward_connect(proxy_pool, proxy_sess, &banner, &banner_nlines); if (res < 0) { xerrno = errno; *successful = FALSE; /* Send a failed USER response to our waiting frontend client, but do * not necessarily close the frontend connection. */ resp = pcalloc(cmd->tmp_pool, sizeof(pr_response_t)); resp->num = R_530; if (banner != NULL) { resp->msg = banner->msg; resp_nlines = banner_nlines; } else { resp->msg = pstrcat(cmd->tmp_pool, "Unable to connect to ", proxy_conn_get_hostport(proxy_sess->dst_pconn), ": ", strerror(xerrno), NULL); resp_nlines = 1; } res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); if (res < 0) { xerrno = errno; pr_response_block(TRUE); errno = xerrno; return -1; } return 1; } } res = proxy_ftp_ctrl_send_cmd(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, user_cmd); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending %s to backend: %s", (char *) user_cmd->argv[0], strerror(xerrno)); errno = xerrno; return -1; } resp = proxy_ftp_ctrl_recv_resp(cmd->tmp_pool, proxy_sess->backend_ctrl_conn, &resp_nlines); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving %s response from backend: %s", (char *) cmd->argv[0], strerror(xerrno)); errno = xerrno; return -1; } if (resp->num[0] == '2' || resp->num[0] == '3') { *successful = TRUE; if (strcmp(resp->num, R_232) == 0) { proxy_sess_state |= PROXY_SESS_STATE_BACKEND_AUTHENTICATED; pr_timer_remove(PR_TIMER_LOGIN, ANY_MODULE); } } /* XXX TODO: Concatenate the banner from the connect with the USER response * message here, and send the entire kit to the frontend client, e.g.: * * Name (gatekeeper:you): [email protected] * 331-(----GATEWAY CONNECTED TO ftp.uu.net----) * 331-(220 ftp.uu.net FTP server (SunOS 4.1) ready. * 331 Guest login ok, send ident as password. * Password: ###### * 230 Guest login ok, access restrictions apply. * ftp> dir */ res = proxy_ftp_ctrl_send_resp(cmd->tmp_pool, proxy_sess->frontend_ctrl_conn, resp, resp_nlines); if (res < 0) { xerrno = errno; pr_response_block(TRUE); errno = xerrno; return -1; } return 1; }
static int copy_dir(pool *p, const char *src_dir, const char *dst_dir) { DIR *dh = NULL; struct dirent *dent = NULL; int res = 0; pool *iter_pool = NULL; dh = opendir(src_dir); if (dh == NULL) { pr_log_pri(PR_LOG_WARNING, MOD_COPY_VERSION ": error reading directory '%s': %s", src_dir, strerror(errno)); return -1; } while ((dent = readdir(dh)) != NULL) { struct stat st; char *src_path, *dst_path; pr_signals_handle(); /* Skip "." and ".." */ if (strncmp(dent->d_name, ".", 2) == 0 || strncmp(dent->d_name, "..", 3) == 0) { continue; } if (iter_pool != NULL) { destroy_pool(iter_pool); } iter_pool = pr_pool_create_sz(p, 128); src_path = pdircat(iter_pool, src_dir, dent->d_name, NULL); dst_path = pdircat(iter_pool, dst_dir, dent->d_name, NULL); if (pr_fsio_lstat(src_path, &st) < 0) { pr_log_debug(DEBUG3, MOD_COPY_VERSION ": unable to stat '%s' (%s), skipping", src_path, strerror(errno)); continue; } /* Is this path to a directory? */ if (S_ISDIR(st.st_mode)) { if (create_path(iter_pool, dst_path) < 0) { res = -1; break; } if (copy_dir(iter_pool, src_path, dst_path) < 0) { res = -1; break; } continue; /* Is this path to a regular file? */ } else if (S_ISREG(st.st_mode)) { cmd_rec *cmd; /* Dispatch fake COPY command, e.g. for mod_quotatab */ cmd = pr_cmd_alloc(iter_pool, 4, pstrdup(iter_pool, "SITE"), pstrdup(iter_pool, "COPY"), pstrdup(iter_pool, src_path), pstrdup(iter_pool, dst_path)); cmd->arg = pstrcat(iter_pool, "COPY ", src_path, " ", dst_path, NULL); cmd->cmd_class = CL_WRITE; pr_response_clear(&resp_list); pr_response_clear(&resp_err_list); if (pr_cmd_dispatch_phase(cmd, PRE_CMD, 0) < 0) { int xerrno = errno; pr_log_debug(DEBUG3, MOD_COPY_VERSION ": COPY of '%s' to '%s' blocked by COPY handler: %s", src_path, dst_path, strerror(xerrno)); pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); errno = xerrno; res = -1; break; } else { if (pr_fs_copy_file(src_path, dst_path) < 0) { pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); res = -1; break; } else { char *abs_path; pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); /* Write a TransferLog entry as well. */ pr_fs_clear_cache2(dst_path); pr_fsio_stat(dst_path, &st); abs_path = dir_abs_path(p, dst_path, TRUE); if (session.sf_flags & SF_ANON) { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a', session.anon_user, 'c', "_"); } else { xferlog_write(0, session.c->remote_name, st.st_size, abs_path, (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r', session.user, 'c', "_"); } } } continue; /* Is this path a symlink? */ } else if (S_ISLNK(st.st_mode)) { if (copy_symlink(iter_pool, src_path, dst_path) < 0) { res = -1; break; } continue; /* All other file types are skipped */ } else { pr_log_debug(DEBUG3, MOD_COPY_VERSION ": skipping supported file '%s'", src_path); continue; } } if (iter_pool != NULL) { destroy_pool(iter_pool); } closedir(dh); return res; }
static int create_path(pool *p, const char *path) { struct stat st; char *curr_path, *dup_path; pr_fs_clear_cache2(path); if (pr_fsio_stat(path, &st) == 0) { return 0; } dup_path = pstrdup(p, path); curr_path = "/"; while (dup_path && *dup_path) { char *curr_dir; int res; cmd_rec *cmd; pool *sub_pool; pr_signals_handle(); curr_dir = strsep(&dup_path, "/"); curr_path = pdircat(p, curr_path, curr_dir, NULL); /* Dispatch fake C_MKD command, e.g. for mod_quotatab */ sub_pool = pr_pool_create_sz(p, 64); cmd = pr_cmd_alloc(sub_pool, 2, pstrdup(sub_pool, C_MKD), pstrdup(sub_pool, curr_path)); cmd->arg = pstrdup(cmd->pool, curr_path); cmd->cmd_class = CL_DIRS|CL_WRITE; pr_response_clear(&resp_list); pr_response_clear(&resp_err_list); res = pr_cmd_dispatch_phase(cmd, PRE_CMD, 0); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG3, MOD_COPY_VERSION ": creating directory '%s' blocked by MKD handler: %s", curr_path, strerror(xerrno)); pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); destroy_pool(sub_pool); errno = xerrno; return -1; } res = create_dir(curr_path); if (res < 0) { pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0); pr_response_clear(&resp_err_list); destroy_pool(sub_pool); return -1; } pr_cmd_dispatch_phase(cmd, POST_CMD, 0); pr_cmd_dispatch_phase(cmd, LOG_CMD, 0); pr_response_clear(&resp_list); destroy_pool(sub_pool); } return 0; }
static array_header *get_sql_filters(pool *p, const char *query_name) { register unsigned int i; cmdtable *sql_cmdtab = NULL; cmd_rec *sql_cmd = NULL; modret_t *sql_res = NULL; array_header *sql_data = NULL; const char **values = NULL; array_header *sql_filters = NULL; sql_cmdtab = pr_stash_get_symbol2(PR_SYM_HOOK, "sql_lookup", NULL, NULL, NULL); if (sql_cmdtab == NULL) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "unable to execute SQLNamedQuery '%s': mod_sql not loaded", query_name); errno = EPERM; return NULL; } sql_cmd = pr_cmd_alloc(p, 2, "sql_lookup", query_name); sql_res = pr_module_call(sql_cmdtab->m, sql_cmdtab->handler, sql_cmd); if (sql_res == NULL || MODRET_ISERROR(sql_res)) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "error processing SQLNamedQuery '%s'; check mod_sql logs for details", query_name); errno = EPERM; return NULL; } sql_data = sql_res->data; pr_trace_msg(trace_channel, 9, "SQLNamedQuery '%s' returned item count %d", query_name, sql_data->nelts); if (sql_data->nelts == 0) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "SQLNamedQuery '%s' returned no values", query_name); errno = ENOENT; return NULL; } if (sql_data->nelts % 2 == 1) { (void) pr_log_writefile(geoip_logfd, MOD_GEOIP_VERSION, "SQLNamedQuery '%s' returned odd number of values (%d), " "expected even number", query_name, sql_data->nelts); errno = EINVAL; return NULL; } values = sql_data->elts; sql_filters = make_array(p, 0, sizeof(struct geoip_filter)); for (i = 0; i < sql_data->nelts; i += 2) { const char *filter_name, *pattern = NULL; struct geoip_filter *filter; filter_name = values[i]; pattern = values[i+1]; filter = make_filter(p, filter_name, pattern); if (filter == NULL) { pr_trace_msg(trace_channel, 3, "unable to use '%s %s' as filter: %s", filter_name, pattern, strerror(errno)); continue; } *((struct geoip_filter **) push_array(sql_filters)) = filter; } return sql_filters; }