From d50b116782008b3d6955079c2c70adee83eaeb5d Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Mon, 31 Dec 2018 15:25:57 +0100 Subject: [PATCH 01/20] Merged changes from jvinet/knock#53 into master Updated copyright notes Fixed issue with uninitialized ip6 pointer in case of packet type DLT_LINUX_SLL and DLT_RAW Removal of whitespaces --- doc/knock.1.in | 6 + doc/knockd.1.in | 42 +++- src/knock.c | 78 +++++-- src/knockd.c | 529 +++++++++++++++++++++++++++++++----------------- 4 files changed, 451 insertions(+), 204 deletions(-) diff --git a/doc/knock.1.in b/doc/knock.1.in index 3205faa..4b598c0 100644 --- a/doc/knock.1.in +++ b/doc/knock.1.in @@ -21,6 +21,12 @@ where a router mistakes your stream of SYN packets as a port scan and blocks them. If the packet rate is slowed with \-\-delay, then the router should let the packets through. .TP +.B "\-4, \-\-ipv4 " +Force usage of IPv4. +.TP +.B "\-6, \-\-ipv6 " +Force usage of IPv6. +.TP .B "\-v, \-\-verbose" Output verbose status messages. .TP diff --git a/doc/knockd.1.in b/doc/knockd.1.in index 2ef6297..8813acd 100644 --- a/doc/knockd.1.in +++ b/doc/knockd.1.in @@ -31,6 +31,9 @@ Output debugging messages. Lookup DNS names for log entries. This may be a security risk! See section \fBSECURITY NOTES\fP. .TP +.B "\-4, \-\-only-ip-v4" +Ignore packets from IPv6 and handle only IPv4. +.TP .B "\-v, \-\-verbose" Output verbose status messages. .TP @@ -116,6 +119,27 @@ sniffing the network). cmd_timeout = 5 stop_command = /usr/sbin/iptables \-D INPUT \-s %IP% \-p tcp \-\-dport 25 \-j ACCEPT +.fi +.TP +.SH Example #4: +.RS +Example to support IPv4 and IPv6. You can provide a dedicated command for each +of the two protocols. + +.nf +[options] + logfile = /var/log/knockd.log + +[opencloseSMTP] + one_time_sequences = /etc/knockd/smtp_sequences + seq_timeout = 15 + tcpflags = fin,!ack + start_command = /usr/sbin/iptables \-A INPUT \-s %IP% \-p tcp \-\-dport 25 \-j ACCEPT + start_command_6 = /usr/sbin/ip6tables \-A INPUT \-s %IP% \-p tcp \-\-dport 25 \-j ACCEPT + cmd_timeout = 5 + stop_command = /usr/sbin/iptables \-D INPUT \-s %IP% \-p tcp \-\-dport 25 \-j ACCEPT + stop_command_6 = /usr/sbin/ip6tables \-D INPUT \-s %IP% \-p tcp \-\-dport 25 \-j ACCEPT + .fi .RE .SH CONFIGURATION: GLOBAL DIRECTIVES @@ -181,10 +205,18 @@ etherwake to send the host a WOL packet. .TP .B "Start_Command = " Specify the command to be executed when a client makes the correct -port-knock. All instances of \fB%IP%\fP will be replaced with the +port-knock with IPv4. All instances of \fB%IP%\fP will be replaced with the knocker's IP address. The \fBCommand\fP directive is an alias for \fBStart_Command\fP. .TP +.B "Start_Command_6 = " +Specify the command to be executed when a client makes the correct +port-knock with IPv6. All instances of \fB%IP%\fP will be replaced with the +knocker's IP address. The \fBCommand_6\fP directive is an alias for +\fBStart_Command_6\fP. If not present it will automatically fallback onto +the same IPV4 \fBStart_Command\fP value. You can use empty value to force +doing nothing. +.TP .B "Cmd_Timeout = " Time to wait (in seconds) between \fBStart_Command\fP and \fBStop_Command\fP. This directive is optional, only required if \fBStop_Command\fP is used. @@ -193,6 +225,14 @@ This directive is optional, only required if \fBStop_Command\fP is used. Specify the command to be executed when \fBCmd_Timeout\fP seconds have passed since \fBStart_Command\fP has been executed. All instances of \fB%IP%\fP will be replaced with the knocker's IP address. This directive is optional. +.TP +.B "Stop_Command_6 = " +Specify the command to be executed when \fBCmd_Timeout\fP seconds have passed +since \fBStart_Command_6\fP has been executed. All instances of \fB%IP%\fP will +be replaced with the knocker's IP address. This directive is optional. +If not present it will automatically fallback onto the same IPV4 +\fBStop_Command\fP value. You can use empty value to force +doing nothing. .SH SECURITY NOTES Using the \fB-l\fP or \fB--lookup\fP commandline option to resolve DNS names for log entries may be a security risk! An attacker may find out the first port diff --git a/src/knock.c b/src/knock.c index 319e51d..bb225cd 100644 --- a/src/knock.c +++ b/src/knock.c @@ -1,7 +1,8 @@ /* * knock.c * - * Copyright (c) 2004-2012 by Judd Vinet + * Copyright (c) 2004-2018 by Judd Vinet + * Sebastien Valat * * 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 @@ -40,6 +41,10 @@ static char version[] = "0.7"; #define PROTO_TCP 1 #define PROTO_UDP 2 +#define IP_DEFAULT AF_UNSPEC +#define IP_V4 AF_INET +#define IP_V6 AF_INET6 + /* function prototypes */ void vprint(char *fmt, ...); void ver(); @@ -48,13 +53,17 @@ void usage(); int o_verbose = 0; int o_udp = 0; int o_delay = 0; +int o_ip = IP_DEFAULT; int main(int argc, char** argv) { int sd; - struct hostent* host; - struct sockaddr_in addr; int opt, optidx = 1; + struct addrinfo hints; + struct addrinfo *infoptr; + char ipname[256]; + int result; + char * hostname; static struct option opts[] = { {"verbose", no_argument, 0, 'v'}, @@ -62,10 +71,12 @@ int main(int argc, char** argv) {"delay", required_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'V'}, + {"ipv4", no_argument, 0, '4'}, + {"ipv6", no_argument, 0, '6'}, {0, 0, 0, 0} }; - while((opt = getopt_long(argc, argv, "vud:hV", opts, &optidx))) { + while((opt = getopt_long(argc, argv, "vud:hV46", opts, &optidx))) { if(opt < 0) { break; } @@ -75,6 +86,8 @@ int main(int argc, char** argv) case 'u': o_udp = 1; break; case 'd': o_delay = (int)atoi(optarg); break; case 'V': ver(); + case '4': o_ip = IP_V4; break; + case '6': o_ip = IP_V6; break; case 'h': /* fallthrough */ default: usage(); } @@ -88,18 +101,20 @@ int main(int argc, char** argv) exit(1); } - host = gethostbyname(argv[optind++]); - if(host == NULL) { - fprintf(stderr, "Cannot resolve hostname\n"); - exit(1); - } + //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++) { - unsigned short port, proto = PROTO_TCP; + const char * port; + unsigned short proto = PROTO_TCP; char *ptr, *arg = strdup(argv[optind]); + //select TCP/UDP protocol if((ptr = strchr(arg, ':'))) { *ptr = '\0'; - port = atoi(arg); + port = arg; arg = ++ptr; if(!strcmp(arg, "udp")) { proto = PROTO_UDP; @@ -107,18 +122,27 @@ int main(int argc, char** argv) proto = PROTO_TCP; } } else { - port = atoi(arg); + port = arg; + } + + //get host and port based on hints + result = getaddrinfo(hostname, port, &hints, &infoptr); + if(result) { + fprintf(stderr, "Fail to resolve hostname '%s' on port %s\n",hostname,port); + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result)); + exit(1); } + //create socket if(o_udp || proto == PROTO_UDP) { - sd = socket(PF_INET, SOCK_DGRAM, 0); + sd = socket(infoptr->ai_family, SOCK_DGRAM, 0); if(sd == -1) { fprintf(stderr, "Cannot open socket\n"); exit(1); } } else { int flags; - sd = socket(PF_INET, SOCK_STREAM, 0); + sd = socket(infoptr->ai_family, SOCK_STREAM, 0); if(sd == -1) { fprintf(stderr, "Cannot open socket\n"); exit(1); @@ -126,19 +150,27 @@ int main(int argc, char** argv) flags = fcntl(sd, F_GETFL, 0); fcntl(sd, F_SETFL, flags | O_NONBLOCK); } - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = *((long*)host->h_addr_list[0]); - addr.sin_port = htons(port); + + //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 if(o_udp || proto == PROTO_UDP) { - vprint("hitting udp %s:%u\n", inet_ntoa(addr.sin_addr), port); - sendto(sd, "", 1, 0, (struct sockaddr*)&addr, sizeof(addr)); + vprint("hitting udp %s:%s\n", ipname, port); + sendto(sd, "", 1, 0, infoptr->ai_addr, infoptr->ai_addrlen); } else { - vprint("hitting tcp %s:%u\n", inet_ntoa(addr.sin_addr), port); - connect(sd, (struct sockaddr*)&addr, sizeof(struct sockaddr)); + vprint("hitting tcp %s:%s\n", ipname, port); + connect(sd, infoptr->ai_addr, infoptr->ai_addrlen); } + + //close socket close(sd); + + //wait delay usleep(1000*o_delay); + + //free temp mem + freeaddrinfo(infoptr); } return(0); @@ -160,6 +192,8 @@ void usage() { printf("options:\n"); printf(" -u, --udp make all ports hits use UDP (default is TCP)\n"); printf(" -d, --delay wait milliseconds between port hits\n"); + printf(" -4, --ipv4 Force usage of IPv4\n"); + printf(" -6, --ipv6 Force usage of IPv6\n"); printf(" -v, --verbose be verbose\n"); printf(" -V, --version display version\n"); printf(" -h, --help this help\n"); diff --git a/src/knockd.c b/src/knockd.c index 70515b5..55e595c 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1,7 +1,9 @@ /* * knockd.c * - * Copyright (c) 2004-2012 by Judd Vinet + * Copyright (c) 2004-2018 by Judd Vinet + * Sebastien Valat + * TDFKAOlli * * 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 @@ -40,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -83,8 +86,10 @@ typedef struct opendoor { char *target; time_t seq_timeout; char *start_command; + char *start_command6; time_t cmd_timeout; char *stop_command; + char *stop_command6; flag_stat flag_fin; flag_stat flag_syn; flag_stat flag_rst; @@ -93,6 +98,7 @@ typedef struct opendoor { flag_stat flag_urg; FILE *one_time_sequences_fd; char *pcap_filter_exp; + char *pcap_filter_expv6; } opendoor_t; PMList *doors = NULL; @@ -102,9 +108,10 @@ PMList *doors = NULL; typedef struct knocker { opendoor_t *door; short stage; - char src[16]; /* IP address */ + char src[64]; /* IP address */ char *srchost; /* Hostname */ time_t seq_start; + int fromIpV6; } knocker_t; PMList *attempts = NULL; @@ -139,11 +146,13 @@ int target_strcmp(char *ip, char *target); pcap_t *cap = NULL; FILE *logfd = NULL; int lltype = -1; +int hasIpV6 = 0; /* list of IP addresses for given interface */ typedef struct ip_literal { struct ip_literal *next; char *value; + int isIpV6; } ip_literal_t; ip_literal_t *myips = NULL; @@ -152,6 +161,7 @@ int o_verbose = 0; int o_debug = 0; int o_daemon = 0; int o_lookup = 0; +int o_skipIpV6 = 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"; @@ -175,11 +185,12 @@ int main(int argc, char **argv) {"help", no_argument, 0, 'h'}, {"pidfile", required_argument, 0, 'p'}, {"logfile", required_argument, 0, 'g'}, + {"only-ip-v4",no_argument, 0, '4'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; - while((opt = getopt_long(argc, argv, "vDdli:c:p:g:hV", opts, &optidx))) { + while((opt = getopt_long(argc, argv, "4vDdli:c:p:g:hV", opts, &optidx))) { if(opt < 0) { break; } @@ -189,6 +200,7 @@ 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 'i': strncpy(o_int, optarg, sizeof(o_int)-1); o_int[sizeof(o_int)-1] = '\0'; break; @@ -265,7 +277,9 @@ 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)) { + 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 (ifa->ifa_addr->sa_family == AF_INET6) + hasIpV6 = 1; if((myip = calloc(1, sizeof(ip_literal_t))) == NULL) { perror("malloc"); exit(1); @@ -273,11 +287,17 @@ int main(int argc, char **argv) perror("malloc"); exit(1); } else { - if(getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), myip->value, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) { + size_t size = (ifa->ifa_addr->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in); + myip->isIpV6 = (ifa->ifa_addr->sa_family == AF_INET6) ? 1 : 0; + + if(getnameinfo(ifa->ifa_addr, size, myip->value, NI_MAXHOST, NULL, 0, NI_NUMERICHOST) != 0) { fprintf(stderr, "error: could not get IP address for %s: %s\n", o_int, strerror(errno)); freeifaddrs(ifaddr); cleanup(1); } else { + char * ptr = strchr(myip->value,'%'); + if (ptr != NULL) + *ptr = '\0'; if(myips) myip->next = myips; myips = myip; @@ -418,6 +438,7 @@ void cleanup(int signum) free(myip->value); myips = myip->next; free(myip); + myip = myips; } } @@ -487,6 +508,7 @@ void usage(int exit_code) { printf(" -p, --pidfile use an alternate pidfile\n"); 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(" -V, --version display version\n"); printf(" -h, --help this help\n"); printf("\n"); @@ -581,8 +603,10 @@ int parseconfig(char *configfile) door->seqcount = 0; door->seq_timeout = SEQ_TIMEOUT; /* default sequence timeout (seconds) */ door->start_command = NULL; + door->start_command6 = NULL; door->cmd_timeout = CMD_TIMEOUT; /* default command timeout (seconds) */ door->stop_command = NULL; + door->stop_command6 = NULL; door->flag_fin = DONT_CARE; door->flag_syn = DONT_CARE; door->flag_rst = DONT_CARE; @@ -591,6 +615,7 @@ int parseconfig(char *configfile) door->flag_urg = DONT_CARE; door->one_time_sequences_fd = NULL; door->pcap_filter_exp = NULL; + door->pcap_filter_expv6 = NULL; doors = list_add(doors, door); } } else { @@ -681,6 +706,14 @@ int parseconfig(char *configfile) } strcpy(door->start_command, ptr); dprint("config: %s: start_command: %s\n", door->name, door->start_command); + } else if(!strcmp(key, "START_COMMAND_6") || !strcmp(key, "COMMAND_6")) { + door->start_command6 = malloc(sizeof(char) * (strlen(ptr)+1)); + if(door->start_command6 == NULL) { + perror("malloc"); + exit(1); + } + strcpy(door->start_command6, ptr); + dprint("config: %s: start_command_6: %s\n", door->name, door->start_command6); } else if(!strcmp(key, "CMD_TIMEOUT")) { door->cmd_timeout = (time_t)atoi(ptr); dprint("config: %s: cmd_timeout: %d\n", door->name, door->cmd_timeout); @@ -692,6 +725,14 @@ int parseconfig(char *configfile) } strcpy(door->stop_command, ptr); dprint("config: %s: stop_command: %s\n", door->name, door->stop_command); + } else if(!strcmp(key, "STOP_COMMAND_6")) { + door->stop_command6 = malloc(sizeof(char) * (strlen(ptr)+1)); + if(door->stop_command6 == NULL) { + perror("malloc"); + exit(1); + } + strcpy(door->stop_command6, ptr); + dprint("config: %s: stop_command_6: %s\n", door->name, door->stop_command6); } else if(!strcmp(key, "TCPFLAGS")) { char *flag; strtoupper(ptr); @@ -898,174 +939,215 @@ void generate_pcap_filter() unsigned int i; short modified_filters = 0; /* flag indicating if at least one filter has changed --> recompile the filter */ struct bpf_program bpf_prog; /* compiled BPF filter program */ + int ipv6; /* generate subfilters for each door having a NULL pcap_filter_exp * * Example filter for one single door: * ((tcp dst port 8000 or 8001 or 8002) and tcp[tcpflags] & tcp-syn != 0) or (udp dst port 4000 or 4001) */ - for(lp = doors; lp; lp = lp->next) { - door = (opendoor_t*)lp->data; - - if(door->pcap_filter_exp != NULL) { + for (ipv6 = 0 ; ipv6 <=1 ; ipv6++) + { + if (ipv6 && o_skipIpV6) continue; - } - - /* if we get here at least one door had a pcap_filter_exp == NULL */ - modified_filters = 1; - head_set = 0; - tcp_present = 0; - udp_present = 0; + for(lp = doors; lp; lp = lp->next) { + door = (opendoor_t*)lp->data; - /* allocate memory for buffer if needed. - * The first allocation will be 200 Bytes (should be large enough for common sequences). If there is - * not enough space, a call to realloc_strcat() will eventually increase its size. The buffer will be - * reused for successive doors */ - if(buffer == NULL) { - bufsize = 200; - buffer = (char*)malloc(sizeof(char) * bufsize); - if(buffer == NULL) { - perror("malloc"); - cleanup(1); + if(ipv6 == 0 && door->pcap_filter_exp != NULL) { + continue; } - buffer[0] = '\0'; - } - - /* accept only incoming packets */ - for(myip = myips; myip != NULL; myip = myip->next) { - if(!head_set) { - bufsize = realloc_strcat(&buffer, "((dst host ", bufsize); - head_set = 1; - } else { - bufsize = realloc_strcat(&buffer, " or dst host ", bufsize); + if(ipv6 == 1 && door->pcap_filter_expv6 != NULL) { + continue; } - bufsize = realloc_strcat(&buffer, door->target ? door->target : myip->value, bufsize); - } - bufsize = realloc_strcat(&buffer, ") and (", bufsize); - head_set = 0; + /* if we get here at least one door had a pcap_filter_exp == NULL */ + modified_filters = 1; - /* generate filter for all TCP ports (i.e. "((tcp dst port 4000 or 4001 or 4002) and tcp[tcpflags] & tcp-syn != 0)" */ - for(i = 0; i < door->seqcount; i++) { - if(door->protocol[i] == IPPROTO_TCP) { - if(!head_set) { /* first TCP port in the sequence */ - bufsize = realloc_strcat(&buffer, "((tcp dst port ", bufsize); - head_set = 1; - tcp_present = 1; - } else { /* not the first TCP port in the sequence */ - bufsize = realloc_strcat(&buffer, " or ", bufsize); + head_set = 0; + tcp_present = 0; + udp_present = 0; + + /* allocate memory for buffer if needed. + * The first allocation will be 200 Bytes (should be large enough for common sequences). If there is + * not enough space, a call to realloc_strcat() will eventually increase its size. The buffer will be + * reused for successive doors */ + if(buffer == NULL) { + bufsize = 200; + buffer = (char*)malloc(sizeof(char) * bufsize); + if(buffer == NULL) { + perror("malloc"); + cleanup(1); } - snprintf(port_str, sizeof(port_str), "%hu", door->sequence[i]); /* unsigned short to string */ - bufsize = realloc_strcat(&buffer, port_str, bufsize); /* append port number */ + buffer[0] = '\0'; } - } - if(tcp_present) { - bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of TCP ports */ - } - /* append the TCP flag filters */ - if(tcp_present) { - if(door->flag_fin != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-fin ", bufsize); - if(door->flag_fin == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); - } - if(door->flag_fin == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); + /* accept only incoming packets */ + for(myip = myips; myip != NULL; myip = myip->next) { + if (myip->isIpV6 != ipv6) + continue; + if(!head_set) { + bufsize = realloc_strcat(&buffer, "((dst host ", bufsize); + head_set = 1; + } else { + bufsize = realloc_strcat(&buffer, " or dst host ", bufsize); } + bufsize = realloc_strcat(&buffer, door->target ? door->target : myip->value, bufsize); } - if(door->flag_syn != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-syn ", bufsize); - if(door->flag_syn == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); - } - if(door->flag_syn == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); + + bufsize = realloc_strcat(&buffer, ") and (", bufsize); + head_set = 0; + + /* generate filter for all TCP ports (i.e. "((tcp dst port 4000 or 4001 or 4002) and tcp[tcpflags] & tcp-syn != 0)" */ + for(i = 0; i < door->seqcount; i++) { + if(door->protocol[i] == IPPROTO_TCP) { + if(!head_set) { /* first TCP port in the sequence */ + bufsize = realloc_strcat(&buffer, "((tcp dst port ", bufsize); + head_set = 1; + tcp_present = 1; + } else { /* not the first TCP port in the sequence */ + bufsize = realloc_strcat(&buffer, " or ", bufsize); + } + snprintf(port_str, sizeof(port_str), "%hu", door->sequence[i]); /* unsigned short to string */ + bufsize = realloc_strcat(&buffer, port_str, bufsize); /* append port number */ } } - if(door->flag_rst != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-rst ", bufsize); - if(door->flag_rst == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); - } - if(door->flag_rst == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); - } + if(tcp_present) { + bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of TCP ports */ } - if(door->flag_psh != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-push ", bufsize); - if(door->flag_psh == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + + /* append the TCP flag filters */ + if(tcp_present) { + if(door->flag_fin != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-fin ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-fin ", bufsize); + if(door->flag_fin == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_fin == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } - if(door->flag_psh == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); + if(door->flag_syn != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-syn ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-syn ", bufsize); + if(door->flag_syn == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_syn == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } - } - if(door->flag_ack != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-ack ", bufsize); - if(door->flag_ack == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + if(door->flag_rst != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-rst ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-rst ", bufsize); + if(door->flag_rst == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_rst == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } - if(door->flag_ack == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); + if(door->flag_psh != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-push ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-push ", bufsize); + if(door->flag_psh == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_psh == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } - } - if(door->flag_urg != DONT_CARE) { - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-urg ", bufsize); - if(door->flag_urg == SET) { - bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + if(door->flag_ack != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-ack ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-ack ", bufsize); + if(door->flag_ack == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_ack == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } - if(door->flag_urg == NOT_SET) { - bufsize = realloc_strcat(&buffer, "== 0", bufsize); + if(door->flag_urg != DONT_CARE) { + if (ipv6) + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-urg ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + else + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-urg ", bufsize); + if(door->flag_urg == SET) { + bufsize = realloc_strcat(&buffer, "!= 0", bufsize); + } + if(door->flag_urg == NOT_SET) { + bufsize = realloc_strcat(&buffer, "== 0", bufsize); + } } + bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of flags */ } - bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of flags */ - } - /* append filter for all UDP ports (i.e. "(udp dst port 6543 or 6544 or 6545)" */ - head_set = 0; - for(i = 0; i < door->seqcount; i++) { - if(door->protocol[i] == IPPROTO_UDP) { - if(!head_set) { /* first UDP port in the sequence */ - if(tcp_present) { + /* append filter for all UDP ports (i.e. "(udp dst port 6543 or 6544 or 6545)" */ + head_set = 0; + for(i = 0; i < door->seqcount; i++) { + if(door->protocol[i] == IPPROTO_UDP) { + if(!head_set) { /* first UDP port in the sequence */ + if(tcp_present) { + bufsize = realloc_strcat(&buffer, " or ", bufsize); + } + bufsize = realloc_strcat(&buffer, "(udp dst port ", bufsize); + head_set = 1; + udp_present = 1; + } else { /* not the first UDP port in the sequence */ bufsize = realloc_strcat(&buffer, " or ", bufsize); } - bufsize = realloc_strcat(&buffer, "(udp dst port ", bufsize); - head_set = 1; - udp_present = 1; - } else { /* not the first UDP port in the sequence */ - bufsize = realloc_strcat(&buffer, " or ", bufsize); + snprintf(port_str, sizeof(port_str), "%hu", door->sequence[i]); /* unsigned short to string */ + bufsize = realloc_strcat(&buffer, port_str, bufsize); /* append port number */ } - snprintf(port_str, sizeof(port_str), "%hu", door->sequence[i]); /* unsigned short to string */ - bufsize = realloc_strcat(&buffer, port_str, bufsize); /* append port number */ } - } - if(udp_present) { - bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of UDP ports */ - } + if(udp_present) { + bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of UDP ports */ + } - bufsize = realloc_strcat(&buffer, "))", bufsize); /* close parantheses around port filters */ + bufsize = realloc_strcat(&buffer, "))", bufsize); /* close parantheses around port filters */ - /* test if in any of the precedent calls to realloc_strcat() failed. We can do this safely here because - * realloc_strcat() returns 0 on failure and if a buffer size of 0 is passed to it, the function does - * nothing but returning 0 again. Because we never read buffer in the above code, it is secure to test - * for failure only at this point (it makes the code more readable than checking for failure each time - * realloc_strcat() is called). */ - if(bufsize == 0) { - perror("realloc"); - cleanup(1); - } + /* test if in any of the precedent calls to realloc_strcat() failed. We can do this safely here because + * realloc_strcat() returns 0 on failure and if a buffer size of 0 is passed to it, the function does + * nothing but returning 0 again. Because we never read buffer in the above code, it is secure to test + * for failure only at this point (it makes the code more readable than checking for failure each time + * realloc_strcat() is called). */ + if(bufsize == 0) { + perror("realloc"); + cleanup(1); + } - /* allocate the buffer in door holding the filter string, copy it and prepare buffer for being reused. */ - door->pcap_filter_exp = (char*)malloc(strlen(buffer) + 1); - if(door->pcap_filter_exp == NULL) { - perror("malloc"); - cleanup(1); + /* allocate the buffer in door holding the filter string, copy it and prepare buffer for being reused. */ + if (ipv6) + { + door->pcap_filter_expv6 = (char*)malloc(strlen(buffer) + 1); + if(door->pcap_filter_expv6 == NULL) { + perror("malloc"); + cleanup(1); + } + strcpy(door->pcap_filter_expv6, buffer); + 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) { + perror("malloc"); + cleanup(1); + } + strcpy(door->pcap_filter_exp, buffer); + dprint("Adding pcap expression for door '%s': %s\n", door->name, door->pcap_filter_exp); + } + buffer[0] = '\0'; /* "clear" the buffer */ } - strcpy(door->pcap_filter_exp, buffer); - dprint("Adding pcap expression for door '%s': %s\n", door->name, door->pcap_filter_exp); - buffer[0] = '\0'; /* "clear" the buffer */ } @@ -1083,14 +1165,26 @@ void generate_pcap_filter() */ if(modified_filters) { /* iterate over all doors */ + int first = 1; for(lp = doors; lp; lp = lp->next) { door = (opendoor_t*)lp->data; - bufsize = realloc_strcat(&buffer, door->pcap_filter_exp, bufsize); - if(lp->next != NULL) { - bufsize = realloc_strcat(&buffer, " or ", bufsize); + for (ipv6 = 0 ; ipv6 <= 1 ; ipv6++) + { + if (first) + first = 0; + else + bufsize = realloc_strcat(&buffer, " or ", bufsize); + if (ipv6 && o_skipIpV6) + continue; + if (ipv6) + bufsize = realloc_strcat(&buffer, door->pcap_filter_expv6, bufsize); + else + bufsize = realloc_strcat(&buffer, door->pcap_filter_exp, bufsize); } } + //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) { @@ -1099,11 +1193,11 @@ void generate_pcap_filter() } if(pcap_compile(cap, &bpf_prog, buffer, 1, 0) < 0) { /* optimize filter (1), no netmask (0) (we're not interested in broadcasts) */ - pcap_perror(cap, "pcap"); + pcap_perror(cap, "pcap_compile"); cleanup(1); } if(pcap_setfilter(cap, &bpf_prog) < 0) { - pcap_perror(cap, "pcap"); + pcap_perror(cap, "pcap_setfilter"); cleanup(1); } pcap_freecode(&bpf_prog); @@ -1292,12 +1386,12 @@ int exec_cmd(char* command, char* name){ * If examining a TCP packet, try to match flags against those in * the door config. */ -int flags_match(opendoor_t* door, struct ip* ip, struct tcphdr* tcp) +int flags_match(opendoor_t* door, int ipTPoto, struct tcphdr* tcp) { /* if tcp, check the flags to ignore the packets we don't want * (don't even use it to cancel sequences) */ - if(ip->ip_p == IPPROTO_TCP) { + if(ipTPoto == IPPROTO_TCP) { if(door->flag_fin != DONT_CARE) { if(door->flag_fin == SET && !(tcp->th_flags & TH_FIN)) { dprint("packet is not FIN, ignoring...\n"); @@ -1369,6 +1463,28 @@ int flags_match(opendoor_t* door, struct ip* ip, struct tcphdr* tcp) */ void process_attempt(knocker_t *attempt) { + //select + char * start_command; + char * stop_command; + + //select + if (attempt->fromIpV6) + { + start_command = attempt->door->start_command6; + stop_command = attempt->door->stop_command6; + + //make default fallback to v4 command if v6 command is not set. + if (start_command == NULL) { + start_command = attempt->door->start_command; + } + if (stop_command == NULL) { + stop_command = attempt->door->stop_command; + } + } else { + start_command = attempt->door->start_command; + stop_command = attempt->door->stop_command; + } + /* level up! */ attempt->stage++; if(attempt->srchost) { @@ -1386,7 +1502,7 @@ void process_attempt(knocker_t *attempt) vprint("%s: %s: OPEN SESAME\n", attempt->src, attempt->door->name); logprint("%s: %s: OPEN SESAME", attempt->src, attempt->door->name); } - if(attempt->door->start_command && strlen(attempt->door->start_command)) { + if(start_command && strlen(start_command)) { /* run the associated command */ if(fork() == 0) { /* child */ @@ -1398,14 +1514,14 @@ void process_attempt(knocker_t *attempt) /* parse start and stop command and check if the parsed commands fit in the given buffer. Don't * execute any command if one of them has been truncated */ - cmd_len = parse_cmd(parsed_start_cmd, sizeof(parsed_start_cmd), attempt->door->start_command, attempt->src); + cmd_len = parse_cmd(parsed_start_cmd, sizeof(parsed_start_cmd), start_command, attempt->src); if(cmd_len >= sizeof(parsed_start_cmd)) { /* command has been truncated --> do NOT execute it */ fprintf(stderr, "error: parsed start command has been truncated! --> won't execute it\n"); logprint("error: parsed start command has been truncated! --> won't execute it"); exit(0); /* exit child */ } - if(attempt->door->stop_command) { - cmd_len = parse_cmd(parsed_stop_cmd, sizeof(parsed_stop_cmd), attempt->door->stop_command, attempt->src); + if(stop_command) { + cmd_len = parse_cmd(parsed_stop_cmd, sizeof(parsed_stop_cmd), stop_command, attempt->src); if(cmd_len >= sizeof(parsed_stop_cmd)) { /* command has been truncated --> do NOT execute it */ fprintf(stderr, "error: parsed stop command has been truncated! --> won't execute start command\n"); logprint("error: parsed stop command has been truncated! --> won't execute start command"); @@ -1416,7 +1532,7 @@ void process_attempt(knocker_t *attempt) /* all parsing ok --> execute the parsed (%IP% = source IP) command */ exec_cmd(parsed_start_cmd, attempt->door->name); /* if stop_command is set, sleep for cmd_timeout and run it*/ - if(attempt->door->stop_command){ + if(stop_command){ sleep(attempt->door->cmd_timeout); if(attempt->srchost) { vprint("%s (%s): %s: command timeout\n", attempt->src, attempt->srchost, attempt->door->name); @@ -1453,13 +1569,15 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) /* packet structs */ struct ether_header* eth = NULL; struct ip* ip = NULL; + struct ip6_hdr * ip6; struct tcphdr* tcp = NULL; struct udphdr* udp = NULL; char proto[8]; /* TCP/IP data */ struct in_addr inaddr; - unsigned short sport, dport; - char srcIP[16], dstIP[16]; + unsigned short sport = 0; + unsigned short dport = 0; + char srcIP[64], dstIP[64]; /* timestamp */ time_t pkt_secs = hdr->ts.tv_sec; struct tm* pkt_tm; @@ -1468,48 +1586,83 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) PMList *lp; knocker_t *attempt = NULL; PMList *found_attempts = NULL; + int ipProto = 0; + int fromIpV6 = 0; if(lltype == DLT_EN10MB) { eth = (struct ether_header*)packet; - if(ntohs(eth->ether_type) != ETHERTYPE_IP) { + if(ntohs(eth->ether_type) != ETHERTYPE_IP && ntohs(eth->ether_type) != ETHERTYPE_IPV6) { return; } ip = (struct ip*)(packet + sizeof(struct ether_header)); + ip6 = (struct ip6_hdr*)(packet + sizeof(struct ether_header)); #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; } - if(ip->ip_v != 4) { + if(ip->ip_v != 4 && ip->ip_v != 6) { /* no IPv6 yet */ - dprint("packet is not IPv4, ignoring...\n"); - return; - } - if(ip->ip_p == IPPROTO_ICMP) { - /* we don't do ICMP */ + dprint("packet is not IPv4 or IPv6, ignoring...\n"); return; } - sport = dport = 0; + if (ip->ip_v == 4) + { + if(ip->ip_p == IPPROTO_ICMP) { + /* we don't do ICMP */ + return; + } - if(ip->ip_p == IPPROTO_TCP) { - strncpy(proto, "tcp", sizeof(proto)); - tcp = (struct tcphdr*)((u_char*)ip + (ip->ip_hl *4)); - sport = ntohs(tcp->th_sport); - dport = ntohs(tcp->th_dport); - } - if(ip->ip_p == IPPROTO_UDP) { - strncpy(proto, "udp", sizeof(proto)); - udp = (struct udphdr*)((u_char*)ip + (ip->ip_hl * 4)); - sport = ntohs(udp->uh_sport); - dport = ntohs(udp->uh_dport); + sport = dport = 0; + fromIpV6 = 0; + ipProto = ip->ip_p; + + if(ip->ip_p == IPPROTO_TCP) { + strncpy(proto, "tcp", sizeof(proto)); + tcp = (struct tcphdr*)((u_char*)ip + (ip->ip_hl *4)); + sport = ntohs(tcp->th_sport); + dport = ntohs(tcp->th_dport); + } + if(ip->ip_p == IPPROTO_UDP) { + strncpy(proto, "udp", sizeof(proto)); + udp = (struct udphdr*)((u_char*)ip + (ip->ip_hl * 4)); + sport = ntohs(udp->uh_sport); + dport = ntohs(udp->uh_dport); + } + } else if (ip->ip_v == 6) { + //we accept only TCP/UDP + if(ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP && ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_UDP) { + /* we don't do ICMP */ + dprint("Unsupported IPv6 protocol\n"); + return; + } + + ipProto = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt; + fromIpV6 = 1; + sport = dport = 0; + + if(ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt == IPPROTO_TCP) { + strncpy(proto, "tcp", sizeof(proto)); + tcp = (struct tcphdr*)(ip6+1); + sport = ntohs(tcp->th_sport); + dport = ntohs(tcp->th_dport); + } + if(ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt == IPPROTO_UDP) { + strncpy(proto, "udp", sizeof(proto)); + udp = (struct udphdr*)(ip6+1); + sport = ntohs(udp->uh_sport); + dport = ntohs(udp->uh_dport); + } } /* get the date/time */ @@ -1520,12 +1673,20 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) pkt_tm->tm_sec); /* convert IPs from binary to string */ - inaddr.s_addr = ip->ip_src.s_addr; - strncpy(srcIP, inet_ntoa(inaddr), sizeof(srcIP)-1); - srcIP[sizeof(srcIP)-1] = '\0'; - inaddr.s_addr = ip->ip_dst.s_addr; - strncpy(dstIP, inet_ntoa(inaddr), sizeof(dstIP)-1); - dstIP[sizeof(dstIP)-1] = '\0'; + if (ip->ip_v == 4) { + inaddr.s_addr = ip->ip_src.s_addr; + strncpy(srcIP, inet_ntoa(inaddr), sizeof(srcIP)-1); + srcIP[sizeof(srcIP)-1] = '\0'; + inaddr.s_addr = ip->ip_dst.s_addr; + strncpy(dstIP, inet_ntoa(inaddr), sizeof(dstIP)-1); + dstIP[sizeof(dstIP)-1] = '\0'; + } else if (ip->ip_v == 6) { + inet_ntop(AF_INET6, &ip6->ip6_src, srcIP,sizeof(srcIP)); + inet_ntop(AF_INET6, &ip6->ip6_dst, dstIP,sizeof(srcIP)); + } else { + //dprint("Invalid protocol (not ipv4 or ipv6) !"); + return; + } dprint("%s %s: %s: %s:%d -> %s:%d %d bytes\n", pkt_date, pkt_time, proto, srcIP, sport, dstIP, dport, hdr->len); @@ -1607,8 +1768,8 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) attempt = (knocker_t*)found_attempts->data; if(attempt) { - int flagsmatch = flags_match(attempt->door, ip, tcp); - if(flagsmatch && ip->ip_p == attempt->door->protocol[attempt->stage] && + int flagsmatch = flags_match(attempt->door, ipProto, tcp); + if(flagsmatch && ipProto == attempt->door->protocol[attempt->stage] && dport == attempt->door->sequence[attempt->stage]) { process_attempt(attempt); } else if(flagsmatch == 0) { @@ -1626,10 +1787,10 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) for(lp = doors; lp; lp = lp->next) { opendoor_t *door = (opendoor_t*)lp->data; /* if we're working with TCP, try to match the flags */ - if(!flags_match(door, ip, tcp)) { + if(!flags_match(door, ipProto, tcp)) { continue; } - if(ip->ip_p == door->protocol[0] && dport == door->sequence[0] && + if(ipProto == door->protocol[0] && dport == door->sequence[0] && !target_strcmp(dstIP, door->target)) { struct hostent *he; /* create a new entry */ @@ -1639,11 +1800,17 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) perror("malloc"); exit(1); } + attempt->fromIpV6 = fromIpV6; strcpy(attempt->src, srcIP); /* try a reverse lookup if enabled */ if (o_lookup) { - inaddr.s_addr = ip->ip_src.s_addr; - he = gethostbyaddr((void *)&inaddr, sizeof(inaddr), AF_INET); + if (fromIpV6 == 0) + { + inaddr.s_addr = ip->ip_src.s_addr; + he = gethostbyaddr((void *)&inaddr, sizeof(inaddr), AF_INET); + } else { + he = gethostbyaddr((void *)&ip6->ip6_src, sizeof(ip6->ip6_src), AF_INET6); + } if(he) { attempt->srchost = strdup(he->h_name); } From e9b9cf5006a6dffce1634630b2481234482e65f2 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Mon, 31 Dec 2018 16:17:57 +0100 Subject: [PATCH 02/20] Merged fix from jvinet/knock#52 by Marius Hoch to master Updated copyright notes --- src/knockd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/knockd.c b/src/knockd.c index 55e595c..a268e6d 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -3,6 +3,7 @@ * * Copyright (c) 2004-2018 by Judd Vinet * Sebastien Valat + * Marius Hoch * TDFKAOlli * * This program is free software; you can redistribute it and/or modify @@ -1795,11 +1796,11 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet) struct hostent *he; /* create a new entry */ attempt = (knocker_t*)malloc(sizeof(knocker_t)); - attempt->srchost = NULL; if(attempt == NULL) { perror("malloc"); exit(1); } + attempt->srchost = NULL; attempt->fromIpV6 = fromIpV6; strcpy(attempt->src, srcIP); /* try a reverse lookup if enabled */ From 91bb0b2f70e4302ba2c36d9f055c48c6368a7dbe Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 2 Jan 2019 12:47:10 +0100 Subject: [PATCH 03/20] Fix tcp-psh filter setting (tcp-push => tcp-psh) in pcap filter Bump version to 8.0 (due to major IPv6 feature) Update copyright notes --- src/knock.c | 7 ++++--- src/knockd.c | 20 ++++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/knock.c b/src/knock.c index bb225cd..49a98be 100644 --- a/src/knock.c +++ b/src/knock.c @@ -1,7 +1,7 @@ /* * knock.c * - * Copyright (c) 2004-2018 by Judd Vinet + * Copyright (c) 2004-2019 by Judd Vinet * Sebastien Valat * * This program is free software; you can redistribute it and/or modify @@ -36,7 +36,7 @@ #include #include -static char version[] = "0.7"; +static char version[] = "0.8"; #define PROTO_TCP 1 #define PROTO_UDP 2 @@ -205,7 +205,8 @@ void usage() { void ver() { printf("knock %s\n", version); - printf("Copyright (C) 2004-2012 Judd Vinet \n"); + printf("Copyright (C) 2004-2019 Judd Vinet ," + " Sebastien Valat \n"); exit(0); } diff --git a/src/knockd.c b/src/knockd.c index a268e6d..d2146ac 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1,7 +1,7 @@ /* * knockd.c * - * Copyright (c) 2004-2018 by Judd Vinet + * Copyright (c) 2004-2019 by Judd Vinet * Sebastien Valat * Marius Hoch * TDFKAOlli @@ -66,7 +66,7 @@ extern int daemon(int, int); #endif -static char version[] = "0.7.8"; +static char version[] = "0.8"; #define SEQ_TIMEOUT 25 /* default knock timeout in seconds */ #define CMD_TIMEOUT 10 /* default timeout in seconds between start and stop commands */ @@ -190,7 +190,7 @@ int main(int argc, char **argv) {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; - + while((opt = getopt_long(argc, argv, "4vDdli:c:p:g:hV", opts, &optidx))) { if(opt < 0) { break; @@ -518,7 +518,11 @@ void usage(int exit_code) { void ver() { printf("knockd %s\n", version); - printf("Copyright (C) 2004-2012 Judd Vinet \n"); + printf("Copyright (C) 2004-2019 Judd Vinet ," + " Sebastien Valat ," + " Marius Hoch ," + " TDFKAOlli " + "\n"); exit(0); } @@ -931,7 +935,7 @@ void generate_pcap_filter() PMList *lp; opendoor_t *door; ip_literal_t *myip; - char *buffer = NULL; /* temporary buffer to create the individual filter strings */ + char *buffer = NULL; /* temporary buffer to create the individual filter strings */ size_t bufsize = 0; /* size of buffer */ char port_str[10]; /* used by snprintf to convert unsigned short --> string */ short head_set = 0; /* flag indicating if protocol head is set (i.e. "((tcp dst port") */ @@ -1057,9 +1061,9 @@ void generate_pcap_filter() } if(door->flag_psh != DONT_CARE) { if (ipv6) - bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-push ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-psh ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 else - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-push ", bufsize); + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-psh ", bufsize); if(door->flag_psh == SET) { bufsize = realloc_strcat(&buffer, "!= 0", bufsize); } @@ -1157,7 +1161,7 @@ void generate_pcap_filter() * Note that we don't check if a port is included in multiple doors, we simply concatenate the individual door * filters and rely on pcap's optimization capabilities. * - * Example filter for two doors with sequences 8000:tcp,4000:udp,8001:tcp,4001:udp,8002:tcp (syn) and + * Example filter for two doors with sequences 8000:tcp,4000:udp,8001:tcp,4001:udp,8002:tcp (syn) and * 1234:tcp,4567:tcp,8901:tcp (syn,ack) : * dst host the.hosts.ip.address and ( * ((tcp dst port 8000 or 8001 or 8002) and tcp[tcpflags] & tcp-syn != 0) or (udp dst port 4000 or 4001) From 7c00e40583dee1bebff38131d004e81f70546088 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Fri, 4 Jan 2019 16:25:40 +0100 Subject: [PATCH 04/20] Fixed some mem-leaks introduced with ipv6 Fixed handling of v6 pcap filter rule recreation in case of one time sequences --- configure.ac | 2 +- src/knockd.c | 25 +++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 02e3012..1d6acb0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT([knock], [0.7.8], [https://github.com/jvinet/knock/issues]) +AC_INIT([knock], [0.8], [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/src/knockd.c b/src/knockd.c index d2146ac..b80b66a 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -792,6 +792,10 @@ int parseconfig(char *configfile) fprintf(stderr, "error: section '%s' has an empty knock sequence\n", door->name); return(1); } + if(door->start_command == NULL && door->start_command6 == NULL) { + fprintf(stderr, "error: section '%s' has no start command\n", door->name); + return(1); + } } return(0); @@ -1257,12 +1261,25 @@ void close_door(opendoor_t *door) doors = list_remove(doors, door); if(door) { free(door->target); - free(door->start_command); - free(door->stop_command); + if(door->start_command) { + free(door->start_command); + } + if(door->start_command6) { + free(door->start_command6); + } + if(door->stop_command) { + free(door->stop_command); + } + if(door->stop_command6) { + free(door->stop_command6); + } if (door->one_time_sequences_fd) { fclose(door->one_time_sequences_fd); } free(door->pcap_filter_exp); + if(door->pcap_filter_expv6) { + free(door->pcap_filter_expv6); + } free(door); } } @@ -1560,6 +1577,10 @@ void process_attempt(knocker_t *attempt) get_new_one_time_sequence(attempt->door); /* update pcap filter */ + if(attempt->door->pcap_filter_expv6) { + free(attempt->door->pcap_filter_expv6); + attempt->door->pcap_filter_expv6 = NULL; + } free(attempt->door->pcap_filter_exp); attempt->door->pcap_filter_exp = NULL; generate_pcap_filter(); From 4c3c5c503a85829196f4420ca19eb24dbe188288 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Sun, 10 Feb 2019 18:20:54 +0100 Subject: [PATCH 05/20] Remove declaration non-existing function runCommand() --- src/knockd.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/knockd.c b/src/knockd.c index b80b66a..2a958ed 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -128,7 +128,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); From 2ac7d47e643ad4bf6805e2881e13803a96505c50 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Sun, 10 Feb 2019 18:26:14 +0100 Subject: [PATCH 06/20] Add patch for ipv6 only interfaces from jvinet/knock #58 (from pull request #1 from primeos/fix-ipv6-only --- src/knockd.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index 2a958ed..78f73db 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -146,6 +146,7 @@ int target_strcmp(char *ip, char *target); pcap_t *cap = NULL; FILE *logfd = NULL; int lltype = -1; +int hasIpV4 = 0; int hasIpV6 = 0; /* list of IP addresses for given interface */ @@ -278,6 +279,8 @@ int main(int argc, char **argv) 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 (ifa->ifa_addr->sa_family == AF_INET) + hasIpV4 = 1; if (ifa->ifa_addr->sa_family == AF_INET6) hasIpV6 = 1; if((myip = calloc(1, sizeof(ip_literal_t))) == NULL) { @@ -956,6 +959,11 @@ void generate_pcap_filter() */ for (ipv6 = 0 ; ipv6 <=1 ; ipv6++) { + if (ipv6 == 0 && !hasIpV4) + continue; + if (ipv6 == 1 && !hasIpV6) + continue; + if (ipv6 && o_skipIpV6) continue; @@ -1178,12 +1186,18 @@ void generate_pcap_filter() door = (opendoor_t*)lp->data; for (ipv6 = 0 ; ipv6 <= 1 ; ipv6++) { + if (ipv6 == 0 && !hasIpV4) + continue; + if (ipv6 == 1 && !hasIpV6) + continue; + + if (ipv6 && o_skipIpV6) + continue; + if (first) first = 0; else bufsize = realloc_strcat(&buffer, " or ", bufsize); - if (ipv6 && o_skipIpV6) - continue; if (ipv6) bufsize = realloc_strcat(&buffer, door->pcap_filter_expv6, bufsize); else From 70fa5950c7cd49fb8f2941a5da024e73332a9e36 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Mon, 18 Mar 2019 23:41:12 +0100 Subject: [PATCH 07/20] Changed get_ip() from ioctl to getifaddr. Added save-mem (-s) option, which in case set will remove the generated filter strings. That reduces the allocated memory, but in case of one-time door sequences consumed requires all filters to be recreated in generate_pcap_filters(). Also take care of proper free() handling. --- src/knockd.c | 67 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index 78f73db..d0a23d6 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -163,6 +162,7 @@ int o_debug = 0; int o_daemon = 0; int o_lookup = 0; int o_skipIpV6 = 0; +int o_saveMem = 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"; @@ -187,11 +187,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; } @@ -202,6 +203,7 @@ int main(int argc, char **argv) case 'd': o_daemon = 1; break; case 'l': o_lookup = 1; break; case '4': o_skipIpV6 = 1; break; + case 's': o_saveMem = 1; break; case 'i': strncpy(o_int, optarg, sizeof(o_int)-1); o_int[sizeof(o_int)-1] = '\0'; break; @@ -512,6 +514,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"); @@ -1198,10 +1201,22 @@ void generate_pcap_filter() 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_saveMem == 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_saveMem == 1) { + // save memory and free door specific filter here directly + free(door->pcap_filter_exp); + door->pcap_filter_exp = NULL; + } + } } } @@ -1289,7 +1304,9 @@ void close_door(opendoor_t *door) if (door->one_time_sequences_fd) { fclose(door->one_time_sequences_fd); } - free(door->pcap_filter_exp); + if(door->pcap_filter_exp) { + free(door->pcap_filter_exp); + } if(door->pcap_filter_expv6) { free(door->pcap_filter_expv6); } @@ -1301,8 +1318,7 @@ void close_door(opendoor_t *door) */ char* get_ip(const char* iface, char *buf, int bufsize) { - int s; - struct ifreq ifr; + struct ifaddrs *addrs=NULL,*tmp=NULL; if(bufsize <= 0) { return(NULL); @@ -1312,23 +1328,22 @@ char* get_ip(const char* iface, char *buf, int bufsize) } 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); + getifaddrs(&addrs); + tmp=addrs; + while(tmp) { + if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *addr_in = (struct sockaddr_in *)tmp->ifa_addr; + if (strcmp(iface,tmp->ifa_name) == 0 ) { + strncpy(buf, inet_ntoa(addr_in->sin_addr), bufsize-1); + buf[bufsize-1] = '\0'; + freeifaddrs(addrs); + return(buf); + } + } + tmp = tmp->ifa_next; } - close(s); - - strncpy(buf, inet_ntoa((*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr), bufsize-1); - buf[bufsize-1] = '\0'; - return(buf); + freeifaddrs(addrs); + return(NULL); } /* Parse a command line, replacing tokens (eg, %IP%) with their real values and @@ -1594,8 +1609,10 @@ void process_attempt(knocker_t *attempt) free(attempt->door->pcap_filter_expv6); attempt->door->pcap_filter_expv6 = NULL; } - free(attempt->door->pcap_filter_exp); - attempt->door->pcap_filter_exp = NULL; + if(attempt->door->pcap_filter_exp) { + free(attempt->door->pcap_filter_exp); + attempt->door->pcap_filter_exp = NULL; + } generate_pcap_filter(); } } From 92a7ae36d3be1e5a142ba6c0e0146d03fcf66dcb Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Mon, 18 Mar 2019 23:46:37 +0100 Subject: [PATCH 08/20] Add documentation of new flag. --- doc/knockd.1.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/knockd.1.in b/doc/knockd.1.in index 8813acd..2223a89 100644 --- a/doc/knockd.1.in +++ b/doc/knockd.1.in @@ -34,6 +34,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 From 1f77a3fd54e949b5c747dc80a3b2d77ba781c337 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Sun, 28 Jun 2020 22:58:32 +0200 Subject: [PATCH 09/20] Fix incorrect TCP ACK/URG flag NOT_SET testing logic in flags_match() fetched from jvinet/knock#68 created by catbref --- src/knockd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index d0a23d6..3937918 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1487,7 +1487,7 @@ int flags_match(opendoor_t* door, int ipTPoto, struct tcphdr* tcp) dprint("packet is not ACK, ignoring...\n"); return 0; } - if(door->flag_ack == NOT_SET && !(tcp->th_flags & TH_ACK)) { + if(door->flag_ack == NOT_SET && (tcp->th_flags & TH_ACK)) { dprint("packet is not !ACK, ignoring...\n"); return 0; } @@ -1497,7 +1497,7 @@ int flags_match(opendoor_t* door, int ipTPoto, struct tcphdr* tcp) dprint("packet is not URG, ignoring...\n"); return 0; } - if(door->flag_urg == NOT_SET && !(tcp->th_flags & TH_URG)) { + if(door->flag_urg == NOT_SET && (tcp->th_flags & TH_URG)) { dprint("packet is not !URG, ignoring...\n"); return 0; } From faee547a7e5d7cca1e682d4b91e7bfaa2b6b047a Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 15 Jul 2020 20:24:12 +0200 Subject: [PATCH 10/20] Bump version to 0.8.1 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1d6acb0..0eeaf0f 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.1], [https://github.com/jvinet/knock/issues]) AM_INIT_AUTOMAKE([dist-xz no-dist-gzip foreign subdir-objects]) AC_CONFIG_HEADER([config.h]) From 39c369366417b38b36e6bd3e31b19c2798086ed7 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Sun, 7 Feb 2021 21:16:36 +0100 Subject: [PATCH 11/20] Fixed use-after-free based on jvinet/knock#74 and jvinet/knock#75. --- src/knockd.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index 3937918..ef2b2f1 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -470,8 +470,10 @@ void reload(int signum) for(lp = doors; lp; lp = lp->next) { door = (opendoor_t*)lp->data; close_door(door); + lp->data = NULL; } list_free(doors); + doors = NULL; res_cfg = parseconfig(o_cfg); @@ -481,21 +483,22 @@ void reload(int signum) /* close the log file */ if(logfd) { fclose(logfd); + logfd = NULL; } if(res_cfg) { 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(); From 58a2ed4585af7c7bd9b19bfd82e3f2fa522c0a42 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Sun, 7 Feb 2021 21:39:10 +0100 Subject: [PATCH 12/20] bump to version 0.8.2 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 0eeaf0f..7abaad9 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ(2.60) -AC_INIT([knock], [0.8.1], [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]) From 04bd747f41aac928f98519376e5bae3be67c718a Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Tue, 22 Mar 2022 23:26:06 +0100 Subject: [PATCH 13/20] Fix build problems due to variable name changes on upstream. Renamed variables of new features to match overall naming style. Fix tcp-psh filter to tcp-push. --- src/knockd.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index b4100fa..ed60b6f 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -162,8 +162,8 @@ int o_verbose = 0; int o_debug = 0; int o_daemon = 0; int o_lookup = 0; -int o_skipIpV6 = 0; -int o_saveMem = 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"; @@ -203,8 +203,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 's': o_saveMem = 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; @@ -281,7 +281,7 @@ 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) && (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) @@ -980,7 +980,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) { @@ -1088,9 +1088,9 @@ void generate_pcap_filter() } if(door->flag_psh != DONT_CARE) { if(ipv6) - bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-psh ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 + bufsize = realloc_strcat(&buffer, " and ip6[13+40] & tcp-push ", bufsize);//using directly mask as pcap didn't yet support flags for IPv6 else - bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-psh ", bufsize); + bufsize = realloc_strcat(&buffer, " and tcp[tcpflags] & tcp-push ", bufsize); if(door->flag_psh == SET) { bufsize = realloc_strcat(&buffer, "!= 0", bufsize); } @@ -1212,12 +1212,12 @@ void generate_pcap_filter() door = (opendoor_t*)lp->data; for(ipv6 = 0 ; ipv6 <= 1 ; ipv6++) { - if(ipv6 == 0 && !hasIpV4) + if(ipv6 == 0 && !has_ipv4) continue; - if(ipv6 == 1 && !hasIpV6) + if(ipv6 == 1 && !has_ipv6) continue; - if(ipv6 && o_skipIpV6) + if(ipv6 && o_skip_ipv6) continue; if(first) @@ -1226,7 +1226,7 @@ void generate_pcap_filter() bufsize = realloc_strcat(&buffer, " or ", bufsize); if(ipv6) { bufsize = realloc_strcat(&buffer, door->pcap_filter_expv6, bufsize); - if(o_saveMem == 1) { + if(o_save_mem == 1) { // save memory and free door specific filter here directly free(door->pcap_filter_expv6); door->pcap_filter_expv6 = NULL; @@ -1234,7 +1234,7 @@ void generate_pcap_filter() } else { bufsize = realloc_strcat(&buffer, door->pcap_filter_exp, bufsize); - if(o_saveMem == 1) { + if(o_save_mem == 1) { // save memory and free door specific filter here directly free(door->pcap_filter_exp); door->pcap_filter_exp = NULL; From bd0b842993c871d20ba2e24cda32d4a56c717f81 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 23 Mar 2022 19:44:50 +0100 Subject: [PATCH 14/20] Correct the copyright notes --- src/knock.c | 3 +-- src/knockd.c | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/knock.c b/src/knock.c index 92c4255..db378e0 100644 --- a/src/knock.c +++ b/src/knock.c @@ -1,8 +1,7 @@ /* * knock.c * - * Copyright (c) 2004-2019 by Judd Vinet - * Sebastien Valat + * 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 diff --git a/src/knockd.c b/src/knockd.c index ed60b6f..6168ca2 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1,10 +1,7 @@ /* * knockd.c * - * Copyright (c) 2004-2019 by Judd Vinet - * Sebastien Valat - * Marius Hoch - * TDFKAOlli + * 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 From c07bc93e1ffc226c769e6c964e14fb9a2ac1c87e Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 23 Mar 2022 19:53:59 +0100 Subject: [PATCH 15/20] Further corrections on copyright notes --- src/knock.c | 3 +-- src/knockd.c | 6 +----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/knock.c b/src/knock.c index db378e0..da87b33 100644 --- a/src/knock.c +++ b/src/knock.c @@ -203,8 +203,7 @@ void usage() { void ver() { printf("knock %s\n", version); - printf("Copyright (C) 2004-2019 Judd Vinet ," - " Sebastien Valat \n"); + printf("Copyright (C) 2004-2019 Judd Vinet \n"); exit(0); } diff --git a/src/knockd.c b/src/knockd.c index 6168ca2..121b9b8 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -521,11 +521,7 @@ void usage(int exit_code) { void ver() { printf("knockd %s\n", version); - printf("Copyright (C) 2004-2019 Judd Vinet ," - " Sebastien Valat ," - " Marius Hoch ," - " TDFKAOlli " - "\n"); + printf("Copyright (C) 2004-2019 Judd Vinet \n"); exit(0); } From abbdc06d1dcba2aff2f935f7a37cd00c03432142 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 23 Mar 2022 20:58:35 +0100 Subject: [PATCH 16/20] Additional small corrections --- src/knock.c | 8 ++------ src/knockd.c | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/knock.c b/src/knock.c index da87b33..b5cc3aa 100644 --- a/src/knock.c +++ b/src/knock.c @@ -127,7 +127,7 @@ int main(int argc, char** argv) //get host and port based on hints result = getaddrinfo(hostname, port, &hints, &infoptr); if(result) { - fprintf(stderr, "Fail to resolve hostname '%s' on port %s\n", hostname, port); + fprintf(stderr, "Failed to resolve hostname '%s' on port %s\n", hostname, port); fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result)); exit(1); } @@ -163,11 +163,7 @@ int main(int argc, char** argv) } close(sd); - - //wait delay usleep(1000*o_delay); - - //free temp mem freeaddrinfo(infoptr); } @@ -203,7 +199,7 @@ void usage() { void ver() { printf("knock %s\n", version); - printf("Copyright (C) 2004-2019 Judd Vinet \n"); + printf("Copyright (C) 2004-2022 Judd Vinet \n"); exit(0); } diff --git a/src/knockd.c b/src/knockd.c index 121b9b8..ff3550e 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -521,7 +521,7 @@ void usage(int exit_code) { void ver() { printf("knockd %s\n", version); - printf("Copyright (C) 2004-2019 Judd Vinet \n"); + printf("Copyright (C) 2004-2022 Judd Vinet \n"); exit(0); } From a7b6411c5e263e691a40eee53fa2334f99426ed4 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 23 Mar 2022 23:03:18 +0100 Subject: [PATCH 17/20] Address review comments of pull request jvinet/knockd#84 --- src/knockd.c | 60 +++++++--------------------------------------------- 1 file changed, 8 insertions(+), 52 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index ff3550e..6f22a2f 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -134,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); @@ -797,7 +796,8 @@ int parseconfig(char *configfile) fprintf(stderr, "error: section '%s' has an empty knock sequence\n", door->name); return(1); } - if(door->start_command == NULL && door->start_command6 == NULL) { + 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); } @@ -1305,27 +1305,15 @@ void free_door(opendoor_t *door) { if(door) { free(door->target); - if(door->start_command) { - free(door->start_command); - } - if(door->start_command6) { - free(door->start_command6); - } - if(door->stop_command) { - free(door->stop_command); - } - if(door->stop_command6) { - free(door->stop_command6); - } + free(door->start_command); + free(door->start_command6); + free(door->stop_command); + free(door->stop_command6); if (door->one_time_sequences_fd) { fclose(door->one_time_sequences_fd); } - if(door->pcap_filter_exp) { - free(door->pcap_filter_exp); - } - if(door->pcap_filter_expv6) { - free(door->pcap_filter_expv6); - } + free(door->pcap_filter_exp); + free(door->pcap_filter_expv6); free(door); } } @@ -1338,38 +1326,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) -{ - struct ifaddrs *addrs=NULL,*tmp=NULL; - - if(bufsize <= 0) { - return(NULL); - } - if(buf == NULL) { - return(NULL); - } - buf[0] = '\0'; - - getifaddrs(&addrs); - tmp=addrs; - while(tmp) { - if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) { - struct sockaddr_in *addr_in = (struct sockaddr_in *)tmp->ifa_addr; - if (strcmp(iface,tmp->ifa_name) == 0 ) { - strncpy(buf, inet_ntoa(addr_in->sin_addr), bufsize-1); - buf[bufsize-1] = '\0'; - freeifaddrs(addrs); - return(buf); - } - } - tmp = tmp->ifa_next; - } - freeifaddrs(addrs); - return(NULL); -} - /* 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 From d4c85aa2d0ee305f3bac22114224ab984f3b73cf Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Wed, 23 Mar 2022 23:16:01 +0100 Subject: [PATCH 18/20] Add support for using 'any' for the configured interface to listen on all network interfaces of the device --- doc/knockd.1.in | 3 ++- src/knockd.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/knockd.1.in b/doc/knockd.1.in index 58a2b43..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. diff --git a/src/knockd.c b/src/knockd.c index 6f22a2f..3afa2ed 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -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_skip_ipv6))) { + if((strcmp(ifa->ifa_name, o_int) == 0 || strcmp("any", o_int)) + && (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) From f3e7e7df7e999831f23ed18ea5fe0653ac452765 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Tue, 29 Mar 2022 20:26:47 +0200 Subject: [PATCH 19/20] Fix issue in 'any' interface handling --- src/knockd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knockd.c b/src/knockd.c index 3afa2ed..51b9c56 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -277,7 +277,7 @@ int main(int argc, char **argv) if(ifa->ifa_addr == NULL) continue; - if((strcmp(ifa->ifa_name, o_int) == 0 || strcmp("any", o_int)) + 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; From 735379d42ab6ea73f9916c367e80883b732e4359 Mon Sep 17 00:00:00 2001 From: TDFKAOlli Date: Thu, 4 Apr 2024 19:43:43 +0200 Subject: [PATCH 20/20] Added fix for VLAN Ethernet as proposed here: https://github.com/jvinet/knock/issues/89. Fixed log output. --- src/knockd.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/knockd.c b/src/knockd.c index 51b9c56..d80a6ff 100644 --- a/src/knockd.c +++ b/src/knockd.c @@ -1170,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 */ } @@ -1631,12 +1631,17 @@ 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);