int proxy_db_finish_stmt(pool *p, const char *stmt) { sqlite3_stmt *pstmt; int res; if (p == NULL || stmt == NULL) { errno = EINVAL; return -1; } if (prepared_stmts == NULL) { errno = ENOENT; return -1; } pstmt = pr_table_get(prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return -1; } res = sqlite3_finalize(pstmt); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 3, "error finishing prepared statement '%s': %s", stmt, sqlite3_errmsg(proxy_dbh)); errno = EPERM; return -1; } (void) pr_table_remove(prepared_stmts, stmt, NULL); return 0; }
const char *pr_session_get_protocol(int flags) { const char *sess_proto; sess_proto = pr_table_get(session.notes, "protocol", NULL); if (sess_proto == NULL) { sess_proto = "ftp"; } if (!(flags & PR_SESS_PROTO_FL_LOGOUT)) { /* Return the protocol as is. */ return sess_proto; } /* Otherwise, we need to return either "FTP" or "SSH2", for consistency. */ if (strncmp(sess_proto, "ftp", 4) == 0 || strncmp(sess_proto, "ftps", 5) == 0) { return "FTP"; } else if (strncmp(sess_proto, "ssh2", 5) == 0 || strncmp(sess_proto, "sftp", 5) == 0 || strncmp(sess_proto, "scp", 4) == 0 || strncmp(sess_proto, "publickey", 10) == 0) { return "SSH2"; } /* Should never reach here, but just in case... */ return "unknown"; }
const char *pr_session_get_ttyname(pool *p) { char ttybuf[32]; const char *sess_proto, *tty_proto = NULL; if (p == NULL) { errno = EINVAL; return NULL; } if (sess_ttyname) { /* Return the cached name. */ return pstrdup(p, sess_ttyname); } sess_proto = pr_table_get(session.notes, "protocol", NULL); if (sess_proto) { if (strncmp(sess_proto, "ftp", 4) == 0 || strncmp(sess_proto, "ftps", 5) == 0) { #if (defined(BSD) && (BSD >= 199103)) tty_proto = "ftp"; #else tty_proto = "ftpd"; #endif } else if (strncmp(sess_proto, "ssh2", 5) == 0 || strncmp(sess_proto, "sftp", 5) == 0 || strncmp(sess_proto, "scp", 4) == 0 || strncmp(sess_proto, "publickey", 10) == 0) { /* Just use the plain "ssh" string for the tty name for these cases. */ tty_proto = "ssh"; /* Cache the originally constructed tty name for any later retrievals. */ sess_ttyname = pstrdup(session.pool, tty_proto); return pstrdup(p, sess_ttyname); } } if (tty_proto == NULL) { #if (defined(BSD) && (BSD >= 199103)) tty_proto = "ftp"; #else tty_proto = "ftpd"; #endif } memset(ttybuf, '\0', sizeof(ttybuf)); #if (defined(BSD) && (BSD >= 199103)) snprintf(ttybuf, sizeof(ttybuf), "%s%ld", tty_proto, (long) (session.pid ? session.pid : getpid())); #else snprintf(ttybuf, sizeof(ttybuf), "%s%d", tty_proto, (int) (session.pid ? session.pid : getpid())); #endif /* Cache the originally constructed tty name for any later retrievals. */ sess_ttyname = pstrdup(session.pool, ttybuf); return pstrdup(p, sess_ttyname); }
const char *pr_var_get(const char *name) { struct var *v; if (!name) { errno = EINVAL; return NULL; } if (!var_tab) { errno = EPERM; return NULL; } v = pr_table_get(var_tab, name, NULL); if (!v) return NULL; switch (v->v_type) { case PR_VAR_TYPE_STR: return (const char *) v->v_val; break; case PR_VAR_TYPE_FUNC: return ((var_vstr_cb) v->v_val)(v->v_data, v->v_datasz); break; default: /* Pass through to the error case. */ ; } errno = EINVAL; return NULL; }
static int forward_handle_pass_userwithproxyauth(cmd_rec *cmd, struct proxy_session *proxy_sess, int *successful, int *block_responses) { if (!(proxy_sess_state & PROXY_SESS_STATE_PROXY_AUTHENTICATED)) { int res; char *user; user = pr_table_get(session.notes, "mod_auth.orig-user", NULL); res = proxy_session_check_password(cmd->pool, user, cmd->arg); if (res < 0) { errno = EINVAL; return -1; } res = proxy_session_setup_env(proxy_pool, user, PROXY_SESSION_FL_CHECK_LOGIN_ACL); if (res < 0) { errno = EINVAL; return -1; } if (session.auth_mech) { pr_log_debug(DEBUG2, "user '%s' authenticated by %s", user, session.auth_mech); } pr_response_send(R_230, _("User %s logged in"), user); return 1; } return forward_handle_pass_passthru(cmd, proxy_sess, successful); }
int proxy_forward_sess_init(pool *p, const char *tables_dir, struct proxy_session *proxy_sess) { config_rec *c; int allowed = FALSE; void *enabled = NULL; /* By default, only allow connections from RFC1918 addresses to use * forward proxying. Otherwise, it must be from an explicitly allowed * connection class, via the class notes. */ if (session.conn_class != NULL) { enabled = pr_table_get(session.conn_class->cls_notes, PROXY_FORWARD_ENABLED_NOTE, NULL); } if (enabled != NULL) { allowed = *((int *) enabled); if (allowed == FALSE) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "forward proxying not allowed from client address %s in <Class %s> " "(see ProxyForwardEnabled)", pr_netaddr_get_ipstr(session.c->remote_addr), session.conn_class->cls_name); } } else { if (pr_netaddr_is_rfc1918(session.c->remote_addr) == TRUE) { allowed = TRUE; } else { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "forward proxying not allowed from non-RFC1918 client address %s", pr_netaddr_get_ipstr(session.c->remote_addr)); } } if (allowed == FALSE) { errno = EPERM; return -1; } c = find_config(main_server->conf, CONF_PARAM, "ProxyForwardMethod", FALSE); if (c != NULL) { proxy_method = *((int *) c->argv[0]); } c = find_config(main_server->conf, CONF_PARAM, "ProxyRetryCount", FALSE); if (c != NULL) { forward_retry_count = *((int *) c->argv[0]); } return 0; }
int proxy_conn_connect_timeout_cb(CALLBACK_FRAME) { struct proxy_session *proxy_sess; pr_netaddr_t *server_addr; proxy_sess = pr_table_get(session.notes, "mod_proxy.proxy-session", NULL); server_addr = pr_table_get(session.notes, "mod_proxy.proxy-connect-address", NULL); (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "timed out connecting to %s:%d after %d %s", pr_netaddr_get_ipstr(server_addr), ntohs(pr_netaddr_get_port(server_addr)), proxy_sess->connect_timeout, proxy_sess->connect_timeout != 1 ? "seconds" : "second"); pr_event_generate("mod_proxy.timeout-connect", NULL); pr_log_pri(PR_LOG_NOTICE, "%s", "Connect timed out, disconnected"); pr_session_disconnect(&proxy_module, PR_SESS_DISCONNECT_TIMEOUT, "ProxyTimeoutConnect"); /* Do not restart the timer (should never be reached). */ return 0; }
int proxy_db_prepare_stmt(pool *p, const char *stmt) { sqlite3_stmt *pstmt = NULL; int res; if (stmt == NULL) { errno = EINVAL; return -1; } pstmt = pr_table_get(prepared_stmts, stmt, NULL); if (pstmt != NULL) { res = sqlite3_reset(pstmt); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 3, "error resetting prepared statement '%s': %s", stmt, sqlite3_errmsg(proxy_dbh)); errno = EPERM; return -1; } return 0; } res = sqlite3_prepare_v2(proxy_dbh, stmt, -1, &pstmt, NULL); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error preparing statement '%s': %s", stmt, sqlite3_errmsg(proxy_dbh)); errno = EINVAL; return -1; } /* The prepared statement handling here relies on this cache, thus if we fail * to stash the prepared statement here, it will cause problems later. */ res = pr_table_add(prepared_stmts, pstrdup(db_pool, stmt), pstmt, sizeof(sqlite3_stmt *)); if (res < 0) { int xerrno = errno; pr_trace_msg(trace_channel, 4, "error stashing prepared statement '%s': %s", stmt, strerror(xerrno)); errno = xerrno; return -1; } return 0; }
const char *pr_var_next(const char **desc) { const char *name; struct var *v; if (!var_tab) { errno = EPERM; return NULL; } name = pr_table_next(var_tab); if (!name) return NULL; v = pr_table_get(var_tab, name, NULL); if (v && desc) *desc = v->v_desc; return name; }
int pr_auth_authorize(pool *p, const char *name) { cmd_rec *cmd = NULL; modret_t *mr = NULL; module *m = NULL; int res = PR_AUTH_OK; cmd = make_cmd(p, 1, name); if (auth_tab) { /* Fetch the specific module to be used for authenticating this user. */ void *v = pr_table_get(auth_tab, name, NULL); if (v) { m = *((module **) v); pr_trace_msg(trace_channel, 4, "using module 'mod_%s.c' from authcache to authorize user '%s'", m->name, name); } } mr = dispatch_auth(cmd, "authorize", m ? &m : NULL); /* Unlike the other auth calls, we assume here that unless the handlers * explicitly return ERROR, the user is authorized. Thus HANDLED and * DECLINED are both treated as "yes, this user is authorized". This * handles the case where the authenticating module (e.g. mod_sql) * does NOT provide an 'authorize' handler. */ if (MODRET_ISERROR(mr)) { res = MODRET_ERROR(mr); } if (cmd->tmp_pool) { destroy_pool(cmd->tmp_pool); cmd->tmp_pool = NULL; } return res; }
const char *pr_var_next(const char **desc) { const char *name; struct var *v; if (var_tab == NULL) { errno = EPERM; return NULL; } name = pr_table_next(var_tab); if (name == NULL) { return NULL; } v = pr_table_get(var_tab, name, NULL); if (v && desc) { *desc = v->v_desc; } return name; }
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; }
static struct trace_levels *trace_get_levels(const char *channel) { void *value; if (channel == NULL) { errno = EINVAL; return NULL; } if (trace_tab == NULL || trace_logfd < 0) { errno = EPERM; return NULL; } value = pr_table_get(trace_tab, channel, NULL); if (value == NULL) { errno = ENOENT; return NULL; } return value; }
const char *pr_var_get(const char *name) { struct var *v = NULL; if (var_tab == NULL) { errno = EPERM; return NULL; } if (name == NULL) { errno = EINVAL; return NULL; } v = pr_table_get(var_tab, name, NULL); if (v == NULL) { return NULL; } switch (v->v_type) { case PR_VAR_TYPE_STR: return (const char *) v->v_val; break; case PR_VAR_TYPE_FUNC: return ((var_vstr_cb) v->v_val)(v->v_data, v->v_datasz); break; default: /* Pass through to the error case. */ pr_trace_msg(trace_channel, 9, "unknown var type (%d) found for name '%s'", v->v_type, name); } errno = EINVAL; return NULL; }
array_header *proxy_db_exec_prepared_stmt(pool *p, const char *stmt, const char **errstr) { sqlite3_stmt *pstmt; int readonly = FALSE, res; array_header *results = NULL; if (p == NULL || stmt == NULL) { errno = EINVAL; return NULL; } if (prepared_stmts == NULL) { errno = ENOENT; return NULL; } pstmt = pr_table_get(prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return NULL; } readonly = sqlite3_stmt_readonly(pstmt); if (!readonly) { /* Assume this is an INSERT/UPDATE/DELETE. */ res = sqlite3_step(pstmt); if (res != SQLITE_DONE) { const char *errmsg; errmsg = sqlite3_errmsg(proxy_dbh); if (errstr) { *errstr = pstrdup(p, errmsg); } pr_trace_msg(trace_channel, 2, "error executing '%s': %s", stmt, errmsg); errno = EPERM; return NULL; } /* Indicate success for non-readonly statements by returning an empty * result set. */ pr_trace_msg(trace_channel, 13, "successfully executed '%s'", stmt); results = make_array(p, 0, sizeof(char *)); return results; } results = make_array(p, 0, sizeof(char *)); res = sqlite3_step(pstmt); while (res == SQLITE_ROW) { register unsigned int i; int ncols; ncols = sqlite3_column_count(pstmt); pr_trace_msg(trace_channel, 12, "executing prepared statement '%s' returned row (columns: %d)", stmt, ncols); for (i = 0; i < ncols; i++) { char *val = NULL; pr_signals_handle(); /* By using sqlite3_column_text, SQLite will coerce the column value * into a string. */ val = pstrdup(p, (const char *) sqlite3_column_text(pstmt, i)); pr_trace_msg(trace_channel, 17, "column %s [%u]: %s", sqlite3_column_name(pstmt, i), i, val); *((char **) push_array(results)) = val; } res = sqlite3_step(pstmt); } if (res != SQLITE_DONE) { const char *errmsg; errmsg = sqlite3_errmsg(proxy_dbh); if (errstr != NULL) { *errstr = pstrdup(p, errmsg); } (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "executing prepared statement '%s' did not complete successfully: %s", stmt, errmsg); errno = EPERM; return NULL; } pr_trace_msg(trace_channel, 13, "successfully executed '%s'", stmt); return results; }
MODRET counter_stor(cmd_rec *cmd) { config_rec *c; int res; pr_fh_t *fh; const char *path; if (counter_engine == FALSE) { return PR_DECLINED(cmd); } c = find_config(CURRENT_CONF, CONF_PARAM, "CounterMaxWriters", FALSE); counter_max_writers = c ? *((int *) c->argv[0]) : COUNTER_DEFAULT_MAX_WRITERS; if (counter_max_writers == 0) { return PR_DECLINED(cmd); } path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL); if (path == NULL) { return PR_DECLINED(cmd); } /* Note that for purposes of our semaphores, we need to use the absolute * path. */ counter_curr_path = counter_abs_path(cmd->pool, path, FALSE); fh = counter_get_fh(cmd->tmp_pool, counter_curr_path); if (fh == NULL) { (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION, "%s: no CounterFile found for path '%s'", (char *) cmd->argv[0], counter_curr_path); /* No CounterFile configured/available for this path. */ return PR_DECLINED(cmd); } counter_curr_semid = counter_get_sem(fh, counter_curr_path); if (counter_curr_semid < 0) { (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION, "unable to get semaphore for '%s': %s", counter_curr_path, strerror(errno)); return PR_DECLINED(cmd); } /* Add a writer to this file by decrementing the writer counter value. * This functions as a sort of "lock". */ res = counter_add_writer(counter_curr_semid); if (res < 0 && errno == EAGAIN) { /* The lock acquisition failed, which means the file is busy. * The upload should be failed. */ (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION, "%s: max number of writers (%d) reached for '%s'", (char *) cmd->argv[0], counter_max_writers, counter_curr_path); pr_response_add_err(R_450, _("%s: File busy"), cmd->arg); return PR_ERROR(cmd); } counter_pending |= COUNTER_HAVE_WRITER; (void) pr_log_writefile(counter_logfd, MOD_COUNTER_VERSION, "%s: added writer counter for '%s' (semaphore ID %d)", (char *) cmd->argv[0], counter_curr_path, counter_curr_semid); return PR_DECLINED(cmd); }
int pr_auth_check(pool *p, const char *cpw, const char *name, const char *pw) { cmd_rec *cmd = NULL; modret_t *mr = NULL; module *m = NULL; int res = PR_AUTH_BADPWD; cmd = make_cmd(p, 3, cpw, name, pw); /* First, check for any of the modules in the "authenticating only" list * of modules. This is usually only mod_auth_pam, but other modules * might also add themselves (e.g. mod_radius under certain conditions). */ if (auth_module_list) { struct auth_module_elt *elt; for (elt = (struct auth_module_elt *) auth_module_list->xas_list; elt; elt = elt->next) { m = pr_module_get(elt->name); if (m) { mr = dispatch_auth(cmd, "check", &m); if (MODRET_ISHANDLED(mr)) { pr_trace_msg(trace_channel, 4, "module '%s' used for authenticating user '%s'", elt->name, name); res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; if (cmd->tmp_pool) { destroy_pool(cmd->tmp_pool); cmd->tmp_pool = NULL; } return res; } if (MODRET_ISERROR(mr)) { res = MODRET_ERROR(mr); if (cmd->tmp_pool) { destroy_pool(cmd->tmp_pool); cmd->tmp_pool = NULL; } return res; } m = NULL; } } } if (auth_tab) { /* Fetch the specific module to be used for authenticating this user. */ void *v = pr_table_get(auth_tab, name, NULL); if (v) { m = *((module **) v); pr_trace_msg(trace_channel, 4, "using module 'mod_%s.c' from authcache to authenticate user '%s'", m->name, name); } } mr = dispatch_auth(cmd, "check", m ? &m : NULL); if (MODRET_ISHANDLED(mr)) res = MODRET_HASDATA(mr) ? PR_AUTH_RFC2228_OK : PR_AUTH_OK; if (cmd->tmp_pool) { destroy_pool(cmd->tmp_pool); cmd->tmp_pool = NULL; } return res; }
static int display_fh(pr_fh_t *fh, const char *fs, const char *code, int flags) { struct stat st; char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; int len, res; unsigned int *current_clients = NULL; unsigned int *max_clients = NULL; off_t fs_size = 0; pool *p; void *v; xaset_t *s; config_rec *c = NULL; const char *serverfqdn = main_server->ServerFQDN; char *outs, mg_size[12] = {'\0'}, mg_size_units[12] = {'\0'}, mg_max[12] = "unlimited"; char total_files_in[12] = {'\0'}, total_files_out[12] = {'\0'}, total_files_xfer[12] = {'\0'}; char mg_class_limit[12] = {'\0'}, mg_cur[12] = {'\0'}, mg_xfer_bytes[12] = {'\0'}, mg_cur_class[12] = {'\0'}; char mg_xfer_units[12] = {'\0'}, *user; const char *mg_time; char *rfc1413_ident = NULL; /* Stat the opened file to determine the optimal buffer size for IO. */ memset(&st, 0, sizeof(st)); if (pr_fsio_fstat(fh, &st) == 0) { fh->fh_iosz = st.st_blksize; } /* Note: The size provided by pr_fs_getsize() is in KB, not bytes. */ res = pr_fs_fgetsize(fh->fh_fd, &fs_size); if (res < 0 && errno != ENOSYS) { (void) pr_log_debug(DEBUG7, "error getting filesystem size for '%s': %s", fh->fh_path, strerror(errno)); fs_size = 0; } snprintf(mg_size, sizeof(mg_size), "%" PR_LU, (pr_off_t) fs_size); format_size_str(mg_size_units, sizeof(mg_size_units), fs_size); p = make_sub_pool(session.pool); pr_pool_tag(p, "Display Pool"); s = (session.anon_config ? session.anon_config->subset : main_server->conf); mg_time = pr_strtime(time(NULL)); max_clients = get_param_ptr(s, "MaxClients", FALSE); v = pr_table_get(session.notes, "client-count", NULL); if (v) { current_clients = v; } snprintf(mg_cur, sizeof(mg_cur), "%u", current_clients ? *current_clients: 1); if (session.conn_class != NULL && session.conn_class->cls_name != NULL) { unsigned int *class_clients = NULL; config_rec *maxc = NULL; unsigned int maxclients = 0; v = pr_table_get(session.notes, "class-client-count", NULL); if (v) { class_clients = v; } snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", class_clients ? *class_clients : 0); /* For the %z variable, first we scan through the MaxClientsPerClass, * and use the first applicable one. If none are found, look for * any MaxClients set. */ maxc = find_config(main_server->conf, CONF_PARAM, "MaxClientsPerClass", FALSE); while (maxc) { pr_signals_handle(); if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) { maxc = find_config_next(maxc, maxc->next, CONF_PARAM, "MaxClientsPerClass", FALSE); continue; } maxclients = *((unsigned int *) maxc->argv[1]); break; } if (maxclients == 0) { maxc = find_config(main_server->conf, CONF_PARAM, "MaxClients", FALSE); if (maxc) maxclients = *((unsigned int *) maxc->argv[0]); } snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", maxclients); } else { snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", max_clients ? *max_clients : 0); snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", 0); } snprintf(mg_xfer_bytes, sizeof(mg_xfer_bytes), "%" PR_LU, (pr_off_t) session.total_bytes >> 10); snprintf(mg_xfer_units, sizeof(mg_xfer_units), "%" PR_LU "B", (pr_off_t) session.total_bytes); if (session.total_bytes >= 10240) { snprintf(mg_xfer_units, sizeof(mg_xfer_units), "%" PR_LU "kB", (pr_off_t) session.total_bytes >> 10); } else if ((session.total_bytes >> 10) >= 10240) {
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; }
MODRET wrap_handle_request(cmd_rec *cmd) { /* these variables are names expected to be set by the TCP wrapper code */ struct request_info request; char *user = NULL; config_rec *conf = NULL, *access_conf = NULL, *syslog_conf = NULL; hosts_allow_table = NULL; hosts_deny_table = NULL; /* hide passwords */ session.hide_password = TRUE; /* Sneaky...found in mod_auth.c's cmd_pass() function. Need to find the * login UID in order to resolve the possibly-login-dependent filename. */ user = pr_table_get(session.notes, "mod_auth.orig-user", NULL); /* It's possible that a PASS command came before USER. This is a PRE_CMD * handler, so it won't be protected from this case; we'll need to do * it manually. */ if (!user) return PR_DECLINED(cmd); /* Use mod_auth's _auth_resolve_user() [imported for use here] to get the * right configuration set, since the user may be logging in anonymously, * and the session struct hasn't yet been set for that yet (thus short- * circuiting the easiest way to get the right context...the macros. */ conf = wrap_resolve_user(cmd->pool, &user); /* Search first for user-specific access files. Multiple TCPUserAccessFiles * directives are allowed. */ if ((access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPUserAccessFiles", FALSE)) != NULL) { int matched = FALSE; array_header *user_array = NULL; while (access_conf) { user_array = make_array(cmd->tmp_pool, 0, sizeof(char *)); *((char **) push_array(user_array)) = pstrdup(cmd->tmp_pool, user); /* Check the user expression -- don't forget the offset, to skip * the access file name strings in argv */ if (wrap_eval_expression(((char **) access_conf->argv) + 2, user_array)) { pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": matched TCPUserAccessFiles expression"); matched = TRUE; break; } access_conf = find_config_next(access_conf, access_conf->next, CONF_PARAM, "TCPUserAccessFiles", FALSE); } if (!matched) access_conf = NULL; } /* Next, search for group-specific access files. Multiple * TCPGroupAccessFiles directives are allowed. */ if (!access_conf && (access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPGroupAccessFiles", FALSE)) != NULL) { unsigned char matched = FALSE; /* NOTE: this gid_array is only necessary until Bug#1461 is fixed */ array_header *gid_array = make_array(cmd->pool, 0, sizeof(gid_t)); array_header *group_array = make_array(cmd->pool, 0, sizeof(char *)); while (access_conf) { if (pr_auth_getgroups(cmd->pool, user, &gid_array, &group_array) < 1) { pr_log_debug(DEBUG3, MOD_WRAP_VERSION ": no supplemental groups found for user '%s'", user); } else { /* Check the group expression -- don't forget the offset, to skip * the access file names strings in argv */ if (wrap_eval_expression(((char **) access_conf->argv) + 2, group_array)) { pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": matched TCPGroupAccessFiles expression"); matched = TRUE; break; } } access_conf = find_config_next(access_conf, access_conf->next, CONF_PARAM, "TCPGroupAccessFiles", FALSE); } if (!matched) access_conf = NULL; } /* Finally for globally-applicable access files. Only one such directive * is allowed. */ if (!access_conf) { access_conf = find_config(conf ? conf->subset : CURRENT_CONF, CONF_PARAM, "TCPAccessFiles", FALSE); } if (access_conf) { hosts_allow_table = (char *) access_conf->argv[0]; hosts_deny_table = (char *) access_conf->argv[1]; } /* Now, check the retrieved filename, and see if it requires a login-time * file. */ if (hosts_allow_table != NULL && hosts_allow_table[0] == '~' && hosts_allow_table[1] == '/') { char *allow_real_table = NULL; allow_real_table = wrap_get_user_table(cmd, user, hosts_allow_table); if (!wrap_is_usable_file(allow_real_table)) { pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION ": configured TCPAllowFile %s is unusable", hosts_allow_table); hosts_allow_table = NULL; } else hosts_allow_table = allow_real_table; } if (hosts_deny_table != NULL && hosts_deny_table[0] == '~' && hosts_deny_table[1] == '/') { char *deny_real_table = NULL; deny_real_table = dir_realpath(cmd->pool, hosts_deny_table); if (!wrap_is_usable_file(deny_real_table)) { pr_log_pri(PR_LOG_WARNING, MOD_WRAP_VERSION ": configured TCPDenyFile %s is unusable", hosts_deny_table); hosts_deny_table = NULL; } else hosts_deny_table = deny_real_table; } /* Make sure that _both_ allow and deny TCPAccessFiles are present. * If not, log the missing file, and by default allow request to succeed. */ if (hosts_allow_table != NULL && hosts_deny_table != NULL) { /* Most common case...nothing more necessary */ } else if (hosts_allow_table == NULL && hosts_deny_table != NULL) { /* Log the missing file */ pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable allow access file -- " "allowing connection"); return PR_DECLINED(cmd); } else if (hosts_allow_table != NULL && hosts_deny_table == NULL) { /* log the missing file */ pr_log_pri(PR_LOG_INFO, MOD_WRAP_VERSION ": no usable deny access file -- " "allowing connection"); return PR_DECLINED(cmd); } else { /* Neither set -- assume the admin hasn't configured these directives * at all. */ return PR_DECLINED(cmd); } /* Log the names of the allow/deny files being used. */ pr_log_pri(PR_LOG_DEBUG, MOD_WRAP_VERSION ": using access files: %s, %s", hosts_allow_table, hosts_deny_table); /* retrieve the user-defined syslog priorities, if any. Fall back to the * defaults as seen in tcpd.h if not defined. */ syslog_conf = find_config(main_server->conf, CONF_PARAM, "TCPAccessSyslogLevels", FALSE); if (syslog_conf) { allow_severity = *((int *) syslog_conf->argv[0]); deny_severity = *((int *) syslog_conf->argv[1]); } else { allow_severity = PR_LOG_INFO; deny_severity = PR_LOG_WARNING; } /* While it may look odd to OR together the syslog facility and level, * that is the way that syslog(3) says to do it: * * "The priority argument is formed by ORing the facility and the level * values..." * * Note that we do this OR here because the allow_severity/deny_severity * values are ALSO used by the libwrap library; it is also why we need * to mask off some bits later, when using proftpd's logging functions. */ allow_severity = log_getfacility() | allow_severity; deny_severity = log_getfacility() | deny_severity; pr_log_debug(DEBUG4, MOD_WRAP_VERSION ": checking under service name '%s'", wrap_service_name); request_init(&request, RQ_DAEMON, wrap_service_name, RQ_FILE, session.c->rfd, 0); fromhost(&request); if (STR_EQ(eval_hostname(request.client), paranoid) || !hosts_access(&request)) { char *denymsg = NULL; /* log the denied connection */ wrap_log_request_denied(deny_severity, &request); /* Broadcast this event to any interested listeners. */ pr_event_generate("mod_wrap.connection-denied", NULL); /* check for AccessDenyMsg */ if ((denymsg = (char *) get_param_ptr(TOPLEVEL_CONF, "AccessDenyMsg", FALSE)) != NULL) denymsg = sreplace(cmd->tmp_pool, denymsg, "%u", user, NULL); if (denymsg) return PR_ERROR_MSG(cmd, R_530, denymsg); else return PR_ERROR_MSG(cmd, R_530, _("Access denied")); } /* If request is allowable, return DECLINED (for engine to act as if this * handler was never called, else ERROR (for engine to abort processing and * deny request. */ wrap_log_request_allowed(allow_severity, &request); return PR_DECLINED(cmd); }
const char *sftp_display_fh_get_msg(pool *p, pr_fh_t *fh) { struct stat st; char buf[PR_TUNABLE_BUFFER_SIZE], *msg = ""; int len, res; unsigned int *current_clients = NULL; unsigned int *max_clients = NULL; off_t fs_size = 0; void *v; const char *serverfqdn = main_server->ServerFQDN; char *outs, mg_size[12] = {'\0'}, mg_size_units[12] = {'\0'}, mg_max[12] = "unlimited"; char mg_class_limit[12] = {'\0'}, mg_cur[12] = {'\0'}, mg_cur_class[12] = {'\0'}; const char *mg_time; char *rfc1413_ident = NULL, *user = NULL; /* Stat the opened file to determine the optimal buffer size for IO. */ memset(&st, 0, sizeof(st)); if (pr_fsio_fstat(fh, &st) == 0) { fh->fh_iosz = st.st_blksize; } res = pr_fs_fgetsize(fh->fh_fd, &fs_size); if (res < 0 && errno != ENOSYS) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error getting filesystem size for '%s': %s", fh->fh_path, strerror(errno)); fs_size = 0; } snprintf(mg_size, sizeof(mg_size), "%" PR_LU, (pr_off_t) fs_size); format_size_str(mg_size_units, sizeof(mg_size_units), fs_size); mg_time = pr_strtime(time(NULL)); max_clients = get_param_ptr(main_server->conf, "MaxClients", FALSE); v = pr_table_get(session.notes, "client-count", NULL); if (v) { current_clients = v; } snprintf(mg_cur, sizeof(mg_cur), "%u", current_clients ? *current_clients: 1); if (session.conn_class != NULL && session.conn_class->cls_name) { unsigned int *class_clients = NULL; config_rec *maxc = NULL; unsigned int maxclients = 0; v = pr_table_get(session.notes, "class-client-count", NULL); if (v) { class_clients = v; } snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", class_clients ? *class_clients : 0); /* For the %z variable, first we scan through the MaxClientsPerClass, * and use the first applicable one. If none are found, look for * any MaxClients set. */ maxc = find_config(main_server->conf, CONF_PARAM, "MaxClientsPerClass", FALSE); while (maxc) { pr_signals_handle(); if (strcmp(maxc->argv[0], session.conn_class->cls_name) != 0) { maxc = find_config_next(maxc, maxc->next, CONF_PARAM, "MaxClientsPerClass", FALSE); continue; } maxclients = *((unsigned int *) maxc->argv[1]); break; } if (maxclients == 0) { maxc = find_config(main_server->conf, CONF_PARAM, "MaxClients", FALSE); if (maxc) maxclients = *((unsigned int *) maxc->argv[0]); } snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", maxclients); } else { snprintf(mg_class_limit, sizeof(mg_class_limit), "%u", max_clients ? *max_clients : 0); snprintf(mg_cur_class, sizeof(mg_cur_class), "%u", 0); } snprintf(mg_max, sizeof(mg_max), "%u", max_clients ? *max_clients : 0); user = pr_table_get(session.notes, "mod_auth.orig-user", NULL); if (user == NULL) user = ""; rfc1413_ident = pr_table_get(session.notes, "mod_ident.rfc1413-ident", NULL); if (rfc1413_ident == NULL) { rfc1413_ident = "UNKNOWN"; } memset(buf, '\0', sizeof(buf)); while (pr_fsio_gets(buf, sizeof(buf), fh) != NULL) { char *tmp; pr_signals_handle(); buf[sizeof(buf)-1] = '\0'; len = strlen(buf); while (len > 0 && (buf[len-1] == '\r' || buf[len-1] == '\n')) { pr_signals_handle(); buf[len-1] = '\0'; len--; } /* Check for any Variable-type strings. */ tmp = strstr(buf, "%{"); while (tmp) { char *key, *tmp2; const char *val; pr_signals_handle(); tmp2 = strchr(tmp, '}'); if (tmp2 == NULL) { /* No closing '}' found in this string, so no need to look for any * aother '%{' opening sequence. Just move on. */ tmp = NULL; break; } key = pstrndup(p, tmp, tmp2 - tmp + 1); /* There are a couple of special-case keys to watch for: * * env:$var * time:$fmt * * The Var API does not easily support returning values for keys * where part of the value depends on part of the key. That's why * these keys are handled here, instead of in pr_var_get(). */ if (strncmp(key, "%{time:", 7) == 0) { char time_str[128], *fmt; time_t now; struct tm *time_info; fmt = pstrndup(p, key + 7, strlen(key) - 8); now = time(NULL); time_info = pr_localtime(NULL, &now); memset(time_str, 0, sizeof(time_str)); strftime(time_str, sizeof(time_str), fmt, time_info); val = pstrdup(p, time_str); } else if (strncmp(key, "%{env:", 6) == 0) { char *env_var; env_var = pstrndup(p, key + 6, strlen(key) - 7); val = pr_env_get(p, env_var); if (val == NULL) { pr_trace_msg("var", 4, "no value set for environment variable '%s', using \"(none)\"", env_var); val = "(none)"; } } else { val = pr_var_get(key); if (val == NULL) { pr_trace_msg("var", 4, "no value set for name '%s', using \"(none)\"", key); val = "(none)"; } } outs = sreplace(p, buf, key, val, NULL); sstrncpy(buf, outs, sizeof(buf)); tmp = strstr(outs, "%{"); } outs = sreplace(p, buf, "%C", (session.cwd[0] ? session.cwd : "(none)"), "%E", main_server->ServerAdmin, "%F", mg_size, "%f", mg_size_units, "%i", "0", "%K", "0", "%k", "0B", "%L", serverfqdn, "%M", mg_max, "%N", mg_cur, "%o", "0", "%R", (session.c && session.c->remote_name ? session.c->remote_name : "(unknown)"), "%T", mg_time, "%t", "0", "%U", user, "%u", rfc1413_ident, "%V", main_server->ServerName, "%x", session.conn_class ? session.conn_class->cls_name : "(unknown)", "%y", mg_cur_class, "%z", mg_class_limit, NULL); /* Always make sure that the lines we send are CRLF-terminated. */ msg = pstrcat(p, msg, outs, "\r\n", NULL); /* Clear the buffer for the next read. */ memset(buf, '\0', sizeof(buf)); } return msg; }
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_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 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; }
int proxy_db_bind_stmt(pool *p, const char *stmt, int idx, int type, void *data) { sqlite3_stmt *pstmt; int res; if (p == NULL || stmt == NULL) { errno = EINVAL; return -1; } /* SQLite3 bind parameters start at index 1. */ if (idx < 1) { errno = EINVAL; return -1; } if (prepared_stmts == NULL) { errno = ENOENT; return -1; } pstmt = pr_table_get(prepared_stmts, stmt, NULL); if (pstmt == NULL) { pr_trace_msg(trace_channel, 19, "unable to find prepared statement for '%s'", stmt); errno = ENOENT; return -1; } switch (type) { case PROXY_DB_BIND_TYPE_INT: { int i; if (data == NULL) { errno = EINVAL; return -1; } i = *((int *) data); res = sqlite3_bind_int(pstmt, idx, i); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to INT %d: %s", idx, stmt, i, sqlite3_errmsg(proxy_dbh)); } break; } case PROXY_DB_BIND_TYPE_LONG: { long l; if (data == NULL) { errno = EINVAL; return -1; } l = *((long *) data); res = sqlite3_bind_int(pstmt, idx, l); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to LONG %ld: %s", idx, stmt, l, sqlite3_errmsg(proxy_dbh)); } break; } case PROXY_DB_BIND_TYPE_TEXT: { const char *text; if (data == NULL) { errno = EINVAL; return -1; } text = (const char *) data; res = sqlite3_bind_text(pstmt, idx, text, -1, NULL); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to TEXT '%s': %s", idx, stmt, text, sqlite3_errmsg(proxy_dbh)); } break; } case PROXY_DB_BIND_TYPE_NULL: res = sqlite3_bind_null(pstmt, idx); if (res != SQLITE_OK) { pr_trace_msg(trace_channel, 4, "error binding parameter %d of '%s' to NULL: %s", idx, stmt, sqlite3_errmsg(proxy_dbh)); } break; default: pr_trace_msg(trace_channel, 2, "unknown/unsupported bind data type %d", type); errno = EINVAL; return -1; } return 0; }
MODRET copy_cpto(cmd_rec *cmd) { register unsigned int i; const char *from, *to = ""; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { return PR_DECLINED(cmd); } if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPTO", 5) != 0) { return PR_DECLINED(cmd); } authenticated = get_param_ptr(cmd->server->conf, "authenticated", FALSE); if (authenticated == NULL || *authenticated == FALSE) { pr_response_add_err(R_530, _("Please login with USER and PASS")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } CHECK_CMD_MIN_ARGS(cmd, 3); from = pr_table_get(session.notes, "mod_copy.cpfr-path", NULL); if (from == NULL) { pr_response_add_err(R_503, _("Bad sequence of commands")); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } /* Construct the target file name by concatenating all the parameters after * the "SITE CPTO", separating them with spaces. */ for (i = 2; i <= cmd->argc-1; i++) { char *decoded_path; decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->argv[i], FSIO_DECODE_FL_TELL_ERRORS); if (decoded_path == NULL) { int xerrno = errno; pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", (char *) cmd->argv[i], strerror(xerrno)); pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"), cmd->arg); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } to = pstrcat(cmd->tmp_pool, to, *to ? " " : "", decoded_path, NULL); } to = dir_canonical_vpath(cmd->tmp_pool, to); if (copy_paths(cmd->tmp_pool, from, to) < 0) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[1], strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_250, "%s", _("Copy successful")); return PR_HANDLED(cmd); }
int xferlog_write(long xfertime, const char *remhost, off_t fsize, char *fname, char xfertype, char direction, char access_mode, char *user, char abort_flag, const char *action_flags) { const char *xfer_proto; char buf[LOGBUFFER_SIZE] = {'\0'}, fbuf[LOGBUFFER_SIZE] = {'\0'}; int have_ident = FALSE, len; char *rfc1413_ident = NULL; register unsigned int i = 0; if (xferlogfd == -1 || remhost == NULL || user == NULL || fname == NULL) { return 0; } for (i = 0; (i + 1 < sizeof(fbuf)) && fname[i] != '\0'; i++) { fbuf[i] = (PR_ISSPACE(fname[i]) || PR_ISCNTRL(fname[i])) ? '_' : fname[i]; } fbuf[i] = '\0'; rfc1413_ident = pr_table_get(session.notes, "mod_ident.rfc1413-ident", NULL); if (rfc1413_ident) { have_ident = TRUE; /* If the retrieved identity is "UNKNOWN", then change the string to be * "*", since "*" is to be logged in the xferlog, as per the doc, when * the authenticated user ID is not available. */ if (strncmp(rfc1413_ident, "UNKNOWN", 8) == 0) rfc1413_ident = "*"; } else { /* If an authenticated user ID is not available, log "*", as per the * xferlog man page/spec. */ rfc1413_ident = "*"; } xfer_proto = pr_session_get_protocol(0); len = snprintf(buf, sizeof(buf), "%s %ld %s %" PR_LU " %s %c %s %c %c %s %s %c %s %c\n", pr_strtime(time(NULL)), xfertime, remhost, (pr_off_t) fsize, fbuf, xfertype, action_flags, direction, access_mode, user, xfer_proto, have_ident ? '1' : '0', rfc1413_ident, abort_flag); buf[sizeof(buf)-1] = '\0'; pr_log_event_generate(PR_LOG_TYPE_XFERLOG, xferlogfd, -1, buf, len); return write(xferlogfd, buf, len); }
const char *pr_session_get_disconnect_reason(char **details) { const char *reason_str = NULL; switch (session.disconnect_reason) { case PR_SESS_DISCONNECT_UNSPECIFIED: reason_str = "Unknown/unspecified"; break; case PR_SESS_DISCONNECT_CLIENT_QUIT: reason_str = "Quit"; break; case PR_SESS_DISCONNECT_CLIENT_EOF: reason_str = "Read EOF from client"; break; case PR_SESS_DISCONNECT_SESSION_INIT_FAILED: reason_str = "Session initialized failed"; break; case PR_SESS_DISCONNECT_SIGNAL: reason_str = "Terminated by signal"; break; case PR_SESS_DISCONNECT_NOMEM: reason_str = "Low memory"; break; case PR_SESS_DISCONNECT_SERVER_SHUTDOWN: reason_str = "Server shutting down"; break; case PR_SESS_DISCONNECT_TIMEOUT: reason_str = "Timeout exceeded"; break; case PR_SESS_DISCONNECT_BANNED: reason_str = "Banned"; break; case PR_SESS_DISCONNECT_CONFIG_ACL: reason_str = "Configured policy"; break; case PR_SESS_DISCONNECT_MODULE_ACL: reason_str = "Module-specific policy"; break; case PR_SESS_DISCONNECT_BAD_CONFIG: reason_str = "Server misconfiguration"; break; case PR_SESS_DISCONNECT_BY_APPLICATION: reason_str = "Application error"; break; } if (details != NULL) { *details = pr_table_get(session.notes, "core.disconnect-details", NULL); } return reason_str; }