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
2016-07-18 17:27:37 +02:00
parent 5723b2e75b
commit 7dfdc2395b
8 changed files with 70 additions and 6 deletions
+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`!
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**
+27
View File
@@ -189,6 +189,33 @@ static void socket_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
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.
* TODO: Add filtering ALLOW/DENY per interface.
*/
+3
View File
@@ -46,6 +46,7 @@ typedef struct {
int proto;
int port;
int forking;
int builtin; /* Set by built-in inetd services only */
int next_id; /* Next child job's id */
char name[10];
int (*cmd)(int type); /* internal inetd service, like 'time' */
@@ -53,6 +54,8 @@ typedef struct {
TAILQ_HEAD(, inetd_filter) filters;
} inetd_t;
int inetd_check_loop (struct sockaddr *sa, socklen_t len, char *name);
int inetd_start (inetd_t *inetd);
void inetd_stop (inetd_t *inetd);
+5 -1
View File
@@ -27,6 +27,7 @@
#include "../plugin.h"
#define NAME "chargen"
#define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ "
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)
return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0;
}
@@ -86,7 +90,7 @@ static int cb(int type)
}
static plugin_t plugin = {
.name = "chargen", /* Must match the inetd /etc/services entry */
.name = NAME, /* Must match the inetd /etc/services entry */
.inetd = {
.cmd = cb
},
+7 -1
View File
@@ -28,6 +28,9 @@
#include "../plugin.h"
#define NAME "daytime"
static char *daytime(char *buf, size_t len)
{
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)
return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0;
}
@@ -72,7 +78,7 @@ static int cb(int type)
}
static plugin_t plugin = {
.name = "daytime", /* Must match the inetd /etc/services entry */
.name = NAME, /* Must match the inetd /etc/services entry */
.inetd = {
.cmd = cb
}
+16 -2
View File
@@ -27,6 +27,20 @@
#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)
{
int sd = STDIN_FILENO;
@@ -35,7 +49,7 @@ static int cb(int type)
struct sockaddr_storage 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)
return -1; /* On error, close connection. */
@@ -43,7 +57,7 @@ static int cb(int type)
}
static plugin_t plugin = {
.name = "echo", /* Must match the inetd /etc/services entry */
.name = NAME, /* Must match the inetd /etc/services entry */
.inetd = {
.cmd = cb
},
+6 -1
View File
@@ -28,6 +28,8 @@
#include "../plugin.h"
#define NAME "time"
/* UNIX epoch starts midnight, 1st Jan, 1970 */
#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)
return -1; /* On error, close connection. */
if (inetd_check_loop(sa, *sa_len, NAME))
return -1;
return 0;
}
@@ -87,7 +92,7 @@ static int cb(int type)
}
static plugin_t plugin = {
.name = "time", /* Must match the inetd /etc/services entry */
.name = NAME, /* Must match the inetd /etc/services entry */
.inetd = {
.cmd = cb
}
+1
View File
@@ -590,6 +590,7 @@ recreate:
if (plugin) {
/* Internal plugin provides this service */
svc->inetd.cmd = plugin->inetd.cmd;
svc->inetd.builtin = 1;
} else {
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
while ((cmd = strtok(NULL, " ")))