Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions manpages/dropbear.8
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,14 @@ can be set in authorized_keys. Wildcard character ('*') may be used in
port specification for matching any port. Hosts must be literal domain names or
IP addresses.

.TP
.B permitlisten=\fR"\fIhost:port\fR"
Restrict remote port forwarding so that connection is allowed only from the
specified host and port. Multiple permitlisten options separated by commas
can be set in authorized_keys. Hosts must be literal domain names or
IP addresses or special addresses like 0.0.0.0. Port can be 0 to allow the
listen port to be dynamically allocated on the server.

.TP
.B command=\fR"\fIforced_command\fR"
Disregard the command provided by the user and always run \fIforced_command\fR.
Expand Down
7 changes: 6 additions & 1 deletion src/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ int svr_pubkey_allows_tcpfwd(void);
int svr_pubkey_allows_x11fwd(void);
int svr_pubkey_allows_pty(void);
int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port);
int svr_pubkey_allows_remote_tcpfwd(const char *host, unsigned int port);
void svr_pubkey_set_forced_command(struct ChanSess *chansess);
void svr_pubkey_options_cleanup(void);
int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename);
Expand All @@ -58,6 +59,8 @@ int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filena
#define svr_pubkey_allows_pty() 1
static inline int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port)
{ (void)host; (void)port; return 1; }
static inline int svr_pubkey_allows_remote_tcpfwd(const char *host, unsigned int port)
{ (void)host; (void)port; return 1; }

static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { }
static inline void svr_pubkey_options_cleanup(void) { }
Expand Down Expand Up @@ -147,7 +150,9 @@ struct PubKeyOptions {
char * forced_command;
/* "permitopen=" option */
m_list *permit_open_destinations;

/* "permitlisten=" option */
m_list *permit_listen_sources;

#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
int no_touch_required_flag;
int verify_required_flag;
Expand Down
79 changes: 77 additions & 2 deletions src/svr-authpubkeyoptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ int svr_pubkey_allows_pty() {
return 1;
}

/* Returns 1 if pubkey allows local tcp fowarding to the provided destination,
/* Returns 1 if pubkey allows local tcp forwarding to the provided destination,
* 0 otherwise */
int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port) {
if (ses.authstate.pubkey_options
Expand All @@ -112,7 +112,28 @@ int svr_pubkey_allows_local_tcpfwd(const char *host, unsigned int port) {
return 1;
}

/* Set chansession command to the one forced
/* Returns 1 if pubkey allows remote tcp forwarding from the provided source,
* 0 otherwise */
int svr_pubkey_allows_remote_tcpfwd(const char *host, unsigned int port) {
if (ses.authstate.pubkey_options
&& ses.authstate.pubkey_options->permit_listen_sources) {
m_list_elem *iter = ses.authstate.pubkey_options->permit_listen_sources->first;
while (iter) {
struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)iter->item;
if (strcmp(entry->host, host) == 0 && entry->port == port) {
return 1;
}

iter = iter->next;
}

return 0;
}

return 1;
}

/* Set chansession command to the one forced
* by any 'command' public key option. */
void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->forced_command) {
Expand Down Expand Up @@ -147,6 +168,16 @@ void svr_pubkey_options_cleanup() {
}
m_free(ses.authstate.pubkey_options->permit_open_destinations);
}
if (ses.authstate.pubkey_options->permit_listen_sources) {
m_list_elem *iter = ses.authstate.pubkey_options->permit_listen_sources->first;
while (iter) {
struct PermitTCPFwdEntry *entry = (struct PermitTCPFwdEntry*)list_remove(iter);
m_free(entry->host);
m_free(entry);
iter = ses.authstate.pubkey_options->permit_listen_sources->first;
}
m_free(ses.authstate.pubkey_options->permit_listen_sources);
}
m_free(ses.authstate.pubkey_options);
}
if (ses.authstate.pubkey_info) {
Expand Down Expand Up @@ -288,6 +319,50 @@ int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filena
}
}

if (match_option(options_buf, "permitlisten=\"") == DROPBEAR_SUCCESS) {
int valid_option = 0;
const unsigned char* permitlisten_start = buf_getptr(options_buf, 0);

if (!ses.authstate.pubkey_options->permit_listen_sources) {
ses.authstate.pubkey_options->permit_listen_sources = list_new();
}

while (options_buf->pos < options_buf->len) {
const char c = buf_getbyte(options_buf);
if (c == '"') {
char *spec = NULL;
char *portstring = NULL;
const int permitlisten_len = buf_getptr(options_buf, 0) - permitlisten_start;
struct PermitTCPFwdEntry *entry =
(struct PermitTCPFwdEntry*)m_malloc(sizeof(struct PermitTCPFwdEntry));

list_append(ses.authstate.pubkey_options->permit_listen_sources, entry);
spec = m_malloc(permitlisten_len);
memcpy(spec, permitlisten_start, permitlisten_len - 1);
spec[permitlisten_len - 1] = '\0';
if ((split_address_port(spec, &entry->host, &portstring) == DROPBEAR_SUCCESS)
&& entry->host && portstring) {
if (m_str_to_uint(portstring, &entry->port) == DROPBEAR_SUCCESS) {
valid_option = 1;
TRACE(("remote port forwarding allowed from host '%s' and port '%u'",
entry->host, entry->port));
}
}

m_free(spec);
m_free(portstring);
break;
}
}

if (valid_option) {
goto next_option;
} else {
dropbear_log(LOG_WARNING, "Badly formatted permitlisten= authorized_keys option");
goto bad_option;
}
}

if (match_option(options_buf, "no-touch-required") == DROPBEAR_SUCCESS) {
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
dropbear_log(LOG_WARNING, "No user presence check required for U2F/FIDO key.");
Expand Down
5 changes: 5 additions & 0 deletions src/svr-tcpfwd.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,11 @@ static int svr_remotetcpreq(int *allocated_listen_port) {
}
}

if (!svr_pubkey_allows_remote_tcpfwd(request_addr, port)) {
TRACE(("remote tcp forwarding not permitted from requested source"));
goto out;
}

tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
tcpinfo->sendaddr = NULL;
tcpinfo->sendport = 0;
Expand Down