diff --git a/configure.ac b/configure.ac index 1d6acb0..7abaad9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT([knock], [0.8], [https://github.com/jvinet/knock/issues]) +AC_INIT([knock], [0.8.2], [https://github.com/jvinet/knock/issues]) AM_INIT_AUTOMAKE([dist-xz no-dist-gzip foreign subdir-objects]) AC_CONFIG_HEADER([config.h]) diff --git a/doc/knockd.1.in b/doc/knockd.1.in index b69ca06..67cb884 100644 --- a/doc/knockd.1.in +++ b/doc/knockd.1.in @@ -15,7 +15,8 @@ up holes in a firewall for quick access. .SH COMMANDLINE OPTIONS .TP .B "\-i, \-\-interface " -Specify an interface to listen on. The default is \fIeth0\fP. +Specify an interface to listen on. The default is \fIeth0\fP. To listen to +all interfaces set interface to \fIany\fP. .TP .B "\-d, \-\-daemon" Become a daemon. This is usually desired for normal server-like operation. @@ -34,6 +35,9 @@ Lookup DNS names for log entries. This may be a security risk! See section .B "\-4, \-\-only-ip-v4" Ignore packets from IPv6 and handle only IPv4. .TP +.B "\-s, \-\-save-mem" +Try save runtime memory usage on low mem systems. +.TP .B "\-v, \-\-verbose" Output verbose status messages. .TP diff --git a/src/knock.c b/src/knock.c index 851f80a..b5cc3aa 100644 --- a/src/knock.c +++ b/src/knock.c @@ -1,7 +1,7 @@ /* * knock.c * - * Copyright (c) 2004-2012 by Judd Vinet + * Copyright (c) 2004-2022 by Judd Vinet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -100,16 +100,17 @@ int main(int argc, char** argv) exit(1); } - /* prepare hints to select ipv4 or v6 if asked */ + //prepare hints to select ipv4 or v6 if asked memset(&hints, 0, sizeof hints); hints.ai_family = o_ip; hostname = argv[optind++]; for(; optind < argc; optind++) { + const char * port; unsigned short proto = PROTO_TCP; - const char *port; char *ptr, *arg = strdup(argv[optind]); + //select TCP/UDP protocol if((ptr = strchr(arg, ':'))) { *ptr = '\0'; port = arg; @@ -123,7 +124,7 @@ int main(int argc, char** argv) port = arg; } - /* get host and port based on hints */ + //get host and port based on hints result = getaddrinfo(hostname, port, &hints, &infoptr); if(result) { fprintf(stderr, "Failed to resolve hostname '%s' on port %s\n", hostname, port); @@ -131,7 +132,7 @@ int main(int argc, char** argv) exit(1); } - /* create socket */ + //create socket if(o_udp || proto == PROTO_UDP) { sd = socket(infoptr->ai_family, SOCK_DGRAM, 0); if(sd == -1) { @@ -149,10 +150,10 @@ int main(int argc, char** argv) fcntl(sd, F_SETFL, flags | O_NONBLOCK); } - /* extract ip as string (v4 or v6) */ + //extract ip as string (v4 or v6) getnameinfo(infoptr->ai_addr, infoptr->ai_addrlen, ipname, sizeof(ipname), NULL, 0, NI_NUMERICHOST); - /* connect or send UDP packet */ + //connect or send UDP packet if(o_udp || proto == PROTO_UDP) { vprint("hitting udp %s:%s\n", ipname, port); sendto(sd, "", 1, 0, infoptr->ai_addr, infoptr->ai_addrlen); @@ -198,7 +199,7 @@ void usage() { void ver() { printf("knock %s\n", version); - printf("Copyright (C) 2004-2012 Judd Vinet \n"); + printf("Copyright (C) 2004-2022 Judd Vinet \n"); exit(0); } diff --git a/src/knockd.c b/src/knockd.c index eff10bc..d80a6ff 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1,7 +1,7 @@ /* * knockd.c * - * Copyright (c) 2004-2012 by Judd Vinet + * Copyright (c) 2004-2022 by Judd Vinet * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,7 +48,6 @@ #include #include #include -#include #include #include #include @@ -125,7 +124,6 @@ void ver(); void usage(int exit_code); char* strtoupper(char *str); char* trim(char *str); -void runCommand(char *cmd); int parseconfig(char *configfile); int parse_port_sequence(char *sequence, opendoor_t *door); int get_new_one_time_sequence(opendoor_t *door); @@ -136,7 +134,6 @@ void generate_pcap_filter(); size_t realloc_strcat(char **dest, const char *src, size_t size); void free_door(opendoor_t *door); void close_door(opendoor_t *door); -char* get_ip(const char *iface, char *buf, int bufsize); size_t parse_cmd(char *dest, size_t size, const char *command, const char *src); int exec_cmd(char *command, char *name); void sniff(u_char *arg, const struct pcap_pkthdr *hdr, const u_char *packet); @@ -161,7 +158,8 @@ int o_verbose = 0; int o_debug = 0; int o_daemon = 0; int o_lookup = 0; -int o_skipIpV6 = 0; +int o_skip_ipv6 = 0; +int o_save_mem = 0; char o_int[32] = ""; /* default (eth0) is set after parseconfig() */ char o_cfg[PATH_MAX] = "/etc/knockd.conf"; char o_pidfile[PATH_MAX] = "/var/run/knockd.pid"; @@ -186,11 +184,12 @@ int main(int argc, char **argv) {"pidfile", required_argument, 0, 'p'}, {"logfile", required_argument, 0, 'g'}, {"only-ip-v4",no_argument, 0, '4'}, + {"save-mem", no_argument, 0, 's'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; - while((opt = getopt_long(argc, argv, "4vDdli:c:p:g:hV", opts, &optidx))) { + while((opt = getopt_long(argc, argv, "4vDdli:c:p:g:hVs", opts, &optidx))) { if(opt < 0) { break; } @@ -200,7 +199,8 @@ int main(int argc, char **argv) case 'D': o_debug = 1; break; case 'd': o_daemon = 1; break; case 'l': o_lookup = 1; break; - case '4': o_skipIpV6 = 1; break; + case '4': o_skip_ipv6 = 1; break; + case 's': o_save_mem = 1; break; case 'i': strncpy(o_int, optarg, sizeof(o_int)-1); o_int[sizeof(o_int)-1] = '\0'; break; @@ -277,7 +277,8 @@ int main(int argc, char **argv) if(ifa->ifa_addr == NULL) continue; - if((strcmp(ifa->ifa_name, o_int) == 0) && (ifa->ifa_addr->sa_family == AF_INET || (ifa->ifa_addr->sa_family == AF_INET6 && !o_skipIpV6))) { + if((strcmp(ifa->ifa_name, o_int) == 0 || strcmp("any", o_int) == 0) + && (ifa->ifa_addr->sa_family == AF_INET || (ifa->ifa_addr->sa_family == AF_INET6 && !o_skip_ipv6))) { if(ifa->ifa_addr->sa_family == AF_INET) has_ipv4 = 1; if(ifa->ifa_addr->sa_family == AF_INET6) @@ -484,15 +485,15 @@ void reload(int signum) exit(1); } - vprint("re-opening log file: %s\n", o_logfile); - logprint("re-opening log file: %s\n", o_logfile); - /* re-open the log file */ logfd = fopen(o_logfile, "a"); if(logfd == NULL) { perror("warning: cannot open logfile"); } + vprint("Re-opening log file: %s\n", o_logfile); + logprint("Re-opening log file: %s\n", o_logfile); + /* Fix issue #2 by regenerating the PCAP filter post config file re-read */ generate_pcap_filter(); @@ -511,6 +512,7 @@ void usage(int exit_code) { printf(" -g, --logfile use an alternate logfile\n"); printf(" -v, --verbose be verbose\n"); printf(" -4, --only-ip-v4 do not track ipv6\n"); + printf(" -s, --save-mem save memory by freeing permanent filter string buffers\n"); printf(" -V, --version display version\n"); printf(" -h, --help this help\n"); printf("\n"); @@ -519,7 +521,7 @@ void usage(int exit_code) { void ver() { printf("knockd %s\n", version); - printf("Copyright (C) 2004-2012 Judd Vinet \n"); + printf("Copyright (C) 2004-2022 Judd Vinet \n"); exit(0); } @@ -795,6 +797,11 @@ int parseconfig(char *configfile) fprintf(stderr, "error: section '%s' has an empty knock sequence\n", door->name); return(1); } + if((door->start_command == NULL || strlen(door->start_command) == 0) && + (door->start_command6 == NULL || strlen(door->start_command6) == 0)) { + fprintf(stderr, "error: section '%s' has no start command\n", door->name); + return(1); + } } return(0); @@ -967,7 +974,7 @@ void generate_pcap_filter() if(ipv6 == 1 && !has_ipv6) continue; - if(ipv6 && o_skipIpV6) + if(ipv6 && o_skip_ipv6) continue; for(lp = doors; lp; lp = lp->next) { @@ -1155,7 +1162,7 @@ void generate_pcap_filter() cleanup(1); } strcpy(door->pcap_filter_expv6, buffer); - dprint("adding pcap expression for door '%s': %s\n", door->name, door->pcap_filter_expv6); + dprint("Adding pcap expression for door '%s': %s\n", door->name, door->pcap_filter_expv6); } else { door->pcap_filter_exp = (char*)malloc(strlen(buffer) + 1); if(door->pcap_filter_exp == NULL) { @@ -1163,7 +1170,7 @@ void generate_pcap_filter() cleanup(1); } strcpy(door->pcap_filter_exp, buffer); - dprint("adding pcap expression for door '%s': %s\n", door->name, door->pcap_filter_exp); + dprint("Adding ipv6 pcap expression for door '%s': %s\n", door->name, door->pcap_filter_exp); } buffer[0] = '\0'; /* "clear" the buffer */ } @@ -1204,20 +1211,34 @@ void generate_pcap_filter() if(ipv6 == 1 && !has_ipv6) continue; - if(ipv6 && o_skipIpV6) + if(ipv6 && o_skip_ipv6) continue; if(first) first = 0; else bufsize = realloc_strcat(&buffer, " or ", bufsize); - if(ipv6) + if(ipv6) { bufsize = realloc_strcat(&buffer, door->pcap_filter_expv6, bufsize); - else + if(o_save_mem == 1) { + // save memory and free door specific filter here directly + free(door->pcap_filter_expv6); + door->pcap_filter_expv6 = NULL; + } + } + else { bufsize = realloc_strcat(&buffer, door->pcap_filter_exp, bufsize); + if(o_save_mem == 1) { + // save memory and free door specific filter here directly + free(door->pcap_filter_exp); + door->pcap_filter_exp = NULL; + } + } } } + //dprint("FULL : %s\n",buffer); + /* test if in any of the precedent calls to realloc_strcat() failed. See above why this is ok to do this only * at this point */ if(bufsize == 0) { @@ -1286,11 +1307,14 @@ void free_door(opendoor_t *door) if(door) { free(door->target); free(door->start_command); + free(door->start_command6); free(door->stop_command); - if(door->one_time_sequences_fd) { + free(door->stop_command6); + if (door->one_time_sequences_fd) { fclose(door->one_time_sequences_fd); } free(door->pcap_filter_exp); + free(door->pcap_filter_expv6); free(door); } } @@ -1303,40 +1327,6 @@ void close_door(opendoor_t *door) free_door(door); } -/* Get the IP address of an interface - */ -char* get_ip(const char* iface, char *buf, int bufsize) -{ - int s; - struct ifreq ifr; - - if(bufsize <= 0) { - return(NULL); - } - if(buf == NULL) { - return(NULL); - } - buf[0] = '\0'; - - s = socket(AF_INET, SOCK_DGRAM, 0); - if(s < 0) { - return(NULL); - } - - bzero((void*)(&ifr.ifr_name), sizeof(ifr.ifr_name)); - strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)-1); - ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0'; - if(ioctl(s, SIOCGIFADDR, &ifr)) { - close(s); - return(NULL); - } - close(s); - - strncpy(buf, inet_ntoa((*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr), bufsize-1); - buf[bufsize-1] = '\0'; - return(buf); -} - /* Parse a command line, replacing tokens (eg, %IP%) with their real values and * copy the result to dest. At most size-1 characters will be copied and the * result will always be NULL terminated (except if size == 0). The return value @@ -1599,8 +1589,14 @@ void process_attempt(knocker_t *attempt) get_new_one_time_sequence(attempt->door); /* update pcap filter */ - free(attempt->door->pcap_filter_exp); - attempt->door->pcap_filter_exp = NULL; + if(attempt->door->pcap_filter_expv6) { + free(attempt->door->pcap_filter_expv6); + attempt->door->pcap_filter_expv6 = NULL; + } + if(attempt->door->pcap_filter_exp) { + free(attempt->door->pcap_filter_exp); + attempt->door->pcap_filter_exp = NULL; + } generate_pcap_filter(); } } @@ -1635,18 +1631,25 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) if(lltype == DLT_EN10MB) { eth = (struct ether_header*)packet; - if(ntohs(eth->ether_type) != ETHERTYPE_IP && ntohs(eth->ether_type) != ETHERTYPE_IPV6) { + if(ntohs(eth->ether_type) != ETHERTYPE_IP && ntohs(eth->ether_type) != ETHERTYPE_IPV6 && ntohs(eth->ether_type) != ETHERTYPE_VLAN) { return; } - ip = (struct ip*)(packet + sizeof(struct ether_header)); - ip6 = (struct ip6_hdr*)(packet + sizeof(struct ether_header)); + int tag_size = 0; + if (ntohs(eth->ether_type) == ETHERTYPE_VLAN) { + tag_size = 4; + } + + ip = (struct ip*)(packet + sizeof(struct ether_header) + tag_size); + ip6 = (struct ip6_hdr*)(packet + sizeof(struct ether_header) + tag_size); #ifdef __linux__ } else if(lltype == DLT_LINUX_SLL) { ip = (struct ip*)((u_char*)packet + 16); + ip6 = (struct ip6_hdr*)((u_char*)packet + 16); #endif } else if(lltype == DLT_RAW) { ip = (struct ip*)((u_char*)packet); + ip6 = (struct ip6_hdr*)((u_char*)packet); } else { dprint("link layer header type of packet not recognized, ignoring...\n"); return;