jcs
/subtext
/amendments
/166
telnet: Add trusted proxy IP setting for web gateway, unblock IPs
When a telnet connection comes from the configured proxy IP, allow the
REMOTE_ADDR environment variable passed to be used as the connection's
IP. Also don't auto-ban connections from the proxy.
Request TSPEED and store it as the session's terminal speed, just for
showing up in 'who' output.
Automatically unban IPs after 30 minutes by tracking the ban time.
jcs made amendment 166 over 2 years ago
--- db.c Tue Jun 21 16:16:07 2022
+++ db.c Wed Jun 22 15:21:14 2022
@@ -41,6 +41,9 @@ struct struct_field config_fields[] = {
{ "Telnet Port", CONFIG_TYPE_SHORT,
offsetof(struct config, telnet_port),
0, 65535 },
+ { "Telnet Trusted Proxy IP", CONFIG_TYPE_IP,
+ offsetof(struct config, trusted_proxy_ip),
+ 0, 1 },
{ "Modem Port", CONFIG_TYPE_SHORT,
offsetof(struct config, modem_port),
0, 2 },
@@ -334,6 +337,18 @@ db_migrate(struct db *tdb, short is_new)
sizeof(new_config));
new_config.blanker_idle_seconds = (60 * 60);
new_config.blanker_runtime_seconds = 30;
+
+ bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config,
+ sizeof(new_config));
+ break;
+ }
+ case 5: {
+ /* 5->6, added trusted_proxy_ip */
+ struct config new_config = { 0 };
+
+ bile_read(tdb->bile, DB_CONFIG_RTYPE, 1, (char *)&new_config,
+ sizeof(new_config));
+ new_config.trusted_proxy_ip = 0;
bile_write(tdb->bile, DB_CONFIG_RTYPE, 1, &new_config,
sizeof(new_config));
--- db.h Tue Jun 21 15:43:59 2022
+++ db.h Wed Jun 22 15:42:33 2022
@@ -23,7 +23,7 @@
#define DB_TYPE 'STDB'
-#define DB_CUR_VERS 5
+#define DB_CUR_VERS 6
#define DB_TRUE 0x100
#define DB_FALSE 0x000
@@ -72,6 +72,7 @@ struct config {
short max_login_seconds;
short blanker_idle_seconds;
short blanker_runtime_seconds;
+ unsigned long trusted_proxy_ip;
};
extern struct struct_field config_fields[];
--- session.c Wed Jun 22 15:59:23 2022
+++ session.c Wed Jun 22 21:57:25 2022
@@ -1156,6 +1156,8 @@ session_pause_return(struct session *s, short enforce,
for (;;) {
c = session_input_char(s);
+ if (s->ending)
+ return;
if (c == 0)
continue;
if (!enforce || c == '\r')
@@ -1230,7 +1232,7 @@ session_recents(struct session *s)
date_tm = localtime((time_t *)&slog.logged_on_at);
strftime(sdate, sizeof(sdate), "%m/%d", date_tm);
- session_printf(s, "%-7s %-7s %-20s %-7s %-6d\r\n",
+ session_printf(s, "%-7s %-7s %-20s %-7s %-6u\r\n",
sdate,
slog.node,
slog.username,
@@ -1276,7 +1278,7 @@ session_who(struct session *s)
sessions[n]->user ? sessions[n]->user->username : GUEST_USERNAME,
sessions[n]->user && sessions[n]->user->is_sysop ? " (sysop)" : "");
- session_printf(s, "%-7s %-20s %-7s %-6d %-6s\r\n",
+ session_printf(s, "%-7s %-20s %-7s %-6u %-6s\r\n",
sessions[n]->node,
username,
sessions[n]->via,
--- tcp.c Wed Jun 15 14:05:31 2022
+++ tcp.c Wed Jun 22 16:44:46 2022
@@ -392,12 +392,12 @@ StrToAddrMarkDone(struct hostInfo *hi, char *data)
*done = 1;
}
-long
+unsigned long
ip2long(char *ip)
{
- long address = 0;
- int dotcount = 0, i;
- unsigned int b = 0;
+ unsigned long address = 0;
+ short dotcount = 0, i;
+ unsigned short b = 0;
for (i = 0; ip[i] != 0; i++) {
if (ip[i] == '.') {
@@ -426,7 +426,7 @@ void
long2ip(unsigned long num, char *ip)
{
unsigned char *tmp = (unsigned char *)#
- (void)sprintf(ip, (const char *)"%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]);
+ sprintf(ip, "%d.%d.%d.%d", tmp[0], tmp[1], tmp[2], tmp[3]);
}
#ifdef SOCKS
--- tcp.h Thu Feb 10 14:48:44 2022
+++ tcp.h Wed Jun 22 16:45:50 2022
@@ -51,7 +51,7 @@ OSErr _UDPMaxMTUSize(UDPiopb *pb, short *mtu);
OSErr TCPResolveName(char **name, unsigned long *ipAddress);
-long ip2long(char *ip);
+unsigned long ip2long(char *ip);
void long2ip(unsigned long num, char *ip);
OSErr SOCKS5TCPActiveOpen(TCPiopb *pb, StreamPtr stream, ip_addr socks_ip,
--- telnet.c Tue Jun 21 13:13:29 2022
+++ telnet.c Wed Jun 22 23:03:56 2022
@@ -88,14 +88,11 @@ enum {
struct telnet_node {
short id;
- char name[8];
short state;
+ char name[8];
ip_addr ip;
char ip_s[20];
- TCPiopb rcv_pb, send_pb, listen_pb;
- StreamPtr stream;
- wdsEntry tcp_wds[2];
- rdsEntry tcp_rds[2];
+ struct session *session;
unsigned short obuflen;
unsigned char obuf[512];
unsigned short sending_iac;
@@ -103,9 +100,13 @@ struct telnet_node {
short iac_state;
unsigned char iac_sb[64];
short sb_len;
- struct session *session;
/* docs say 4*MTU+1024, but MTU will probably be <1500 */
unsigned char tcp_buf[(4 * 1500) + 1024];
+ bool from_trusted_proxy;
+ TCPiopb rcv_pb, send_pb, listen_pb;
+ StreamPtr stream;
+ wdsEntry tcp_wds[2];
+ rdsEntry tcp_rds[2];
};
#define MAX_TELNET_NODES 5
@@ -115,8 +116,12 @@ static TCPiopb telnet_exit_pb;
static TCPStatusPB telnet_status_pb;
static bool did_initial_log = false;
-#define MAX_BANNED_IPS 25
-static ip_addr banned_ips[MAX_BANNED_IPS] = { 0 };
+#define MAX_BANNED_IPS 20
+#define IP_BAN_SECONDS (60 * 30)
+static struct banned_ip {
+ ip_addr ip;
+ unsigned long time;
+} banned_ips[MAX_BANNED_IPS] = { 0 };
/* terminals that will have vt100 set by default */
static const char * vt100_terms[] = {
@@ -131,7 +136,9 @@ static const char * vt100_terms[] = {
void telnet_setup(struct session *session);
void telnet_create_listener_node(short node_idx);
-void telnet_output_iac(struct session *session, char *iacs, size_t len);
+void telnet_output_iac(struct session *session, const char *iacs,
+ size_t len);
+bool telnet_ip_is_banned(ip_addr ip);
struct node_funcs telnet_node_funcs = {
telnet_setup,
@@ -261,15 +268,20 @@ telnet_idle(void)
node->ip = telnet_status_pb.remoteHost;
long2ip(telnet_status_pb.remoteHost, node->ip_s);
- for (j = 0; j < nitems(banned_ips); j++) {
- if (banned_ips[j] != telnet_status_pb.remoteHost)
- continue;
-
+ if (db->config.trusted_proxy_ip != 0 &&
+ db->config.trusted_proxy_ip == node->ip) {
+ node->from_trusted_proxy = true;
+ snprintf(node->name, sizeof(node->name), "ttyw%d",
+ node->id);
+ }
+
+ if (!node->from_trusted_proxy &&
+ telnet_ip_is_banned(telnet_status_pb.remoteHost)) {
logger_printf(logger, "[%s] Refusing telnet "
"connection from banned IP %s", node->name,
node->ip_s);
- _TCPRelease(&node->listen_pb, node->stream, nil, nil,
- false);
+ _TCPRelease(&node->listen_pb, node->stream, nil,
+ nil, false);
telnet_nodes[n] = NULL;
free(node);
@@ -277,10 +289,12 @@ telnet_idle(void)
}
logger_printf(logger, "[%s] New telnet connection "
- "from %s", node->name, node->ip_s);
+ "from %s%s", node->name, node->ip_s,
+ node->from_trusted_proxy ? " (via trusted proxy)" : "");
node->state = TELNET_PB_STATE_CONNECTED;
- node->session = session_create(node->name, "telnet",
+ node->session = session_create(node->name,
+ node->from_trusted_proxy ? "web" : "telnet",
&telnet_node_funcs);
node->session->cookie = (void *)node;
node->session->tspeed = 19200;
@@ -331,12 +345,13 @@ next_node:
void
telnet_setup(struct session *session)
{
- char iacs[] = {
+ const char iacs[] = {
IAC, WILL, IAC_SGA, /* we will supress go-ahead */
IAC, WILL, IAC_ECHO, /* and we will echo for you */
IAC, DO, IAC_TSPEED, /* send your TSPEED */
IAC, DO, IAC_NAWS, /* send your NAWS */
IAC, DO, IAC_TTYPE, /* send your TTYPE */
+ IAC, DO, IAC_NEWENV, /* send your NEWENV vars */
/* IAC, DO, IAC_LINEMODE, */
};
@@ -479,7 +494,7 @@ telnet_input(struct session *session)
session_log(session, "IAC TTYPE IS %s",
session->terminal_type);
- for (j = 0; vt100_terms[j][0] != 0; j++) {
+ for (j = 0; vt100_terms[j] != NULL; j++) {
if (strncasecmp(session->terminal_type,
vt100_terms[j], strlen(vt100_terms[j])) == 0) {
session_log(session, "Activating vt100 ANSI "
@@ -490,7 +505,72 @@ telnet_input(struct session *session)
}
break;
+ case IAC_NEWENV: {
+ char k[sizeof(node->iac_sb)], v[sizeof(node->iac_sb)];
+ char l = 0;
+ bool inv = false;
+
+ k[0] = v[0] = '\0';
+
+ /* \40\0\0USER\1blah\0DISPLAY\1... */
+ for (j = 3; j < node->sb_len; j++) {
+ if (j == node->sb_len - 1 ||
+ node->iac_sb[j] == 0 || node->iac_sb[j] == 3) {
+ v[l] = '\0';
+ if (l) {
+ session_log(session,
+ "NEWENV \"%s\" = \"%s\"", k, v);
+
+ if (node->from_trusted_proxy &&
+ strcmp(k, "REMOTE_ADDR") == 0) {
+ node->ip = ip2long(v);
+ strlcpy(node->ip_s, v,
+ sizeof(node->ip_s));
+ node->session->log.ip_address =
+ node->ip;
+ }
+ }
+ l = 0;
+ inv = false;
+ } else if (node->iac_sb[j] == 1) {
+ k[l] = '\0';
+ l = 0;
+ inv = true;
+ } else {
+ if (inv)
+ v[l++] = node->iac_sb[j];
+ else
+ k[l++] = node->iac_sb[j];
+ }
+ }
+ break;
}
+ case IAC_TSPEED: {
+ unsigned long tspeed;
+
+ /* IAC SB TSPEED IS 38400,19200 SE */
+ node->iac_sb[node->sb_len - 1] = '\0';
+
+ session_log(session, "IAC TSPEED IS %s",
+ node->iac_sb + 2);
+
+ for (j = 2; j < node->sb_len; j++) {
+ if (node->iac_sb[j] == ',') {
+ node->iac_sb[j] = '\0';
+ break;
+ }
+ }
+
+ tspeed = atol((const char *)(node->iac_sb + 2));
+ if (tspeed > 0) {
+ if (tspeed > 57600)
+ tspeed = 57600;
+ node->session->tspeed =
+ node->session->log.tspeed = (unsigned short)tspeed;
+ }
+ break;
+ }
+ }
node->iac_state = TELNET_IAC_STATE_IDLE;
} else {
/* accumulate bytes into iac_sb buffer */
@@ -512,9 +592,11 @@ telnet_input(struct session *session)
iac_out[2] = IAC_ECHO;
telnet_output_iac(session, iac_out, 3);
break;
+ case IAC_NEWENV:
case IAC_TTYPE:
+ case IAC_TSPEED:
iac_out[1] = SB;
- iac_out[2] = IAC_TTYPE;
+ iac_out[2] = c;
iac_out[3] = SEND;
iac_out[4] = IAC;
iac_out[5] = SE;
@@ -666,7 +748,7 @@ process_result:
}
void
-telnet_output_iac(struct session *session, char *iacs, size_t len)
+telnet_output_iac(struct session *session, const char *iacs, size_t len)
{
struct telnet_node *node = (struct telnet_node *)session->cookie;
@@ -686,21 +768,32 @@ telnet_close(struct session *session)
struct telnet_node *node = (struct telnet_node *)session->cookie;
short error, n;
+ if (node->from_trusted_proxy)
+ session->ban_node_source = false;
+
session_log(session, "%s telnet connection from %s",
(session->ban_node_source ? "Banning" : "Closing"), node->ip_s);
- if (session->ban_node_source) {
- for (n = 0; n <= nitems(banned_ips); n++) {
- if (n == nitems(banned_ips)) {
+ if (session->ban_node_source && !node->from_trusted_proxy) {
+ session_log(session, "Closing telnet connection from %s and "
+ "banning IP", node->ip_s);
+
+ for (n = 0; n <= MAX_BANNED_IPS; n++) {
+ if (n == MAX_BANNED_IPS) {
memmove(banned_ips + sizeof(banned_ips[0]), banned_ips,
sizeof(banned_ips) - sizeof(banned_ips[0]));
- banned_ips[0] = node->ip;
- } else if (banned_ips[n] == 0) {
- banned_ips[n] = node->ip;
+ banned_ips[0].ip = node->ip;
+ banned_ips[0].time = Time;
+ } else if (banned_ips[n].ip == 0) {
+ banned_ips[n].ip = node->ip;
+ banned_ips[n].time = Time;
break;
}
}
} else {
+ session_log(session, "Closing telnet connection from %s",
+ node->ip_s);
+
error = _TCPClose(&telnet_exit_pb, node->stream, nil, nil, false);
/* TODO: allow some time to fully close? */
}
@@ -714,4 +807,28 @@ telnet_close(struct session *session)
session->cookie = NULL;
telnet_nodes[node->id] = NULL;
free(node);
-}
+}
+
+bool
+telnet_ip_is_banned(ip_addr ip)
+{
+ short j;
+ char ip_s[16];
+ bool ret = false;
+
+ for (j = 0; j < MAX_BANNED_IPS; j++) {
+ if (banned_ips[j].ip &&
+ (Time - banned_ips[j].time > IP_BAN_SECONDS)) {
+ long2ip(banned_ips[j].ip, ip_s);
+ logger_printf(logger, "[%s] Unbanning IP %s after %d seconds",
+ ip_s, Time - banned_ips[j].time);
+ banned_ips[j].ip = 0;
+ continue;
+ }
+
+ if (banned_ips[j].ip == ip)
+ ret = true;
+ }
+
+ return ret;
+}