New upstream version 0.7

This commit is contained in:
Leo Antunes
2016-09-28 19:41:01 +02:00
parent 0e2c96c9ef
commit b2567e2868
19 changed files with 8417 additions and 7773 deletions
+25 -12
View File
@@ -1,8 +1,8 @@
/*
* knock.c
*
* Copyright (c) 2004-2005 by Judd Vinet <jvinet@zeroflux.org>
*
*
* Copyright (c) 2004-2012 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
* the Free Software Foundation; either version 2 of the License, or
@@ -23,16 +23,20 @@
#include <stdlib.h>
#include <stdarg.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/socket.h>
#if defined(__FreeBSD__) || defined(__APPLE__)
#include <netinet/in.h>
#endif
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <resolv.h>
#include <getopt.h>
#include <fcntl.h>
static char version[] = "0.5";
static char version[] = "0.7";
#define PROTO_TCP 1
#define PROTO_UDP 2
@@ -44,6 +48,7 @@ void usage();
int o_verbose = 0;
int o_udp = 0;
int o_delay = 0;
int main(int argc, char** argv)
{
@@ -55,12 +60,13 @@ int main(int argc, char** argv)
{
{"verbose", no_argument, 0, 'v'},
{"udp", no_argument, 0, 'u'},
{"delay", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0}
};
while((opt = getopt_long(argc, argv, "vuhV", opts, &optidx))) {
while((opt = getopt_long(argc, argv, "vud:hV", opts, &optidx))) {
if(opt < 0) {
break;
}
@@ -68,6 +74,7 @@ int main(int argc, char** argv)
case 0: break;
case 'v': o_verbose = 1; break;
case 'u': o_udp = 1; break;
case 'd': o_delay = (int)atoi(optarg); break;
case 'V': ver();
case 'h': /* fallthrough */
default: usage();
@@ -77,6 +84,11 @@ int main(int argc, char** argv)
usage();
}
if(o_delay < 0) {
fprintf(stderr, "error: delay cannot be negative\n");
exit(1);
}
host = gethostbyname(argv[optind++]);
if(host == NULL) {
fprintf(stderr, "Cannot resolve hostname\n");
@@ -98,16 +110,16 @@ int main(int argc, char** argv)
} else {
port = atoi(arg);
}
if(o_udp || proto == PROTO_UDP) {
sd = socket(PF_INET, SOCK_DGRAM, 0);
sd = socket(PF_INET, 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(PF_INET, SOCK_STREAM, 0);
if(sd == -1) {
fprintf(stderr, "Cannot open socket\n");
exit(1);
@@ -121,13 +133,13 @@ int main(int argc, char** argv)
addr.sin_port = htons(port);
if(o_udp || proto == PROTO_UDP) {
vprint("hitting udp %s:%u\n", inet_ntoa(addr.sin_addr), port);
connect(sd, (struct sockaddr*)&addr, sizeof(struct sockaddr));
send(sd, NULL, 0, MSG_DONTWAIT);
sendto(sd, "", 1, 0, (struct sockaddr*)&addr, sizeof(addr));
} else {
vprint("hitting tcp %s:%u\n", inet_ntoa(addr.sin_addr), port);
connect(sd, (struct sockaddr*)&addr, sizeof(struct sockaddr));
}
close(sd);
usleep(1000*o_delay);
}
return(0);
@@ -148,6 +160,7 @@ void usage() {
printf("usage: knock [options] <host> <port[:proto]> [port[:proto]] ...\n");
printf("options:\n");
printf(" -u, --udp make all ports hits use UDP (default is TCP)\n");
printf(" -d, --delay <t> wait <t> milliseconds between port hits\n");
printf(" -v, --verbose be verbose\n");
printf(" -V, --version display version\n");
printf(" -h, --help this help\n");
@@ -159,7 +172,7 @@ void usage() {
void ver() {
printf("knock %s\n", version);
printf("Copyright (C) 2004-2005 Judd Vinet <jvinet@zeroflux.org>\n");
printf("Copyright (C) 2004-2012 Judd Vinet <jvinet@zeroflux.org>\n");
exit(0);
}
+317 -289
View File
@@ -1,8 +1,8 @@
/*
* knockd.c
*
* Copyright (c) 2004-2005 by Judd Vinet <jvinet@zeroflux.org>
*
*
* Copyright (c) 2004-2012 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
* the Free Software Foundation; either version 2 of the License, or
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
@@ -28,6 +28,9 @@
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/socket.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
@@ -35,8 +38,6 @@
#include <netinet/udp.h>
#include <netinet/ip_icmp.h>
#include <net/if.h>
#include <bits/time.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
@@ -48,7 +49,7 @@
#include <errno.h>
#include "list.h"
static char version[] = "0.5";
static char version[] = "0.7";
#define SEQ_TIMEOUT 25 /* default knock timeout in seconds */
#define CMD_TIMEOUT 10 /* default timeout in seconds between start and stop commands */
@@ -66,6 +67,7 @@ typedef struct opendoor {
unsigned short seqcount;
unsigned short sequence[SEQ_MAX];
unsigned short protocol[SEQ_MAX];
char *target;
time_t seq_timeout;
char *start_command;
time_t cmd_timeout;
@@ -81,7 +83,7 @@ typedef struct opendoor {
} opendoor_t;
PMList *doors = NULL;
/* we keep one list of knock attempts, one per IP address,
/* we keep one list of knock attempts per IP address,
* and increment the stage as they progress through the sequence.
*/
typedef struct knocker {
@@ -100,9 +102,9 @@ void logprint(char *fmt, ...);
void dprint_sequence(opendoor_t *door, char *fmt, ...);
void cleanup(int signum);
void child_exit(int signum);
void read_cfg(int signum);
void reload(int signum);
void ver();
void usage();
void usage(int exit_code);
char* strtoupper(char *str);
char* trim(char *str);
void runCommand(char *cmd);
@@ -115,10 +117,10 @@ long get_current_one_time_sequence_position(opendoor_t *door);
void generate_pcap_filter();
size_t realloc_strcat(char **dest, const char *src, size_t size);
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);
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);
pcap_t *cap = NULL;
FILE *logfd = NULL;
@@ -139,6 +141,7 @@ int main(int argc, char **argv)
{
char pcapErr[PCAP_ERRBUF_SIZE] = "";
int opt, ret, optidx = 1;
static struct option opts[] =
{
{"verbose", no_argument, 0, 'v'},
@@ -148,11 +151,13 @@ int main(int argc, char **argv)
{"interface", required_argument, 0, 'i'},
{"config", required_argument, 0, 'c'},
{"help", no_argument, 0, 'h'},
{"pidfile", required_argument, 0, 'p'},
{"logfile", required_argument, 0, 'g'},
{"version", no_argument, 0, 'V'},
{0, 0, 0, 0}
};
while((opt = getopt_long(argc, argv, "vDdli:c:hV", opts, &optidx))) {
while((opt = getopt_long(argc, argv, "vDdli:c:p:g:hV", opts, &optidx))) {
if(opt < 0) {
break;
}
@@ -168,15 +173,22 @@ int main(int argc, char **argv)
case 'c': strncpy(o_cfg, optarg, sizeof(o_cfg)-1);
o_cfg[sizeof(o_cfg)-1] = '\0';
break;
case 'p': strncpy(o_pidfile, optarg, sizeof(o_pidfile)-1);
o_pidfile[sizeof(o_pidfile)-1] = '\0';
break;
case 'g': strncpy(o_logfile, optarg, sizeof(o_logfile)-1);
o_logfile[sizeof(o_logfile)-1] = '\0';
break;
case 'V': ver();
case 'h': /* fallthrough */
default: usage();
default: usage(0);
}
}
if(parseconfig(o_cfg)) {
exit(1);
usage(1);
}
/* set o_int to a default value if it has not been set by the -i switch nor by
* the config file */
if(strlen(o_int) == 0) {
@@ -193,7 +205,9 @@ int main(int argc, char **argv)
}
}
cap = pcap_open_live(o_int, 65535, 0, 0, pcapErr);
/* 50ms timeout for packet capture. See pcap(3pcap) manpage, which
* recommends that a timeout of 0 not be used. */
cap = pcap_open_live(o_int, 65535, 0, 50, pcapErr);
if(strlen(pcapErr)) {
fprintf(stderr, "could not open %s: %s\n", o_int, pcapErr);
}
@@ -212,7 +226,7 @@ int main(int argc, char **argv)
case DLT_RAW:
dprint("raw interface detected, no encapsulation\n");
break;
default:
default:
fprintf(stderr, "error: unsupported link-layer type: %d\n", lltype);
cleanup(1);
break;
@@ -247,7 +261,7 @@ int main(int argc, char **argv)
signal(SIGINT, cleanup);
signal(SIGTERM, cleanup);
signal(SIGCHLD, child_exit);
signal(SIGHUP, read_cfg);
signal(SIGHUP, reload);
vprint("listening on %s...\n", o_int);
logprint("starting up, listening on %s", o_int);
@@ -301,7 +315,7 @@ void logprint(char *fmt, ...)
struct tm *tm;
t = time(NULL);
tm = localtime(&t);
fprintf(logfd, "[%04d-%02d-%02d %02d:%02d] %s\n", tm->tm_year+1900,
tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, msg);
fflush(logfd);
@@ -357,7 +371,7 @@ void child_exit(int signum)
return;
}
void read_cfg(int signum)
void reload(int signum)
{
PMList *lp;
opendoor_t *door;
@@ -372,10 +386,23 @@ void read_cfg(int signum)
list_free(doors);
parseconfig(o_cfg);
vprint("Closing and re-opening log file: %s\n", o_logfile);
logprint("Closing and re-opening log file: %s\n", o_logfile);
/* close and re-open the log file */
if(logfd) {
fclose(logfd);
}
logfd = fopen(o_logfile, "a");
if(logfd == NULL) {
perror("warning: cannot open logfile");
}
return;
}
void usage() {
void usage(int exit_code) {
printf("usage: knockd [options]\n");
printf("options:\n");
printf(" -i, --interface <int> network interface to listen on (default \"eth0\")\n");
@@ -383,16 +410,18 @@ void usage() {
printf(" -c, --config <file> use an alternate config file\n");
printf(" -D, --debug output debug messages\n");
printf(" -l, --lookup lookup DNS names (may be a security risk)\n");
printf(" -p, --pidfile use an alternate pidfile\n");
printf(" -g, --logfile use an alternate logfile\n");
printf(" -v, --verbose be verbose\n");
printf(" -V, --version display version\n");
printf(" -h, --help this help\n");
printf("\n");
exit(1);
exit(exit_code);
}
void ver() {
printf("knockd %s\n", version);
printf("Copyright (C) 2004-2005 Judd Vinet <jvinet@zeroflux.org>\n");
printf("Copyright (C) 2004-2012 Judd Vinet <jvinet@zeroflux.org>\n");
exit(0);
}
@@ -420,7 +449,7 @@ char* trim(char *str)
if(pch != str) {
memmove(str, pch, (strlen(pch) + 1));
}
pch = (char*)(str + (strlen(str) - 1));
while(isspace(*pch)) {
pch--;
@@ -474,6 +503,7 @@ int parseconfig(char *configfile)
}
strncpy(door->name, section, sizeof(door->name)-1);
door->name[sizeof(door->name)-1] = '\0';
door->target = 0;
door->seqcount = 0;
door->seq_timeout = SEQ_TIMEOUT; /* default sequence timeout (seconds) */
door->start_command = NULL;
@@ -503,7 +533,7 @@ int parseconfig(char *configfile)
dprint("config: usesyslog\n");
} else {
fprintf(stderr, "config: line %d: syntax error\n", linenum);
return(1);
return(1);
}
} else {
trim(ptr);
@@ -533,7 +563,15 @@ int parseconfig(char *configfile)
linenum, key);
return(1);
}
if(!strcmp(key, "SEQUENCE")) {
if(!strcmp(key, "TARGET")) {
door->target = malloc(sizeof(char) * (strlen(ptr)+1));
if(door->target == NULL) {
perror("malloc");
exit(1);
}
strcpy(door->target, ptr);
dprint("config: %s: target: %s\n", door->name, door->target);
} else if(!strcmp(key, "SEQUENCE")) {
int i;
i = parse_port_sequence(ptr, door);
if (i > 0) {
@@ -681,7 +719,7 @@ int get_new_one_time_sequence(opendoor_t *door)
return(1);
}
dprint_sequence(door, "new sequence for door %s: ", door->name);
return(0);
}
@@ -694,7 +732,7 @@ long get_next_one_time_sequence(opendoor_t *door)
{
char line[PATH_MAX+1];
int pos;
pos = ftell(door->one_time_sequences_fd);
while(fgets(line, PATH_MAX, door->one_time_sequences_fd)) {
trim(line);
@@ -746,7 +784,7 @@ long get_current_one_time_sequence_position(opendoor_t *door)
rewind(door->one_time_sequences_fd);
pseudo_door.one_time_sequences_fd = door->one_time_sequences_fd;
pos = get_next_one_time_sequence(&pseudo_door);
while(pos >= 0) {
if(door->seqcount == pseudo_door.seqcount) {
@@ -768,20 +806,18 @@ long get_current_one_time_sequence_position(opendoor_t *door)
*/
void generate_pcap_filter()
{
/* NOTE: We're doing string manipulations in a daemon -- use defensive programming! */
PMList *lp;
opendoor_t *door;
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") */
short tcp_present = 0; /* flag indicating if TCP is used */
short udp_present = 0; /* flag indicating if UDP is used */
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") */
short tcp_present = 0; /* flag indicating if TCP is used */
short udp_present = 0; /* flag indicating if UDP is used */
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 */
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 */
/* generate subfilters for each door having a NULL pcap_filter_exp
*
* Example filter for one single door:
@@ -796,11 +832,11 @@ void generate_pcap_filter()
/* 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;
/* 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
@@ -815,6 +851,10 @@ void generate_pcap_filter()
buffer[0] = '\0';
}
bufsize = realloc_strcat(&buffer, "(dst host ", bufsize); /* accept only incoming packets */
bufsize = realloc_strcat(&buffer, door->target ? door->target : myip, bufsize);
bufsize = realloc_strcat(&buffer, " and (", bufsize);
/* 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) {
@@ -913,7 +953,9 @@ void generate_pcap_filter()
if(udp_present) {
bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parentheses of UDP ports */
}
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
@@ -930,8 +972,8 @@ void generate_pcap_filter()
perror("malloc");
cleanup(1);
}
strcpy(door->pcap_filter_exp, 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 */
}
@@ -949,9 +991,6 @@ void generate_pcap_filter()
* )
*/
if(modified_filters) {
bufsize = realloc_strcat(&buffer, "dst host ", bufsize); /* accept only incoming packets */
bufsize = realloc_strcat(&buffer, myip, bufsize);
bufsize = realloc_strcat(&buffer, " and (", bufsize);
/* iterate over all doors */
for(lp = doors; lp; lp = lp->next) {
door = (opendoor_t*)lp->data;
@@ -960,7 +999,6 @@ void generate_pcap_filter()
bufsize = realloc_strcat(&buffer, " or ", bufsize);
}
}
bufsize = realloc_strcat(&buffer, ")", bufsize); /* close parantheses around port filters */
/* 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 */
@@ -1005,7 +1043,7 @@ size_t realloc_strcat(char **dest, const char *src, size_t size)
needed_size = strlen(*dest) + strlen(src) + 1; /* '+ 1' for '\0' */
new_size = size;
while(needed_size > new_size) {
new_size *= 2;
}
@@ -1018,7 +1056,7 @@ size_t realloc_strcat(char **dest, const char *src, size_t size)
/* now dest is large enough to strcat() the src */
strcat(*dest, src);
return new_size;
}
@@ -1027,19 +1065,14 @@ size_t realloc_strcat(char **dest, const char *src, size_t size)
void close_door(opendoor_t *door)
{
doors = list_remove(doors, door);
if (door) {
if (door->start_command) {
free(door->start_command);
}
if (door->stop_command) {
free(door->stop_command);
}
if(door) {
free(door->target);
free(door->start_command);
free(door->stop_command);
if (door->one_time_sequences_fd) {
fclose(door->one_time_sequences_fd);
}
if (door->pcap_filter_exp) {
free(door->pcap_filter_exp);
}
free(door->pcap_filter_exp);
free(door);
}
}
@@ -1093,24 +1126,30 @@ size_t parse_cmd(char* dest, size_t size, const char* command, const char* src)
size_t n = size;
size_t command_len = strlen(command);
size_t total_len = 0;
int size_larger_than_zero = 1; /* allows us to calculate total length of result string even if the size is zero
by setting n to 1 (--> noting will be ever written to dest) */
/* allows us to calculate total length of result string even if the size */
/* is zero by setting n to 1 (--> noting will be ever written to dest) */
int size_larger_than_zero = 1;
if(size == 0) {
size_larger_than_zero = 0;
n = 1;
}
token = strstr(c, "%IP%"); /* get location of first token */
/* get location of first token */
token = strstr(c, "%IP%");
if(!token) {
token = (char*) (c + command_len + 1); /* point token past command (we won't access it anymore) */
/* point token past command (we won't access it anymore) */
token = (char*) (c + command_len + 1);
}
while(*c != '\0') {
if(c < token) { /* not reached a token yet --> append from command */
/* not reached a token yet --> append from command */
if(c < token) {
if(n != 1) {
*d++ = *c;
n--;
}
} else { /* we reached a token --> append from src */
} else {
/* we reached a token --> append from src */
while(*s != '\0') {
if(n != 1) {
*d++ = *s;
@@ -1119,19 +1158,21 @@ size_t parse_cmd(char* dest, size_t size, const char* command, const char* src)
s++;
total_len++;
}
c += 4; /* skip the token in command */
s = src; /* "rewind" src string for next token */
token = strstr(c, "%IP%"); /* get location of next token */
c += 4; /* skip the token in command */
s = src; /* "rewind" src string for next token */
token = strstr(c, "%IP%"); /* get location of next token */
if(!token) {
token = (char*) (c + command_len + 1); /* point token past command (we won't access it anymore) */
/* point token past command (we won't access it anymore) */
token = (char*) (c + command_len + 1);
}
c--; /* compensate for the following c++ */
total_len--; /* compensate for the following total_len++ */
c--; /* compensate for the following c++ */
total_len--; /* compensate for the following total_len++ */
}
c++;
total_len++;
}
if(size_larger_than_zero) { /* terminate dest if its size is larger than 0 */
if(size_larger_than_zero) {
/* terminate dest if its size is larger than 0 */
*d = '\0';
}
@@ -1156,13 +1197,171 @@ int exec_cmd(char* command, char* name){
return ret;
}
/*
* 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)
{
/* 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(door->flag_fin != DONT_CARE) {
if(door->flag_fin == SET && !(tcp->th_flags & TH_FIN)) {
dprint("packet is not FIN, ignoring...\n");
return 0;
}
if(door->flag_fin == NOT_SET && (tcp->th_flags & TH_FIN)) {
dprint("packet is not !FIN, ignoring...\n");
return 0;
}
}
if(door->flag_syn != DONT_CARE) {
if(door->flag_syn == SET && !(tcp->th_flags & TH_SYN)) {
dprint("packet is not SYN, ignoring...\n");
return 0;
}
if(door->flag_syn == NOT_SET && (tcp->th_flags & TH_SYN)) {
dprint("packet is not !SYN, ignoring...\n");
return 0;
}
}
if(door->flag_rst != DONT_CARE) {
if(door->flag_rst == SET && !(tcp->th_flags & TH_RST)) {
dprint("packet is not RST, ignoring...\n");
return 0;
}
if(door->flag_rst == NOT_SET && (tcp->th_flags & TH_RST)) {
dprint("packet is not !RST, ignoring...\n");
return 0;
}
}
if(door->flag_psh != DONT_CARE) {
if(door->flag_psh == SET && !(tcp->th_flags & TH_PUSH)) {
dprint("packet is not PSH, ignoring...\n");
return 0;
}
if(door->flag_psh == NOT_SET && (tcp->th_flags & TH_PUSH)) {
dprint("packet is not !PSH, ignoring...\n");
return 0;
}
}
if(door->flag_ack != DONT_CARE) {
if(door->flag_ack == SET && !(tcp->th_flags & TH_ACK)) {
dprint("packet is not ACK, ignoring...\n");
return 0;
}
if(door->flag_ack == NOT_SET && !(tcp->th_flags & TH_ACK)) {
dprint("packet is not !ACK, ignoring...\n");
return 0;
}
}
if(door->flag_urg != DONT_CARE) {
if(door->flag_urg == SET && !(tcp->th_flags & TH_URG)) {
dprint("packet is not URG, ignoring...\n");
return 0;
}
if(door->flag_urg == NOT_SET && !(tcp->th_flags & TH_URG)) {
dprint("packet is not !URG, ignoring...\n");
return 0;
}
}
}
return 1;
}
/**
* Process a knock attempt to see if the knocker has graduated to the next
* sequence. If they've completed all sequences correctly, then we open the
* door.
*/
void process_attempt(knocker_t *attempt)
{
/* level up! */
attempt->stage++;
if(attempt->srchost) {
vprint("%s (%s): %s: Stage %d\n", attempt->src, attempt->srchost, attempt->door->name, attempt->stage);
logprint("%s (%s): %s: Stage %d", attempt->src, attempt->srchost, attempt->door->name, attempt->stage);
} else {
vprint("%s: %s: Stage %d\n", attempt->src, attempt->door->name, attempt->stage);
logprint("%s: %s: Stage %d", attempt->src, attempt->door->name, attempt->stage);
}
if(attempt->stage >= attempt->door->seqcount) {
if(attempt->srchost) {
vprint("%s (%s): %s: OPEN SESAME\n", attempt->src, attempt->srchost, attempt->door->name);
logprint("%s (%s): %s: OPEN SESAME", attempt->src, attempt->srchost, attempt->door->name);
} else {
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)) {
/* run the associated command */
if(fork() == 0) {
/* child */
char parsed_start_cmd[PATH_MAX];
char parsed_stop_cmd[PATH_MAX];
size_t cmd_len = 0;
setsid();
/* 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);
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(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");
exit(0); /* exit child */
}
}
/* 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){
sleep(attempt->door->cmd_timeout);
if(attempt->srchost) {
vprint("%s (%s): %s: command timeout\n", attempt->src, attempt->srchost, attempt->door->name);
logprint("%s (%s): %s: command timeout", attempt->src, attempt->srchost, attempt->door->name);
} else {
vprint("%s: %s: command timeout\n", attempt->src, attempt->door->name);
logprint("%s: %s: command timeout", attempt->src, attempt->door->name);
}
exec_cmd(parsed_stop_cmd, attempt->door->name);
}
exit(0); /* exit child */
}
}
/* change to next sequence if one time sequences are used.
* Note that here the door will eventually be closed in
* get_new_one_time_sequence() if no more sequences are left */
if(attempt->door->one_time_sequences_fd) {
disable_used_one_time_sequence(attempt->door);
get_new_one_time_sequence(attempt->door);
/* update pcap filter */
free(attempt->door->pcap_filter_exp);
attempt->door->pcap_filter_exp = NULL;
generate_pcap_filter();
}
}
}
/* Sniff an interface, looking for port-knock sequences
*/
void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet)
{
/* packet structs */
struct ethhdr* eth = NULL;
struct iphdr* ip = NULL;
struct ether_header* eth = NULL;
struct ip* ip = NULL;
struct tcphdr* tcp = NULL;
struct udphdr* udp = NULL;
char proto[8];
@@ -1179,51 +1378,46 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet)
knocker_t *attempt = NULL;
if(lltype == DLT_EN10MB) {
eth = (struct ethhdr*)packet;
if(ntohs(eth->h_proto) != ETH_P_IP) {
eth = (struct ether_header*)packet;
if(ntohs(eth->ether_type) != ETHERTYPE_IP) {
return;
}
ip = (struct iphdr*)(packet + sizeof(struct ethhdr));
ip = (struct ip*)(packet + sizeof(struct ether_header));
#ifdef __linux__
} else if(lltype == DLT_LINUX_SLL) {
ip = (struct iphdr*)((u_char*)packet + 16);
ip = (struct ip*)((u_char*)packet + 16);
#endif
} else if(lltype == DLT_RAW) {
ip = (struct iphdr*)((u_char*)packet);
ip = (struct ip*)((u_char*)packet);
} else {
dprint("link layer header type of packet not recognized, ignoring...\n");
return;
}
if(ip->version != 4) {
if(ip->ip_v != 4) {
/* no IPv6 yet */
dprint("packet is not IPv4, ignoring...\n");
return;
}
if(ip->protocol == IPPROTO_ICMP) {
if(ip->ip_p == IPPROTO_ICMP) {
/* we don't do ICMP */
return;
}
/* make sure this packet was sent TO us, not FROM us or THROUGH us.
* Actually the pcap filter will take care of forwarding only packets
* destined for us, but another check won't hurt... */
if(inet_aton(myip, &inaddr) == 0) {
fprintf(stderr, "error: could not understand IP address: %s\n", myip);
return;
}
if(ip->daddr != inaddr.s_addr) {
dprint("packet destined for another host, ignoring...\n");
return;
}
sport = dport = 0;
if(ip->protocol == IPPROTO_TCP) {
if(ip->ip_p == IPPROTO_TCP) {
strncpy(proto, "tcp", sizeof(proto));
tcp = (struct tcphdr*)((u_char*)ip + (ip->ihl * 4));
sport = ntohs(tcp->source);
dport = ntohs(tcp->dest);
tcp = (struct tcphdr*)((u_char*)ip + (ip->ip_hl *4));
sport = ntohs(tcp->th_sport);
dport = ntohs(tcp->th_dport);
}
if(ip->protocol == IPPROTO_UDP) {
if(ip->ip_p == IPPROTO_UDP) {
strncpy(proto, "udp", sizeof(proto));
udp = (struct udphdr*)((u_char*)ip + (ip->ihl * 4));
sport = ntohs(udp->source);
dport = ntohs(udp->dest);
udp = (struct udphdr*)((u_char*)ip + (ip->ip_hl * 4));
sport = ntohs(udp->uh_sport);
dport = ntohs(udp->uh_dport);
}
/* get the date/time */
@@ -1234,10 +1428,10 @@ 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->saddr;
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->daddr;
inaddr.s_addr = ip->ip_dst.s_addr;
strncpy(dstIP, inet_ntoa(inaddr), sizeof(dstIP)-1);
dstIP[sizeof(dstIP)-1] = '\0';
@@ -1286,156 +1480,19 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet)
attempt = NULL;
/* look for this guy in our attempts list */
for(lp = attempts; lp; lp = lp->next) {
if(!strncmp(((knocker_t*)lp->data)->src, srcIP, sizeof(srcIP))) {
attempt = (knocker_t*)lp->data;
knocker_t *att = (knocker_t*)lp->data;
if(!strncmp(att->src, srcIP, sizeof(srcIP)) &&
!strncmp(att->door->target ? att->door->target : myip, dstIP, sizeof(dstIP))) {
attempt = att;
break;
}
}
if(attempt) {
int flagsmatch = 1;
/* if tcp, check the flags to ignore the packets we don't want
* (don't even use it to cancel sequences)
*/
if(ip->protocol == IPPROTO_TCP) {
if(attempt->door->flag_fin != DONT_CARE) {
if(attempt->door->flag_fin == SET && tcp->fin != 1) {
dprint("packet is not FIN, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_fin == NOT_SET && tcp->fin == 1) {
dprint("packet is not !FIN, ignoring...\n");
flagsmatch = 0;
}
}
if(attempt->door->flag_syn != DONT_CARE) {
if(attempt->door->flag_syn == SET && tcp->syn != 1) {
dprint("packet is not SYN, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_syn == NOT_SET && tcp->syn == 1) {
dprint("packet is not !SYN, ignoring...\n");
flagsmatch = 0;
}
}
if(attempt->door->flag_rst != DONT_CARE) {
if(attempt->door->flag_rst == SET && tcp->rst != 1) {
dprint("packet is not RST, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_rst == NOT_SET && tcp->rst == 1) {
dprint("packet is not !RST, ignoring...\n");
flagsmatch = 0;
}
}
if(attempt->door->flag_psh != DONT_CARE) {
if(attempt->door->flag_psh == SET && tcp->psh != 1) {
dprint("packet is not PSH, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_psh == NOT_SET && tcp->psh == 1) {
dprint("packet is not !PSH, ignoring...\n");
flagsmatch = 0;
}
}
if(attempt->door->flag_ack != DONT_CARE) {
if(attempt->door->flag_ack == SET && tcp->ack != 1) {
dprint("packet is not ACK, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_ack == NOT_SET && tcp->ack == 1) {
dprint("packet is not !ACK, ignoring...\n");
flagsmatch = 0;
}
}
if(attempt->door->flag_urg != DONT_CARE) {
if(attempt->door->flag_urg == SET && tcp->urg != 1) {
dprint("packet is not URG, ignoring...\n");
flagsmatch = 0;
}
if(attempt->door->flag_urg == NOT_SET && tcp->urg == 1) {
dprint("packet is not !URG, ignoring...\n");
flagsmatch = 0;
}
}
}
if(flagsmatch && ip->protocol == attempt->door->protocol[attempt->stage] &&
int flagsmatch = flags_match(attempt->door, ip, tcp);
if(flagsmatch && ip->ip_p == attempt->door->protocol[attempt->stage] &&
dport == attempt->door->sequence[attempt->stage]) {
/* level up! */
attempt->stage++;
if(attempt->srchost) {
vprint("%s (%s): %s: Stage %d\n", attempt->src, attempt->srchost, attempt->door->name, attempt->stage);
logprint("%s (%s): %s: Stage %d", attempt->src, attempt->srchost, attempt->door->name, attempt->stage);
} else {
vprint("%s: %s: Stage %d\n", attempt->src, attempt->door->name, attempt->stage);
logprint("%s: %s: Stage %d", attempt->src, attempt->door->name, attempt->stage);
}
if(attempt->stage >= attempt->door->seqcount) {
if(attempt->srchost) {
vprint("%s (%s): %s: OPEN SESAME\n", attempt->src, attempt->srchost, attempt->door->name);
logprint("%s (%s): %s: OPEN SESAME", attempt->src, attempt->srchost, attempt->door->name);
} else {
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)) {
/* run the associated command */
if(fork() == 0) {
/* child */
char parsed_start_cmd[PATH_MAX];
char parsed_stop_cmd[PATH_MAX];
size_t cmd_len = 0;
setsid();
/* 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);
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(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");
exit(0); /* exit child */
}
}
/* 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){
sleep(attempt->door->cmd_timeout);
if(attempt->srchost) {
vprint("%s (%s): %s: command timeout\n", attempt->src, attempt->srchost, attempt->door->name);
logprint("%s (%s): %s: command timeout", attempt->src, attempt->srchost, attempt->door->name);
} else {
vprint("%s: %s: command timeout\n", attempt->src, attempt->door->name);
logprint("%s: %s: command timeout", attempt->src, attempt->door->name);
}
exec_cmd(parsed_stop_cmd, attempt->door->name);
}
exit(0); /* exit child */
}
}
/* change to next sequence if one time sequences are used.
* Note that here the door will eventually be closed in
* get_new_one_time_sequence() if no more sequences are left */
if(attempt->door->one_time_sequences_fd) {
disable_used_one_time_sequence(attempt->door);
get_new_one_time_sequence(attempt->door);
/* update pcap filter */
free(attempt->door->pcap_filter_exp);
attempt->door->pcap_filter_exp = NULL;
generate_pcap_filter();
}
}
process_attempt(attempt);
} else if(flagsmatch == 0) {
/* TCP flags didn't match -- just ignore this packet, don't
* invalidate the knock.
@@ -1451,34 +1508,11 @@ 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(ip->protocol == IPPROTO_TCP){
if(door->flag_fin != DONT_CARE) {
if(door->flag_fin == SET && tcp->fin != 1) {dprint("packet is not FIN, ignoring...\n");continue;}
if(door->flag_fin == NOT_SET && tcp->fin == 1) {dprint("packet is not !FIN, ignoring...\n");continue;}
}
if(door->flag_syn != DONT_CARE) {
if(door->flag_syn == SET && tcp->syn != 1) {dprint("packet is not SYN, ignoring...\n");continue;}
if(door->flag_syn == NOT_SET && tcp->syn == 1) {dprint("packet is not !SYN, ignoring...\n");continue;}
}
if(door->flag_rst != DONT_CARE) {
if(door->flag_rst == SET && tcp->rst != 1) {dprint("packet is not RST, ignoring...\n");continue;}
if(door->flag_rst == NOT_SET && tcp->rst == 1) {dprint("packet is not !RST, ignoring...\n");continue;}
}
if(door->flag_psh != DONT_CARE) {
if(door->flag_psh == SET && tcp->psh != 1) {dprint("packet is not PSH, ignoring...\n");continue;}
if(door->flag_psh == NOT_SET && tcp->psh == 1) {dprint("packet is not !PSH, ignoring...\n");continue;}
}
if(door->flag_ack != DONT_CARE) {
if(door->flag_ack == SET && tcp->ack != 1) {dprint("packet is not ACK, ignoring...\n");continue;}
if(door->flag_ack == NOT_SET && tcp->ack == 1) {dprint("packet is not !ACK, ignoring...\n");continue;}
}
if(door->flag_urg != DONT_CARE) {
if(door->flag_urg == SET && tcp->urg != 1) {dprint("packet is not URG, ignoring...\n");continue;}
if(door->flag_urg == NOT_SET && tcp->urg == 1) {dprint("packet is not !URG, ignoring...\n");continue;}
}
if(!flags_match(door, ip, tcp)) {
continue;
}
if(ip->protocol == door->protocol[0] && dport == door->sequence[0]) {
if(ip->ip_p == door->protocol[0] && dport == door->sequence[0] &&
!strcmp(dstIP, door->target ? door->target : myip)) {
struct hostent *he;
/* create a new entry */
attempt = (knocker_t*)malloc(sizeof(knocker_t));
@@ -1490,24 +1524,18 @@ void sniff(u_char* arg, const struct pcap_pkthdr* hdr, const u_char* packet)
strcpy(attempt->src, srcIP);
/* try a reverse lookup if enabled */
if (o_lookup) {
inaddr.s_addr = ip->saddr;
inaddr.s_addr = ip->ip_src.s_addr;
he = gethostbyaddr((void *)&inaddr, sizeof(inaddr), AF_INET);
if(he) {
attempt->srchost = strdup(he->h_name);
}
}
attempt->stage = 1;
attempt->stage = 0;
attempt->seq_start = pkt_secs;
attempt->door = door;
if(attempt->srchost) {
vprint("%s (%s): %s: Stage 1\n", attempt->src, attempt->srchost, door->name);
logprint("%s (%s): %s: Stage 1", attempt->src, attempt->srchost, door->name);
} else {
vprint("%s: %s: Stage 1\n", attempt->src, door->name);
logprint("%s: %s: Stage 1", attempt->src, door->name);
}
attempts = list_add(attempts, attempt);
process_attempt(attempt);
}
}
}