Add protection against UDP inetd looping attacks

This patch adds protection against a common inetd attack vector wherein
the reply port to UDP inetd services is forged to an internal inetd
service port.

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson committed 2016-07-18 17:27:37 +02:00
1 parent 5723b2e75b
commit 7dfdc2395b
8 files changed
+70 -6

No files matched your search

+5 -1
View File
@@ -476,7 +476,11 @@ default port. To run ssh on port 222, and all others on port 22:
``` ```
Compared to Finit v1.12 you must *explicitly deny* access from `eth0`! Compared to Finit v1.12 you must *explicitly deny* access from `eth0`!
To protect against looping attacks, the inetd server will refuse UDP
service if the reply port corresponds to any internal service. Similar
to how the FreeBSD inetd operates.
**Internal Services** **Internal Services**
+27
View File
@@ -189,6 +189,33 @@ static void socket_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
service_step(task); service_step(task);
} }
/*
* Refuse service if the request specifies a reply port corresponding to any internal service.
* This is done as a defense against looping attacks; the remote IP address is logged.
* http://www.freebsd.org/cgi/man.cgi?inetd(8)
* https://svnweb.freebsd.org/base/head/usr.sbin/inetd/inetd.c?revision=298909&view=markup#l2094
*/
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
{
svc_t *svc;
char pname[NI_MAXHOST];
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
inetd_t *i = &svc->inetd;
if (!i->builtin || i->type != SOCK_DGRAM)
continue;
if (((const struct sockaddr_in *)sa)->sin_port == i->port) {
getnameinfo(sa, len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST);
FLOG_WARN("%s/%s:%s/%s loop request REFUSED from %s", i->name, "UDP", name, "UDP", pname);
return 1;
}
}
return 0;
}
/* Launch Inet socket for service. /* Launch Inet socket for service.
* TODO: Add filtering ALLOW/DENY per interface. * TODO: Add filtering ALLOW/DENY per interface.
*/ */
+3
View File
@@ -46,6 +46,7 @@ typedef struct {
int proto; int proto;
int port; int port;
int forking; int forking;
int builtin; /* Set by built-in inetd services only */
int next_id; /* Next child job's id */ int next_id; /* Next child job's id */
char name[10]; char name[10];
int (*cmd)(int type); /* internal inetd service, like 'time' */ int (*cmd)(int type); /* internal inetd service, like 'time' */
@@ -53,6 +54,8 @@ typedef struct {
TAILQ_HEAD(, inetd_filter) filters; TAILQ_HEAD(, inetd_filter) filters;
} inetd_t; } inetd_t;
int inetd_check_loop (struct sockaddr *sa, socklen_t len, char *name);
int inetd_start (inetd_t *inetd); int inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd); void inetd_stop (inetd_t *inetd);
+5 -1
View File
@@ -27,6 +27,7 @@
#include "../plugin.h" #include "../plugin.h"
#define NAME "chargen"
#define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " #define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
static char *generator(char *buf, size_t len) static char *generator(char *buf, size_t len)
@@ -61,6 +62,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle
if (-1 == len) if (-1 == len)
return -1; /* On error, close connection. */ return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0; return 0;
} }
@@ -86,7 +90,7 @@ static int cb(int type)
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "chargen", /* Must match the inetd /etc/services entry */ .name = NAME, /* Must match the inetd /etc/services entry */
.inetd = { .inetd = {
.cmd = cb .cmd = cb
}, },
+7 -1
View File
@@ -28,6 +28,9 @@
#include "../plugin.h" #include "../plugin.h"
#define NAME "daytime"
static char *daytime(char *buf, size_t len) static char *daytime(char *buf, size_t len)
{ {
time_t t; time_t t;
@@ -48,6 +51,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle
if (-1 == len) if (-1 == len)
return -1; /* On error, close connection. */ return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0; return 0;
} }
@@ -72,7 +78,7 @@ static int cb(int type)
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "daytime", /* Must match the inetd /etc/services entry */ .name = NAME, /* Must match the inetd /etc/services entry */
.inetd = { .inetd = {
.cmd = cb .cmd = cb
} }
+16 -2
View File
@@ -27,6 +27,20 @@
#include "../plugin.h" #include "../plugin.h"
#define NAME "echo"
static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, socklen_t *sa_len)
{
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, sa, sa_len);
if (-1 == len)
return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return len;
}
static int cb(int type) static int cb(int type)
{ {
int sd = STDIN_FILENO; int sd = STDIN_FILENO;
@@ -35,7 +49,7 @@ static int cb(int type)
struct sockaddr_storage sa; struct sockaddr_storage sa;
socklen_t sa_len = sizeof(sa); socklen_t sa_len = sizeof(sa);
len = recvfrom(sd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&sa, &sa_len); len = recv_peer(sd, buf, sizeof(buf), (struct sockaddr *)&sa, &sa_len);
if (-1 == len) if (-1 == len)
return -1; /* On error, close connection. */ return -1; /* On error, close connection. */
@@ -43,7 +57,7 @@ static int cb(int type)
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "echo", /* Must match the inetd /etc/services entry */ .name = NAME, /* Must match the inetd /etc/services entry */
.inetd = { .inetd = {
.cmd = cb .cmd = cb
}, },
+6 -1
View File
@@ -28,6 +28,8 @@
#include "../plugin.h" #include "../plugin.h"
#define NAME "time"
/* UNIX epoch starts midnight, 1st Jan, 1970 */ /* UNIX epoch starts midnight, 1st Jan, 1970 */
#define EPOCH_OFFSET 2208988800ULL #define EPOCH_OFFSET 2208988800ULL
@@ -59,6 +61,9 @@ static int recv_peer(int sd, char *buf, ssize_t len, struct sockaddr *sa, sockle
if (-1 == len) if (-1 == len)
return -1; /* On error, close connection. */ return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0; return 0;
} }
@@ -87,7 +92,7 @@ static int cb(int type)
} }
static plugin_t plugin = { static plugin_t plugin = {
.name = "time", /* Must match the inetd /etc/services entry */ .name = NAME, /* Must match the inetd /etc/services entry */
.inetd = { .inetd = {
.cmd = cb .cmd = cb
} }
+1
View File
@@ -590,6 +590,7 @@ recreate:
if (plugin) { if (plugin) {
/* Internal plugin provides this service */ /* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd; svc->inetd.cmd = plugin->inetd.cmd;
svc->inetd.builtin = 1;
} else { } else {
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0])); strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " "))) while ((cmd = strtok(NULL, " ")))