Download | Plain Text | No Line Numbers


  1. /*
  2.  * ProFTPD: mod_fw_ipset
  3.  *
  4.  * Allows passive ftp connections through stateful firewall by adding the
  5.  * clients address to an ipset of type ip,port,ip.
  6.  *
  7.  * This module can be configured with:
  8.  * - FwIPsetName4 <name_of_ipset_for_ipv4>
  9.  * - FwIPsetName6 <name_of_ipset_for_ipv6>
  10.  * - FwIPsetTimeout <ipset_default_timeout> (default 30)
  11.  *
  12.  * Make sure to match incoming connections against the ipset. e.g. for iptables:
  13.  * -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEP
  14.  * -A INPUT -p tcp -m set --match-set <set_name> dst,dst,src -j ACCEPT
  15.  *
  16.  * Copyright (c) 2017 Manuel Mausz
  17.  *
  18.  * This program is free software; you can redistribute it and/or modify
  19.  * it under the terms of the GNU General Public License as published by
  20.  * the Free Software Foundation; either version 2 of the License, or
  21.  * (at your option) any later version.
  22.  *
  23.  * This program is distributed in the hope that it will be useful,
  24.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26.  * GNU General Public License for more details.
  27.  *
  28.  * You should have received a copy of the GNU General Public License
  29.  * along with this program; if not, write to the Free Software
  30.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  31.  *
  32.  * $Libraries: -lipset -lcap$
  33.  */
  34.  
  35. #include "conf.h"
  36. #include "privs.h"
  37.  
  38. #include <libipset/types.h>
  39. #include <libipset/session.h>
  40. #include <linux/capability.h>
  41. #include <sys/capability.h>
  42.  
  43. #define MOD_FW_IPSET_VERSION "mod_fw_ipset/0.1.1"
  44.  
  45. static const char *trace_channel = "fw_ipset";
  46. static struct ipset_session *ipset;
  47. static cap_t capabilities = NULL;
  48. static int disable_id_switching = TRUE;
  49.  
  50. static const char *setname4 = NULL;
  51. static const char *setname6 = NULL;
  52. static uint32_t settimeout = 30;
  53.  
  54. module fw_ipset_module;
  55.  
  56. static int fw_ipset_sess_init(void);
  57.  
  58. static int try_ipset_create(struct ipset_session *session, const char *setname,
  59. const char *typename, int family, uint32_t timeout)
  60. {
  61. pr_trace_msg(trace_channel, 5, "creating ipset: %s with timeout %d",
  62. setname, timeout);
  63. ipset_session_data_set(session, IPSET_SETNAME, setname);
  64. ipset_session_data_set(session, IPSET_OPT_TYPENAME, typename);
  65. PRIVS_ROOT
  66. const struct ipset_type *type = ipset_type_get(session, IPSET_CMD_CREATE);
  67. PRIVS_RELINQUISH
  68. if (type == NULL) {
  69. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Cannot find ipset type %s: %s",
  70. typename, ipset_session_error(session));
  71. return FALSE;
  72. }
  73.  
  74. ipset_session_data_set(session, IPSET_OPT_TIMEOUT, &timeout);
  75. ipset_session_data_set(session, IPSET_OPT_TYPE, type);
  76. ipset_session_data_set(session, IPSET_OPT_FAMILY, &family);
  77.  
  78. PRIVS_ROOT
  79. int res = ipset_cmd(session, IPSET_CMD_CREATE, /*lineno*/ 0);
  80. PRIVS_RELINQUISH
  81. if (res != 0) {
  82. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Failed to create ipset %s: %s",
  83. setname, ipset_session_error(session));
  84. return FALSE;
  85. }
  86.  
  87. return TRUE;
  88. }
  89.  
  90. static int try_ipset_cmd(struct ipset_session *session, enum ipset_cmd cmd,
  91. const char *setname, const int family,
  92. const pr_netaddr_t *local_addr, const pr_netaddr_t *remote_addr)
  93. {
  94. pr_trace_msg(trace_channel, 5, "adding data connection to ipset %s", setname);
  95. ipset_session_data_set(session, IPSET_SETNAME, setname);
  96. const struct ipset_type *type = ipset_type_get(session, cmd);
  97. if (type == NULL) {
  98. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Cannot find ipset %s: %s",
  99. setname, ipset_session_error(session));
  100. return FALSE;
  101. }
  102.  
  103. ipset_session_data_set(session, IPSET_OPT_FAMILY, &family);
  104. ipset_session_data_set(session, IPSET_OPT_IP,
  105. pr_netaddr_get_inaddr(local_addr));
  106. ipset_session_data_set(session, IPSET_OPT_IP2,
  107. pr_netaddr_get_inaddr(remote_addr));
  108. uint8_t proto = 6; /* TCP */
  109. ipset_session_data_set(session, IPSET_OPT_PROTO, &proto);
  110. uint16_t local_port = ntohs(pr_netaddr_get_port(local_addr));
  111. ipset_session_data_set(session, IPSET_OPT_PORT, &local_port);
  112.  
  113. int res = ipset_cmd(session, cmd, /*lineno*/ 0);
  114. if (res != 0) {
  115. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Failed to add ipset %s: %s",
  116. setname, ipset_session_error(session));
  117. return FALSE;
  118. }
  119.  
  120. return TRUE;
  121. }
  122.  
  123. static int lp_get_cap(void)
  124. {
  125. if ((capabilities = cap_get_proc()) == NULL) {
  126. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION
  127. ": fetching capabilities failed: %s", strerror(errno));
  128. return -1;
  129. }
  130. return 0;
  131. }
  132.  
  133. static void lp_free_cap()
  134. {
  135. if (cap_free(capabilities) < 0)
  136. pr_log_pri(PR_LOG_NOTICE, MOD_FW_IPSET_VERSION
  137. ": error freeing cap at line %d: %s", __LINE__ - 2, strerror(errno));
  138. capabilities = NULL;
  139. }
  140.  
  141. static int lp_modify_cap(cap_flag_value_t flag, cap_value_t cap, cap_flag_t set)
  142. {
  143. if (cap_set_flag(capabilities, set, 1, &cap, flag) == -1) {
  144. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": cap_set_flag failed: %s",
  145. strerror(errno));
  146. return -1;
  147. }
  148. if (cap_set_proc(capabilities) == -1) {
  149. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": cap_set_proc failed: %s",
  150. strerror(errno));
  151. return -1;
  152. }
  153. return 0;
  154. }
  155.  
  156. static int cap_net_admin(void)
  157. {
  158. if (getuid() != PR_ROOT_UID) {
  159. pr_trace_msg(trace_channel, 5, "set capability to cap_net_admin+ep");
  160. return (lp_get_cap() != -1
  161. && lp_modify_cap(CAP_SET, CAP_NET_ADMIN, CAP_EFFECTIVE) != -1);
  162. }
  163. else {
  164. disable_id_switching = session.disable_id_switching;
  165. session.disable_id_switching = FALSE;
  166. PRIVS_ROOT
  167. return 0;
  168. }
  169. }
  170.  
  171. static int no_cap_net_admin(void)
  172. {
  173. if (capabilities != NULL) {
  174. pr_trace_msg(trace_channel, 5, "set capability to cap_net_admin+p again");
  175. int res = lp_modify_cap(CAP_CLEAR, CAP_NET_ADMIN, CAP_EFFECTIVE);
  176. (void)lp_free_cap();
  177. return (res != -1);
  178. }
  179. else {
  180. PRIVS_RELINQUISH
  181. session.disable_id_switching = disable_id_switching;
  182. return 0;
  183. }
  184. }
  185.  
  186. /* configuration handlers */
  187. MODRET set_ipset_name(cmd_rec *cmd)
  188. {
  189. CHECK_ARGS(cmd, 1);
  190. CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  191.  
  192. add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
  193. return PR_HANDLED(cmd);
  194. }
  195.  
  196.  
  197. MODRET set_ipset_timeout(cmd_rec *cmd)
  198. {
  199. int timeout = -1;
  200.  
  201. CHECK_ARGS(cmd, 1);
  202. CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
  203.  
  204. if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
  205. CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
  206. cmd->argv[1], "': ", strerror(errno), NULL));
  207. }
  208.  
  209. config_rec *c = add_config_param(cmd->argv[0], 1, NULL);
  210. c->argv[0] = pcalloc(c->pool, sizeof(int));
  211. *((int *) c->argv[0]) = timeout;
  212.  
  213. return PR_HANDLED(cmd);
  214. }
  215.  
  216. /* event handlers */
  217. static void fw_ipset_data_listen_ev(const void *event_data, void *user_data)
  218. {
  219. const struct socket_ctx *sc = event_data;
  220. if (session.c->remote_addr == NULL)
  221. return;
  222.  
  223. int family;
  224. const char *setname;
  225. switch(pr_netaddr_get_family(sc->addr)) {
  226. case AF_INET:
  227. family = NFPROTO_IPV4;
  228. setname = setname4;
  229. break;
  230. #ifdef PR_USE_IPV6
  231. case AF_INET6:
  232. if (pr_netaddr_use_ipv6()) {
  233. family = NFPROTO_IPV6;
  234. setname = setname6;
  235. break;
  236. }
  237. #endif
  238. default:
  239. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION ": Unknown address family");
  240. return;
  241. }
  242.  
  243. cap_net_admin();
  244. try_ipset_cmd(ipset, IPSET_CMD_ADD, setname, family, sc->addr,
  245. session.c->remote_addr);
  246. no_cap_net_admin();
  247. }
  248.  
  249. #if defined(PR_SHARED_MODULE)
  250. static void fw_ipset_mod_unload_ev(const void *event_data, void *user_data)
  251. {
  252. if (strcmp("mod_ipset.c", (const char *)event_data) == 0)
  253. pr_event_unregister(&fw_ipset_module, NULL, NULL);
  254. }
  255. #endif
  256.  
  257. static void fw_ipset_exit_ev(const void *event_data, void *user_data)
  258. {
  259. ipset_session_fini(ipset);
  260. }
  261.  
  262. static void fw_ipset_sess_reinit_ev(const void *event_data, void *user_data)
  263. {
  264. /* A HOST command changed the main_server pointer, reinitialize ourselves. */
  265. pr_event_unregister(&fw_ipset_module, "core.exit",
  266. fw_ipset_exit_ev);
  267. pr_event_unregister(&fw_ipset_module, "core.data-listen",
  268. fw_ipset_data_listen_ev);
  269. pr_event_unregister(&fw_ipset_module, "core.session-reinit",
  270. fw_ipset_sess_reinit_ev);
  271.  
  272. ipset_session_fini(ipset);
  273.  
  274. if (fw_ipset_sess_init() < 0)
  275. pr_session_disconnect(&fw_ipset_module,
  276. PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
  277. }
  278.  
  279. /* initialization routines */
  280. static int fw_ipset_trace(const char *fmt, ...)
  281. {
  282. va_list msg;
  283. va_start(msg, fmt);
  284. int res = pr_trace_vmsg(trace_channel, 5, fmt, msg);
  285. va_end(msg);
  286. return res;
  287. }
  288.  
  289. static int fw_ipset_sess_init(void)
  290. {
  291. config_rec *c = NULL;
  292. c = find_config(main_server->conf, CONF_PARAM, "FwIPsetTimeout", FALSE);
  293. if (c != NULL)
  294. settimeout = *((uint32_t *)c->argv[0]);
  295.  
  296. setname4 = get_param_ptr(main_server->conf, "FwIPsetName4", FALSE);
  297. setname6 = get_param_ptr(main_server->conf, "FwIPsetName6", FALSE);
  298. if (setname4 == NULL && setname6 == NULL)
  299. return 0;
  300.  
  301. // don't hardfail the client if an error occured. worst case
  302. // the client can't transmit files in passive mode
  303. if ((ipset = ipset_session_init(fw_ipset_trace)) == NULL) {
  304. pr_log_pri(PR_LOG_ERR, MOD_FW_IPSET_VERSION
  305. ": Unable to initialize ipset session");
  306. return 0;
  307. }
  308.  
  309. /* ignore already created sets */
  310. ipset_envopt_parse(ipset, IPSET_ENV_EXIST, NULL);
  311.  
  312. if (setname4 != NULL) {
  313. if (!try_ipset_create(ipset, setname4, "hash:ip,port,ip", NFPROTO_IPV4,
  314. settimeout))
  315. return 0;
  316. }
  317.  
  318. #ifdef PR_USE_IPV6
  319. if (setname6 != NULL && pr_netaddr_use_ipv6())
  320. {
  321. if (!try_ipset_create(ipset, setname6, "hash:ip,port,ip", NFPROTO_IPV6,
  322. settimeout))
  323. return 0;
  324. }
  325. #endif
  326.  
  327. pr_event_register(&fw_ipset_module, "core.data-listen",
  328. fw_ipset_data_listen_ev, NULL);
  329. pr_event_register(&fw_ipset_module, "core.session-reinit",
  330. fw_ipset_sess_reinit_ev, NULL);
  331. pr_event_register(&fw_ipset_module, "core.exit",
  332. fw_ipset_exit_ev, NULL);
  333.  
  334. return 0;
  335. }
  336.  
  337. static int fw_ipset_init(void)
  338. {
  339. #if defined(PR_SHARED_MODULE)
  340. pr_event_register(&fw_ipset_module, "core.module-unload",
  341. fw_ipset_mod_unload_ev, NULL);
  342. #endif
  343. ipset_load_types();
  344. return 0;
  345. }
  346.  
  347. /* module api tables */
  348. static conftable fw_ipset_conftab[] =
  349. {
  350. { "FwIPsetName4", set_ipset_name, NULL },
  351. { "FwIPsetName6", set_ipset_name, NULL },
  352. { "FwIPsetTimeout", set_ipset_timeout, NULL },
  353. { NULL }
  354. };
  355.  
  356. module fw_ipset_module =
  357. {
  358. /* always NULL */
  359. NULL, NULL,
  360.  
  361. /* module api version 2.0 */
  362. 0x20,
  363.  
  364. /* module name */
  365. "fw_ipset",
  366.  
  367. /* module configuration handler table */
  368. fw_ipset_conftab,
  369.  
  370. /* module command handler table */
  371. NULL,
  372.  
  373. /* module authentication handler table */
  374. NULL,
  375.  
  376. /* module initialization function */
  377. fw_ipset_init,
  378.  
  379. /* module session initialization function */
  380. fw_ipset_sess_init,
  381.  
  382. /* module version */
  383. MOD_FW_IPSET_VERSION
  384. };
  385.