commit f724fcf66ad2f1cf9d9d51a379aae2346d9caf56
parent 00a85994ac403d816340f4e1610ca3ded79a7eb2
Author: Jan Pobrislo <ccx@te2000.cz>
Date: Tue, 27 Aug 2024 11:02:45 +0000
Extract common SOCKS constants and structures, add access control module.
Diffstat:
4 files changed, 307 insertions(+), 77 deletions(-)
diff --git a/src/socks.h b/src/socks.h
@@ -0,0 +1,53 @@
+
+#define SOCKS_RESERVED 0x00
+#define SOCKS_VERSION_4 0x04
+#define SOCKS_VERSION_5 0x05
+
+typedef unsigned char socks5_auth_mode_t;
+#define SOCKS5_AUTH_METHOD_NOAUTH 0x00
+#define SOCKS5_AUTH_METHOD_USERPASS 0x02
+#define SOCKS5_AUTH_METHOD_NOMETHOD 0xff
+
+#define SOCKS5_AUTH_OK 0x00
+#define SOCKS5_AUTH_VERSION 0x01
+#define SOCKS5_AUTH_FAIL 0xff
+
+typedef unsigned char socks5_command_t;
+#define SOCKS5_CMD_CONNECT 0x01
+#define SOCKS5_BIND 0x02
+#define SOCKS5_UDP_ASSOCIATE 0x03
+
+typedef unsigned char socks5_addr_type_t;
+#define SOCKS5_ADDR_TYPE_IP4 0x01
+#define SOCKS5_ADDR_TYPE_DOMAIN 0x03
+#define SOCKS5_ADDR_TYPE_IP6 0x04
+
+typedef unsigned char socks5_reply_t;
+#define SOCKS5_REPLY_OK 0x00 // succeeded
+#define SOCKS5_REPLY_ERR_GENERAL 0x01 // general SOCKS server failure
+#define SOCKS5_REPLY_ERR_FORBIDDEN 0x02 // connection not allowed by ruleset
+#define SOCKS5_REPLY_ERR_NET_UNREACH 0x03 // Network unreachable
+#define SOCKS5_REPLY_ERR_HOST_UNREACH 0x04 // Host unreachable
+#define SOCKS5_REPLY_ERR_CONN_REFUSED 0x05 // Connection refused
+#define SOCKS5_REPLY_ERR_TTL 0x06 // TTL expired
+#define SOCKS5_REPLY_ERR_CMD 0x07 // Command not supported
+#define SOCKS5_REPLY_ERR_ATYPE 0x08 // Address type not supported
+
+#define SOCKS4_STATUS_OK 0x5a
+#define SOCKS4_STATUS_FAILED 0x5b
+
+typedef struct socks_header_s {
+ unsigned char version;
+ unsigned char methods;
+} socks_header;
+
+
+typedef struct socks5_request_header_s {
+ unsigned char version;
+ socks5_command_t cmd;
+ unsigned char reserved;
+ socks5_addr_type_t atyp;
+} socks5_request_header;
+
+/* vim: sw=4 sts=4 et
+*/
diff --git a/src/ucspi-socksserver-access.c b/src/ucspi-socksserver-access.c
@@ -0,0 +1,205 @@
+#include <string.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include <skalibs/exec.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr.h>
+#include <skalibs/cdb.h>
+#include <skalibs/ip46.h>
+
+#include <s6/accessrules.h>
+
+#include "socks.h"
+
+#define PROG "ucspi-socksserver-access"
+#define USAGE "ucspi-socksserve-access [ -i rulesdir | -x rulesfile ] prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+#define X() strerr_dief1x(101, "internal error")
+
+typedef struct app_options_s {
+ unsigned int rulestype;
+ char const * rules;
+ char const * port;
+ char const * addr_type;
+ char const * addr;
+} app_options;
+
+typedef struct check_result_s {
+ s6_accessrules_result_t accepted;
+ s6_accessrules_params_t params;
+} check_result;
+
+
+char * required_getenv(const char *name){
+ char *x = getenv(name);
+ if (!x) {
+ strerr_dienotset(100, name);
+ }
+ return x;
+}
+
+void write1(const void *buf, ssize_t n)
+{
+ ssize_t nwrite, left = n;
+ while (left > 0) {
+ if ((nwrite = write(STDOUT_FILENO, buf, left)) < 0) {
+ if (errno == EINTR || errno == EAGAIN) {
+ continue;
+ }
+ strerr_diefu1sys(111, "write(stdout)");
+ } else {
+ assert(left >= nwrite);
+ left -= nwrite;
+ buf += nwrite;
+ }
+ }
+}
+
+void socks5_reply_fail(socks5_reply_t status)
+{
+ char reply[] = {
+ SOCKS_VERSION_5,
+ status,
+ SOCKS_RESERVED,
+ SOCKS5_ADDR_TYPE_DOMAIN,
+ 0, /* domain length */
+ 0, /* port MSB */
+ 0 /* port LSB */
+ };
+ write1(reply, sizeof(reply));
+ close(1);
+}
+
+void socks4_reply_fail()
+{
+ const char resp[8] = {0x00, SOCKS4_STATUS_FAILED, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ write1(resp, sizeof(resp));
+ close(1);
+}
+
+
+void check_access_ip(app_options *opt, check_result *result) {
+ ip46 ip;
+ if (!ip46_scan(opt->addr_type, &ip)) {
+ strerr_dieinvalid(100, "SOCKS_ADDR") ;
+ }
+ switch (opt->rulestype)
+ {
+ case 1 :
+ result->accepted = s6_accessrules_ip46_fs(&ip, (void *)opt->rules, &result->params);
+ break;
+ case 2 :
+ {
+ cdb c = CDB_ZERO ;
+ if (!cdb_init(&c, opt->rules)) {
+ strerr_diefu2sys(111, "cdb_init ", opt->rules);
+ }
+ result->accepted = s6_accessrules_ip46_cdb(&ip, &c, &result->params);
+ if (result->accepted == S6_ACCESSRULES_ALLOW) {
+ cdb_free(&c);
+ }
+ }
+ break;
+ default : X();
+ }
+}
+
+s6_accessrules_result_t check_access_dns(app_options *opt, check_result *result) {
+ switch (opt->rulestype)
+ {
+ case 1 :
+ result->accepted = s6_accessrules_reversedns_fs(opt->addr, (void *)opt->rules, &result->params);
+ break;
+ case 2 :
+ {
+ cdb c = CDB_ZERO ;
+ if (!cdb_init(&c, opt->rules)) {
+ strerr_diefu2sys(111, "cdb_init ", opt->rules);
+ }
+ result->accepted = s6_accessrules_reversedns_cdb(opt->addr, &c, &result->params);
+ if (result->accepted == S6_ACCESSRULES_ALLOW) {
+ cdb_free(&c);
+ }
+ }
+ break;
+ default : X();
+ }
+}
+
+
+
+int main (int argc, char const *const *argv)
+{
+ char const *rulestypestr[3] = { "no", "fs", "cdb" } ;
+ app_options opt;
+ opt.rulestype = 0;
+ opt.rules = 0;
+
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int o = subgetopt_r(argc, argv, "i:x:", &l) ;
+ if (o == -1) break ;
+ switch (o)
+ {
+ case 'i' : opt.rules = l.arg ; opt.rulestype = 1 ; break ;
+ case 'x' : opt.rules = l.arg ; opt.rulestype = 2 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (!argc) dieusage() ;
+ if (!*argv[0]) dieusage() ;
+
+ opt.port = required_getenv("SOCKS_PORT");
+ opt.addr_type = required_getenv("SOCKS_ADDR_TYPE");
+ opt.addr = required_getenv("SOCKS_ADDR");
+
+ if(opt.rulestype == 0) {
+ xexec(argv);
+ }
+
+ check_result result = { S6_ACCESSRULES_ALLOW, S6_ACCESSRULES_PARAMS_ZERO };
+
+ if(strcmp(opt.addr_type, "dns") == 0) {
+ check_access_dns(&opt, &result);
+ } else if ( strcmp(opt.addr_type, "ip4") == 0 || strcmp(opt.addr_type, "ip6") == 0) {
+ check_access_ip(&opt, &result);
+ } else {
+ strerr_dieinvalid(100, "SOCKS_ADDR_TYPE") ;
+ }
+
+ switch (result.accepted) {
+ case S6_ACCESSRULES_ERROR:
+ strerr_diefu6sys(111, "check ", rulestypestr[opt.rulestype], " ruleset for ", opt.addr_type, " in ", opt.rules);
+ case S6_ACCESSRULES_ALLOW:
+ break ;
+ case S6_ACCESSRULES_DENY:
+ // if (verbosity >= 2) log_deny(getpid(), &remoteip) ;
+ return 1;
+ case S6_ACCESSRULES_NOTFOUND:
+ // if (flagdnslookup) {
+ // break;
+ // }
+ // if (verbosity >= 2) {
+ // log_deny(getpid(), opt.addr);
+ // }
+ return 1 ;
+ default: X() ;
+ }
+
+ if (result.params.exec.len)
+ {
+ char *specialargv[4] = { "execlineb", "-c", result.params.exec.s, 0 } ;
+ xmexec_m((char const *const *)specialargv, result.params.env.s, result.params.env.len) ;
+ }
+ xexec(argv);
+
+}
+
+/* vim: sw=4 sts=4 et
+*/
diff --git a/src/ucspi-socksserver-connected.c b/src/ucspi-socksserver-connected.c
@@ -10,6 +10,8 @@
#include <skalibs/strerr.h>
#include <skalibs/types.h>
+#include "socks.h"
+
#define PROG "ucspi-socketserver-connect"
#define SOCKS_RESERVED 0x00
@@ -21,7 +23,7 @@ typedef unsigned char socks5_addr_type_t;
#define SOCKS5_ADDR_TYPE_DOMAIN 0x03
#define SOCKS5_ADDR_TYPE_IP6 0x04
-typedef unsigned char socks5_response_t;
+typedef unsigned char socks5_reply_t;
#define SOCKS5_REPLY_OK 0x00 // succeeded
#define SOCKS5_REPLY_ERR_GENERAL 0x01 // general SOCKS server failure
#define SOCKS5_REPLY_ERR_FORBIDDEN 0x02 // connection not allowed by ruleset
@@ -32,9 +34,6 @@ typedef unsigned char socks5_response_t;
#define SOCKS5_REPLY_ERR_CMD 0x07 // Command not supported
#define SOCKS5_REPLY_ERR_ATYPE 0x08 // Address type not supported
-#define SOCKS4_STATUS_OK 0x5a
-#define SOCKS4_STATUS_FAILED 0x5b
-
void fd_block(int fd)
{
int flags = fcntl(fd, F_GETFL);
@@ -63,7 +62,7 @@ void write1(const void *buf, ssize_t n)
}
}
-void socks5_response_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port)
+void socks5_reply_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port)
{
size_t size;
switch(addr_type) {
@@ -81,13 +80,13 @@ void socks5_response_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port
break;
}
- char response[] = {
+ char reply[] = {
SOCKS_VERSION_5,
SOCKS5_REPLY_OK,
SOCKS_RESERVED,
addr_type,
};
- write1(response, sizeof(response));
+ write1(reply, sizeof(reply));
if (addr_type == SOCKS5_ADDR_TYPE_DOMAIN) {
/* prefix domain by length */
@@ -100,7 +99,7 @@ void socks5_response_ok(socks5_addr_type_t addr_type, void *addr, in_port_t port
write1(&port, sizeof(port));
}
-void socks4_send_response(int status)
+void socks4_send_reply(int status)
{
char resp[8] = {0x00, (char)status, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
write1(resp, sizeof(resp));
@@ -127,19 +126,19 @@ int main (int argc, char const *const *argv)
switch(local_addr.sa_family) {
case AF_INET:
struct sockaddr_in *in_addr = (struct sockaddr_in *)&local_addr;
- socks5_response_ok(SOCKS5_ADDR_TYPE_IP4, &in_addr->sin_addr, in_addr->sin_port);
+ socks5_reply_ok(SOCKS5_ADDR_TYPE_IP4, &in_addr->sin_addr, in_addr->sin_port);
break;
case AF_INET6:
struct sockaddr_in6 *in6_addr = (struct sockaddr_in6 *)&local_addr;
- socks5_response_ok(SOCKS5_ADDR_TYPE_IP6, &in6_addr->sin6_addr, in6_addr->sin6_port);
+ socks5_reply_ok(SOCKS5_ADDR_TYPE_IP6, &in6_addr->sin6_addr, in6_addr->sin6_port);
break;
default:
- socks5_response_ok(SOCKS5_ADDR_TYPE_DOMAIN, "localhost", 0);
+ socks5_reply_ok(SOCKS5_ADDR_TYPE_DOMAIN, "localhost", 0);
break;
}
} else if (strcmp(env_socks_version, "4") == 0) {
- socks4_send_response(SOCKS4_STATUS_OK);
+ socks4_send_reply(SOCKS4_STATUS_OK);
} else {
strerr_dieinvalid(100, "SOCKS_VERSION") ;
diff --git a/src/ucspi-socksserver.c b/src/ucspi-socksserver.c
@@ -10,58 +10,12 @@
#include <skalibs/exec.h>
#include <skalibs/strerr.h>
+#include "socks.h"
+
#define PROG "ucspi-socketserver"
#define MAX_DOMAIN_LENGTH 256
-#define SOCKS_RESERVED 0x00
-#define SOCKS_VERSION_4 0x04
-#define SOCKS_VERSION_5 0x05
-
-typedef unsigned char socks5_auth_mode_t;
-#define SOCKS5_AUTH_METHOD_NOAUTH 0x00
-#define SOCKS5_AUTH_METHOD_USERPASS 0x02
-#define SOCKS5_AUTH_METHOD_NOMETHOD 0xff
-
-#define SOCKS5_AUTH_OK 0x00
-#define SOCKS5_AUTH_VERSION 0x01
-#define SOCKS5_AUTH_FAIL 0xff
-
-typedef unsigned char socks5_command_t;
-#define SOCKS5_CMD_CONNECT 0x01
-#define SOCKS5_BIND 0x02
-#define SOCKS5_UDP_ASSOCIATE 0x03
-
-typedef unsigned char socks5_addr_type_t;
-#define SOCKS5_ADDR_TYPE_IP4 0x01
-#define SOCKS5_ADDR_TYPE_DOMAIN 0x03
-#define SOCKS5_ADDR_TYPE_IP6 0x04
-
-typedef unsigned char socks5_response_t;
-#define SOCKS5_REPLY_OK 0x00 // succeeded
-#define SOCKS5_REPLY_ERR_GENERAL 0x01 // general SOCKS server failure
-#define SOCKS5_REPLY_ERR_FORBIDDEN 0x02 // connection not allowed by ruleset
-#define SOCKS5_REPLY_ERR_NET_UNREACH 0x03 // Network unreachable
-#define SOCKS5_REPLY_ERR_HOST_UNREACH 0x04 // Host unreachable
-#define SOCKS5_REPLY_ERR_CONN_REFUSED 0x05 // Connection refused
-#define SOCKS5_REPLY_ERR_TTL 0x06 // TTL expired
-#define SOCKS5_REPLY_ERR_CMD 0x07 // Command not supported
-#define SOCKS5_REPLY_ERR_ATYPE 0x08 // Address type not supported
-
-typedef struct socks_header_s {
- unsigned char version;
- unsigned char methods;
-} socks_header;
-
-
-typedef struct socks5_cmd_header_s {
- unsigned char version;
- socks5_command_t cmd;
- unsigned char reserved;
- socks5_addr_type_t atyp;
-} socks5_cmd_header;
-
-
void fd_block(int fd);
void read0(void *buf, ssize_t n);
void write1(const void *buf, ssize_t n);
@@ -73,7 +27,7 @@ void socks5_auth(int methods_count, socks5_auth_mode_t supported_auth_method);
void socks5_auth_userpass(void);
void socks5_auth_noauth(void);
void socks5_auth_notsupported(void);
-void socks5_command(socks5_cmd_header *chdr);
+void socks5_command(socks5_request_header *rhdr);
void handle_socks4(void);
void do_connect(unsigned char socks_version, socks5_addr_type_t addr_type, char *buf, unsigned short int port);
@@ -199,18 +153,17 @@ unsigned short socks_read_port(void) {
void handle_socks5(char methods)
{
socks5_auth(methods, SOCKS5_AUTH_METHOD_NOAUTH); /* TODO: authentication support */
- socks5_cmd_header chdr;
- socks5_command(&chdr);
+ socks5_request_header rhdr;
+ socks5_command(&rhdr);
unsigned short port;
- switch (chdr.atyp) {
+ switch (rhdr.atyp) {
case SOCKS5_ADDR_TYPE_IP4:
char ip4[4];
read0(ip4, sizeof(ip4));
port = socks_read_port();
do_connect(5, SOCKS5_ADDR_TYPE_IP4, ip4, port);
- /* socks5_ip_send_response(ip, htons(port)); */
break;
case SOCKS5_ADDR_TYPE_IP6:
char ip6[16];
@@ -218,7 +171,6 @@ void handle_socks5(char methods)
port = socks_read_port();
do_connect(5, SOCKS5_ADDR_TYPE_IP6, ip6, port);
- /* socks5_ip_send_response(ip, htons(port)); */
break;
case SOCKS5_ADDR_TYPE_DOMAIN:
unsigned char size;
@@ -230,7 +182,6 @@ void handle_socks5(char methods)
port = socks_read_port();
do_connect(5, SOCKS5_ADDR_TYPE_DOMAIN, domain, port);
- /* socks5_domain_send_response(address, size, htons(port)); */
break;
default:
strerr_dief1x(1, "Unsupported SOCKS5 command");
@@ -244,7 +195,7 @@ void socks5_auth(int methods_count, socks5_auth_mode_t supported_auth_method)
for (int i = 0; i < methods_count; i++) {
char type;
read0(&type, 1);
- log_message("Method AUTH 0x%hhX", type);
+ // log_message("Method AUTH 0x%hhX", type);
if (type == supported_auth_method) {
supported = true;
}
@@ -283,13 +234,13 @@ void socks5_auth_notsupported(void)
write1(answer, sizeof(answer));
}
-void socks5_command(socks5_cmd_header *chdr)
+void socks5_command(socks5_request_header *rhdr)
{
- read0(chdr, sizeof(socks5_cmd_header));
- log_message("Command %02hhX %02hhX %02hhX %02hhX",
- chdr->version, chdr->cmd, chdr->reserved, chdr->atyp);
- assert(chdr->version == SOCKS_VERSION_5);
- assert(chdr->cmd == SOCKS5_CMD_CONNECT);
+ read0(rhdr, sizeof(socks5_request_header));
+ // log_message("Command %02hhX %02hhX %02hhX %02hhX",
+ // rhdr->version, rhdr->cmd, rhdr->reserved, rhdr->atyp);
+ assert(rhdr->version == SOCKS_VERSION_5);
+ assert(rhdr->cmd == SOCKS5_CMD_CONNECT);
}
@@ -306,10 +257,10 @@ void handle_socks4(void)
if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] != 0) {
char domain[MAX_DOMAIN_LENGTH];
read0_cstring(domain, sizeof(domain));
- log_message("Socks4A: ident:%s; domain:%s;", ident, domain);
+ // log_message("Socks4A: ident:%s; domain:%s;", ident, domain);
do_connect(4, SOCKS5_ADDR_TYPE_DOMAIN, domain, port);
} else {
- log_message("Socks4: connect by ip & port");
+ // log_message("Socks4: connect by ip & port");
do_connect(4, SOCKS5_ADDR_TYPE_IP4, ip, port);
}
@@ -344,6 +295,27 @@ void socks_env_dns(const char *domain) {
xenv("SOCKS_ADDR_TYPE", "dns");
}
+void log_connect(unsigned char socks_version, socks5_addr_type_t addr_type, char *buf, unsigned short int port) {
+ char address[43];
+ switch (addr_type) {
+ case SOCKS5_ADDR_TYPE_IP4:
+ snprintf(address, sizeof(address), "%hhu.%hhu.%hhu.%hhu",
+ buf[0], buf[1], buf[2], buf[3]);
+ break;
+ case SOCKS5_ADDR_TYPE_IP6:
+ snprintf(address, sizeof(address),
+ "[%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx:%02hhx%02hhx]",
+ buf[0x0], buf[0x1], buf[0x2], buf[0x3], buf[0x4], buf[0x5], buf[0x6], buf[0x7],
+ buf[0x8], buf[0x9], buf[0xa], buf[0xb], buf[0xc], buf[0xd], buf[0xe], buf[0xf]);
+ break;
+ case SOCKS5_ADDR_TYPE_DOMAIN:
+ break;
+ default:
+ strerr_dief1x(110, "Internal error: unhandled address type");
+ }
+ log_message("SOCKS%hhd %s:%hd", socks_version, addr_type == SOCKS5_ADDR_TYPE_DOMAIN ? buf : address, port);
+}
+
void do_connect(unsigned char socks_version, socks5_addr_type_t addr_type, char *buf, unsigned short int port) {
switch(socks_version) {
case 4:
@@ -381,8 +353,9 @@ void do_connect(unsigned char socks_version, socks5_addr_type_t addr_type, char
break;
default:
strerr_dief1x(110, "Internal error: unhandled address type");
- break;
}
+
+ log_connect(socks_version, addr_type, buf, port);
xmexec(argv);
}