Skip to content

Commit 1ff4783

Browse files
committed
Fix anomalies in epmd not yet reported as security issues
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.
1 parent 716d3f5 commit 1ff4783

File tree

5 files changed

+262
-36
lines changed

5 files changed

+262
-36
lines changed

erts/epmd/src/Makefile.in

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#
22
# %CopyrightBegin%
33
#
4-
# Copyright Ericsson AB 1998-2009. All Rights Reserved.
4+
# Copyright Ericsson AB 1998-2010. All Rights Reserved.
55
#
66
# The contents of this file are subject to the Erlang Public License,
77
# Version 1.1, (the "License"); you may not use this file except in
@@ -49,15 +49,27 @@ include ../epmd.mk
4949

5050
BINDIR = $(ERL_TOP)/bin/$(TARGET)
5151
OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
52+
ERTS_INCL = -I$(ERL_TOP)/erts/include \
53+
-I$(ERL_TOP)/erts/include/$(TARGET) \
54+
-I$(ERL_TOP)/erts/include/internal \
55+
-I$(ERL_TOP)/erts/include/internal/$(TARGET)
56+
57+
# On windows we always need reentrant libraries.
58+
ifeq ($(TARGET),win32)
59+
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
60+
else
61+
ERTS_INTERNAL_LIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@ -lm
62+
endif
5263

5364
CC = @CC@
5465
WFLAGS = @WFLAGS@
55-
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS)
66+
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) $(WFLAGS) $(ERTS_INCL)
5667
LD = @LD@
57-
LIBS = @LIBS@
68+
LIBS = @LIBS@ $(ERTS_INTERNAL_LIBS)
5869
LDFLAGS = @LDFLAGS@
5970

6071

72+
6173
# ----------------------------------------------------
6274
# Release directory specification
6375
# ----------------------------------------------------
@@ -90,7 +102,7 @@ EPMD_OBJS = $(OBJDIR)/epmd.o \
90102
#---------------------------------
91103

92104

93-
all: $(BINDIR)/$(EPMD)
105+
all: erts_lib $(BINDIR)/$(EPMD)
94106

95107
docs:
96108

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

112-
$(OBJDIR)/%.o: %.c
124+
$(OBJDIR)/%.o: %.c epmd.h epmd_int.h
113125
$(CC) $(CFLAGS) $(EPMD_FLAGS) -o $@ -c $<
114126

127+
erts_lib:
128+
cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
129+
115130
include $(ERL_TOP)/make/otp_release_targets.mk
116131

117132
release_spec: all

erts/epmd/src/epmd.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#endif
2424
#include "epmd.h" /* Renamed from 'epmd_r4.h' */
2525
#include "epmd_int.h"
26+
#include "erl_printf.h"
2627

2728
#ifdef HAVE_STDLIB_H
2829
# include <stdlib.h>
@@ -33,6 +34,7 @@
3334
static void usage(EpmdVars *);
3435
static void run_daemon(EpmdVars*);
3536
static int get_port_no(void);
37+
static int check_relaxed(void);
3638
#ifdef __WIN32__
3739
static int has_console(void);
3840
#endif
@@ -161,6 +163,7 @@ int main(int argc, char** argv)
161163

162164
g->silent = 0;
163165
g->is_daemon = 0;
166+
g->brutal_kill = check_relaxed();
164167
g->packet_timeout = CLOSE_TIMEOUT; /* Default timeout */
165168
g->delay_accept = 0;
166169
g->delay_write = 0;
@@ -196,6 +199,9 @@ int main(int argc, char** argv)
196199
} else if (strcmp(argv[0], "-daemon") == 0) {
197200
g->is_daemon = 1;
198201
argv++; argc--;
202+
} else if (strcmp(argv[0], "-relaxed_command_check") == 0) {
203+
g->brutal_kill = 1;
204+
argv++; argc--;
199205
} else if (strcmp(argv[0], "-kill") == 0) {
200206
if (argc == 1)
201207
kill_epmd(g);
@@ -387,10 +393,10 @@ static void run_daemon(EpmdVars *g)
387393

388394
static void usage(EpmdVars *g)
389395
{
390-
fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon]\n");
396+
fprintf(stderr, "usage: epmd [-d|-debug] [DbgExtra...] [-port No] [-daemon] [-allow_brutal_kill]\n");
391397
fprintf(stderr, " [-d|-debug] [-port No] [-names|-kill|-stop name]\n\n");
392-
fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n");
393-
fprintf(stderr, "The -port and DbgExtra options are\n\n");
398+
fprintf(stderr, "See the Erlang epmd manual page for info about the usage.\n\n");
399+
fprintf(stderr, "Regular options\n");
394400
fprintf(stderr, " -port No\n");
395401
fprintf(stderr, " Let epmd listen to another port than default %d\n",
396402
EPMD_PORT_NO);
@@ -401,6 +407,14 @@ static void usage(EpmdVars *g)
401407
fprintf(stderr, " the number of saved used node names to 5.\n\n");
402408
fprintf(stderr, " If you give more than one debug flag you may\n");
403409
fprintf(stderr, " get more debugging information.\n\n");
410+
fprintf(stderr, " -daemon\n");
411+
fprintf(stderr, " Start epmd detached (as a daemon)\n");
412+
fprintf(stderr, " -relaxed_command_check\n");
413+
fprintf(stderr, " Allow this instance of epmd to be killed with\n");
414+
fprintf(stderr, " epmd -kill even if there "
415+
"are registered nodes.\n");
416+
fprintf(stderr, " Also allows forced unregister (epmd -stop).\n");
417+
fprintf(stderr, "\nDbgExtra options\n");
404418
fprintf(stderr, " -packet_timout Seconds\n");
405419
fprintf(stderr, " Set the number of seconds a connection can be\n");
406420
fprintf(stderr, " inactive before epmd times out and closes the\n");
@@ -413,6 +427,18 @@ static void usage(EpmdVars *g)
413427
fprintf(stderr, " -delay_write Seconds\n");
414428
fprintf(stderr, " Also a simulation of a busy server. Inserts\n");
415429
fprintf(stderr, " a delay before a reply is sent.\n");
430+
fprintf(stderr, "\nInteractive options\n");
431+
fprintf(stderr, " -names\n");
432+
fprintf(stderr, " List names registered with the currently "
433+
"running epmd\n");
434+
fprintf(stderr, " -kill\n");
435+
fprintf(stderr, " Kill the currently runniing epmd\n");
436+
fprintf(stderr, " (only allowed if -names show empty database or\n");
437+
fprintf(stderr, " -allow_brutal_kill was given when epmd was started).\n");
438+
fprintf(stderr, " -stop Name\n");
439+
fprintf(stderr, " Forcibly unregisters a name with epmd\n");
440+
fprintf(stderr, " (only allowed if -allow_brutal_kill was given when \n");
441+
fprintf(stderr, " epmd was started).\n");
416442
epmd_cleanup_exit(g,1);
417443
}
418444

@@ -432,20 +458,20 @@ static void usage(EpmdVars *g)
432458
* args... Arguments to print out according to the format
433459
*
434460
*/
435-
461+
#define DEBUG_BUFFER_SIZE 2048
436462
static void dbg_gen_printf(int onsyslog,int perr,int from_level,
437463
EpmdVars *g,const char *format, va_list args)
438464
{
439465
time_t now;
440466
char *timestr;
441-
char buf[2048];
467+
char buf[DEBUG_BUFFER_SIZE];
442468

443469
if (g->is_daemon)
444470
{
445471
#ifndef NO_SYSLOG
446472
if (onsyslog)
447473
{
448-
vsprintf(buf, format, args);
474+
erts_vsnprintf(buf, DEBUG_BUFFER_SIZE, format, args);
449475
syslog(LOG_ERR,"epmd: %s",buf);
450476
}
451477
#endif
@@ -456,9 +482,10 @@ static void dbg_gen_printf(int onsyslog,int perr,int from_level,
456482

457483
time(&now);
458484
timestr = (char *)ctime(&now);
459-
sprintf(buf, "epmd: %.*s: ", (int) strlen(timestr)-1, timestr);
485+
erts_snprintf(buf, DEBUG_BUFFER_SIZE, "epmd: %.*s: ",
486+
(int) strlen(timestr)-1, timestr);
460487
len = strlen(buf);
461-
vsprintf(buf + len, format, args);
488+
erts_vsnprintf(buf + len, DEBUG_BUFFER_SIZE - len, format, args);
462489
if (perr == 1)
463490
perror(buf);
464491
else
@@ -545,4 +572,9 @@ static int get_port_no(void)
545572
char* port_str = getenv("ERL_EPMD_PORT");
546573
return (port_str != NULL) ? atoi(port_str) : EPMD_PORT_NO;
547574
}
575+
static int check_relaxed(void)
576+
{
577+
char* port_str = getenv("ERL_EPMD_RELAXED_COMMAND_CHECK");
578+
return (port_str != NULL) ? 1 : 0;
579+
}
548580

erts/epmd/src/epmd_int.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,10 @@
246246

247247
typedef struct {
248248
int fd; /* File descriptor */
249-
unsigned open:1; /* TRUE if open */
250-
unsigned keep:1; /* Don't close when sent reply */
249+
unsigned char open; /* TRUE if open */
250+
unsigned char keep; /* Don't close when sent reply */
251+
unsigned char local_peer; /* The peer of this connection is via
252+
loopback interface */
251253
unsigned got; /* # of bytes we have got */
252254
unsigned want; /* Number of bytes we want */
253255
char *buf; /* The remaining buffer */
@@ -287,6 +289,7 @@ typedef struct {
287289
int debug;
288290
int silent;
289291
int is_daemon;
292+
int brutal_kill;
290293
unsigned packet_timeout;
291294
unsigned delay_accept;
292295
unsigned delay_write;
@@ -308,6 +311,7 @@ void epmd_call(EpmdVars*,int);
308311
void run(EpmdVars*);
309312
void epmd_cleanup_exit(EpmdVars*, int);
310313
int epmd_conn_close(EpmdVars*,Connection*);
314+
void stop_cli(EpmdVars *g, char *name);
311315

312316
#ifdef DONT_USE_MAIN
313317
int start_epmd(char *,char *,char *,char *,char *,char *,char *,char *,char *,char *);

erts/epmd/src/epmd_srv.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ static void do_request(g, fd, s, buf, bsize)
386386
{
387387
case EPMD_ALIVE2_REQ:
388388
dbg_printf(g,1,"** got ALIVE2_REQ");
389+
if (!s->local_peer) {
390+
dbg_printf(g,0,"ALIVE2_REQ from non local address");
391+
return;
392+
}
389393

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

556560
case EPMD_DUMP_REQ:
557561
dbg_printf(g,1,"** got DUMP_REQ");
562+
if (!s->local_peer) {
563+
dbg_printf(g,0,"DUMP_REQ from non local address");
564+
return;
565+
}
558566
{
559567
Node *node;
560568

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

606614
case EPMD_KILL_REQ:
615+
if (!s->local_peer) {
616+
dbg_printf(g,0,"KILL_REQ from non local address");
617+
return;
618+
}
607619
dbg_printf(g,1,"** got KILL_REQ");
620+
621+
if (!g->brutal_kill && (g->nodes.reg != NULL)) {
622+
dbg_printf(g,0,"Disallowed KILL_REQ, live nodes");
623+
if (reply(g, fd,"NO",2) != 2)
624+
dbg_printf(g,0,"failed to send reply to KILL_REQ");
625+
return;
626+
}
627+
608628
if (reply(g, fd,"OK",2) != 2)
609629
dbg_printf(g,0,"failed to send reply to KILL_REQ");
610630
dbg_tty_printf(g,1,"epmd killed");
@@ -614,6 +634,15 @@ static void do_request(g, fd, s, buf, bsize)
614634

615635
case EPMD_STOP_REQ:
616636
dbg_printf(g,1,"** got STOP_REQ");
637+
if (!s->local_peer) {
638+
dbg_printf(g,0,"STOP_REQ from non local address");
639+
return;
640+
}
641+
if (!g->brutal_kill) {
642+
dbg_printf(g,0,"Disallowed STOP_REQ, no relaxed_command_check");
643+
return;
644+
}
645+
617646
if (bsize <= 1 )
618647
{
619648
dbg_printf(g,0,"packet too small for request STOP_REQ (%d)",bsize);
@@ -701,6 +730,14 @@ static int conn_open(EpmdVars *g,int fd)
701730

702731
for (i = 0; i < g->max_conn; i++) {
703732
if (g->conn[i].open == EPMD_FALSE) {
733+
struct sockaddr_in si;
734+
#ifdef HAVE_SOCKLEN_T
735+
socklen_t st;
736+
#else
737+
int st;
738+
#endif
739+
st = sizeof(si);
740+
704741
g->active_conn++;
705742
s = &g->conn[i];
706743

@@ -710,6 +747,20 @@ static int conn_open(EpmdVars *g,int fd)
710747
s->fd = fd;
711748
s->open = EPMD_TRUE;
712749
s->keep = EPMD_FALSE;
750+
751+
/* Determine if connection is from localhost */
752+
if (getpeername(s->fd,(struct sockaddr*) &si,&st) ||
753+
st < sizeof(si)) {
754+
/* Failure to get peername is regarder as non local host */
755+
s->local_peer = EPMD_FALSE;
756+
} else {
757+
s->local_peer =
758+
((((unsigned) ntohl(si.sin_addr.s_addr)) & 0xFF000000U) ==
759+
0x7F000000U); /* Only 127.x.x.x allowed, no false positives */
760+
}
761+
dbg_tty_printf(g,2,(s->local_peer) ? "Local peer connected" :
762+
"Non-local peer connected");
763+
713764
s->want = 0; /* Currently unknown */
714765
s->got = 0;
715766
s->mod_time = current_time(g); /* Note activity */

0 commit comments

Comments
 (0)