pr_netio_t *proxy_netio_unset(int strm_type, const char *fn) { pr_netio_t *netio = NULL; if (fn == NULL) { errno = EINVAL; return NULL; } netio = pr_get_netio(strm_type); if (netio != NULL) { const char *owner_name = "core", *typestr; if (netio->owner_name != NULL) { owner_name = netio->owner_name; } typestr = netio_strm_typestr(strm_type); pr_trace_msg(trace_channel, 18, "(%s) found %s %s NetIO", fn, owner_name, typestr); if (pr_unregister_netio(strm_type) < 0) { pr_trace_msg(trace_channel, 3, "(%s) error unregistering %s NetIO: %s", fn, typestr, strerror(errno)); } } /* Regardless of whether we found a previously registered NetIO, make * sure to use our own NetIO, if any. */ switch (strm_type) { case PR_NETIO_STRM_CTRL: if (ctrl_netio != NULL) { if (pr_register_netio(ctrl_netio, strm_type) < 0) { pr_trace_msg(trace_channel, 3, "(%s) error registering proxy %s NetIO: %s", fn, netio_strm_typestr(strm_type), strerror(errno)); } else { pr_trace_msg(trace_channel, 19, "(%s) using proxy %s NetIO", fn, netio_strm_typestr(strm_type)); } } break; case PR_NETIO_STRM_DATA: if (data_netio != NULL) { if (pr_register_netio(data_netio, strm_type) < 0) { pr_trace_msg(trace_channel, 3, "(%s) error registering proxy %s NetIO: %s", fn, netio_strm_typestr(strm_type), strerror(errno)); } else { pr_trace_msg(trace_channel, 19, "(%s) using proxy %s NetIO", fn, netio_strm_typestr(strm_type)); } } break; default: break; } return netio; }
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; }
static int sftppam_converse(int nmsgs, PR_PAM_CONST struct pam_message **msgs, struct pam_response **resps, void *app_data) { register int i = 0, j = 0; array_header *list; uint32_t recvd_count = 0; const char **recvd_responses = NULL; struct pam_response *res = NULL; if (nmsgs <= 0 || nmsgs > PAM_MAX_NUM_MSG) { pr_trace_msg(trace_channel, 3, "bad number of PAM messages (%d)", nmsgs); return PAM_CONV_ERR; } pr_trace_msg(trace_channel, 9, "handling %d PAM %s", nmsgs, nmsgs == 1 ? "message" : "messages"); /* First, send these messages to the client. */ list = make_array(sftppam_driver.driver_pool, 1, sizeof(sftp_kbdint_challenge_t)); for (i = 0; i < nmsgs; i++) { sftp_kbdint_challenge_t *challenge; /* Skip PAM_ERROR_MSG messages; we don't want to send these to the client. */ if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_TEXT_INFO) { if (sftppam_opts & SFTP_PAM_OPT_NO_INFO_MSGS) { pr_trace_msg(trace_channel, 9, "skipping sending of PAM_TEXT_INFO '%s' to client", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); } else { pr_trace_msg(trace_channel, 9, "sending PAM_TEXT_INFO '%s' to client", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); sftp_auth_send_banner(SFTP_PAM_MSG_MEMBER(msgs, i, msg)); } continue; #ifdef PAM_RADIO_TYPE } else if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_RADIO_TYPE) { if (sftppam_opts & SFTP_PAM_OPT_NO_RADIO_MSGS) { pr_trace_msg(trace_channel, 9, "skipping sending of PAM_RADIO_TYPE '%s' to client", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); } else { pr_trace_msg(trace_channel, 9, "sending PAM_RADIO_TYPE '%s' to client", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); sftp_auth_send_banner(SFTP_PAM_MSG_MEMBER(msgs, i, msg)); } continue; #endif /* PAM_RADIO_TYPE */ } else if (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style) == PAM_ERROR_MSG) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "received PAM_ERROR_MSG '%s'", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); continue; } challenge = push_array(list); challenge->challenge = pstrdup(sftppam_driver.driver_pool, SFTP_PAM_MSG_MEMBER(msgs, i, msg)); challenge->display_response = FALSE; } if (list->nelts == 0) { /* Nothing to see here, move along. */ return PAM_SUCCESS; } if (sftp_kbdint_send_challenge(NULL, NULL, list->nelts, list->elts) < 0) { pr_trace_msg(trace_channel, 3, "error sending keyboard-interactive challenges: %s", strerror(errno)); return PAM_CONV_ERR; } if (sftp_kbdint_recv_response(sftppam_driver.driver_pool, list->nelts, &recvd_count, &recvd_responses) < 0) { pr_trace_msg(trace_channel, 3, "error receiving keyboard-interactive responses: %s", strerror(errno)); return PAM_CONV_ERR; } res = calloc(nmsgs, sizeof(struct pam_response)); if (res == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!"); return PAM_BUF_ERR; } for (i = 0; i < nmsgs; i++) { res[i].resp_retcode = 0; switch (SFTP_PAM_MSG_MEMBER(msgs, i, msg_style)) { case PAM_PROMPT_ECHO_ON: pr_trace_msg(trace_channel, 9, "received PAM_PROMPT_ECHO_ON message '%s', responding with '%s'", SFTP_PAM_MSG_MEMBER(msgs, i, msg), recvd_responses[i]); res[i].resp = strdup(recvd_responses[i]); break; case PAM_PROMPT_ECHO_OFF: pr_trace_msg(trace_channel, 9, "received PAM_PROMPT_ECHO_OFF message '%s', responding with text", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); res[i].resp = strdup(recvd_responses[i]); break; case PAM_TEXT_INFO: pr_trace_msg(trace_channel, 9, "received PAM_TEXT_INFO message: %s", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); res[i].resp = NULL; break; case PAM_ERROR_MSG: pr_trace_msg(trace_channel, 9, "received PAM_ERROR_MSG message: %s", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); res[i].resp = NULL; break; #ifdef PAM_RADIO_TYPE case PAM_RADIO_TYPE: pr_trace_msg(trace_channel, 9, "received PAM_RADIO_TYPE message: %s", SFTP_PAM_MSG_MEMBER(msgs, i, msg)); res[i].resp = NULL; break; #endif /* PAM_RADIO_TYPE */ default: pr_trace_msg(trace_channel, 3, "received unknown PAM message style (%d), treating it as an error", SFTP_PAM_MSG_MEMBER(msgs, i, msg_style)); for (j = 0; j < nmsgs; j++) { if (res[i].resp != NULL) { free(res[i].resp); res[i].resp = NULL; } } free(res); return PAM_CONV_ERR; } } *resps = res; return PAM_SUCCESS; }
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; }
char *pr_ident_lookup(pool *p, conn_t *c) { char *ret = "UNKNOWN"; pool *tmp_pool = NULL; conn_t *ident_conn = NULL, *ident_io = NULL; char buf[256] = {'\0'}, *tok = NULL, *tmp = NULL; int timerno, i = 0; int ident_port = pr_inet_getservport(p, "ident", "tcp"); tmp_pool = make_sub_pool(p); ident_timeout = 0; nstrm = NULL; if (ident_port == -1) { destroy_pool(tmp_pool); return pstrdup(p, ret); } /* Set up our timer before going any further. */ timerno = pr_timer_add(PR_TUNABLE_TIMEOUTIDENT, -1, NULL, (callback_t) ident_timeout_cb, "ident lookup"); if (timerno <= 0) { destroy_pool(tmp_pool); return pstrdup(p, ret); } ident_conn = pr_inet_create_connection(tmp_pool, NULL, -1, c->local_addr, INPORT_ANY, FALSE); pr_inet_set_nonblock(tmp_pool, ident_conn); i = pr_inet_connect_nowait(tmp_pool, ident_conn, c->remote_addr, ident_port); if (i < 0) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 5, "connection to %s, port %d failed: %s", pr_netaddr_get_ipstr(c->remote_addr), ident_port, strerror(xerrno)); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } if (!i) { /* Not yet connected. */ nstrm = pr_netio_open(p, PR_NETIO_STRM_OTHR, ident_conn->listen_fd, PR_NETIO_IO_RD); pr_netio_set_poll_interval(nstrm, 1); switch (pr_netio_poll(nstrm)) { /* Aborted, timed out */ case 1: { if (ident_timeout) { pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 5, "lookup timed out, returning '%s'", ret); destroy_pool(tmp_pool); return pstrdup(p, ret); } break; } /* Error. */ case -1: { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 6, "lookup failed (%s), returning '%s'", strerror(xerrno), ret); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } /* Connected. */ default: { ident_conn->mode = CM_OPEN; if (pr_inet_get_conn_info(ident_conn, ident_conn->listen_fd) < 0) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_netio_close(nstrm); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 2, "lookup timed out (%s), returning '%s'", strerror(xerrno), ret); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } break; } } } ident_io = pr_inet_openrw(tmp_pool, ident_conn, NULL, PR_NETIO_STRM_OTHR, -1, -1, -1, FALSE); if (ident_io == NULL) { int xerrno = errno; pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_conn); pr_trace_msg(trace_channel, 3, "failed opening read/write connection: %s", strerror(xerrno)); destroy_pool(tmp_pool); errno = xerrno; return pstrdup(p, ret); } nstrm = ident_io->instrm; pr_inet_set_nonblock(tmp_pool, ident_io); pr_netio_set_poll_interval(ident_io->instrm, 1); pr_netio_set_poll_interval(ident_io->outstrm, 1); pr_netio_printf(ident_io->outstrm, "%d, %d\r\n", c->remote_port, c->local_port); /* If the timer fires while in netio_gets(), netio_gets() will simply return * either a partial string, or NULL. This works because ident_timeout_cb * aborts the stream from which we are reading. netio_set_poll_interval() is * used to make sure significant delays don't occur on systems that * automatically restart syscalls after the SIGALRM signal. */ pr_trace_msg(trace_channel, 4, "reading response from remote ident server"); if (pr_netio_gets(buf, sizeof(buf), ident_io->instrm)) { strip_end(buf, "\r\n"); pr_trace_msg(trace_channel, 6, "received '%s' from remote ident server", buf); tmp = buf; tok = get_token(&tmp, ":"); if (tok && (tok = get_token(&tmp, ":"))) { while (*tok && isspace((int) *tok)) { pr_signals_handle(); tok++; } strip_end(tok, " \t"); if (strcasecmp(tok, "ERROR") == 0) { if (tmp) { while (*tmp && isspace((int) *tmp)) { pr_signals_handle(); tmp++; } strip_end(tmp, " \t"); if (strcasecmp(tmp, "HIDDEN-USER") == 0) ret = "HIDDEN-USER"; } } else if (strcasecmp(tok, "USERID") == 0) { if (tmp && (tok = get_token(&tmp, ":"))) { if (tmp) { while (*tmp && isspace((int) *tmp)) { pr_signals_handle(); tmp++; } strip_end(tmp, " \t"); ret = tmp; } } } } } pr_timer_remove(timerno, ANY_MODULE); pr_inet_close(tmp_pool, ident_io); pr_inet_close(tmp_pool, ident_conn); destroy_pool(tmp_pool); return pstrdup(p, ret); }
void pr_event_generate(const char *event, const void *event_data) { int use_cache = FALSE; struct event_list *evl; if (!event) return; /* If there are no registered callbacks, be done. */ if (!events) return; /* If there is a cached event, see if the given event matches. */ if (curr_event && strcmp(curr_event, event) == 0) use_cache = TRUE; /* Lookup callbacks for this event. */ for (evl = use_cache ? curr_evl : events; evl; evl = evl->next) { if (strcmp(evl->event, event) == 0) { struct event_handler *evh; /* If there are no registered callbacks for this event, be done. */ if (!evl->handlers) { pr_trace_msg(trace_channel, 8, "no event handlers registered for '%s'", event); return; } curr_event = event; curr_evl = evl; for (evh = use_cache ? curr_evh : evl->handlers; evh; evh = evh->next) { /* Make sure that if the same event is generated by the current * listener, the next time through we go to the next listener, rather * sending the same event against to the same listener (Bug#3619). */ curr_evh = evh->next; if (evh->module) { pr_trace_msg(trace_channel, 8, "dispatching event '%s' to mod_%s (at %p)", event, evh->module->name, evh->cb); } else { pr_trace_msg(trace_channel, 8, "dispatching event '%s' to core (at %p)", event, evh->cb); } evh->cb(event_data, evh->user_data); } break; } } /* Clear any cached data after publishing the event to all interested * listeners. */ curr_event = NULL; curr_evl = NULL; curr_evh = NULL; return; }
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; }
static int regexp_exec_pcre(pr_regex_t *pre, const char *str, size_t nmatches, regmatch_t *matches, int flags, unsigned long match_limit, unsigned long match_limit_recursion) { if (pre == NULL || str == NULL) { errno = EINVAL; return -1; } if (pre->pcre != NULL) { int res; size_t str_len; str_len = strlen(str); /* Use the default match limits, if set and if the caller did not * explicitly provide limits. */ if (match_limit == 0) { match_limit = pcre_match_limit; } if (match_limit_recursion == 0) { match_limit_recursion = pcre_match_limit_recursion; } if (match_limit > 0) { if (pre->pcre_extra == NULL) { pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra)); } pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT; pre->pcre_extra->match_limit = match_limit; } if (match_limit_recursion > 0) { if (pre->pcre_extra == NULL) { pre->pcre_extra = pcalloc(pre->regex_pool, sizeof(pcre_extra)); } pre->pcre_extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; pre->pcre_extra->match_limit_recursion = match_limit_recursion; } pr_trace_msg(trace_channel, 9, "executing PCRE regex '%s' against subject '%s'", pr_regexp_get_pattern(pre), str); res = pcre_exec(pre->pcre, pre->pcre_extra, str, str_len, 0, flags, NULL, 0); if (res < 0) { if (pr_trace_get_level(trace_channel) >= 9) { const char *reason = "unknown"; switch (res) { case PCRE_ERROR_NOMATCH: reason = "subject did not match pattern"; break; case PCRE_ERROR_NULL: reason = "null regex or subject"; break; case PCRE_ERROR_BADOPTION: reason = "unsupported options bit"; break; case PCRE_ERROR_BADMAGIC: reason = "bad magic number in regex"; break; case PCRE_ERROR_UNKNOWN_OPCODE: case PCRE_ERROR_INTERNAL: reason = "internal PCRE error or corrupted regex"; break; case PCRE_ERROR_NOMEMORY: reason = "not enough memory for backreferences"; break; case PCRE_ERROR_MATCHLIMIT: reason = "match limit reached/exceeded"; break; case PCRE_ERROR_RECURSIONLIMIT: reason = "match limit recursion reached/exceeded"; break; case PCRE_ERROR_BADUTF8: reason = "invalid UTF8 subject used"; break; case PCRE_ERROR_PARTIAL: reason = "subject matched only partially; PCRE_PARTIAL flag not used"; break; } pr_trace_msg(trace_channel, 9, "PCRE regex '%s' failed to match subject '%s': %s", pr_regexp_get_pattern(pre), str, reason); } else { pr_trace_msg(trace_channel, 9, "PCRE regex '%s' successfully match subject '%s'", pr_regexp_get_pattern(pre), str); } } return res; } errno = EINVAL; return -1; }
const pr_netaddr_t *proxy_ftp_msg_parse_addr(pool *p, const char *msg, int addr_family) { int valid_fmt = FALSE; const char *ptr; char *addr_buf; unsigned int h1, h2, h3, h4, p1, p2; unsigned short port; size_t addrlen; pr_netaddr_t *addr; if (p == NULL || msg == NULL) { errno = EINVAL; return NULL; } /* Have to scan the message for the encoded address/port. Note that we may * see some strange formats for PASV responses from FTP servers here. * * We can't predict where the expected address/port numbers start in the * string, so start from the beginning. */ for (ptr = msg; *ptr; ptr++) { if (sscanf(ptr, "%u,%u,%u,%u,%u,%u", &h1, &h2, &h3, &h4, &p1, &p2) == 6) { valid_fmt = TRUE; break; } } if (valid_fmt == FALSE) { pr_trace_msg(trace_channel, 12, "unable to find PORT/PASV address/port format in '%s'", msg); errno = EPERM; return NULL; } if (h1 > 255 || h2 > 255 || h3 > 255 || h4 > 255 || p1 > 255 || p2 > 255 || (h1|h2|h3|h4) == 0 || (p1|p2) == 0) { pr_trace_msg(trace_channel, 9, "message '%s' has invalid address/port value(s)", msg); errno = EINVAL; return NULL; } /* A dotted quad address has a maximum size of 16 bytes: 4 numbers of 3 digits * (max), 3 periods, and 1 terminating NUL. */ addrlen = 16; #ifdef PR_USE_IPV6 /* Allow extra room for any necessary "::ffff:" prefix, for IPv6 sessions. */ addrlen += 7; #endif /* PR_USE_IPV6 */ addr_buf = pcalloc(p, addrlen); #ifdef PR_USE_IPV6 if (pr_netaddr_use_ipv6()) { if (addr_family == AF_INET6) { snprintf(addr_buf, addrlen-1, "::ffff:%u.%u.%u.%u", h1, h2, h3, h4); } else { snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); } } else { snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); } #else snprintf(addr_buf, addrlen-1, "%u.%u.%u.%u", h1, h2, h3, h4); #endif /* PR_USE_IPV6 */ /* XXX Ideally we would NOT be using session pool here, but some other * pool. These objects can't be destroyed (they have no pools of their own), * so they will just clutter up the session pool. Perhaps we could have * a pool of addrs in this API, for reusing. */ addr = (pr_netaddr_t *) pr_netaddr_get_addr(session.pool, addr_buf, NULL); if (addr == NULL) { int xerrno = errno; pr_trace_msg(trace_channel, 7, "unable to resolve '%s' from message '%s': %s", addr_buf, msg, strerror(xerrno)); errno = xerrno; return NULL; } port = (p1 << 8) + p2; pr_netaddr_set_port2(addr, port); return addr; }
/* This function does the work of iterating through the list of registered * timers, checking to see if their callbacks should be invoked and whether * they should be removed from the registration list. Its return value is * the amount of time remaining on the first timer in the list. */ static int process_timers(int elapsed) { struct timer *t = NULL, *next = NULL; if (!recycled) recycled = xaset_create(timer_pool, NULL); if (!elapsed && !recycled->xas_list) { if (!timers) return 0; return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0); } /* Critical code, no interruptions please */ if (_indispatch) return 0; pr_alarms_block(); _indispatch++; if (elapsed) { for (t = (struct timer *) timers->xas_list; t; t = next) { /* If this timer has already been handled, skip */ next = t->next; if (t->remove) { /* Move the timer onto the free_timers chain, for later reuse. */ xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); } else if ((t->count -= elapsed) <= 0) { /* This timer's interval has elapsed, so trigger its callback. */ pr_trace_msg("timer", 4, "%ld %s for timer ID %d ('%s', for module '%s') elapsed, invoking " "callback (%p)", t->interval, t->interval != 1 ? "seconds" : "second", t->timerno, t->desc ? t->desc : "<unknown>", t->mod ? t->mod->name : "<none>", t->callback); if (t->callback(t->interval, t->timerno, t->interval - t->count, t->mod) == 0) { /* A return value of zero means this timer is done, and can be * removed. */ xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); } else { /* A non-zero return value from a timer callback signals that * the timer should be reused/restarted. */ pr_trace_msg("timer", 6, "restarting timer ID %d ('%s'), as per " "callback", t->timerno, t->desc ? t->desc : "<unknown>"); xaset_remove(timers, (xasetmember_t *) t); t->count = t->interval; xaset_insert(recycled, (xasetmember_t *) t); } } } } /* Put the recycled timers back into the main timer list. */ while ((t = (struct timer *) recycled->xas_list) != NULL) { xaset_remove(recycled, (xasetmember_t *) t); xaset_insert_sort(timers, (xasetmember_t *) t, TRUE); } _indispatch--; pr_alarms_unblock(); /* If no active timers remain in the list, there is no reason to set the * SIGALRM handle. */ return (timers->xas_list ? ((struct timer *) timers->xas_list)->count : 0); }
static int dso_load_module(char *name) { int res; char *symbol_name, *path, *tmp; module *m; lt_ptr mh = NULL; lt_dladvise advise; if (name == NULL) { errno = EINVAL; return -1; } if (strncmp(name, "mod_", 4) != 0 || name[strlen(name)-2] != '.' || name[strlen(name)-1] != 'c') { errno = EINVAL; return -1; } pr_log_debug(DEBUG7, "loading '%s'", name); tmp = strrchr(name, '.'); if (tmp == NULL) { errno = EINVAL; return -1; } if (lt_dladvise_init(&advise) < 0) { pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": unable to initialise advise: %s", lt_dlerror()); errno = EPERM; return -1; } if (lt_dladvise_ext(&advise) < 0) { pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": unable to setting 'ext' advise hint: %s", lt_dlerror()); lt_dladvise_destroy(&advise); errno = EPERM; return -1; } if (lt_dladvise_global(&advise) < 0) { pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": unable to setting 'global' advise hint: %s", lt_dlerror()); lt_dladvise_destroy(&advise); errno = EPERM; return -1; } *tmp = '\0'; /* Load file: $prefix/libexec/<module> */ path = pdircat(dso_pool, dso_module_path, name, NULL); pr_trace_msg(trace_channel, 5, "loading module '%s'", path); mh = lt_dlopenadvise(path, advise); if (mh == NULL) { *tmp = '.'; pr_log_debug(DEBUG3, MOD_DSO_VERSION ": unable to dlopen '%s': %s (%s)", name, lt_dlerror(), strerror(errno)); pr_log_debug(DEBUG3, MOD_DSO_VERSION ": defaulting to 'self' for symbol resolution"); lt_dladvise_destroy(&advise); mh = lt_dlopen(NULL); if (mh == NULL) { pr_log_debug(DEBUG0, MOD_DSO_VERSION ": error loading 'self': %s", lt_dlerror()); if (errno == ENOENT) { pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": check to see if '%s.la' exists", path); } return -1; } } lt_dladvise_destroy(&advise); /* Tease name of the module structure out of the given name: * <module>.<ext> --> <module>_module */ *tmp = '\0'; symbol_name = pstrcat(dso_pool, name+4, "_module", NULL); /* Lookup module structure symbol by name. */ pr_trace_msg(trace_channel, 7, "looking for symbol '%s' in loaded module", symbol_name); m = (module *) lt_dlsym(mh, symbol_name); if (m == NULL) { *tmp = '.'; pr_log_debug(DEBUG1, MOD_DSO_VERSION ": unable to find module symbol '%s' in '%s'", symbol_name, mh ? name : "self"); pr_trace_msg(trace_channel, 1, "unable to find module symbol '%s' in '%s'", symbol_name, mh ? name : "self"); lt_dlclose(mh); mh = NULL; if (errno == ENOENT) { pr_log_pri(PR_LOG_NOTICE, MOD_DSO_VERSION ": check to see if '%s.la' exists", path); } return -1; } *tmp = '.'; m->handle = mh; /* Add the module to the core structures */ res = pr_module_load(m); if (res < 0) { if (errno == EEXIST) { pr_log_pri(PR_LOG_INFO, MOD_DSO_VERSION ": module 'mod_%s.c' already loaded", m->name); pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' already loaded", m->name); } else if (errno == EACCES) { pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION ": module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x", m->name, m->api_version, PR_MODULE_API_VERSION); pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' has wrong API version (0x%x), must be 0x%x", m->name, m->api_version, PR_MODULE_API_VERSION); } else if (errno == EPERM) { pr_log_pri(PR_LOG_ERR, MOD_DSO_VERSION ": module 'mod_%s.c' failed to initialize", m->name); pr_trace_msg(trace_channel, 1, "module 'mod_%s.c' failed to initialize", m->name); } lt_dlclose(mh); mh = NULL; return -1; } pr_trace_msg(trace_channel, 8, "module '%s' successfully loaded", path); return 0; }
int pr_timer_add(int seconds, int timerno, module *mod, callback_t cb, const char *desc) { struct timer *t = NULL; if (seconds <= 0 || cb == NULL || desc == NULL) { errno = EINVAL; return -1; } if (!timers) timers = xaset_create(timer_pool, (XASET_COMPARE) timer_cmp); /* Check to see that, if specified, the timerno is not already in use. */ if (timerno >= 0) { for (t = (struct timer *) timers->xas_list; t; t = t->next) { if (t->timerno == timerno) { errno = EPERM; return -1; } } } if (!free_timers) free_timers = xaset_create(timer_pool, NULL); /* Try to use an old timer first */ pr_alarms_block(); t = (struct timer *) free_timers->xas_list; if (t != NULL) { xaset_remove(free_timers, (xasetmember_t *) t); } else { if (timer_pool == NULL) { timer_pool = make_sub_pool(permanent_pool); pr_pool_tag(timer_pool, "Timer Pool"); } /* Must allocate a new one */ t = palloc(timer_pool, sizeof(struct timer)); } if (timerno < 0) { /* Dynamic timer */ if (dynamic_timerno < PR_TIMER_DYNAMIC_TIMERNO) { dynamic_timerno = PR_TIMER_DYNAMIC_TIMERNO; } timerno = dynamic_timerno++; } t->timerno = timerno; t->count = t->interval = seconds; t->callback = cb; t->mod = mod; t->remove = 0; t->desc = desc; /* If called while _indispatch, add to the recycled list to prevent * list corruption */ if (_indispatch) { if (!recycled) recycled = xaset_create(timer_pool, NULL); xaset_insert(recycled, (xasetmember_t *) t); } else { xaset_insert_sort(timers, (xasetmember_t *) t, TRUE); nalarms++; set_sig_alarm(); /* The handle_alarm() function also readjusts the timers lists * as part of its processing, so it needs to be called when a timer * is added. */ handle_alarm(); } pr_alarms_unblock(); pr_trace_msg("timer", 7, "added timer ID %d ('%s', for module '%s'), " "triggering in %ld %s", t->timerno, t->desc, t->mod ? t->mod->name : "[none]", t->interval, t->interval != 1 ? "seconds" : "second"); return timerno; }
int pr_timer_remove(int timerno, module *mod) { struct timer *t = NULL, *tnext = NULL; int nremoved = 0; /* If there are no timers currently registered, do nothing. */ if (!timers) return 0; pr_alarms_block(); for (t = (struct timer *) timers->xas_list; t; t = tnext) { tnext = t->next; if ((timerno < 0 || t->timerno == timerno) && (mod == ANY_MODULE || t->mod == mod)) { nremoved++; if (_indispatch) { t->remove++; } else { xaset_remove(timers, (xasetmember_t *) t); xaset_insert(free_timers, (xasetmember_t *) t); nalarms++; /* The handle_alarm() function also readjusts the timers lists * as part of its processing, so it needs to be called when a timer * is removed. */ handle_alarm(); } pr_trace_msg("timer", 7, "removed timer ID %d ('%s', for module '%s')", t->timerno, t->desc, t->mod ? t->mod->name : "[none]"); } /* If we are removing a specific timer, break out of the loop now. * Otherwise, keep removing any matching timers. */ if (nremoved > 0 && timerno >= 0) { break; } } pr_alarms_unblock(); if (nremoved == 0) { errno = ENOENT; return -1; } /* If we removed a specific timer because of the given timerno, return * that timerno value. */ if (timerno >= 0) { return timerno; } return nremoved; }
MODRET site_chgrp(cmd_rec *cmd) { int res; gid_t gid; char *path = NULL, *tmp = NULL, *arg = ""; struct stat st; register unsigned int i = 0; #ifdef PR_USE_REGEX pr_regex_t *pre; #endif if (cmd->argc < 3) { pr_response_add_err(R_500, _("'SITE %s' not understood"), _get_full_cmd(cmd)); return NULL; } /* Construct the target file name by concatenating all the parameters after * the mode, 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, _("SITE %s: Illegal character sequence in command"), (char *) cmd->argv[1]); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", decoded_path, NULL); } #ifdef PR_USE_REGEX pre = get_param_ptr(CURRENT_CONF, "PathAllowFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) != 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathAllowFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } pre = get_param_ptr(CURRENT_CONF, "PathDenyFilter", FALSE); if (pre != NULL && pr_regexp_exec(pre, arg, 0, NULL, 0, 0, 0) == 0) { pr_log_debug(DEBUG2, "'%s %s' denied by PathDenyFilter", (char *) cmd->argv[0], arg); pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } #endif if (pr_fsio_lstat(arg, &st) == 0) { if (S_ISLNK(st.st_mode)) { char link_path[PR_TUNABLE_PATH_MAX]; int len; memset(link_path, '\0', sizeof(link_path)); len = dir_readlink(cmd->tmp_pool, arg, link_path, sizeof(link_path)-1, PR_DIR_READLINK_FL_HANDLE_REL_PATH); if (len > 0) { link_path[len] = '\0'; arg = pstrdup(cmd->tmp_pool, link_path); } } } path = dir_realpath(cmd->tmp_pool, arg); if (path == NULL) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } /* Map the given group argument, if a string, to a GID. If already a * number, pass through as is. */ gid = strtoul(cmd->argv[1], &tmp, 10); if (tmp && *tmp) { /* Try the parameter as a group name. */ gid = pr_auth_name2gid(cmd->tmp_pool, cmd->argv[1]); if (gid == (gid_t) -1) { int xerrno = EINVAL; pr_log_debug(DEBUG9, "SITE CHGRP: Unable to resolve group name '%s' to GID", (char *) cmd->argv[1]); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } } res = core_chgrp(cmd, path, (uid_t) -1, gid); if (res < 0) { int xerrno = errno; (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): " "error chown'ing '%s' to GID %s: %s", (char *) cmd->argv[0], session.user, pr_uid2str(cmd->tmp_pool, session.uid), pr_gid2str(cmd->tmp_pool, session.gid), path, pr_gid2str(cmd->tmp_pool, gid), strerror(xerrno)); pr_response_add_err(R_550, "%s: %s", arg, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } pr_response_add(R_200, _("SITE %s command successful"), (char *) cmd->argv[0]); return PR_HANDLED(cmd); }
MODRET copy_cpfr(cmd_rec *cmd) { register unsigned int i; int res; char *path = ""; unsigned char *authenticated = NULL; if (copy_engine == FALSE) { return PR_DECLINED(cmd); } if (cmd->argc < 3 || strncasecmp(cmd->argv[1], "CPFR", 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); /* Construct the target file name by concatenating all the parameters after * the "SITE CPFR", 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); } path = pstrcat(cmd->tmp_pool, path, *path ? " " : "", decoded_path, NULL); } res = pr_filter_allow_path(CURRENT_CONF, path); switch (res) { case 0: break; case PR_FILTER_ERR_FAILS_ALLOW_FILTER: pr_log_debug(DEBUG2, MOD_COPY_VERSION ": 'CPFR %s' denied by PathAllowFilter", path); pr_response_add_err(R_550, _("%s: Forbidden filename"), path); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); case PR_FILTER_ERR_FAILS_DENY_FILTER: pr_log_debug(DEBUG2, MOD_COPY_VERSION ": 'CPFR %s' denied by PathDenyFilter", path); pr_response_add_err(R_550, _("%s: Forbidden filename"), path); pr_cmd_set_errno(cmd, EPERM); errno = EPERM; return PR_ERROR(cmd); } /* Allow renaming a symlink, even a dangling one. */ path = dir_canonical_vpath(cmd->tmp_pool, path); if (!path || !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL) || !exists(path)) { int xerrno = errno; pr_response_add_err(R_550, "%s: %s", path, strerror(xerrno)); pr_cmd_set_errno(cmd, xerrno); errno = xerrno; return PR_ERROR(cmd); } if (pr_table_add(session.notes, "mod_copy.cpfr-path", pstrdup(session.pool, path), 0) < 0) { pr_trace_msg(trace_channel, 4, "error adding 'mod_copy.cpfr-path' note: %s", strerror(errno)); } pr_response_add(R_350, _("File or directory exists, ready for destination " "name")); return PR_HANDLED(cmd); }
const pr_netaddr_t *proxy_ftp_msg_parse_ext_addr(pool *p, const char *msg, const pr_netaddr_t *addr, int cmd_id, const char *net_proto) { pr_netaddr_t *res = NULL, na; int family = 0; unsigned short port = 0; char delim, *msg_str, *ptr; size_t msglen; if (p == NULL || msg == NULL || addr == NULL) { errno = EINVAL; return NULL; } if (cmd_id == PR_CMD_EPSV_ID) { /* First, find the opening '(' character. */ ptr = strchr(msg, '('); if (ptr == NULL) { pr_trace_msg(trace_channel, 12, "missing starting '(' character for extended address in '%s'", msg); errno = EINVAL; return NULL; } /* Make sure that the last character is a closing ')'. */ msglen = strlen(ptr); if (ptr[msglen-1] != ')') { pr_trace_msg(trace_channel, 12, "missing ending ')' character for extended address in '%s'", msg); errno = EINVAL; return NULL; } msg_str = pstrndup(p, ptr+1, msglen-2); } else { msg_str = pstrdup(p, msg); } /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order), * where <d> is an arbitrary delimiter character. */ delim = *msg_str++; /* If the network protocol string (e.g. sent by client in EPSV command) is * null, then determine the protocol family from the address family we were * given. */ /* XXX Hack to skip "all", e.g. "EPSV ALL" commands. */ if (net_proto != NULL) { if (strncasecmp(net_proto, "all", 4) == 0) { net_proto = NULL; } } if (net_proto == NULL) { if (*msg_str == delim) { switch (pr_netaddr_get_family(addr)) { case AF_INET: family = 1; break; #ifdef PR_USE_IPV6 case AF_INET6: if (pr_netaddr_use_ipv6()) { family = 2; break; } #endif /* PR_USE_IPV6 */ } } else { family = atoi(msg_str); } } else { family = atoi(net_proto); } switch (family) { case 1: break; #ifdef PR_USE_IPV6 case 2: if (pr_netaddr_use_ipv6()) { break; } #endif /* PR_USE_IPV6 */ default: pr_trace_msg(trace_channel, 12, "unsupported network protocol %d", family); errno = EPROTOTYPE; return NULL; } /* Now, skip past those numeric characters that atoi() used. */ while (PR_ISDIGIT(*msg_str)) { msg_str++; } /* If the next character is not the delimiter, it's a badly formatted * parameter. */ if (*msg_str == delim) { msg_str++; } else { pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'", msg_str); errno = EPERM; return NULL; } pr_netaddr_clear(&na); /* If the next character IS the delimiter, then the address portion is * omitted (which is permissible). */ if (*msg_str == delim) { pr_netaddr_set_family(&na, pr_netaddr_get_family(addr)); pr_netaddr_set_sockaddr(&na, pr_netaddr_get_sockaddr(addr)); msg_str++; } else { ptr = strchr(msg_str, delim); if (ptr == NULL) { /* Badly formatted message. */ errno = EINVAL; return NULL; } /* Twiddle the string so that just the address portion will be processed * by pr_inet_pton(). */ *ptr = '\0'; /* Use pr_inet_pton() to translate the address string into the address * value. */ switch (family) { case 1: { struct sockaddr *sa = NULL; pr_netaddr_set_family(&na, AF_INET); sa = pr_netaddr_get_sockaddr(&na); if (sa) { sa->sa_family = AF_INET; } if (pr_inet_pton(AF_INET, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) { pr_trace_msg(trace_channel, 2, "error converting IPv4 address '%s': %s", msg_str, strerror(errno)); errno = EPERM; return NULL; } break; } case 2: { struct sockaddr *sa = NULL; pr_netaddr_set_family(&na, AF_INET6); sa = pr_netaddr_get_sockaddr(&na); if (sa) { sa->sa_family = AF_INET6; } if (pr_inet_pton(AF_INET6, msg_str, pr_netaddr_get_inaddr(&na)) <= 0) { pr_trace_msg(trace_channel, 2, "error converting IPv6 address '%s': %s", msg_str, strerror(errno)); errno = EPERM; return NULL; } break; } } /* Advance past the address portion of the argument. */ msg_str = ++ptr; } port = atoi(msg_str); while (PR_ISDIGIT(*msg_str)) { msg_str++; } /* If the next character is not the delimiter, it's a badly formatted * parameter. */ if (*msg_str != delim) { pr_trace_msg(trace_channel, 17, "rejecting badly formatted message '%s'", msg_str); errno = EPERM; return NULL; } /* XXX Use a pool other than session.pool here, in the future. */ res = pr_netaddr_dup(session.pool, &na); pr_netaddr_set_port(res, htons(port)); return res; }
int pr_event_unregister(module *m, const char *event, void (*cb)(const void *, void *)) { struct event_list *evl; int unregistered = FALSE; if (!events) return 0; pr_trace_msg(trace_channel, 3, "module '%s' (%p) unregistering handler for event '%s'", m ? m->name : "(none)", m, event ? event : "(all)"); /* For now, simply remove the event_handler entry for this callback. In * the future, add a static counter, and churn the event pool after a * certain number of unregistrations, so that the memory pool doesn't * grow unnecessarily. */ for (evl = events; evl; evl = evl->next) { pr_signals_handle(); if (!event || strcmp(evl->event, event) == 0) { struct event_handler *evh; /* If there are no handlers for this event, there is nothing to * unregister. Skip on to the next list. */ if (!evl->handlers) { continue; } for (evh = evl->handlers; evh;) { if ((m == NULL || evh->module == m) && (cb == NULL || evh->cb == cb)) { struct event_handler *tmp = evh->next; if (evh->next) { evh->next->prev = evh->prev; } if (evh->prev) { evh->prev->next = evh->next; } else { /* This is the head of the list. */ evl->handlers = evh->next; } evh->module = NULL; evh = tmp; unregistered = TRUE; } else { evh = evh->next; } } } } /* Clear any cached data. */ curr_event = NULL; curr_evl = NULL; curr_evh = NULL; if (!unregistered) { errno = ENOENT; return -1; } return 0; }
const char *proxy_ftp_msg_fmt_ext_addr(pool *p, const pr_netaddr_t *addr, unsigned short port, int cmd_id, int use_masqaddr) { const char *addr_str; char delim = '|', *msg; int family = 0; size_t addr_strlen, msglen; if (p == NULL || addr == NULL) { errno = EINVAL; return NULL; } if (use_masqaddr) { config_rec *c; /* Handle MasqueradeAddress. */ c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE); if (c != NULL) { addr = c->argv[0]; } } /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order), * where <d> is an arbitrary delimiter character. */ switch (pr_netaddr_get_family(addr)) { case AF_INET: family = 1; break; #ifdef PR_USE_IPV6 case AF_INET6: family = 2; break; #endif /* PR_USE_IPV6 */ default: /* Unlikely to happen. */ errno = EINVAL; return NULL; } addr_str = pr_netaddr_get_ipstr(addr); addr_strlen = strlen(addr_str); /* 4 delimiters, the network protocol, the IP address, the port, and a NUL. */ msglen = (4 * 1) + addr_strlen + 6 + 1; msg = pcalloc(p, msglen); switch (cmd_id) { case PR_CMD_EPRT_ID: snprintf(msg, msglen, "%c%d%c%s%c%hu%c", delim, family, delim, addr_str, delim, port, delim); break; case PR_CMD_EPSV_ID: snprintf(msg, msglen-1, "%c%c%c%u%c", delim, delim, delim, port, delim); break; default: pr_trace_msg(trace_channel, 3, "invalid/unsupported command ID: %d", cmd_id); errno = EINVAL; return NULL; } return msg; }
int pr_event_register(module *m, const char *event, void (*cb)(const void *, void *), void *user_data) { struct event_handler *evh; struct event_list *evl; pool *evl_pool; if (!event || !cb) { errno = EINVAL; return -1; } if (!event_pool) { event_pool = make_sub_pool(permanent_pool); pr_pool_tag(event_pool, "Event Pool"); } pr_trace_msg(trace_channel, 3, "module '%s' (%p) registering handler for event '%s' (at %p)", m ? m->name : "(none)", m, event, cb); evh = pcalloc(event_pool, sizeof(struct event_handler)); evh->module = m; evh->cb = cb; evh->user_data = user_data; /* Scan the currently registered lists, looking for where to add this * registration. */ for (evl = events; evl; evl = evl->next) { if (strcmp(evl->event, event) == 0) { struct event_handler *evhi = evl->handlers; if (evhi) { /* Make sure this event handler is added to the end of the list, * in order to preserve module load order handling of events. */ while (evhi) { if (evhi->cb == evh->cb) { /* Duplicate callback */ errno = EEXIST; return -1; } if (evhi->next == NULL) break; evhi = evhi->next; } evh->prev = evhi; evhi->next = evh; } else evl->handlers = evh; /* All done */ return 0; } } evl_pool = pr_pool_create_sz(event_pool, EVENT_POOL_SZ); pr_pool_tag(evl_pool, "Event listener list pool"); evl = pcalloc(evl_pool, sizeof(struct event_list)); evl->pool = evl_pool; evl->event = pstrdup(evl->pool, event); evl->handlers = evh; evl->next = events; events = evl; /* Clear any cached data. */ curr_event = NULL; curr_evl = NULL; curr_evh = NULL; return 0; }
static int forward_connect(pool *p, struct proxy_session *proxy_sess, pr_response_t **resp, unsigned int *resp_nlines) { conn_t *server_conn = NULL; int banner_ok = TRUE, use_tls, xerrno = 0; pr_netaddr_t *dst_addr; array_header *other_addrs = NULL; dst_addr = proxy_sess->dst_addr; other_addrs = proxy_sess->other_addrs; server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn == NULL) { xerrno = errno; if (other_addrs != NULL) { register unsigned int i; /* Try the other IP addresses for the requested name (if any) as well. */ for (i = 0; i < other_addrs->nelts; i++) { dst_addr = ((pr_netaddr_t **) other_addrs->elts)[i]; pr_trace_msg(trace_channel, 8, "attempting to connect to other address #%u (%s) for requested " "URI '%.100s'", i+1, pr_netaddr_get_ipstr(dst_addr), proxy_conn_get_uri(proxy_sess->dst_pconn)); server_conn = proxy_conn_get_server_conn(p, proxy_sess, dst_addr); if (server_conn != NULL) { proxy_sess->dst_addr = dst_addr; break; } } } if (server_conn == NULL) { xerrno = errno; /* EINVALs lead to strange-looking error responses; change them to * EPERM. */ if (xerrno == EINVAL) { xerrno = EPERM; } } errno = xerrno; return -1; } /* XXX Support/send a CLNT command of our own? Configurable via e.g. * "UserAgent" string? */ proxy_sess->frontend_ctrl_conn = session.c; proxy_sess->backend_ctrl_conn = server_conn; /* Read the response from the backend server. */ *resp = proxy_ftp_ctrl_recv_resp(p, proxy_sess->backend_ctrl_conn, resp_nlines); if (*resp == NULL) { xerrno = errno; pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to read banner from server %s:%u: %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), strerror(xerrno)); errno = EPERM; return -1; } if ((*resp)->num[0] != '2') { banner_ok = FALSE; } (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "received banner from backend %s:%u%s: %s %s", pr_netaddr_get_ipstr(proxy_sess->backend_ctrl_conn->remote_addr), ntohs(pr_netaddr_get_port(proxy_sess->backend_ctrl_conn->remote_addr)), banner_ok ? "" : ", DISCONNECTING", (*resp)->num, (*resp)->msg); if (banner_ok == FALSE) { pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; errno = EPERM; return -1; } /* Get the features supported by the backend server */ if (proxy_ftp_sess_get_feat(p, proxy_sess) < 0) { if (errno != EPERM) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "unable to determine features of backend server: %s", strerror(errno)); } } use_tls = proxy_tls_use_tls(); if (use_tls != PROXY_TLS_ENGINE_OFF) { if (proxy_ftp_sess_send_auth_tls(p, proxy_sess) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error enabling TLS on control connection to backend server: %s", strerror(xerrno)); pr_inet_close(p, proxy_sess->backend_ctrl_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } } if (proxy_netio_postopen(server_conn->instrm) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "postopen error for backend control connection input stream: %s", strerror(xerrno)); proxy_inet_close(session.pool, server_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } if (proxy_netio_postopen(server_conn->outstrm) < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "postopen error for backend control connection output stream: %s", strerror(xerrno)); proxy_inet_close(session.pool, server_conn); proxy_sess->backend_ctrl_conn = NULL; *resp = NULL; errno = xerrno; return -1; } if (use_tls != PROXY_TLS_ENGINE_OFF) { if (proxy_sess_state & PROXY_SESS_STATE_BACKEND_HAS_CTRL_TLS) { /* NOTE: should this be a fatal error? */ (void) proxy_ftp_sess_send_pbsz_prot(p, proxy_sess); } } (void) proxy_ftp_sess_send_host(p, proxy_sess); proxy_sess_state |= PROXY_SESS_STATE_CONNECTED; return 0; }
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; }
static int forward_cmd_parse_dst(pool *p, const char *arg, char **name, struct proxy_conn **pconn) { const char *default_proto = NULL, *default_port = NULL, *proto = NULL, *port, *uri = NULL; char *host = NULL, *hostport = NULL, *host_ptr = NULL, *port_ptr = NULL; /* TODO: Revisit theses default once we start supporting other protocols. */ default_proto = "ftp"; default_port = "21"; /* First, look for the optional port. */ port_ptr = strrchr(arg, ':'); if (port_ptr == NULL) { port = default_port; } else { char *tmp2 = NULL; long num; num = strtol(port_ptr+1, &tmp2, 10); if (tmp2 && *tmp2) { /* Trailing garbage found in port number. */ (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "malformed port number '%s' found in USER '%s', rejecting", port_ptr+1, arg); errno = EINVAL; return -1; } if (num < 0 || num > 65535) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "invalid port number %ld found in USER '%s', rejecting", num, arg); errno = EINVAL; return -1; } port = pstrdup(p, port_ptr + 1); } /* Find the required '@' delimiter. */ host_ptr = strrchr(arg, '@'); if (host_ptr == NULL) { (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "missing required '@' delimiter in USER '%s', rejecting", arg); errno = EINVAL; return -1; } if (port_ptr == NULL) { host = pstrdup(p, host_ptr + 1); } else { host = pstrndup(p, host_ptr + 1, (port_ptr - host_ptr - 1)); } *name = pstrndup(p, arg, (host_ptr - arg)); proto = default_proto; hostport = pstrcat(p, host, ":", port, NULL); if (forward_dst_filter(p, hostport) < 0) { return -1; } uri = pstrcat(p, proto, "://", hostport, NULL); /* Note: We deliberately use proxy_pool, rather than the given pool, here * so that the created structure (especially the pr_netaddr_t) are * longer-lived. */ *pconn = proxy_conn_create(proxy_pool, uri); if (*pconn == NULL) { int xerrno = errno; pr_trace_msg(trace_channel, 1, "error handling URI '%.100s': %s", uri, strerror(xerrno)); errno = xerrno; return -1; } return 0; }
int erase_erase(pool *p, int fd, off_t filesz, int pattern, unsigned char *eraser, size_t erasersz) { int res; off_t max, pos = 0; if (p == NULL || eraser == NULL) { errno = EINVAL; return -1; } res = erase_pattern_fill(p, pattern, eraser, erasersz); if (res < 0) { int xerrno = errno; pr_trace_msg(erase_channel, 6, "error filling buffer for '%s' pattern: %s", erase_strpattern(p, pattern), strerror(errno)); errno = xerrno; return -1; } /* Always remember that pos is an INDEX, and filesz is a LENGTH. Thus the * max position value is filesz-1. */ max = filesz - 1; while (pos < max) { size_t eraser_len; off_t eraser_rem; pr_signals_handle(); eraser_len = erasersz; eraser_rem = max - pos; if (eraser_rem < eraser_len) { eraser_len = (size_t) eraser_rem; } res = write_eraser(fd, eraser, eraser_len); if (res < 0) { break; } /* Note: Should this sync happen after every write, or only after * the entire filesz has been written? Tunable, perhaps, for performance? */ if (erase_fsync(p, fd) < 0) { pr_trace_msg(erase_channel, 7, "error flushing data to disk for fd %d: %s", fd, strerror(errno)); } pos += eraser_len; if (pos < max) { res = refill_pattern(p, pattern, eraser, erasersz); if (res < 0) { break; } } } return res; }
static void log_write(int priority, int f, char *s, int discard) { unsigned int max_priority = 0, *ptr = NULL; char serverinfo[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; memset(serverinfo, '\0', sizeof(serverinfo)); if (main_server && main_server->ServerFQDN) { pr_netaddr_t *remote_addr = pr_netaddr_get_sess_remote_addr(); const char *remote_name = pr_netaddr_get_sess_remote_name(); snprintf(serverinfo, sizeof(serverinfo)-1, "%s", main_server->ServerFQDN); serverinfo[sizeof(serverinfo)-1] = '\0'; if (remote_addr && remote_name) { size_t serverinfo_len; serverinfo_len = strlen(serverinfo); snprintf(serverinfo + serverinfo_len, sizeof(serverinfo) - serverinfo_len, " (%s[%s])", remote_name, pr_netaddr_get_ipstr(remote_addr)); serverinfo[sizeof(serverinfo)-1] = '\0'; } } if (!discard && (logstderr || !main_server)) { char buf[LOGBUFFER_SIZE] = {'\0'}; size_t buflen, len; struct timeval now; struct tm *tm = NULL; unsigned long millis; gettimeofday(&now, NULL); tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec)); if (tm == NULL) { return; } len = strftime(buf, sizeof(buf)-1, "%Y-%m-%d %H:%M:%S", tm); buflen = len; buf[sizeof(buf)-1] = '\0'; /* Convert microsecs to millisecs. */ millis = now.tv_usec / 1000; len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis); buflen += len; buf[sizeof(buf)-1] = '\0'; if (*serverinfo) { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u] %s: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s); } else { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u]: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), s); } buflen += len; buf[sizeof(buf)-1] = '\0'; pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, STDERR_FILENO, priority, buf, buflen); fprintf(stderr, "%s", buf); return; } if (syslog_discard) { /* Only return now if we don't have any log listeners. */ if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 && pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) { return; } } if (main_server != NULL) { ptr = get_param_ptr(main_server->conf, "SyslogLevel", FALSE); } if (ptr != NULL) { max_priority = *ptr; } else { /* Default SyslogLevel is NOTICE. Note, however, that for backward * compatibility of debugging, if the DebugLevel is set higher * than DEBUG0, we will automatically ASSUME that the admin wants * the syslog level to be e.g. DEBUG. */ max_priority = default_level; if (debug_level != DEBUG0) { max_priority = PR_LOG_DEBUG; } } if (priority > max_priority) { /* Only return now if we don't have any log listeners. */ if (pr_log_event_listening(PR_LOG_TYPE_SYSLOG) <= 0 && pr_log_event_listening(PR_LOG_TYPE_SYSTEMLOG) <= 0) { return; } } if (systemlog_fd != -1) { char buf[LOGBUFFER_SIZE] = {'\0'}; size_t buflen, len; struct timeval now; struct tm *tm; unsigned long millis; gettimeofday(&now, NULL); tm = pr_localtime(NULL, (const time_t *) &(now.tv_sec)); if (tm == NULL) { return; } len = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); buflen = len; buf[sizeof(buf) - 1] = '\0'; /* Convert microsecs to millisecs. */ millis = now.tv_usec / 1000; len = snprintf(buf + buflen, sizeof(buf) - len, ",%03lu ", millis); buflen += len; buf[sizeof(buf) - 1] = '\0'; if (*serverinfo) { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u] %s: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), serverinfo, s); } else { len = snprintf(buf + buflen, sizeof(buf) - buflen, "%s proftpd[%u]: %s\n", systemlog_host, (unsigned int) (session.pid ? session.pid : getpid()), s); } buflen += len; buf[sizeof(buf)-1] = '\0'; pr_log_event_generate(PR_LOG_TYPE_SYSTEMLOG, systemlog_fd, priority, buf, buflen); /* Now we need to enforce the discard, syslog_discard and SyslogLevel * filtering. */ if (discard) { return; } if (syslog_discard) { return; } if (priority > max_priority) { return; } while (write(systemlog_fd, buf, buflen) < 0) { if (errno == EINTR) { pr_signals_handle(); continue; } break; } return; } pr_log_event_generate(PR_LOG_TYPE_SYSLOG, syslog_sockfd, priority, s, strlen(s)); if (set_facility != -1) { f = set_facility; } if (!syslog_open) { syslog_sockfd = pr_openlog("proftpd", LOG_NDELAY|LOG_PID, f); if (syslog_sockfd < 0) { (void) pr_trace_msg(trace_channel, 1, "error opening syslog fd: %s", strerror(errno)); return; } syslog_open = TRUE; } else if (f != facility) { /* If this message is to be sent to a different log facility than a * default one (or the facility configured via SyslogFacility), then * OR in the facility with the priority value, as per the syslog(3) * docs. */ priority |= f; } if (*serverinfo) { pr_syslog(syslog_sockfd, priority, "%s - %s\n", serverinfo, s); } else { pr_syslog(syslog_sockfd, priority, "%s\n", s); } }
int sftp_auth_publickey(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, unsigned char **buf, uint32_t *buflen, int *send_userauth_fail) { int have_signature, res; enum sftp_key_type_e pubkey_type; unsigned char *pubkey_data; char *pubkey_algo = NULL; const char *fp = NULL, *fp_algo = NULL; uint32_t pubkey_len; struct passwd *pw; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication request for user '%s' blocked by '%s' handler", orig_user, pass_cmd->argv[0]); pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); *send_userauth_fail = TRUE; errno = EPERM; return 0; } have_signature = sftp_msg_read_bool(pkt->pool, buf, buflen); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) { pubkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen); } pubkey_len = sftp_msg_read_int(pkt->pool, buf, buflen); pubkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, pubkey_len); if (pubkey_algo == NULL) { /* The client did not send the string identifying the public key algorithm. * Thus we need to extract the algorithm string from the public key data. */ pubkey_algo = sftp_msg_read_string(pkt->pool, &pubkey_data, &pubkey_len); } pr_trace_msg(trace_channel, 9, "client sent '%s' public key %s", pubkey_algo, have_signature ? "with signature" : "without signature"); if (strncmp(pubkey_algo, "ssh-rsa", 8) == 0) { pubkey_type = SFTP_KEY_RSA; } else if (strncmp(pubkey_algo, "ssh-dss", 8) == 0) { pubkey_type = SFTP_KEY_DSA; #ifdef PR_USE_OPENSSL_ECC } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp256", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_256; } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp384", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_384; } else if (strncmp(pubkey_algo, "ecdsa-sha2-nistp521", 20) == 0) { pubkey_type = SFTP_KEY_ECDSA_521; #endif /* PR_USE_OPENSSL_ECC */ /* XXX This is where we would add support for X509 public keys, e.g.: * * x509v3-ssh-dss * x509v3-ssh-rsa * x509v3-sign (older) * */ } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unsupported public key algorithm '%s' requested, rejecting request", pubkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } res = sftp_keys_verify_pubkey_type(pkt->pool, pubkey_data, pubkey_len, pubkey_type); if (res != TRUE) { int xerrno = errno; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to verify that given public key matches given '%s' algorithm", pubkey_algo); if (res < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error verifying public key algorithm '%s': %s", pubkey_algo, strerror(xerrno)); } *send_userauth_fail = TRUE; errno = EINVAL; return 0; } #ifdef OPENSSL_FIPS if (FIPS_mode()) { fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len, SFTP_KEYS_FP_DIGEST_SHA1); if (fp != NULL) { fp_algo = "SHA1"; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key %s fingerprint: %s", fp_algo, fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key SHA1 fingerprint: %s", strerror(errno)); } } else { #endif /* OPENSSL_FIPS */ fp = sftp_keys_get_fingerprint(pkt->pool, pubkey_data, pubkey_len, SFTP_KEYS_FP_DIGEST_MD5); if (fp != NULL) { fp_algo = "MD5"; (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key %s fingerprint: %s", fp_algo, fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key MD5 fingerprint: %s", strerror(errno)); } #ifdef OPENSSL_FIPS } #endif /* OPENSSL_FIPS */ if (fp != NULL) { const char *k, *v; /* Log the fingerprint (and fingerprinting algorithm used), for * debugging/auditing; make it available via environment variable as well. */ k = pstrdup(session.pool, "SFTP_USER_PUBLICKEY_FINGERPRINT"); v = pstrdup(session.pool, fp); pr_env_unset(session.pool, k); pr_env_set(session.pool, k, v); k = pstrdup(session.pool, "SFTP_USER_PUBLICKEY_FINGERPRINT_ALGO"); v = pstrdup(session.pool, fp_algo); pr_env_unset(session.pool, k); pr_env_set(session.pool, k, v); } pw = pr_auth_getpwnam(pkt->pool, user); if (pw == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "no account for user '%s' found", user); pr_log_auth(PR_LOG_NOTICE, "USER %s: no such user found from %s [%s] to %s:%d", user, session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } if (!have_signature) { /* We don't perform the actual authentication just yet; we need to * let the client know that the pubkey algorithms are acceptable. */ if (send_pubkey_ok(pubkey_algo, pubkey_data, pubkey_len) < 0) { return -1; } return 0; } else { const unsigned char *id; unsigned char *buf2, *ptr2, *signature_data; uint32_t buflen2, bufsz2, id_len, signature_len; /* XXX This should become a more generic "is this key data * usable/acceptable?" check (and take the pubkey_type parameter), so that * that is where we would check the validity/usability of an X509v3 cert * (if a cert), or if the key is on the blacklist (if a key). */ if (sftp_blacklist_reject_key(pkt->pool, pubkey_data, pubkey_len)) { *send_userauth_fail = TRUE; errno = EPERM; return 0; } signature_len = sftp_msg_read_int(pkt->pool, buf, buflen); signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len); /* The client signed the request as well; we need to authenticate the * user with the given pubkey now. If that succeeds, we use the * signature to verify the request. And if that succeeds, then we're * done authenticating. */ /* XXX Need to pass the pubkey_type here as well, so that the * verification routines can handle different databases of keys/certs. * * For X509v3 certs, we will want a way to enforce/restrict which * user names can be used with the provided cert. Perhaps a database * mapping cert fingerprints to user names/UIDs? Configurable callback * check (HOOK?), for modules to enforce. */ if (sftp_keystore_verify_user_key(pkt->pool, user, pubkey_data, pubkey_len) < 0) { *send_userauth_fail = TRUE; errno = EACCES; return 0; } /* Make sure the signature matches as well. */ id_len = sftp_session_get_id(&id); /* Make sure to allocate a buffer large enough to hold the publickey * signature and data we want to send back. */ bufsz2 = buflen2 = pubkey_len + 1024; ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2); sftp_msg_write_data(&buf2, &buflen2, id, id_len, TRUE); sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST); sftp_msg_write_string(&buf2, &buflen2, orig_user); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_PUBKEY_SIG)) { sftp_msg_write_string(&buf2, &buflen2, service); } else { sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth"); } if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_HAVE_PUBKEY_ALGO)) { sftp_msg_write_string(&buf2, &buflen2, "publickey"); sftp_msg_write_bool(&buf2, &buflen2, TRUE); sftp_msg_write_string(&buf2, &buflen2, pubkey_algo); } else { sftp_msg_write_bool(&buf2, &buflen2, TRUE); } sftp_msg_write_data(&buf2, &buflen2, pubkey_data, pubkey_len, TRUE); if (sftp_keys_verify_signed_data(pkt->pool, pubkey_algo, pubkey_data, pubkey_len, signature_data, signature_len, (unsigned char *) ptr2, (bufsz2 - buflen2)) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "failed to verify '%s' signature on public key auth request for " "user '%s'", pubkey_algo, orig_user); *send_userauth_fail = TRUE; errno = EACCES; return 0; } } /* Make sure the user is authorized to login. Normally this is checked * as part of the password verification process, but in the case of * publickey authentication, there is no password to verify. */ if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication for user '%s' failed: User not authorized", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized " "for login", user); *send_userauth_fail = TRUE; errno = EACCES; return 0; } return 1; }
int sftp_auth_hostbased(struct ssh2_packet *pkt, cmd_rec *pass_cmd, const char *orig_user, const char *user, const char *service, char **buf, uint32_t *buflen, int *send_userauth_fail) { struct passwd *pw; char *hostkey_algo, *host_fqdn, *host_user, *host_user_utf8; char *hostkey_data, *signature_data; char *buf2, *ptr2; const char *fp = NULL; const unsigned char *id; uint32_t buflen2, bufsz2, hostkey_datalen, id_len, signature_len; int pubkey_type; if (pr_cmd_dispatch_phase(pass_cmd, PRE_CMD, 0) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication request for user '%s' blocked by '%s' handler", orig_user, pass_cmd->argv[0]); pr_cmd_dispatch_phase(pass_cmd, POST_CMD_ERR, 0); pr_cmd_dispatch_phase(pass_cmd, LOG_CMD_ERR, 0); *send_userauth_fail = TRUE; errno = EPERM; return 0; } hostkey_algo = sftp_msg_read_string(pkt->pool, buf, buflen); hostkey_datalen = sftp_msg_read_int(pkt->pool, buf, buflen); hostkey_data = sftp_msg_read_data(pkt->pool, buf, buflen, hostkey_datalen); host_fqdn = sftp_msg_read_string(pkt->pool, buf, buflen); host_user_utf8 = sftp_msg_read_string(pkt->pool, buf, buflen); host_user = sftp_utf8_decode_str(pkt->pool, host_user_utf8); signature_len = sftp_msg_read_int(pkt->pool, buf, buflen); signature_data = sftp_msg_read_data(pkt->pool, buf, buflen, signature_len); pr_trace_msg(trace_channel, 9, "client sent '%s' host key, FQDN %s, and remote user '%s'", hostkey_algo, host_fqdn, host_user); if (strncmp(hostkey_algo, "ssh-rsa", 8) == 0) { pubkey_type = EVP_PKEY_RSA; } else if (strncmp(hostkey_algo, "ssh-dss", 8) == 0) { pubkey_type = EVP_PKEY_DSA; /* XXX Need to support X509v3 certs here */ } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unsupported host key algorithm '%s' requested, rejecting request", hostkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } if (sftp_keys_verify_pubkey_type(pkt->pool, hostkey_data, hostkey_datalen, pubkey_type) != TRUE) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to verify that given host key matches given '%s' algorithm", hostkey_algo); *send_userauth_fail = TRUE; errno = EINVAL; return 0; } #ifdef OPENSSL_FIPS if (FIPS_mode()) { fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen, SFTP_KEYS_FP_DIGEST_SHA1); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key SHA1 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key SHA1 fingerprint: %s", strerror(errno)); } } else { #endif /* OPENSSL_FIPS */ fp = sftp_keys_get_fingerprint(pkt->pool, hostkey_data, hostkey_datalen, SFTP_KEYS_FP_DIGEST_MD5); if (fp != NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "public key MD5 fingerprint: %s", fp); } else { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error obtaining public key MD5 fingerprint: %s", strerror(errno)); } #ifdef OPENSSL_FIPS } #endif /* OPENSSL_FIPS */ pw = pr_auth_getpwnam(pkt->pool, user); if (pw == NULL) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "no account for user '%s' found", user); pr_log_auth(PR_LOG_NOTICE, "USER %s: no such user found from %s [%s] to %s:%d", user, session.c->remote_name, pr_netaddr_get_ipstr(session.c->remote_addr), pr_netaddr_get_ipstr(session.c->local_addr), session.c->local_port); *send_userauth_fail = TRUE; errno = ENOENT; return 0; } /* XXX Should we check the given FQDN here against the client's actual * DNS name and/or IP address? Or leave that up to the keystore's * verify_host_key() function? */ if (sftp_blacklist_reject_key(pkt->pool, hostkey_data, hostkey_datalen)) { *send_userauth_fail = TRUE; errno = EPERM; return 0; } /* The client signed the request as well; we need to authenticate the * host with the given key now. If that succeeds, we use the signature to * verify the request. And if that succeeds, then we're done authenticating. */ if (sftp_keystore_verify_host_key(pkt->pool, user, host_fqdn, host_user, hostkey_data, hostkey_datalen) < 0) { *send_userauth_fail = TRUE; errno = EPERM; return 0; } /* Make sure the signature matches as well. */ id_len = sftp_session_get_id(&id); /* XXX Is this buffer large enough? Too large? */ bufsz2 = buflen2 = 2048; ptr2 = buf2 = sftp_msg_getbuf(pkt->pool, bufsz2); sftp_msg_write_data(&buf2, &buflen2, (char *) id, id_len, TRUE); sftp_msg_write_byte(&buf2, &buflen2, SFTP_SSH2_MSG_USER_AUTH_REQUEST); sftp_msg_write_string(&buf2, &buflen2, orig_user); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_SERVICE_IN_HOST_SIG)) { sftp_msg_write_string(&buf2, &buflen2, service); } else { sftp_msg_write_string(&buf2, &buflen2, "ssh-userauth"); } sftp_msg_write_string(&buf2, &buflen2, "hostbased"); sftp_msg_write_string(&buf2, &buflen2, hostkey_algo); sftp_msg_write_data(&buf2, &buflen2, hostkey_data, hostkey_datalen, TRUE); sftp_msg_write_string(&buf2, &buflen2, host_fqdn); sftp_msg_write_string(&buf2, &buflen2, host_user_utf8); if (sftp_keys_verify_signed_data(pkt->pool, hostkey_algo, hostkey_data, hostkey_datalen, signature_data, signature_len, (unsigned char *) ptr2, (bufsz2 - buflen2)) < 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "failed to verify '%s' signature on hostbased auth request for " "user '%s', host %s", hostkey_algo, orig_user, host_fqdn); *send_userauth_fail = TRUE; return 0; } /* Make sure the user is authorized to login. Normally this is checked * as part of the password verification process, but in the case of * hostbased authentication, there is no password to verify. */ if (pr_auth_authorize(pkt->pool, user) != PR_AUTH_OK) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "authentication for user '%s' failed: User not authorized", user); pr_log_auth(PR_LOG_NOTICE, "USER %s (Login failed): User not authorized " "for login", user); *send_userauth_fail = TRUE; errno = EACCES; return 0; } return 1; }
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 copy_paths(pool *p, const char *from, const char *to) { struct stat st; int res; xaset_t *set; set = get_dir_ctxt(p, (char *) to); res = pr_filter_allow_path(set, to); switch (res) { case 0: break; case PR_FILTER_ERR_FAILS_ALLOW_FILTER: pr_log_debug(DEBUG7, MOD_COPY_VERSION ": path '%s' denied by PathAllowFilter", to); errno = EPERM; return -1; case PR_FILTER_ERR_FAILS_DENY_FILTER: pr_log_debug(DEBUG7, MOD_COPY_VERSION ": path '%s' denied by PathDenyFilter", to); errno = EPERM; return -1; } /* Check whether from is a file, a directory, a symlink, or something * unsupported. */ res = pr_fsio_lstat(from, &st); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error checking '%s': %s", from, strerror(xerrno)); errno = xerrno; return -1; } if (S_ISREG(st.st_mode)) { char *abs_path; pr_fs_clear_cache2(to); res = pr_fsio_stat(to, &st); if (res == 0) { unsigned char *allow_overwrite; allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE); if (allow_overwrite == NULL || *allow_overwrite == FALSE) { pr_log_debug(DEBUG6, MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to); errno = EACCES; return -1; } } res = pr_fs_copy_file(from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying file '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } pr_fs_clear_cache2(to); if (pr_fsio_stat(to, &st) < 0) { pr_trace_msg(trace_channel, 3, "error stat'ing '%s': %s", to, strerror(errno)); } /* Write a TransferLog entry as well. */ abs_path = dir_abs_path(p, to, 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', "_"); } } else if (S_ISDIR(st.st_mode)) { res = create_path(p, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error creating path '%s': %s", to, strerror(xerrno)); errno = xerrno; return -1; } res = copy_dir(p, from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying directory '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } } else if (S_ISLNK(st.st_mode)) { pr_fs_clear_cache2(to); res = pr_fsio_stat(to, &st); if (res == 0) { unsigned char *allow_overwrite; allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE); if (allow_overwrite == NULL || *allow_overwrite == FALSE) { pr_log_debug(DEBUG6, MOD_COPY_VERSION ": AllowOverwrite permission denied for '%s'", to); errno = EACCES; return -1; } } res = copy_symlink(p, from, to); if (res < 0) { int xerrno = errno; pr_log_debug(DEBUG7, MOD_COPY_VERSION ": error copying symlink '%s' to '%s': %s", from, to, strerror(xerrno)); errno = xerrno; return -1; } } else { pr_log_debug(DEBUG7, MOD_COPY_VERSION ": unsupported file type for '%s'", from); errno = EINVAL; return -1; } return 0; }
static int sftppam_driver_open(sftp_kbdint_driver_t *driver, const char *user) { int res; config_rec *c; /* XXX Should we pay attention to AuthOrder here? I.e. if AuthOrder * does not include mod_sftp_pam or mod_auth_pam, should we fail to * open this driver, since the AuthOrder indicates that no PAM check is * desired? For this to work, AuthOrder needs to have been processed * prior to this callback being invoked... */ /* Figure out our default return style: whether or not PAM should allow * other auth modules a shot at this user or not is controlled by adding * '*' to a module name in the AuthOrder directive. By default, auth * modules are not authoritative, and allow other auth modules a chance at * authenticating the user. This is not the most secure configuration, but * it allows things like AuthUserFile to work "out of the box". */ if (sftppam_authtab[0].auth_flags & PR_AUTH_FL_REQUIRED) { sftppam_authoritative = TRUE; } sftppam_userlen = strlen(user) + 1; if (sftppam_userlen > (PAM_MAX_MSG_SIZE + 1)) { sftppam_userlen = PAM_MAX_MSG_SIZE + 1; } #ifdef MAXLOGNAME /* Some platforms' PAM libraries do not handle login strings that exceed * this length. */ if (sftppam_userlen > MAXLOGNAME) { pr_log_pri(PR_LOG_NOTICE, "PAM(%s): Name exceeds maximum login length (%u)", user, MAXLOGNAME); pr_trace_msg(trace_channel, 1, "user name '%s' exceeds maximum login length %u, declining", user, MAXLOGNAME); errno = EPERM; return -1; } #endif sftppam_user = malloc(sftppam_userlen); if (sftppam_user == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_PAM_VERSION ": Out of memory!"); exit(1); } memset(sftppam_user, '\0', sftppam_userlen); sstrncpy(sftppam_user, user, sftppam_userlen); c = find_config(main_server->conf, CONF_PARAM, "SFTPPAMOptions", FALSE); while (c != NULL) { unsigned long opts; pr_signals_handle(); opts = *((unsigned long *) c->argv[0]); sftppam_opts |= opts; c = find_config_next(c, c->next, CONF_PARAM, "SFTPPAMOptions", FALSE); } #ifdef SOLARIS2 /* For Solaris environments, the TTY environment will always be set, * in order to workaround a bug (Solaris Bug ID 4250887) where * pam_open_session() will crash unless both PAM_RHOST and PAM_TTY are * set, and the PAM_TTY setting is at least greater than the length of * the string "/dev/". */ sftppam_opts &= ~SFTP_PAM_OPT_NO_TTY; #endif /* SOLARIS2 */ pr_signals_block(); PRIVS_ROOT res = pam_start(sftppam_service, sftppam_user, &sftppam_conv, &sftppam_pamh); if (res != PAM_SUCCESS) { PRIVS_RELINQUISH pr_signals_unblock(); free(sftppam_user); sftppam_user = NULL; sftppam_userlen = 0; switch (res) { case PAM_SYSTEM_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: %s", strerror(errno)); break; case PAM_BUF_ERR: (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "error starting PAM service: Memory buffer error"); break; } return -1; } pam_set_item(sftppam_pamh, PAM_RUSER, sftppam_user); pam_set_item(sftppam_pamh, PAM_RHOST, session.c->remote_name); if (!(sftppam_opts & SFTP_PAM_OPT_NO_TTY)) { memset(sftppam_tty, '\0', sizeof(sftppam_tty)); snprintf(sftppam_tty, sizeof(sftppam_tty), "/dev/ftpd%02lu", (unsigned long) (session.pid ? session.pid : getpid())); sftppam_tty[sizeof(sftppam_tty)-1] = '\0'; pr_trace_msg(trace_channel, 9, "setting PAM_TTY to '%s'", sftppam_tty); pam_set_item(sftppam_pamh, PAM_TTY, sftppam_tty); } PRIVS_RELINQUISH pr_signals_unblock(); /* We need to disable mod_auth_pam, since both mod_auth_pam and us want * to talk to the PAM API, just in different fashions. */ c = add_config_param_set(&(main_server->conf), "AuthPAM", 1, NULL); c->argv[0] = palloc(c->pool, sizeof(unsigned char)); *((unsigned char *) c->argv[0]) = FALSE; if (pr_auth_remove_auth_only_module("mod_auth_pam.c") < 0) { if (errno != ENOENT) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error removing 'mod_auth_pam.c' from the auth-only module list: %s", strerror(errno)); } } if (pr_auth_add_auth_only_module("mod_sftp_pam.c") < 0) { if (errno != EEXIST) { pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": error adding 'mod_sftp_pam.c' to the auth-only module list: %s", strerror(errno)); } } sftppam_handle_auth = TRUE; driver->driver_pool = make_sub_pool(permanent_pool); pr_pool_tag(driver->driver_pool, "PAM keyboard-interactive driver pool"); return 0; }
int proxy_ftp_ctrl_handle_async(pool *p, conn_t *backend_conn, conn_t *frontend_conn) { if (!(proxy_sess_state & PROXY_SESS_STATE_CONNECTED)) { /* Nothing to do if we're not yet connected to the backend server. */ return 0; } while (TRUE) { fd_set rfds; struct timeval tv; int ctrlfd, res, xerrno = 0; /* By using a timeout of zero, we effect a poll on the fd. */ tv.tv_sec = 0; tv.tv_usec = 0; pr_signals_handle(); FD_ZERO(&rfds); ctrlfd = PR_NETIO_FD(backend_conn->instrm); FD_SET(ctrlfd, &rfds); res = select(ctrlfd + 1, &rfds, NULL, NULL, &tv); if (res < 0) { xerrno = errno; if (xerrno == EINTR) { pr_signals_handle(); continue; } (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error calling select(2) on backend control connection (fd %d): %s", ctrlfd, strerror(xerrno)); return 0; } if (res == 0) { /* Nothing there. */ break; } pr_trace_msg(trace_channel, 19, "select(2) reported %d for backend %s (fd %d)", res, backend_conn->remote_name, ctrlfd); if (FD_ISSET(ctrlfd, &rfds)) { unsigned int resp_nlines = 0; pr_response_t *resp; pr_timer_reset(PR_TIMER_IDLE, ANY_MODULE); pr_trace_msg(trace_channel, 9, "reading async response from backend %s", backend_conn->remote_name); resp = proxy_ftp_ctrl_recv_resp(p, backend_conn, &resp_nlines); if (resp == NULL) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error receiving response from backend control connection: %s", strerror(xerrno)); errno = xerrno; return -1; } res = proxy_ftp_ctrl_send_resp(p, frontend_conn, resp, resp_nlines); if (res < 0) { xerrno = errno; (void) pr_log_writefile(proxy_logfd, MOD_PROXY_VERSION, "error sending response to frontend control connection: %s", strerror(xerrno)); errno = xerrno; return -1; } } break; } return 0; }