Skip to content

Commit

Permalink
Fix anomalies in epmd not yet reported as security issues
Browse files Browse the repository at this point in the history
Use erts_(v)snprintf to ensure no buffer overruns in debug printouts.
Disallow everything except port and name requests from remote nodes.
Disallow kill command even from localhost if alive nodes exist.
  -relaxed_command_check when starting epmd returns the possibility to
  kill this epmd when nodes are alive (from localhost).
Disallow stop command completely except if -relaxed_command_check is given
  when epmd was started.
Environment variable ERL_EPMD_RELAXED_COMMAND_CHECK can be set to always get
  -relaxed_command_check.
  • Loading branch information
bufflig committed Aug 31, 2010
1 parent 716d3f5 commit 1ff4783
Show file tree
Hide file tree
Showing 5 changed files with 262 additions and 36 deletions.
25 changes: 20 additions & 5 deletions erts/epmd/src/Makefile.in
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
# Copyright Ericsson AB 1998-2009. All Rights Reserved.
# Copyright Ericsson AB 1998-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
Expand Down Expand Up @@ -49,15 +49,27 @@ include ../epmd.mk

BINDIR = $(ERL_TOP)/bin/$(TARGET)
OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
ERTS_INCL = -I$(ERL_TOP)/erts/include \
-I$(ERL_TOP)/erts/include/$(TARGET) \
-I$(ERL_TOP)/erts/include/internal \
-I$(ERL_TOP)/erts/include/internal/$(TARGET)

# On windows we always need reentrant libraries.
ifeq ($(TARGET),win32)
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
else
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
endif

CC = @CC@
WFLAGS = @WFLAGS@
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS)
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL)
LD = @LD@
LIBS = @LIBS@
LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS)
LDFLAGS = @LDFLAGS@



# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
Expand Down Expand Up @@ -90,7 +102,7 @@ EPMD_OBJS = $(OBJDIR)/epmd.o \
#---------------------------------


all: $(BINDIR)/$(EPMD)
all: erts_lib $(BINDIR)/$(EPMD)

docs:

Expand All @@ -109,9 +121,12 @@ clean:
$(BINDIR)/$(EPMD): $(EPMD_OBJS)
$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(EPMD_OBJS) $(LIBS)

$(OBJDIR)/%.o: %.c
$(OBJDIR)/%.o: %.c epmd.h epmd_int.h
$(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<

erts_lib:
cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)

include $(ERL_TOP)/make/otp_release_targets.mk

release_spec: all
Expand Down
48 changes: 40 additions & 8 deletions erts/epmd/src/epmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#endif
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
#include "epmd_int.h"
#include "erl_printf.h"

#ifdef HAVE_STDLIB_H
# include <stdlib.h>
Expand All @@ -33,6 +34,7 @@
static void usage(EpmdVars *);
static void run_daemon(EpmdVars*);
static int get_port_no(void);
static int check_relaxed(void);
#ifdef __WIN32__
static int has_console(void);
#endif
Expand Down Expand Up @@ -161,6 +163,7 @@ int main(int argc, char** argv)

g->silent = 0;
g->is_daemon = 0;
g->brutal_kill = check_relaxed();
g->packet_timeout = CLOSE_TIMEOUT; /* Default timeout */
g->delay_accept = 0;
g->delay_write = 0;
Expand Down Expand Up @@ -196,6 +199,9 @@ int main(int argc, char** argv)
} else if (strcmp(argv[0], "-daemon") == 0) {
g->is_daemon = 1;
argv++; argc--;
} else if (strcmp(argv[0], "-relaxed_command_check") == 0) {
g->brutal_kill = 1;
argv++; argc--;
} else if (strcmp(argv[0], "-kill") == 0) {
if (argc == 1)
kill_epmd(g);
Expand Down Expand Up @@ -387,10 +393,10 @@ static void run_daemon(EpmdVars *g)

static void usage(EpmdVars *g)
{
fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\n");
fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-allow_brutal_kill]\n");
fprintf(stderr, " [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n");
fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n");
fprintf(stderr, "The -port and DbgExtra options are\n\n");
fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n\n");
fprintf(stderr, "Regular options\n");
fprintf(stderr, " -port No\n");
fprintf(stderr, " Let epmd listen to another port than default %d\n",
EPMD_PORT_NO);
Expand All @@ -401,6 +407,14 @@ static void usage(EpmdVars *g)
fprintf(stderr, " the number of saved used node names to 5.\n\n");
fprintf(stderr, " If you give more than one debug flag you may\n");
fprintf(stderr, " get more debugging information.\n\n");
fprintf(stderr, " -daemon\n");
fprintf(stderr, " Start epmd detached (as a daemon)\n");
fprintf(stderr, " -relaxed_command_check\n");
fprintf(stderr, " Allow this instance of epmd to be killed with\n");
fprintf(stderr, " epmd -kill even if there "
"are registered nodes.\n");
fprintf(stderr, " Also allows forced unregister (epmd -stop).\n");
fprintf(stderr, "\nDbgExtra options\n");
fprintf(stderr, " -packet_timout Seconds\n");
fprintf(stderr, " Set the number of seconds a connection can be\n");
fprintf(stderr, " inactive before epmd times out and closes the\n");
Expand All @@ -413,6 +427,18 @@ static void usage(EpmdVars *g)
fprintf(stderr, " -delay_write Seconds\n");
fprintf(stderr, " Also a simulation of a busy server. Inserts\n");
fprintf(stderr, " a delay before a reply is sent.\n");
fprintf(stderr, "\nInteractive options\n");
fprintf(stderr, " -names\n");
fprintf(stderr, " List names registered with the currently "
"running epmd\n");
fprintf(stderr, " -kill\n");
fprintf(stderr, " Kill the currently runniing epmd\n");
fprintf(stderr, " (only allowed if -names show empty database or\n");
fprintf(stderr, " -allow_brutal_kill was given when epmd was started).\n");
fprintf(stderr, " -stop Name\n");
fprintf(stderr, " Forcibly unregisters a name with epmd\n");
fprintf(stderr, " (only allowed if -allow_brutal_kill was given when \n");
fprintf(stderr, " epmd was started).\n");
epmd_cleanup_exit(g,1);
}

Expand All @@ -432,20 +458,20 @@ static void usage(EpmdVars *g)
* args... Arguments to print out according to the format
*
*/

#define DEBUG_BUFFER_SIZE 2048
static void dbg_gen_printf(int onsyslog,int perr,int from_level,
EpmdVars *g,const char *format, va_list args)
{
time_t now;
char *timestr;
char buf[2048];
char buf[DEBUG_BUFFER_SIZE];

if (g->is_daemon)
{
#ifndef NO_SYSLOG
if (onsyslog)
{
vsprintf(buf, format, args);
erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args);
syslog(LOG_ERR,"epmd: %s",buf);
}
#endif
Expand All @@ -456,9 +482,10 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level,

time(&now);
timestr = (char *)ctime(&now);
sprintf(buf, "epmd: %.*s: ", (int) strlen(timestr)-1, timestr);
erts_snprintf(buf, DEBUG_BUFFER_SIZE, "epmd: %.*s: ",
(int) strlen(timestr)-1, timestr);
len = strlen(buf);
vsprintf(buf + len, format, args);
erts_vsnprintf(buf + len, DEBUG_BUFFER_SIZE - len, format, args);
if (perr == 1)
perror(buf);
else
Expand Down Expand Up @@ -545,4 +572,9 @@ static int get_port_no(void)
char* port_str = getenv("ERL_EPMD_PORT");
return (port_str != NULL) ? atoi(port_str) : EPMD_PORT_NO;
}
static int check_relaxed(void)
{
char* port_str = getenv("ERL_EPMD_RELAXED_COMMAND_CHECK");
return (port_str != NULL) ? 1 : 0;
}

8 changes: 6 additions & 2 deletions erts/epmd/src/epmd_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,10 @@

typedef struct {
int fd; /* File descriptor */
unsigned open:1; /* TRUE if open */
unsigned keep:1; /* Don't close when sent reply */
unsigned char open; /* TRUE if open */
unsigned char keep; /* Don't close when sent reply */
unsigned char local_peer; /* The peer of this connection is via
loopback interface */
unsigned got; /* # of bytes we have got */
unsigned want; /* Number of bytes we want */
char *buf; /* The remaining buffer */
Expand Down Expand Up @@ -287,6 +289,7 @@ typedef struct {
int debug;
int silent;
int is_daemon;
int brutal_kill;
unsigned packet_timeout;
unsigned delay_accept;
unsigned delay_write;
Expand All @@ -308,6 +311,7 @@ void epmd_call(EpmdVars*,int);
void run(EpmdVars*);
void epmd_cleanup_exit(EpmdVars*, int);
int epmd_conn_close(EpmdVars*,Connection*);
void stop_cli(EpmdVars *g, char *name);

#ifdef DONT_USE_MAIN
int start_epmd(char *,char *,char *,char *,char *,char *,char *,char *,char *,char *);
Expand Down
51 changes: 51 additions & 0 deletions erts/epmd/src/epmd_srv.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,10 @@ static void do_request(g, fd, s, buf, bsize)
{
case EPMD_ALIVE2_REQ:
dbg_printf(g,1,"** got ALIVE2_REQ");
if (!s->local_peer) {
dbg_printf(g,0,"ALIVE2_REQ from non local address");
return;
}

/* The packet has the format "axxyyyyyy" where xx is port, given
in network byte order, and yyyyyy is symname, possibly null
Expand Down Expand Up @@ -555,6 +559,10 @@ static void do_request(g, fd, s, buf, bsize)

case EPMD_DUMP_REQ:
dbg_printf(g,1,"** got DUMP_REQ");
if (!s->local_peer) {
dbg_printf(g,0,"DUMP_REQ from non local address");
return;
}
{
Node *node;

Expand Down Expand Up @@ -604,7 +612,19 @@ static void do_request(g, fd, s, buf, bsize)
break;

case EPMD_KILL_REQ:
if (!s->local_peer) {
dbg_printf(g,0,"KILL_REQ from non local address");
return;
}
dbg_printf(g,1,"** got KILL_REQ");

if (!g->brutal_kill && (g->nodes.reg != NULL)) {
dbg_printf(g,0,"Disallowed KILL_REQ, live nodes");
if (reply(g, fd,"NO",2) != 2)
dbg_printf(g,0,"failed to send reply to KILL_REQ");
return;
}

if (reply(g, fd,"OK",2) != 2)
dbg_printf(g,0,"failed to send reply to KILL_REQ");
dbg_tty_printf(g,1,"epmd killed");
Expand All @@ -614,6 +634,15 @@ static void do_request(g, fd, s, buf, bsize)

case EPMD_STOP_REQ:
dbg_printf(g,1,"** got STOP_REQ");
if (!s->local_peer) {
dbg_printf(g,0,"STOP_REQ from non local address");
return;
}
if (!g->brutal_kill) {
dbg_printf(g,0,"Disallowed STOP_REQ, no relaxed_command_check");
return;
}

if (bsize <= 1 )
{
dbg_printf(g,0,"packet too small for request STOP_REQ (%d)",bsize);
Expand Down Expand Up @@ -701,6 +730,14 @@ static int conn_open(EpmdVars *g,int fd)

for (i = 0; i < g->max_conn; i++) {
if (g->conn[i].open == EPMD_FALSE) {
struct sockaddr_in si;
#ifdef HAVE_SOCKLEN_T
socklen_t st;
#else
int st;
#endif
st = sizeof(si);

g->active_conn++;
s = &g->conn[i];

Expand All @@ -710,6 +747,20 @@ static int conn_open(EpmdVars *g,int fd)
s->fd = fd;
s->open = EPMD_TRUE;
s->keep = EPMD_FALSE;

/* Determine if connection is from localhost */
if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
st < sizeof(si)) {
/* Failure to get peername is regarder as non local host */
s->local_peer = EPMD_FALSE;
} else {
s->local_peer =
((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
0x7F000000U); /* Only 127.x.x.x allowed, no false positives */
}
dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
"Non-local peer connected");

s->want = 0; /* Currently unknown */
s->got = 0;
s->mod_time = current_time(g); /* Note activity */
Expand Down
Loading

0 comments on commit 1ff4783

Please sign in to comment.