From 184023807c8f1080df9d2a6e460259a7dd36925f Mon Sep 17 00:00:00 2001 From: Brian Naylor Date: Tue, 11 Nov 2025 15:46:43 -0500 Subject: [PATCH] Claude took a crack at some modernization --- Makefile | 4 +-- auth_proto_v2.c | 34 ++++++++++---------- common.c | 74 +++++++++++++++++++++++++++++++++++++++++-- common.h | 7 +++++ fw_test.c | 7 +++-- fw_touch.c | 55 +++++++++++++++++++++++++++++--- in.ipscrompd.c | 71 +++++++++++++++++++++++++++++------------- in.ipscrompd.h | 6 ++-- ipscromp.c | 83 +++++++++++++++++++++++++------------------------ 9 files changed, 247 insertions(+), 94 deletions(-) diff --git a/Makefile b/Makefile index b324c3d..290642d 100644 --- a/Makefile +++ b/Makefile @@ -56,10 +56,10 @@ in.ipscrompd: $(FW_OBJS) in.ipscrompd.o common.o auth_proto_v2.o auth_proto_v2.o $(FW_OBJS) $(LDFLAGS) $(LIBS) fw_test: $(FW_OBJS) common.o fw_test.o - $(CC) $(CFLAGS) -o fw_test $(FW_OBJS) common.o fw_test.o $(LIBS) + $(CC) $(CFLAGS) -o fw_test $(FW_OBJS) common.o fw_test.o $(LDFLAGS) $(LIBS) ipscromp_gatekeeper: ipscromp_gatekeeper.o - $(CC) $(CFLAGS) -o ipscromp_gatekeeper ipscromp_gatekeeper.c $(LIBS) + $(CC) $(CFLAGS) -o ipscromp_gatekeeper ipscromp_gatekeeper.c $(LDFLAGS) $(LIBS) clean:; diff --git a/auth_proto_v2.c b/auth_proto_v2.c index 5d7e0fe..fa0ba83 100644 --- a/auth_proto_v2.c +++ b/auth_proto_v2.c @@ -39,16 +39,16 @@ char *pass_for(char *user) return NULL; } - while(pass == NULL && !feof(passfile)) { - char *colon; - if (fgets(buffer, PASS_BUFFSIZE, passfile) != NULL - && (colon = index(buffer, ':')) != NULL) { - *colon = '\0'; - if (strcmp(buffer, user) == 0) { - chomp(colon + 1); - pass = strdup(colon + 1); - } - } + while(pass == NULL && !feof(passfile)) { + char *colon; + if (fgets(buffer, PASS_BUFFSIZE, passfile) != NULL + && (colon = strchr(buffer, ':')) != NULL) { + *colon = '\0'; + if (strcmp(buffer, user) == 0) { + chomp(colon + 1); + pass = strdup(colon + 1); + } + } } fclose(passfile); @@ -95,13 +95,13 @@ errorcode auth_proto_v2(authrequest *req) return ERROR_CREDENTIALS; } - auth_len = strlen(req->user) + strlen(challenge) + strlen(pass) + 3; - if (alt_ip != NULL) { - if (inet_aton(alt_ip, &req->ip_to_add) == 0) { - syslog(LOG_ERR, "Invalid IP specified with IPERMIT, got '%s'", response); - return ERROR_IP_INVALID; - } - auth_len += strlen(alt_ip) + 1; + auth_len = strlen(req->user) + strlen(challenge) + strlen(pass) + 3; + if (alt_ip != NULL) { + if (string_to_sockaddr(alt_ip, &req->ip_to_add, &req->ip_to_add_len) < 0) { + syslog(LOG_ERR, "Invalid IP specified with IPERMIT, got '%s'", response); + return ERROR_IP_INVALID; + } + auth_len += strlen(alt_ip) + 1; } if ((auth_str = malloc(auth_len)) == NULL) { diff --git a/common.c b/common.c index 7cfa2de..029b7f9 100644 --- a/common.c +++ b/common.c @@ -7,6 +7,11 @@ #include #include #include +#include +#include +#include +#include +#include #ifdef USE_MD #include @@ -145,11 +150,11 @@ void chomp(char *string) { char *c; - if ((c = index(string, '\n')) != NULL) { + if ((c = strchr(string, '\n')) != NULL) { *c = '\0'; } - if ((c = index(string, '\r')) != NULL) { + if ((c = strchr(string, '\r')) != NULL) { *c = '\0'; } } @@ -225,7 +230,7 @@ char *hash(int version, char *fmt, ...) char *progname(char *progpath) { - char *progname = rindex(progpath, '/'); + char *progname = strrchr(progpath, '/'); if (progname == NULL) { progname = progpath; @@ -236,3 +241,66 @@ char *progname(char *progpath) return progname; } + + +/* + * Convert a sockaddr_storage to a string representation + * Returns a newly allocated string that must be freed by caller + */ +char *sockaddr_to_string(struct sockaddr_storage *ss, socklen_t sslen) +{ + char buffer[INET6_ADDRSTRLEN]; + void *addr; + + if (ss->ss_family == AF_INET) { + addr = &((struct sockaddr_in *)ss)->sin_addr; + } + else if (ss->ss_family == AF_INET6) { + addr = &((struct sockaddr_in6 *)ss)->sin6_addr; + } + else { + return NULL; + } + + if (inet_ntop(ss->ss_family, addr, buffer, sizeof(buffer)) == NULL) { + return NULL; + } + + return strdup(buffer); +} + + +/* + * Convert a string (hostname or IP) to a sockaddr_storage + * Returns 0 on success, -1 on error + * Prefers IPv6 if available, falls back to IPv4 + */ +int string_to_sockaddr(const char *str, struct sockaddr_storage *ss, socklen_t *sslen) +{ + struct addrinfo hints, *res, *rp; + int rc; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; /* Only return addresses we can use */ + + rc = getaddrinfo(str, NULL, &hints, &res); + if (rc != 0) { + dbg("getaddrinfo(%s) failed: %s\n", str, gai_strerror(rc)); + return -1; + } + + /* Try each address until we find one that works */ + for (rp = res; rp != NULL; rp = rp->ai_next) { + if (rp->ai_family == AF_INET || rp->ai_family == AF_INET6) { + memcpy(ss, rp->ai_addr, rp->ai_addrlen); + *sslen = rp->ai_addrlen; + freeaddrinfo(res); + return 0; + } + } + + freeaddrinfo(res); + return -1; +} diff --git a/common.h b/common.h index 76a6a52..c901c9c 100644 --- a/common.h +++ b/common.h @@ -1,4 +1,7 @@ +#include +#include + extern int debug; extern int debug_to_syslog; @@ -11,3 +14,7 @@ extern char *progname(char *progpath); extern void random_string(char *buffer, size_t bufflen); extern void dbg(char *fmt, ...); +/* New dual-stack helper functions */ +extern char *sockaddr_to_string(struct sockaddr_storage *ss, socklen_t sslen); +extern int string_to_sockaddr(const char *str, struct sockaddr_storage *ss, socklen_t *sslen); + diff --git a/fw_test.c b/fw_test.c index bb04c3c..2b96b53 100644 --- a/fw_test.c +++ b/fw_test.c @@ -15,7 +15,8 @@ int main(int argc, char *argv[]) { int rc; - struct in_addr addr; + struct sockaddr_storage addr; + socklen_t addrlen; if (argc != 3) { @@ -23,11 +24,11 @@ int main(int argc, char *argv[]) return 1; } - if (inet_aton(argv[1], &addr) == 0) + if (string_to_sockaddr(argv[1], &addr, &addrlen) < 0) { printf("%s is not a valid IP\n", argv[1]); } - else if ((rc = fw_add_ip(addr, argv[2])) >= 0) + else if ((rc = fw_add_ip(&addr, addrlen, argv[2])) >= 0) { printf("%s added successfully. Limited to %d hours\n", argv[1], rc); } diff --git a/fw_touch.c b/fw_touch.c index 66a2fb0..baa4a8f 100644 --- a/fw_touch.c +++ b/fw_touch.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -15,18 +16,57 @@ #error You must define FW_DIRECTORY for fw_touch.c #endif +/* Helper function to make IP address safe for use as filename */ +/* Replaces : with _ for IPv6 addresses */ +static char *ip_to_filename(char *ip_str) +{ + char *result, *p; + + if (ip_str == NULL) { + return NULL; + } + + result = strdup(ip_str); + if (result == NULL) { + return NULL; + } + + /* Replace colons with underscores for IPv6 */ + for (p = result; *p != '\0'; p++) { + if (*p == ':') { + *p = '_'; + } + } + + return result; +} + // this routine creates a file in FW_DIRECTORY/ // and then runs the firewall reload script... // -int fw_add_ip(struct in_addr ip, char *user) +int fw_add_ip(struct sockaddr_storage *addr, socklen_t addrlen, char *user) { FILE *fp; struct stat st; - char path[512]; + char path[1024], cmd[1024]; char need_fw=0; + char *ip_str, *filename_str; - sprintf(path, "%s/%s", FW_DIRECTORY, inet_ntoa(ip)); + ip_str = sockaddr_to_string(addr, addrlen); + if (ip_str == NULL) { + syslog(LOG_ERR, "Unable to convert address to string"); + return -EINVAL; + } + + filename_str = ip_to_filename(ip_str); + if (filename_str == NULL) { + syslog(LOG_ERR, "Unable to create filename for IP"); + free(ip_str); + return -ENOMEM; + } + + snprintf(path, sizeof(path), "%s/%s", FW_DIRECTORY, filename_str); // if IP has already been auth'd, simply touch the spool file but // don't invoke dynfw again... @@ -38,6 +78,8 @@ int fw_add_ip(struct in_addr ip, char *user) // if ((fp = fopen(path, "w")) == NULL) { syslog(LOG_ERR, "Unable to open '%s': %m", path); + free(filename_str); + free(ip_str); return -errno; } @@ -45,10 +87,13 @@ int fw_add_ip(struct in_addr ip, char *user) fclose(fp); if (need_fw) { - sprintf(path, "/usr/local/sbin/ipscromp_dynfw open %s > /dev/null 2>&1", inet_ntoa(ip)); - system(path); + snprintf(cmd, sizeof(cmd), "/usr/local/sbin/ipscromp_dynfw open %s > /dev/null 2>&1", ip_str); + system(cmd); } + free(filename_str); + free(ip_str); + // system("/etc/rc.d/rc.fw > /dev/null 2> /dev/null"); return 0; diff --git a/in.ipscrompd.c b/in.ipscrompd.c index 7f2949e..89f9473 100644 --- a/in.ipscrompd.c +++ b/in.ipscrompd.c @@ -44,18 +44,38 @@ char *errormsgs[] = #define ANNOYANCE_PAUSE 10 -int addable_ip(struct in_addr addr) +int addable_ip(struct sockaddr_storage *ss) { - int rc = 1; - unsigned long ip = htonl(addr.s_addr); + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + unsigned long ip; - if (ip == INADDR_LOOPBACK - || IN_MULTICAST(ip)) - { - rc = 0; + if (ss->ss_family == AF_INET) { + sin = (struct sockaddr_in *)ss; + ip = ntohl(sin->sin_addr.s_addr); + + if (ip == INADDR_LOOPBACK || IN_MULTICAST(ip)) { + return 0; + } + } + else if (ss->ss_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)ss; + + /* Check for IPv6 loopback (::1) */ + if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) { + return 0; + } + + /* Check for IPv6 multicast (ff00::/8) */ + if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { + return 0; + } + } + else { + return 0; /* Unknown address family */ } - return rc; + return 1; } void alarm_handler(int junk) @@ -75,9 +95,9 @@ void usage(char *progpath) int main(int argc, char *argv[]) { int opt, rc, proto_version_num, auth_rc; - char *command, *response, *user, *proto_version; + char *command, *response, *user, *proto_version, *peer_addr_str; pid_t pid = getpid(); - struct sockaddr_in sa; + struct sockaddr_storage sa; socklen_t sa_size = sizeof(sa); authrequest authreq; @@ -123,7 +143,8 @@ int main(int argc, char *argv[]) return 1; } - syslog(LOG_NOTICE, "Connect from %s\n", inet_ntoa(sa.sin_addr)); + peer_addr_str = sockaddr_to_string(&sa, sa_size); + syslog(LOG_NOTICE, "Connect from %s\n", peer_addr_str ? peer_addr_str : "unknown"); response = recv_sock(STDIN_FILENO); @@ -159,7 +180,8 @@ int main(int argc, char *argv[]) */ authreq.user = user; authreq.proto_version_num = proto_version_num; - authreq.ip_to_add = sa.sin_addr; + memcpy(&authreq.ip_to_add, &sa, sa_size); + authreq.ip_to_add_len = sa_size; switch (proto_version_num) { @@ -194,36 +216,43 @@ int main(int argc, char *argv[]) } /* Check we can add this IP. Refuse to add 127.0.0.1 and some others */ - if (!addable_ip(authreq.ip_to_add)) + if (!addable_ip(&authreq.ip_to_add)) { + char *ip_str = sockaddr_to_string(&authreq.ip_to_add, authreq.ip_to_add_len); syslog(LOG_ERR, "Refusing to add IP '%s' for user '%s'", - inet_ntoa(authreq.ip_to_add), user); + ip_str ? ip_str : "unknown", user); send_sock(STDOUT_FILENO, - errormsgs[ERROR_IP_INVALID], inet_ntoa(authreq.ip_to_add)); + errormsgs[ERROR_IP_INVALID], ip_str ? ip_str : "unknown"); + free(ip_str); return 1; } - if((rc = fw_add_ip(authreq.ip_to_add, authreq.user)) < 0) + if((rc = fw_add_ip(&authreq.ip_to_add, authreq.ip_to_add_len, authreq.user)) < 0) { + char *ip_str = sockaddr_to_string(&authreq.ip_to_add, authreq.ip_to_add_len); syslog(LOG_ERR, "User '%s' successfully authed but couldn't amend rules. " "IP was '%s', rc was %d (%s)\n", user, - inet_ntoa(authreq.ip_to_add), rc, strerror(-rc)); + ip_str ? ip_str : "unknown", rc, strerror(-rc)); + free(ip_str); send_sock(STDOUT_FILENO, errormsgs[ERROR_AMENDING]); } - else + else { + char *ip_str = sockaddr_to_string(&authreq.ip_to_add, authreq.ip_to_add_len); syslog(LOG_NOTICE, "User '%s' opened firewall for %s.\n", - user, inet_ntoa(authreq.ip_to_add)); + user, ip_str ? ip_str : "unknown"); if (rc == 0) { - send_sock(STDOUT_FILENO, PERMIT_OK, inet_ntoa(authreq.ip_to_add)); + send_sock(STDOUT_FILENO, PERMIT_OK, ip_str ? ip_str : "unknown"); } else { - send_sock(STDOUT_FILENO, PERMIT_OK_TIMED, inet_ntoa(authreq.ip_to_add), rc); + send_sock(STDOUT_FILENO, PERMIT_OK_TIMED, ip_str ? ip_str : "unknown", rc); } + free(ip_str); } + free(peer_addr_str); return 0; } diff --git a/in.ipscrompd.h b/in.ipscrompd.h index 7d39f95..c1cd78d 100644 --- a/in.ipscrompd.h +++ b/in.ipscrompd.h @@ -1,5 +1,6 @@ #include +#include typedef enum { @@ -16,7 +17,8 @@ typedef struct { char *user; int proto_version_num; - struct in_addr ip_to_add; + struct sockaddr_storage ip_to_add; + socklen_t ip_to_add_len; } authrequest; errorcode auth_proto_v2(authrequest *req); @@ -27,4 +29,4 @@ errorcode auth_proto_v2(authrequest *req); * 0 : Firewall updated, no limit. * >0 : Firewall updated, limit in hours */ -int fw_add_ip(struct in_addr addr, char *user); +int fw_add_ip(struct sockaddr_storage *addr, socklen_t addrlen, char *user); diff --git a/ipscromp.c b/ipscromp.c index 82be7a0..ce6c568 100644 --- a/ipscromp.c +++ b/ipscromp.c @@ -20,65 +20,66 @@ char *ip_string(char *data) { - struct hostent *he; + struct sockaddr_storage ss; + socklen_t sslen; - if (inet_aton(data, NULL) == 1) { - return data; - } - - if ((he = gethostbyname(data)) == NULL) { + if (string_to_sockaddr(data, &ss, &sslen) < 0) { return NULL; } - return strdup(inet_ntoa(*(struct in_addr *)he->h_addr)); + + return sockaddr_to_string(&ss, sslen); } int connect_host(char *host, int port) { int fd; - struct protoent *proto; - struct hostent *he; - struct sockaddr_in s; + struct addrinfo hints, *res, *rp; + char port_str[16]; + int rc; - memset(&s, 0, sizeof(s)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; - if (inet_aton(host, &s.sin_addr) != 0) { - /* Do nothing */ - } - else if ((he = gethostbyname(host)) != NULL) { - memcpy(&s.sin_addr, he->h_addr, he->h_length); - } - else { - fprintf(stderr, "Unable to determine address of '%s'\n", host); - return -1; - } + snprintf(port_str, sizeof(port_str), "%d", port); - if ((proto = getprotobyname("tcp")) == NULL) { - fprintf(stderr, "getprotobyname(\"tcp\") failed!\n"); - return -2; + rc = getaddrinfo(host, port_str, &hints, &res); + if (rc != 0) { + fprintf(stderr, "Unable to determine address of '%s': %s\n", + host, gai_strerror(rc)); + return -1; } - if ((fd = socket(PF_INET, SOCK_STREAM, proto->p_proto)) < 0) { - perror("socket()"); - return -3; - } + /* Try each address until we successfully connect */ + for (rp = res; rp != NULL; rp = rp->ai_next) { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (fd < 0) { + continue; + } - s.sin_family = PF_INET; - s.sin_port = htons(port); + if (debug > 1) { + struct sockaddr_storage ss; + memcpy(&ss, rp->ai_addr, rp->ai_addrlen); + char *addr_str = sockaddr_to_string(&ss, rp->ai_addrlen); + dbg("connect_host() trying %s:%d\n", addr_str ? addr_str : "unknown", port); + free(addr_str); + } - if (debug > 1) { - dbg("connect_host() connecting to %s:%d\n", - inet_ntoa(s.sin_addr), - ntohs(s.sin_port)); - } + if (connect(fd, rp->ai_addr, rp->ai_addrlen) == 0) { + /* Success */ + freeaddrinfo(res); + return fd; + } - if (connect(fd, (struct sockaddr *)&s, sizeof(s)) < 0) { - perror("connect()"); close(fd); - return -4; } - return fd; + /* All connection attempts failed */ + freeaddrinfo(res); + perror("connect()"); + return -4; } @@ -88,7 +89,7 @@ int find_port(char *host, char *default_service, int default_port) int port = -1; char *preferred = NULL; - if (host != NULL && (preferred = rindex(host, ':')) != NULL) { + if (host != NULL && (preferred = strrchr(host, ':')) != NULL) { *preferred = '\0'; preferred++; } @@ -156,7 +157,7 @@ int connect_ipscrompd(char *host, char *dflt_user, char *password, int port, auth_len, fd; - if ((at_symbol = index(host, '@')) != NULL) { + if ((at_symbol = strchr(host, '@')) != NULL) { user = malloc(at_symbol - host + 1); if (user == NULL) { fprintf(stderr, "Unable to malloc() space for user string.\n");