Download | Plain Text | No Line Numbers
- /*
- * ProFTPD: mod_fw_ipset
- *
- * Allows passive ftp connections through stateful firewall by adding the
- * clients address to an ipset of type ip,port,ip.
- *
- * This module can be configured with:
- * - FwIPsetName4 <name_of_ipset_for_ipv4>
- * - FwIPsetName6 <name_of_ipset_for_ipv6>
- * - FwIPsetTimeout <ipset_default_timeout> (default 30)
- *
- * Make sure to match incoming connections against the ipset. e.g. for iptables:
- * -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEP
- * -A INPUT -p tcp -m set --match-set <set_name> dst,dst,src -j ACCEPT
- *
- * Copyright (c) 2017 Manuel Mausz
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Libraries: -lipset -lcap$
- */
-
- #include "conf.h"
- #include "privs.h"
-
- #include <libipset/types.h>
- #include <libipset/session.h>
- #include <linux/capability.h>
- #include <sys/capability.h>
-
- #define MOD_FW_IPSET_VERSION "mod_fw_ipset/0.1.1"
-
- static const char *trace_channel = "fw_ipset";
- static struct ipset_session *ipset;
- static cap_t capabilities = NULL;
- static int disable_id_switching = TRUE;
-
- static const char *setname4 = NULL;
- static const char *setname6 = NULL;
- static uint32_t settimeout = 30;
-
- module fw_ipset_module;
-
- static int fw_ipset_sess_init(void);
-
- static int try_ipset_create(struct ipset_session *session, const char *setname,
- const char *typename, int family, uint32_t timeout)
- {
- pr_trace_msg(trace_channel, 5, "creating ipset: %s with timeout %d",
- setname, timeout);
- ipset_session_data_set(session, IPSET_SETNAME, setname);
- ipset_session_data_set(session, IPSET_OPT_TYPENAME, typename);
- PRIVS_ROOT
- const struct ipset_type *type = ipset_type_get(session, IPSET_CMD_CREATE);
- PRIVS_RELINQUISH
- if (type == NULL) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Cannot find ipset type %s: %s",
- typename, ipset_session_error(session));
- return FALSE;
- }
-
- ipset_session_data_set(session, IPSET_OPT_TIMEOUT, &timeout);
- ipset_session_data_set(session, IPSET_OPT_TYPE, type);
- ipset_session_data_set(session, IPSET_OPT_FAMILY, &family);
-
- PRIVS_ROOT
- int res = ipset_cmd(session, IPSET_CMD_CREATE, /*lineno*/ 0);
- PRIVS_RELINQUISH
- if (res != 0) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Failed to create ipset %s: %s",
- setname, ipset_session_error(session));
- return FALSE;
- }
-
- return TRUE;
- }
-
- static int try_ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
- const char *setname, const int family,
- const pr_netaddr_t *local_addr, const pr_netaddr_t *remote_addr)
- {
- pr_trace_msg(trace_channel, 5, "adding data connection to ipset %s", setname);
- ipset_session_data_set(session, IPSET_SETNAME, setname);
- const struct ipset_type *type = ipset_type_get(session, cmd);
- if (type == NULL) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Cannot find ipset %s: %s",
- setname, ipset_session_error(session));
- return FALSE;
- }
-
- ipset_session_data_set(session, IPSET_OPT_FAMILY, &family);
- ipset_session_data_set(session, IPSET_OPT_IP,
- pr_netaddr_get_inaddr(local_addr));
- ipset_session_data_set(session, IPSET_OPT_IP2,
- pr_netaddr_get_inaddr(remote_addr));
- uint8_t proto = 6; /* TCP */
- ipset_session_data_set(session, IPSET_OPT_PROTO, &proto);
- uint16_t local_port = ntohs(pr_netaddr_get_port(local_addr));
- ipset_session_data_set(session, IPSET_OPT_PORT, &local_port);
-
- int res = ipset_cmd(session, cmd, /*lineno*/ 0);
- if (res != 0) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Failed to add ipset %s: %s",
- setname, ipset_session_error(session));
- return FALSE;
- }
-
- return TRUE;
- }
-
- static int lp_get_cap(void)
- {
- if ((capabilities = cap_get_proc()) == NULL) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION
- return -1;
- }
- return 0;
- }
-
- static void lp_free_cap()
- {
- if (cap_free(capabilities) < 0)
- pr_log_pri(PR_LOG_NOTICE, MOD_FW_IPSET_VERSION
- capabilities = NULL;
- }
-
- static int lp_modify_cap(cap_flag_value_t flag, cap_value_t cap, cap_flag_t set)
- {
- if (cap_set_flag(capabilities, set, 1, &cap, flag) == -1) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": cap_set_flag failed: %s",
- return -1;
- }
- if (cap_set_proc(capabilities) == -1) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": cap_set_proc failed: %s",
- return -1;
- }
- return 0;
- }
-
- static int cap_net_admin(void)
- {
- if (getuid() != PR_ROOT_UID) {
- pr_trace_msg(trace_channel, 5, "set capability to cap_net_admin+ep");
- return (lp_get_cap() != -1
- && lp_modify_cap(CAP_SET, CAP_NET_ADMIN, CAP_EFFECTIVE) != -1);
- }
- else {
- disable_id_switching = session.disable_id_switching;
- session.disable_id_switching = FALSE;
- PRIVS_ROOT
- return 0;
- }
- }
-
- static int no_cap_net_admin(void)
- {
- if (capabilities != NULL) {
- pr_trace_msg(trace_channel, 5, "set capability to cap_net_admin+p again");
- int res = lp_modify_cap(CAP_CLEAR, CAP_NET_ADMIN, CAP_EFFECTIVE);
- (void)lp_free_cap();
- return (res != -1);
- }
- else {
- PRIVS_RELINQUISH
- session.disable_id_switching = disable_id_switching;
- return 0;
- }
- }
-
- /* configuration handlers */
- MODRET set_ipset_name(cmd_rec *cmd)
- {
- CHECK_ARGS(cmd, 1);
- CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
-
- add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
- return PR_HANDLED(cmd);
- }
-
-
- MODRET set_ipset_timeout(cmd_rec *cmd)
- {
- int timeout = -1;
-
- CHECK_ARGS(cmd, 1);
- CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
-
- if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
- CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
- }
-
- config_rec *c = add_config_param(cmd->argv[0], 1, NULL);
- c->argv[0] = pcalloc(c->pool, sizeof(int));
- *((int *) c->argv[0]) = timeout;
-
- return PR_HANDLED(cmd);
- }
-
- /* event handlers */
- static void fw_ipset_data_listen_ev(const void *event_data, void *user_data)
- {
- const struct socket_ctx *sc = event_data;
- if (session.c->remote_addr == NULL)
- return;
-
- int family;
- const char *setname;
- switch(pr_netaddr_get_family(sc->addr)) {
- case AF_INET:
- family = NFPROTO_IPV4;
- setname = setname4;
- break;
- #ifdef PR_USE_IPV6
- case AF_INET6:
- if (pr_netaddr_use_ipv6()) {
- family = NFPROTO_IPV6;
- setname = setname6;
- break;
- }
- #endif
- default:
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Unknown address family");
- return;
- }
-
- cap_net_admin();
- try_ipset_cmd(ipset, IPSET_CMD_ADD, setname, family, sc->addr,
- session.c->remote_addr);
- no_cap_net_admin();
- }
-
- #if defined(PR_SHARED_MODULE)
- static void fw_ipset_mod_unload_ev(const void *event_data, void *user_data)
- {
- pr_event_unregister(&fw_ipset_module, NULL, NULL);
- }
- #endif
-
- static void fw_ipset_exit_ev(const void *event_data, void *user_data)
- {
- ipset_session_fini(ipset);
- }
-
- static void fw_ipset_sess_reinit_ev(const void *event_data, void *user_data)
- {
- /* A HOST command changed the main_server pointer, reinitialize ourselves. */
- pr_event_unregister(&fw_ipset_module, "core.exit",
- fw_ipset_exit_ev);
- pr_event_unregister(&fw_ipset_module, "core.data-listen",
- fw_ipset_data_listen_ev);
- pr_event_unregister(&fw_ipset_module, "core.session-reinit",
- fw_ipset_sess_reinit_ev);
-
- ipset_session_fini(ipset);
-
- if (fw_ipset_sess_init() < 0)
- pr_session_disconnect(&fw_ipset_module,
- PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
- }
-
- /* initialization routines */
- static int fw_ipset_trace(const char *fmt, ...)
- {
- va_list msg;
- int res = pr_trace_vmsg(trace_channel, 5, fmt, msg);
- return res;
- }
-
- static int fw_ipset_sess_init(void)
- {
- config_rec *c = NULL;
- c = find_config(main_server->conf, CONF_PARAM, "FwIPsetTimeout", FALSE);
- if (c != NULL)
- settimeout = *((uint32_t *)c->argv[0]);
-
- setname4 = get_param_ptr(main_server->conf, "FwIPsetName4", FALSE);
- setname6 = get_param_ptr(main_server->conf, "FwIPsetName6", FALSE);
- if (setname4 == NULL && setname6 == NULL)
- return 0;
-
- // don't hardfail the client if an error occured. worst case
- // the client can't transmit files in passive mode
- if ((ipset = ipset_session_init(fw_ipset_trace)) == NULL) {
- pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION
- ": Unable to initialize ipset session");
- return 0;
- }
-
- /* ignore already created sets */
- ipset_envopt_parse(ipset, IPSET_ENV_EXIST, NULL);
-
- if (setname4 != NULL) {
- if (!try_ipset_create(ipset, setname4, "hash:ip,port,ip", NFPROTO_IPV4,
- settimeout))
- return 0;
- }
-
- #ifdef PR_USE_IPV6
- if (setname6 != NULL && pr_netaddr_use_ipv6())
- {
- if (!try_ipset_create(ipset, setname6, "hash:ip,port,ip", NFPROTO_IPV6,
- settimeout))
- return 0;
- }
- #endif
-
- pr_event_register(&fw_ipset_module, "core.data-listen",
- fw_ipset_data_listen_ev, NULL);
- pr_event_register(&fw_ipset_module, "core.session-reinit",
- fw_ipset_sess_reinit_ev, NULL);
- pr_event_register(&fw_ipset_module, "core.exit",
- fw_ipset_exit_ev, NULL);
-
- return 0;
- }
-
- static int fw_ipset_init(void)
- {
- #if defined(PR_SHARED_MODULE)
- pr_event_register(&fw_ipset_module, "core.module-unload",
- fw_ipset_mod_unload_ev, NULL);
- #endif
- ipset_load_types();
- return 0;
- }
-
- /* module api tables */
- static conftable fw_ipset_conftab[] =
- {
- { "FwIPsetName4", set_ipset_name, NULL },
- { "FwIPsetName6", set_ipset_name, NULL },
- { "FwIPsetTimeout", set_ipset_timeout, NULL },
- { NULL }
- };
-
- module fw_ipset_module =
- {
- /* always NULL */
- NULL, NULL,
-
- /* module api version 2.0 */
- 0x20,
-
- /* module name */
- "fw_ipset",
-
- /* module configuration handler table */
- fw_ipset_conftab,
-
- /* module command handler table */
- NULL,
-
- /* module authentication handler table */
- NULL,
-
- /* module initialization function */
- fw_ipset_init,
-
- /* module session initialization function */
- fw_ipset_sess_init,
-
- /* module version */
- MOD_FW_IPSET_VERSION
- };
-