Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
d50b116
Merged changes from jvinet/knock#53 into master
TDFKAOlli Dec 31, 2018
e9b9cf5
Merged fix from jvinet/knock#52 by Marius Hoch to master
TDFKAOlli Dec 31, 2018
91bb0b2
Fix tcp-psh filter setting (tcp-push => tcp-psh) in pcap filter
TDFKAOlli Jan 2, 2019
7c00e40
Fixed some mem-leaks introduced with ipv6
TDFKAOlli Jan 4, 2019
4c3c5c5
Remove declaration non-existing function runCommand()
TDFKAOlli Feb 10, 2019
2ac7d47
Add patch for ipv6 only interfaces from jvinet/knock #58 (from pull r…
TDFKAOlli Feb 10, 2019
70fa595
Changed get_ip() from ioctl to getifaddr. Added save-mem (-s) option,…
TDFKAOlli Mar 18, 2019
92a7ae3
Add documentation of new flag.
TDFKAOlli Mar 18, 2019
1f77a3f
Fix incorrect TCP ACK/URG flag NOT_SET testing logic in flags_match()…
TDFKAOlli Jun 28, 2020
faee547
Bump version to 0.8.1
TDFKAOlli Jul 15, 2020
39c3693
Fixed use-after-free based on jvinet/knock#74 and jvinet/knock#75.
TDFKAOlli Feb 7, 2021
58a2ed4
bump to version 0.8.2
TDFKAOlli Feb 7, 2021
4f0ad22
Merge branch 'master' into resolve_conflict_from_upstream
TDFKAOlli Mar 22, 2022
04bd747
Fix build problems due to variable name changes on upstream. Renamed …
TDFKAOlli Mar 22, 2022
bd0b842
Correct the copyright notes
TDFKAOlli Mar 23, 2022
c07bc93
Further corrections on copyright notes
TDFKAOlli Mar 23, 2022
abbdc06
Additional small corrections
TDFKAOlli Mar 23, 2022
a7b6411
Address review comments of pull request jvinet/knockd#84
TDFKAOlli Mar 23, 2022
d4c85aa
Add support for using 'any' for the configured interface to listen on…
TDFKAOlli Mar 23, 2022
f3e7e7d
Fix issue in 'any' interface handling
TDFKAOlli Mar 29, 2022
735379d
Added fix for VLAN Ethernet as proposed here: https://github.com/jvin…
TDFKAOlli Apr 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
@@ -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])
Expand Down
6 changes: 5 additions & 1 deletion doc/knockd.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ up holes in a firewall for quick access.
.SH COMMANDLINE OPTIONS
.TP
.B "\-i, \-\-interface <int>"
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.
Expand All @@ -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
Expand Down
17 changes: 9 additions & 8 deletions src/knock.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* knock.c
*
* Copyright (c) 2004-2012 by Judd Vinet <jvinet@zeroflux.org>
* Copyright (c) 2004-2022 by Judd Vinet <jvinet@zeroflux.org>
*
* 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
Expand Down Expand Up @@ -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;
Expand All @@ -123,15 +124,15 @@ 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);
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
exit(1);
}

/* create socket */
//create socket
if(o_udp || proto == PROTO_UDP) {
sd = socket(infoptr->ai_family, SOCK_DGRAM, 0);
if(sd == -1) {
Expand All @@ -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);
Expand Down Expand Up @@ -198,7 +199,7 @@ void usage() {

void ver() {
printf("knock %s\n", version);
printf("Copyright (C) 2004-2012 Judd Vinet <jvinet@zeroflux.org>\n");
printf("Copyright (C) 2004-2022 Judd Vinet <jvinet@zeroflux.org>\n");
exit(0);
}

Expand Down
119 changes: 61 additions & 58 deletions src/knockd.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* knockd.c
*
* Copyright (c) 2004-2012 by Judd Vinet <jvinet@zeroflux.org>
* Copyright (c) 2004-2022 by Judd Vinet <jvinet@zeroflux.org>
*
* 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
Expand Down Expand Up @@ -48,7 +48,6 @@
#include <netinet/if_ether.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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";
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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();

Expand All @@ -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");
Expand All @@ -519,7 +521,7 @@ void usage(int exit_code) {

void ver() {
printf("knockd %s\n", version);
printf("Copyright (C) 2004-2012 Judd Vinet <jvinet@zeroflux.org>\n");
printf("Copyright (C) 2004-2022 Judd Vinet <jvinet@zeroflux.org>\n");
exit(0);
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -1155,15 +1162,15 @@ 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) {
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);
dprint("Adding ipv6 pcap expression for door '%s': %s\n", door->name, door->pcap_filter_exp);
}
buffer[0] = '\0'; /* "clear" the buffer */
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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
Expand Down Expand Up @@ -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();
}
}
Expand Down Expand Up @@ -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;
Expand Down