This commit has been accessed 3,589 times via Git panel.
commit 6c3dc833bdde64b0c2c097228b9b34d0a99c617a
tree acdc640ff4b70fddf7a8731fc71b089804d57217
author Engels Antonio <engels@majcms.org> 1314301223 +0800
committer Engels Antonio <engels@majcms.org> 1314301223 +0800
Initial commit, warts and all
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ca83f7c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,191 @@
+# Makefile for Kalasag
+#
+# STEALTH MODE: Only works on Linux systems right now.
+#
+# The snprintf included with the package is for use with NEXTSTEP only,
+# (Thanks Timothy <tjl@luomat.org>) although it may work elsewhere.
+# We've not tried it under any other OS to date. It shouldn't be needed
+# by any modern OS.
+#
+# Others have used the snprintf from:
+#
+# http://www.ijs.si/software/snprintf/
+#
+# We've not tried this yet but others have had good success. Our only
+# piece of advice for those running an OS without built in snprintf()
+# is to upgrade. :)
+#
+#
+# Generic compiler (usually linked to gcc on most platforms)
+CC = cc
+
+# GNU..
+#CC = gcc
+
+# Normal systems flags
+CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -Wall
+
+# Debug mode for kalasag
+#CFLAGS = -Wall -g -DNODAEMON -DDEBUG
+#CFLAGS = -Wall -g -DNODAEMON
+#CFLAGS = -Wall -g -DDEBUG
+
+# Profiler mode for kalasag
+#CFLAGS = -pg -O -Wall -DNODAEMON
+#LIBS = /usr/lib/libefence.a
+
+INSTALLDIR = /opt
+CHILDDIR=/kalasag
+
+all:
+ @echo "Usage: make <systype>"
+ @echo "<systype> is one of: linux, debian-linux, bsd, solaris, hpux, hpux-gcc,"
+ @echo "freebsd, osx, openbsd, netbsd, bsdi, aix, osf, irix, generic"
+ @echo ""
+ @echo "This code requires snprintf()/vsnprintf() system calls"
+ @echo "to work. If you run a modern OS it should work on"
+ @echo "your system with 'make generic'. If you get it to"
+ @echo "work on an unlisted OS please write us with the"
+ @echo "changes."
+ @echo ""
+ @echo "Install: make install"
+ @echo ""
+ @echo "NOTE: This will install the package in this"
+ @echo " directory: $(INSTALLDIR)"
+ @echo ""
+ @echo "Edit the makefile if you wish to change these paths."
+ @echo "Any existing files will be overwritten."
+ @echo ""
+
+clean:
+ /bin/rm ./kalasag
+
+uninstall:
+ /bin/rm $(INSTALLDIR)$(CHILDDIR)/*
+ /bin/rmdir $(INSTALLDIR)$(CHILDDIR)
+
+install:
+ @echo "Creating directory $(INSTALLDIR)"
+ @if [ ! -d $(INSTALLDIR) ]; then /bin/mkdir $(INSTALLDIR); fi
+ @echo "Setting directory permissions"
+ @echo "Creating kalasag directory $(INSTALLDIR)$(CHILDDIR)"
+ @if [ ! -d $(INSTALLDIR)$(CHILDDIR) ]; then /bin/mkdir $(INSTALLDIR)$(CHILDDIR); fi
+ @echo "Setting directory permissions"
+ chmod 700 $(INSTALLDIR)$(CHILDDIR)
+ @echo "Copying files"
+ cp ./kalasag.conf $(INSTALLDIR)$(CHILDDIR)
+ cp ./kalasag.ignore $(INSTALLDIR)$(CHILDDIR)
+ cp ./kalasag $(INSTALLDIR)$(CHILDDIR)
+ @echo "Setting permissions"
+ chmod 600 $(INSTALLDIR)$(CHILDDIR)/kalasag.ignore
+ chmod 600 $(INSTALLDIR)$(CHILDDIR)/kalasag.conf
+ chmod 700 $(INSTALLDIR)$(CHILDDIR)/kalasag
+ @echo ""
+ @echo ""
+ @echo "Edit $(INSTALLDIR)$(CHILDDIR)/kalasag.conf and change"
+ @echo "your settings if you haven't already. (route, etc)"
+ @echo ""
+
+linux:
+ SYSTYPE=linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DLINUX -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+linux-x86_64:
+ SYSTYPE=linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -m64 -DLINUX -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+debian-linux:
+ SYSTYPE=debian-linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DLINUX -DDEBIAN -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+
+bsd:
+ SYSTYPE=bsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+openbsd:
+ SYSTYPE=openbsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+freebsd:
+ SYSTYPE=freebsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+osx:
+ SYSTYPE=osx
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+netbsd:
+ SYSTYPE=netbsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+bsdi:
+ SYSTYPE=bsdi
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+generic:
+ SYSTYPE=generic
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+hpux:
+ SYSTYPE=hpux
+ @echo "Making $(SYSTYPE)"
+ $(CC) -Ae -DHPUX -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+hpux-gcc:
+ SYSTYPE=hpux-gcc
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DHPUX -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+solaris:
+ SYSTYPE=solaris
+ @echo "Making $(SYSTYPE)"
+ $(CC) -lnsl -lsocket -lresolv -lc -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+aix:
+ SYSTYPE=aix
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+osf:
+ SYSTYPE=osf
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -taso -ldb -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+irix:
+ SYSTYPE=irix
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -O -n32 -mips3 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+# NeXTSTEP Users. NeXT used to work, but we changed the log function and
+# it now uses vsnprintf() to format strings. This means that this
+# version does not work under NeXTSTEP until we can find a workable
+# vsnprintf() call to put in the program. Sorry. If you have some good
+# vsnprintf() code to use under NeXT please send it to us and we'll
+# include it on the next update.
+#next:
+# SYSTYPE=next
+# @echo "Making $(SYSTYPE)"
+# $(CC) $(CFLAGS) -DNEXT -DHAS_NO_SNPRINTF -posix -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
diff --git a/kalasag.c b/kalasag.c
new file mode 100644
index 0000000..2145b43
--- /dev/null
+++ b/kalasag.c
@@ -0,0 +1,1536 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* Global variables */
+char gblScanDetectHost[MAXSTATE][IPMAXBUF];
+char gblKillRoute[MAXBUF];
+char gblKillHostsDeny[MAXBUF];
+char gblKillRunCmd[MAXBUF];
+char gblBlockedFile[MAXBUF];
+char gblHistoryFile[MAXBUF];
+char gblIgnoreFile[MAXBUF];
+char gblDetectionType[MAXBUF];
+
+int gblScanDetectCount = 0;
+int gblBlockTCP = 0;
+int gblBlockUDP = 0;
+int gblRunCmdFirst = 0;
+int gblResolveHost = 0;
+int gblConfigTriggerCount = 0;
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ Usage();
+ Exit(ERROR);
+ }
+
+ if ((geteuid()) && (getuid()) != 0) {
+ printf("You need to be root to run this.\n");
+ Exit(ERROR);
+ }
+
+
+ /* Cheesy arg parser. Some systems don't support getopt and I don't want to port it. */
+ if ((strcmp(argv[1], "-tcp")) && (strcmp(argv[1], "-udp"))
+ && (strcmp(argv[1], "-stcp")) && (strcmp(argv[1], "-atcp"))
+ && (strcmp(argv[1], "-sudp")) && (strcmp(argv[1], "-audp")) != 0) {
+ Usage();
+ Exit(ERROR);
+ } else {
+ Start();
+ /* This copies the startup type to a global for later use */
+ if ((SafeStrncpy
+ (gblDetectionType, strstr(argv[1], "-") + 1, MAXBUF))
+ == NULL) {
+ Log("adminalert: ERROR: Error setting internal scan detection type.\n");
+ printf("ERROR: Error setting internal scan detection type.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ } else if (CheckConfig() == FALSE) {
+ Log("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n");
+ printf("ERROR: Configuration files are missing/corrupted.\n");
+ printf
+ ("ERROR: Check your syslog for a more detailed error message.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ } else if (InitConfig() == FALSE) {
+ Log("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shutting down.\n");
+ printf
+ ("ERROR: Your config file is corrupted/missing mandatory option!\n");
+ printf
+ ("ERROR: Check your syslog for a more detailed error message.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ }
+#ifndef NODAEMON
+ else if (DaemonSeed() == ERROR) {
+ Log("adminalert: ERROR: could not go into daemon mode. Shutting down.\n");
+ printf
+ ("ERROR: could not go into daemon mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+#endif
+ }
+
+
+ if (strcmp(argv[1], "-tcp") == 0) {
+ if (KalasagModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+#ifdef SUPPORT_STEALTH
+ else if (strcmp(argv[1], "-stcp") == 0) {
+ if (KalasagStealthModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-atcp") == 0) {
+ if (KalasagAdvancedStealthModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-sudp") == 0) {
+ if (KalasagStealthModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-audp") == 0) {
+ if (KalasagAdvancedStealthModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+#endif
+ else if (strcmp(argv[1], "-udp") == 0) {
+ if (KalasagModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+
+ Exit(TRUE);
+ /* shuts up compiler warning */
+ return (0);
+}
+
+/****************************************************************/
+/* Reads generic config options into global variables */
+/****************************************************************/
+int InitConfig(void)
+{
+ FILE *input;
+ char configToken[MAXBUF];
+
+ gblBlockTCP = CheckFlag("BLOCK_TCP");
+ gblBlockUDP = CheckFlag("BLOCK_UDP");
+ gblResolveHost = CheckFlag("RESOLVE_HOST");
+
+ memset(gblKillRoute, '\0', MAXBUF);
+ memset(gblKillHostsDeny, '\0', MAXBUF);
+ memset(gblKillRunCmd, '\0', MAXBUF);
+
+ if ((ConfigTokenRetrieve("SCAN_TRIGGER", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER");
+ gblConfigTriggerCount = 0;
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n",
+ configToken);
+#endif
+ gblConfigTriggerCount = atoi(configToken);
+ }
+
+ if ((ConfigTokenRetrieve("KILL_ROUTE", gblKillRoute)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_ROUTE option: %s \n",
+ gblKillRoute);
+#endif
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("KILL_HOSTS_DENY", gblKillHostsDeny)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n",
+ gblKillHostsDeny);
+#endif
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("KILL_RUN_CMD", gblKillRunCmd)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n",
+ gblKillRunCmd);
+#endif
+ /* Check the order we should run the KILL_RUN_CMD */
+ /* Default is to run the command after blocking */
+ gblRunCmdFirst = CheckFlag("KILL_RUN_CMD_FIRST");
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("BLOCKED_FILE", gblBlockedFile)) == TRUE) {
+ if (strlen(gblBlockedFile) < MAXBUF - 5) {
+ strncat(gblBlockedFile, ".", 1);
+ strncat(gblBlockedFile, gblDetectionType, 4);
+ } else {
+ Log("adminalert: ERROR: Blocked filename is too long to append detection type file extension: %s.\n", gblBlockedFile);
+ return (FALSE);
+ }
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n",
+ gblBlockedFile);
+ Log("debug: CheckConfig: Removing old block file: %s \n",
+ gblBlockedFile);
+#endif
+
+ if ((input = fopen(gblBlockedFile, "w")) == NULL) {
+ Log("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n", gblBlockedFile);
+ return (FALSE);
+ } else
+ fclose(input);
+ } else {
+ Log("InitConfig: Cannot retrieve BLOCKED_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+
+ if ((ConfigTokenRetrieve("HISTORY_FILE", gblHistoryFile)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved HISTORY_FILE option: %s \n",
+ gblHistoryFile);
+#endif
+ } else {
+ Log("InitConfig: Cannot retrieve HISTORY_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+ if ((ConfigTokenRetrieve("IGNORE_FILE", gblIgnoreFile)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved IGNORE_FILE option: %s \n",
+ gblIgnoreFile);
+#endif
+ } else {
+ Log("InitConfig: Cannot retrieve IGNORE_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+#ifdef SUPPORT_STEALTH
+
+/* Read in a TCP packet taking into account IP options and other */
+/* errors */
+int PacketReadTCP(int socket, struct iphdr *ipPtr, struct tcphdr *tcpPtr)
+{
+ char packetBuffer[TCPPACKETLEN];
+ struct in_addr addr;
+
+ bzero(ipPtr, sizeof(struct iphdr));
+ bzero(tcpPtr, sizeof(struct tcphdr));
+
+ if (read(socket, packetBuffer, TCPPACKETLEN) == ERROR)
+ return (ERROR);
+
+ memcpy(ipPtr, (struct iphdr *) packetBuffer, sizeof(struct iphdr));
+
+ if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) {
+ addr.s_addr = (u_int) ipPtr->saddr;
+ Log("attackalert: Illegal IP header length detected in TCP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa(addr));
+ return (FALSE);
+ } else {
+ memcpy(tcpPtr,
+ (struct tcphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
+ sizeof(struct tcphdr));
+ return (TRUE);
+ }
+
+}
+
+/* Read in a UDP packet taking into account IP options and other */
+/* errors */
+int PacketReadUDP(int socket, struct iphdr *ipPtr, struct udphdr *udpPtr)
+{
+ char packetBuffer[UDPPACKETLEN];
+ struct in_addr addr;
+
+ bzero(ipPtr, sizeof(struct iphdr));
+ bzero(udpPtr, sizeof(struct udphdr));
+
+ if (read(socket, packetBuffer, UDPPACKETLEN) == ERROR)
+ return (ERROR);
+
+ memcpy(ipPtr, (struct iphdr *) packetBuffer, sizeof(struct iphdr));
+
+ if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) {
+ addr.s_addr = (u_int) ipPtr->saddr;
+ Log("attackalert: Illegal IP header length detected in UDP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa(addr));
+ return (FALSE);
+ } else {
+ memcpy(udpPtr,
+ (struct udphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
+ sizeof(struct udphdr));
+ return (TRUE);
+ }
+
+}
+
+/****************************************************************/
+/* Stealth scan detection Mode One */
+/* */
+/* This mode will read in a list of ports to monitor and will */
+/* then open a raw socket to look for packets matching the port. */
+/* */
+/****************************************************************/
+int KalasagStealthModeTCP(void)
+{
+ struct sockaddr_in client, server;
+ int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS];
+ int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE, result =
+ TRUE;
+ int openSockfd = 0, incomingPort = 0;
+ char *temp, target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *packetType;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct tcphdr tcp;
+
+ if ((ConfigTokenRetrieve("TCP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read TCP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* ok, now check if they have a network daemon on the socket already, if they do */
+ /* then skip that port because it will cause false alarms */
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into stealth listen mode on TCP port: %d\n",
+ ports[count]);
+ if ((openSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket(openSockfd, client, server, ports[count]) == ERROR)
+ Log("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]);
+ else { /* well we at least bound to one socket so we'll continue */
+
+ gotBound = TRUE;
+ ports2[portCount2++] = ports[count];
+ }
+ close(openSockfd);
+ }
+
+ /* if we didn't bind to anything then abort */
+ if (gotBound == FALSE) {
+ Log("adminalert: ERROR: All supplied TCP sockets are in use and will not be listened to. Shutting down.\n");
+ return (ERROR);
+ }
+
+ /* Open our raw socket for network IO */
+ if ((openSockfd = OpenRAWTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadTCP(openSockfd, &ip, &tcp) != TRUE)
+ continue;
+
+
+ incomingPort = ntohs(tcp.dest);
+
+ /* check for an ACK/RST to weed out established connections in case the user */
+ /* is monitoring high ephemeral port numbers */
+ if ((tcp.ack != 1) && (tcp.rst != 1)) {
+ /* this iterates the list of ports looking for a match */
+ for (count = 0; count < portCount; count++) {
+ if (incomingPort == ports2[count]) {
+ if (SmartVerifyTCP(client, server, incomingPort) ==
+ TRUE)
+ break;
+
+ /* copy the clients address into our buffer for nuking */
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ packetType = ReportPacketType(tcp);
+ Log("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, ports2[count]);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, ports2[count]);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ /* toast the prick */
+ if (DisposeTCP(target, ports2[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s !!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports2[count],
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ break; /* get out of for(count) loop above */
+ } /* end if(incoming port) == protected port */
+ } /* end for( check for protected port loop ) loop */
+ } /* end if(TH_ACK) check */
+ } /* end for( ; ; ) loop */
+
+} /* end KalasagStealthModeTCP */
+
+
+/****************************************************************/
+/* Advanced Stealth scan detection Mode One */
+/* */
+/* This mode will see what ports are listening below 1024 */
+/* and will then monitor all the rest. This is very sensitive */
+/* and will react on any packet hitting any monitored port, */
+/* regardless of TCP flags set */
+/* */
+/****************************************************************/
+int KalasagAdvancedStealthModeTCP(void)
+{
+ struct sockaddr_in client, server;
+ int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
+ int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
+ unsigned int advancedPorts = 1024;
+ unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
+ char target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *temp, *packetType;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct tcphdr tcp;
+
+ if ((ConfigTokenRetrieve("ADVANCED_PORTS_TCP", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read ADVANCED_PORTS_TCP option from config file. Assuming 1024.");
+ advancedPorts = 1024;
+ } else
+ advancedPorts = atoi(configToken);
+
+ Log("adminalert: Advanced mode will monitor first %d ports",
+ advancedPorts);
+
+ /* try to bind to all ports below 1024, any that are taken we exclude later */
+ for (count = 0; count < advancedPorts; count++) {
+ if ((openSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+ if (BindSocket(openSockfd, client, server, count) == ERROR)
+ inUsePorts[portCount++] = count;
+
+ close(openSockfd);
+ }
+
+ if ((ConfigTokenRetrieve("ADVANCED_EXCLUDE_TCP", configToken)) !=
+ FALSE) {
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ for (count = 0; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ } else
+ break;
+ }
+ }
+ } else
+ Log("adminalert: Advanced mode will manually exclude no ports");
+
+
+ for (count = 0; count < portCount; count++)
+ Log("adminalert: Advanced Stealth scan detection mode activated. Ignored TCP port: %d\n", inUsePorts[count]);
+
+ /* open raw socket for reading */
+ if ((openSockfd = OpenRAWTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadTCP(openSockfd, &ip, &tcp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(tcp.dest);
+
+ /* don't monitor packets with ACK set (established) or RST */
+ /* This could be a hole in some cases */
+ if ((tcp.ack != 1) && (tcp.rst != 1)) {
+ /* check if we should ignore this connection to this port */
+ for (count = 0; count < portCount; count++) {
+ if ((incomingPort == inUsePorts[count])
+ || (incomingPort >= advancedPorts)) {
+ hotPort = FALSE;
+ break;
+ } else
+ hotPort = TRUE;
+ }
+
+ if (hotPort) {
+ smartVerify = SmartVerifyTCP(client, server, incomingPort);
+
+ if (smartVerify != TRUE) {
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ packetType = ReportPacketType(tcp);
+ Log("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, incomingPort);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ /* toast the prick */
+ if (DisposeTCP(target, incomingPort) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ incomingPort,
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ } /* end if(smartVerify) */
+ } /* end if(hotPort) */
+ } /* end if(TH_ACK) */
+ } /* end for( ; ; ) loop */
+}
+
+/* end KalasagAdvancedStealthModeTCP */
+
+
+
+/****************************************************************/
+/* UDP "stealth" scan detection */
+/* */
+/* This mode will read in a list of ports to monitor and will */
+/* then open a raw socket to look for packets matching the port. */
+/* */
+/****************************************************************/
+int KalasagStealthModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS],
+ result = TRUE;
+ int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE;
+ int openSockfd = 0, incomingPort = 0;
+ char *temp, target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ struct in_addr addr;
+ struct iphdr ip;
+ struct udphdr udp;
+
+
+ if ((ConfigTokenRetrieve("UDP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read UDP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* ok, now check if they have a network daemon on the socket already, if they do */
+ /* then skip that port because it will cause false alarms */
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into stealth listen mode on UDP port: %d\n",
+ ports[count]);
+ if ((openSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket(openSockfd, client, server, ports[count]) == ERROR)
+ Log("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]);
+ else {
+ gotBound = TRUE;
+ ports2[portCount2++] = ports[count];
+ }
+ close(openSockfd);
+ }
+
+ if (gotBound == FALSE) {
+ Log("adminalert: ERROR: All supplied UDP sockets are in use and will not be listened to. Shutting down.\n");
+ return (ERROR);
+ }
+
+ if ((openSockfd = OpenRAWUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadUDP(openSockfd, &ip, &udp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(udp.dest);
+
+ /* this iterates the list of ports looking for a match */
+ for (count = 0; count < portCount; count++) {
+ if (incomingPort == ports2[count]) {
+ if (SmartVerifyUDP(client, server, incomingPort) == TRUE)
+ break;
+
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr), IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target) !=
+ TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, ports2[count]);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, ports2[count]) != TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports2[count], gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } /* end IsBlocked check */
+ else {
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ }
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ break; /* get out of for(count) loop above */
+ } /* end if(incoming port) == protected port */
+ } /* end for( check for protected port loop ) loop */
+ } /* end for( ; ; ) loop */
+
+} /* end KalasagStealthModeUDP */
+
+
+/****************************************************************/
+/* Advanced Stealth scan detection mode for UDP */
+/* */
+/* This mode will see what ports are listening below 1024 */
+/* and will then monitor all the rest. This is very sensitive */
+/* and will react on any packet hitting any monitored port. */
+/* This is a very dangerous option and is for advanced users */
+/* */
+/****************************************************************/
+int KalasagAdvancedStealthModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
+ int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
+ unsigned int advancedPorts = 1024;
+ unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
+ char target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *temp;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct udphdr udp;
+
+
+ if ((ConfigTokenRetrieve("ADVANCED_PORTS_UDP", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read ADVANCED_PORTS_UDP option from config file. Assuming 1024.");
+ advancedPorts = 1024;
+ } else
+ advancedPorts = atoi(configToken);
+
+ Log("adminalert: Advanced mode will monitor first %d ports",
+ advancedPorts);
+
+ /* try to bind to all ports below 1024, any that are taken we exclude later */
+ for (count = 0; count < advancedPorts; count++) {
+ if ((openSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+ if (BindSocket(openSockfd, client, server, count) == ERROR)
+ inUsePorts[portCount++] = count;
+
+ close(openSockfd);
+ }
+
+ if ((ConfigTokenRetrieve("ADVANCED_EXCLUDE_UDP", configToken)) !=
+ FALSE) {
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ for (count = 0; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ } else
+ break;
+ }
+ }
+ } else
+ Log("adminalert: Advanced mode will manually exclude no ports");
+
+
+ for (count = 0; count < portCount; count++)
+ Log("adminalert: Advanced Stealth scan detection mode activated. Ignored UDP port: %d\n", inUsePorts[count]);
+
+ if ((openSockfd = OpenRAWUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadUDP(openSockfd, &ip, &udp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(udp.dest);
+
+ /* check if we should ignore this connection to this port */
+ for (count = 0; count < portCount; count++) {
+ if ((incomingPort == inUsePorts[count])
+ || (incomingPort >= advancedPorts)) {
+ hotPort = FALSE;
+ break;
+ } else
+ hotPort = TRUE;
+ }
+
+ if (hotPort) {
+ smartVerify = SmartVerifyUDP(client, server, incomingPort);
+
+ if (smartVerify != TRUE) {
+ /* copy the clients address into our buffer for nuking */
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr), IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target) !=
+ TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, incomingPort);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, incomingPort) != TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ incomingPort, gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ } /* end if (smartVerify) */
+ } /* end if(hotPort) */
+ } /* end for( ; ; ) loop */
+}
+
+/* end KalasagAdvancedStealthModeUDP */
+
+#endif
+
+
+
+
+/****************************************************************/
+/* Classic detection Mode */
+/* */
+/* This mode will bind to a list of TCP sockets and wait for */
+/* connections to happen. Although the least prone to false */
+/* alarms, it also won't detect stealth scans */
+/* */
+/****************************************************************/
+int KalasagModeTCP(void)
+{
+
+ struct sockaddr_in client, server;
+ int length, portCount = 0, ports[MAXSOCKS];
+ int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
+ int count = 0, scanDetectTrigger = TRUE, showBanner =
+ FALSE, boundPortCount = 0;
+ int selectResult = 0;
+ char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF],
+ configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ fd_set selectFds;
+
+ if ((ConfigTokenRetrieve("TCP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read TCP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* read in the banner if one is given */
+ if ((ConfigTokenRetrieve("PORT_BANNER", configToken)) == TRUE) {
+ showBanner = TRUE;
+ SafeStrncpy(bannerBuffer, configToken, MAXBUF);
+ }
+
+
+ /* setup select call */
+ FD_ZERO(&selectFds);
+
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into listen mode on TCP port: %d\n",
+ ports[count]);
+ if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket
+ (openSockfd[boundPortCount], client, server,
+ ports[count]) == ERROR) {
+ Log("adminalert: ERROR: could not bind TCP socket: %d. Attempting to continue\n", ports[count]);
+ } else /* well we at least bound to one socket so we'll continue */
+ boundPortCount++;
+ }
+
+
+ /* if we didn't bind to anything then abort */
+ if (boundPortCount == 0) {
+ Log("adminalert: ERROR: could not bind ANY TCP sockets. Shutting down.\n");
+ return (ERROR);
+ }
+
+ length = sizeof(client);
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main loop for multiplexing/resetting */
+ for (;;) {
+ /* set up select call */
+ for (count = 0; count < boundPortCount; count++)
+ FD_SET(openSockfd[count], &selectFds);
+ selectResult =
+ select(MAXSOCKS, &selectFds, NULL, NULL,
+ (struct timeval *) NULL);
+
+ /* something blew up */
+ if (selectResult < 0) {
+ Log("adminalert: ERROR: select call failed. Shutting down.\n");
+ return (ERROR);
+ } else if (selectResult == 0) {
+#ifdef DEBUG
+ Log("Select timeout");
+#endif
+ }
+
+ /* select is reporting a waiting socket. Poll them all to find out which */
+ else if (selectResult > 0) {
+ for (count = 0; count < boundPortCount; count++) {
+ if (FD_ISSET(openSockfd[count], &selectFds)) {
+ incomingSockfd =
+ accept(openSockfd[count],
+ (struct sockaddr *) &client, &length);
+ if (incomingSockfd < 0) {
+ Log("attackalert: Possible stealth scan from unknown host to TCP port: %d (accept failed)", ports[count]);
+ break;
+ }
+
+ /* copy the clients address into our buffer for nuking */
+ SafeStrncpy(target,
+ (char *) inet_ntoa(client.sin_addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ /* show the banner if one was selected */
+ if (showBanner == TRUE)
+ write(incomingSockfd, bannerBuffer,
+ strlen(bannerBuffer));
+ /* we don't need the bonehead anymore */
+ close(incomingSockfd);
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: Connect from host: %s/%s to TCP port: %d", resolvedHost, target, ports[count]);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeTCP(target, ports[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s !!", target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports[count],
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } else
+ Log("attackalert: Host: %s is already blocked. Ignoring", target);
+ }
+ }
+ close(incomingSockfd);
+ break;
+ } /* end if(FD_ISSET) */
+ } /* end for() */
+ } /* end else (selectResult > 0) */
+ } /* end main for(; ; ) loop */
+
+/* not reached */
+ close(incomingSockfd);
+}
+
+
+
+
+
+/****************************************************************/
+/* Classic detection Mode */
+/* */
+/* This mode will bind to a list of UDP sockets and wait for */
+/* connections to happen. Stealth scanning really doesn't apply */
+/* here. */
+/* */
+/****************************************************************/
+int KalasagModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int length, ports[MAXSOCKS], openSockfd[MAXSOCKS], result = TRUE;
+ int count = 0, portCount = 0, selectResult = 0, scanDetectTrigger = 0;
+ int boundPortCount = 0, showBanner = FALSE;
+ char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF],
+ configToken[MAXBUF];
+ char buffer[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ fd_set selectFds;
+
+ if ((ConfigTokenRetrieve("UDP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read UDP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* read in the banner if one is given */
+ if ((ConfigTokenRetrieve("PORT_BANNER", configToken)) == TRUE) {
+ showBanner = TRUE;
+ SafeStrncpy(bannerBuffer, configToken, MAXBUF);
+ }
+
+ /* setup select call */
+ FD_ZERO(&selectFds);
+
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into listen mode on UDP port: %d\n",
+ ports[count]);
+ if ((openSockfd[boundPortCount] = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting\n");
+ return (ERROR);
+ }
+ if (BindSocket
+ (openSockfd[boundPortCount], client, server,
+ ports[count]) == ERROR) {
+ Log("adminalert: ERROR: could not bind UDP socket: %d. Attempting to continue\n", ports[count]);
+ } else /* well we at least bound to one socket so we'll continue */
+ boundPortCount++;
+ }
+
+/* if we didn't bind to anything then abort */
+ if (boundPortCount == 0) {
+ Log("adminalert: ERROR: could not bind ANY UDP sockets. Shutting down.\n");
+ return (ERROR);
+ }
+
+
+ length = sizeof(client);
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+/* main loop for multiplexing/resetting */
+ for (;;) {
+ /* set up select call */
+ for (count = 0; count < boundPortCount; count++)
+ FD_SET(openSockfd[count], &selectFds);
+ /* setup the select multiplexing (blocking mode) */
+ selectResult =
+ select(MAXSOCKS, &selectFds, NULL, NULL,
+ (struct timeval *) NULL);
+
+ if (selectResult < 0) {
+ Log("adminalert: ERROR: select call failed. Shutting down.\n");
+ return (ERROR);
+ } else if (selectResult == 0) {
+#ifdef DEBUG
+ Log("Select timeout");
+#endif
+ }
+
+ /* select is reporting a waiting socket. Poll them all to find out which */
+ else if (selectResult > 0) {
+ for (count = 0; count < portCount; count++) {
+ if (FD_ISSET(openSockfd[count], &selectFds)) {
+ /* here just read in one byte from the UDP socket, that's all we need to */
+ /* know that this person is a jerk */
+ if (recvfrom(openSockfd[count], buffer, 1, 0,
+ (struct sockaddr *) &client, &length) < 0)
+ {
+ Log("adminalert: ERROR: could not accept incoming socket for UDP port: %d\n", ports[count]);
+ break;
+ }
+
+ /* copy the clients address into our buffer for nuking */
+ SafeStrncpy(target,
+ (char *) inet_ntoa(client.sin_addr),
+ IPMAXBUF);
+#ifdef DEBUG
+ Log("debug: KalasagModeUDP: accepted UDP connection from: %s\n", target);
+#endif
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ /* show the banner if one was selected */
+ if (showBanner == TRUE)
+ sendto(openSockfd[count], bannerBuffer,
+ strlen(bannerBuffer), 0,
+ (struct sockaddr *) &client,
+ length);
+
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: Connect from host: %s/%s to UDP port: %d", resolvedHost, target, ports[count]);
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, ports[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s !!", target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports[count],
+ gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } else
+ Log("attackalert: Host: %s is already blocked. Ignoring", target);
+ }
+ }
+ break;
+ } /* end if(FD_ISSET) */
+ } /* end for() */
+ } /* end else (selectResult > 0) */
+ } /* end main for(; ; ) loop */
+
+} /* end UDP Kalasag */
+
+
+
+
+/* kill the TCP connection depending on config option */
+int DisposeTCP(char *target, int port)
+{
+ int status = TRUE;
+
+#ifdef DEBUG
+ Log("debug: DisposeTCP: disposing of host %s on port %d with option: %d", target, port, gblBlockTCP);
+ Log("debug: DisposeTCP: killRunCmd: %s", gblKillRunCmd);
+ Log("debug: DisposeTCP: gblRunCmdFirst: %d", gblRunCmdFirst);
+ Log("debug: DisposeTCP: killHostsDeny: %s", gblKillHostsDeny);
+ Log("debug: DisposeTCP: killRoute: %s %d", gblKillRoute,
+ strlen(gblKillRoute));
+#endif
+/* Should we ignore TCP from active response? */
+ if (gblBlockTCP == 1) {
+ /* run external command first, hosts.deny second, dead route last */
+ if (gblRunCmdFirst) {
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ }
+ /* run hosts.deny first, dead route second, external command last */
+ else {
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ }
+ } else if (gblBlockTCP == 2) {
+ /* run external command only */
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd(target, port, gblKillRunCmd, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ } else
+ Log("attackalert: Ignoring TCP response per configuration file setting.");
+
+ return (status);
+}
+
+
+/* kill the UDP connection depending on config option */
+int DisposeUDP(char *target, int port)
+{
+ int status = TRUE;
+
+#ifdef DEBUG
+ Log("debug: DisposeUDP: disposing of host %s on port %d with option: %d", target, port, gblBlockUDP);
+ Log("debug: DisposeUDP: killRunCmd: %d", gblKillRunCmd);
+ Log("debug: DisposeUDP: gblRunCmdFirst: %s", gblRunCmdFirst);
+ Log("debug: DisposeUDP: killHostsDeny: %s", gblKillHostsDeny);
+ Log("debug: DisposeUDP: killRoute: %s %d", gblKillRoute,
+ strlen(gblKillRoute));
+#endif
+/* Should we ignore TCP from active response? */
+ if (gblBlockUDP == 1) {
+ /* run external command first, hosts.deny second, dead route last */
+ if (gblRunCmdFirst) {
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ }
+ /* run hosts.deny first, dead route second, external command last */
+ else {
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ }
+ } else if (gblBlockUDP == 2) {
+ /* run external command only */
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd(target, port, gblKillRunCmd, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ } else
+ Log("attackalert: Ignoring UDP response per configuration file setting.");
+
+ return (status);
+}
+
+
+/* duh */
+void Usage(void)
+{
+ printf("Kalasag Port Scan Detector\n");
+ printf("Version: %s\n\n", VERSION);
+#ifdef SUPPORT_STEALTH
+ printf("usage: kalasag [-tcp -udp -stcp -atcp -sudp -audp]\n\n");
+#else
+ printf("Stealth scan detection not supported on this platform\n");
+ printf("usage: kalasag [-tcp -udp]\n\n");
+#endif
+/*
+ printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n");
+*/
+}
+
+
+
+/* our cheesy state engine to monitor who has connected here before */
+int CheckStateEngine(char *target)
+{
+ int count = 0, scanDetectTrigger = TRUE;
+ int gotOne = 0;
+
+/* This is the rather basic scan state engine. It maintains */
+/* an array of past hosts who triggered a connection on a port */
+/* when a new host arrives it is compared against the array */
+/* if it is found in the array it increments a state counter by */
+/* one and checks the remainder of the array. It does this until */
+/* the end is reached or the trigger value has been exceeded */
+/* This would probably be better as a linked list/hash table, */
+/* but for the number of hosts we are tracking this is just as good. */
+/* This will probably change in the future */
+
+ gotOne = 1; /* our flag counter if we get a match */
+ scanDetectTrigger = TRUE; /* set to TRUE until set otherwise */
+
+ if (gblConfigTriggerCount > 0) {
+ for (count = 0; count < MAXSTATE; count++) {
+ /* if the array has the IP address then increment the gotOne counter and */
+ /* check the trigger value. If it is exceeded break out of the loop and */
+ /* set the detecttrigger to TRUE */
+ if (strcmp(gblScanDetectHost[count], target) == 0) {
+ /* compare the number of matches to the configured trigger value */
+ /* if we've exceeded we can stop this noise */
+ if (++gotOne >= gblConfigTriggerCount) {
+ scanDetectTrigger = TRUE;
+#ifdef DEBUG
+ Log("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n", gblScanDetectHost[count], gblConfigTriggerCount);
+#endif
+ break;
+ }
+ } else
+ scanDetectTrigger = FALSE;
+ }
+
+ /* now add the fresh meat into the state engine */
+ /* if our array is still less than MAXSTATE large add it to the end */
+ if (gblScanDetectCount < MAXSTATE) {
+ SafeStrncpy(gblScanDetectHost[gblScanDetectCount], target,
+ IPMAXBUF);
+ gblScanDetectCount++;
+ } else {
+ /* otherwise tack it to the beginning and start overwriting older ones */
+ gblScanDetectCount = 0;
+ SafeStrncpy(gblScanDetectHost[gblScanDetectCount], target,
+ IPMAXBUF);
+ gblScanDetectCount++;
+ }
+
+#ifdef DEBUG
+ for (count = 0; count < MAXSTATE; count++)
+ Log("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n", gblScanDetectHost[count], count, scanDetectTrigger);
+#endif
+ /* end catch to set state if gblConfigTriggerCount == 0 */
+ if (gotOne >= gblConfigTriggerCount)
+ scanDetectTrigger = TRUE;
+ }
+
+
+ if (gblConfigTriggerCount > MAXSTATE) {
+ Log("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n", gblConfigTriggerCount);
+ Log("Adjust the value lower or recompile with a larger state engine value.\n", MAXSTATE);
+ Log("securityalert: Blocking host anyway because of invalid trigger value");
+ scanDetectTrigger = TRUE;
+ }
+ return (scanDetectTrigger);
+}
+
+
+#ifdef SUPPORT_STEALTH
+/* This takes a tcp packet and reports what type of scan it is */
+char *ReportPacketType(struct tcphdr tcpPkt)
+{
+ static char packetDesc[MAXBUF];
+ static char *packetDescPtr = packetDesc;
+
+ if ((tcpPkt.syn == 0) && (tcpPkt.fin == 0) && (tcpPkt.ack == 0)
+ && (tcpPkt.psh == 0) && (tcpPkt.rst == 0) && (tcpPkt.urg == 0))
+ snprintf(packetDesc, MAXBUF, " TCP NULL scan");
+ else if ((tcpPkt.fin == 1) && (tcpPkt.urg == 1) && (tcpPkt.psh == 1))
+ snprintf(packetDesc, MAXBUF, "TCP XMAS scan");
+ else if ((tcpPkt.fin == 1) && (tcpPkt.syn != 1) && (tcpPkt.ack != 1)
+ && (tcpPkt.psh != 1) && (tcpPkt.rst != 1)
+ && (tcpPkt.urg != 1))
+ snprintf(packetDesc, MAXBUF, "TCP FIN scan");
+ else if ((tcpPkt.syn == 1) && (tcpPkt.fin != 1) && (tcpPkt.ack != 1)
+ && (tcpPkt.psh != 1) && (tcpPkt.rst != 1)
+ && (tcpPkt.urg != 1))
+ snprintf(packetDesc, MAXBUF, "TCP SYN/Normal scan");
+ else
+ snprintf(packetDesc, MAXBUF,
+ "Unknown Type: TCP Packet Flags: SYN: %d FIN: %d ACK: %d PSH: %d URG: %d RST: %d",
+ tcpPkt.syn, tcpPkt.fin, tcpPkt.ack, tcpPkt.psh,
+ tcpPkt.urg, tcpPkt.rst);
+
+ return (packetDescPtr);
+}
+
+int
+SmartVerifyTCP(struct sockaddr_in client, struct sockaddr_in server,
+ int port)
+{
+
+ int testSockfd;
+
+/* Ok here is where we "Smart-Verify" the socket. If the port was previously */
+/* unbound, but now appears to have someone there, then we will skip responding */
+/* to this inbound packet. This a basic "stateful" inspection of the */
+/* the connection */
+
+ if ((testSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket to smart-verify.\n");
+ return (FALSE);
+ } else {
+ if (BindSocket(testSockfd, client, server, port) == ERROR) {
+#ifdef DEBUG
+ Log("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
+#endif
+ close(testSockfd);
+ return (TRUE);
+ }
+ }
+
+ close(testSockfd);
+ return (FALSE);
+}
+
+int
+SmartVerifyUDP(struct sockaddr_in client, struct sockaddr_in server,
+ int port)
+{
+ int testSockfd;
+
+/* Ok here is where we "Smart-Verify" the socket. If the port was previously */
+/* unbound, but now appears to have someone there, then we will skip responding */
+/* to this inbound packet. This essentially is a "stateful" inspection of the */
+/* the connection */
+
+ if ((testSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket to smart-verify.\n");
+ return (FALSE);
+ } else {
+ if (BindSocket(testSockfd, client, server, port) == ERROR) {
+#ifdef DEBUG
+ Log("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
+#endif
+ close(testSockfd);
+ return (TRUE);
+ }
+ }
+
+ close(testSockfd);
+ return (FALSE);
+}
+
+#endif /* SUPPORT_STEALTH */
diff --git a/kalasag.conf b/kalasag.conf
new file mode 100644
index 0000000..aaa111b
--- /dev/null
+++ b/kalasag.conf
@@ -0,0 +1,293 @@
+# Kalasag Configuration
+#
+# IMPORTANT NOTE: You CAN NOT put spaces between your port arguments.
+#
+# The default ports will catch a large number of common probes
+#
+# All entries must be in quotes.
+
+
+#######################
+# Port Configurations #
+#######################
+#
+#
+# Some example port configs for classic and basic Stealth modes
+#
+# I like to always keep some ports at the "low" end of the spectrum.
+# This will detect a sequential port sweep really quickly and usually
+# these ports are not in use (i.e. tcpmux port 1)
+#
+# ** X-Windows Users **: If you are running X on your box, you need to be sure
+# you are not binding Kalasag to port 6000 (or port 2000 for OpenWindows users).
+# Doing so will prevent the X-client from starting properly.
+#
+# These port bindings are *ignored* for Advanced Stealth Scan Detection Mode.
+#
+
+# Un-comment these if you are really anal:
+#TCP_PORTS="1,7,9,11,15,70,79,80,109,110,111,119,138,139,143,512,513,514,515,540,635,1080,1524,2000,2001,4000,4001,5742,6000,6001,6667,12345,12346,20034,27665,30303,32771,32772,32773,32774,31337,40421,40425,49724,54320"
+#UDP_PORTS="1,7,9,66,67,68,69,111,137,138,161,162,474,513,517,518,635,640,641,666,700,2049,31335,27444,34555,32770,32771,32772,32773,32774,31337,54321"
+#
+# Use these if you just want to be aware:
+TCP_PORTS="1,11,15,79,111,119,143,540,635,1080,1524,2000,5742,6667,12345,12346,20034,27665,31337,32771,32772,32773,32774,40421,49724,54320"
+UDP_PORTS="1,7,9,69,161,162,513,635,640,641,700,37444,34555,31335,32770,32771,32772,32773,32774,31337,54321"
+#
+# Use these for just bare-bones
+#TCP_PORTS="1,11,15,110,111,143,540,635,1080,1524,2000,12345,12346,20034,32771,32772,32773,32774,49724,54320"
+#UDP_PORTS="1,7,9,69,161,162,513,640,700,32770,32771,32772,32773,32774,31337,54321"
+
+###########################################
+# Advanced Stealth Scan Detection Options #
+###########################################
+#
+# This is the number of ports you want Kalasag to monitor in Advanced mode.
+# Any port *below* this number will be monitored. Right now it watches
+# everything below 1024.
+#
+# On many Linux systems you cannot bind above port 61000. This is because
+# these ports are used as part of IP masquerading. I don't recommend you
+# bind over this number of ports. Realistically: I DON'T RECOMMEND YOU MONITOR
+# OVER 1024 PORTS AS YOUR FALSE ALARM RATE WILL ALMOST CERTAINLY RISE. You've been
+# warned! Don't write me if you have have a problem because I'll only tell
+# you to RTFM and don't run above the first 1024 ports.
+#
+#
+ADVANCED_PORTS_TCP="1024"
+ADVANCED_PORTS_UDP="1024"
+#
+# This field tells Kalasag what ports (besides listening daemons) to
+# ignore. This is helpful for services like ident that services such
+# as FTP, SMTP, and wrappers look for but you may not run (and probably
+# *shouldn't* IMHO).
+#
+# By specifying ports here Kalasag will simply not respond to
+# incoming requests, in effect Kalasag treats them as if they are
+# actual bound daemons. The default ports are ones reported as
+# problematic false alarms and should probably be left alone for
+# all but the most isolated systems/networks.
+#
+# Default TCP ident and NetBIOS service
+ADVANCED_EXCLUDE_TCP="113,139"
+# Default UDP route (RIP), NetBIOS, bootp broadcasts.
+ADVANCED_EXCLUDE_UDP="520,138,137,67"
+
+
+######################
+# Configuration Files#
+######################
+#
+# Hosts to ignore
+IGNORE_FILE="/opt/kalasag/kalasag.ignore"
+# Hosts that have been denied (running history)
+HISTORY_FILE="/opt/kalasag/kalasag.history"
+# Hosts that have been denied this session only (temporary until next restart)
+BLOCKED_FILE="/opt/kalasag/kalasag.blocked"
+
+##############################
+# Misc. Configuration Options#
+##############################
+#
+# DNS Name resolution - Setting this to "1" will turn on DNS lookups
+# for attacking hosts. Setting it to "0" (or any other value) will shut
+# it off.
+RESOLVE_HOST = "1"
+
+###################
+# Response Options#
+###################
+# Options to dispose of attacker. Each is an action that will
+# be run if an attack is detected. If you don't want a particular
+# option then comment it out and it will be skipped.
+#
+# The variable $TARGET$ will be substituted with the target attacking
+# host when an attack is detected. The variable $PORT$ will be substituted
+# with the port that was scanned.
+#
+##################
+# Ignore Options #
+##################
+# These options allow you to enable automatic response
+# options for UDP/TCP. This is useful if you just want
+# warnings for connections, but don't want to react for
+# a particular protocol (i.e. you want to block TCP, but
+# not UDP). To prevent a possible Denial of service attack
+# against UDP and stealth scan detection for TCP, you may
+# want to disable blocking, but leave the warning enabled.
+# I personally would wait for this to become a problem before
+# doing though as most attackers really aren't doing this.
+# The third option allows you to run just the external command
+# in case of a scan to have a pager script or such execute
+# but not drop the route. This may be useful for some admins
+# who want to block TCP, but only want pager/e-mail warnings
+# on UDP, etc.
+#
+#
+# 0 = Do not block UDP/TCP scans.
+# 1 = Block UDP/TCP scans.
+# 2 = Run external command only (KILL_RUN_CMD)
+
+BLOCK_UDP="1"
+BLOCK_TCP="1"
+
+###################
+# Dropping Routes:#
+###################
+# This command is used to drop the route or add the host into
+# a local filter table.
+#
+# The gateway (333.444.555.666) should ideally be a dead host on
+# the *local* subnet. On some hosts you can also point this at
+# localhost (127.0.0.1) and get the same effect. NOTE THAT
+# 333.444.555.66 WILL *NOT* WORK. YOU NEED TO CHANGE IT!!
+#
+# ALL KILL ROUTE OPTIONS ARE COMMENTED OUT INITIALLY. Make sure you
+# uncomment the correct line for your OS. If you OS is not listed
+# here and you have a route drop command that works then please
+# mail it to me so I can include it. ONLY ONE KILL_ROUTE OPTION
+# CAN BE USED AT A TIME SO DON'T UNCOMMENT MULTIPLE LINES.
+#
+# NOTE: The route commands are the least optimal way of blocking
+# and do not provide complete protection against UDP attacks and
+# will still generate alarms for both UDP and stealth scans. I
+# always recommend you use a packet filter because they are made
+# for this purpose.
+#
+
+# Generic
+#KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666"
+
+# Generic Linux
+#KILL_ROUTE="/sbin/route add -host $TARGET$ gw 333.444.555.666"
+
+# Newer versions of Linux support the reject flag now. This
+# is cleaner than the above option.
+#KILL_ROUTE="/sbin/route add -host $TARGET$ reject"
+
+# Generic BSD (BSDI, OpenBSD, NetBSD, FreeBSD)
+#KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666"
+
+# Generic Sun
+#KILL_ROUTE="/usr/sbin/route add $TARGET$ 333.444.555.666 1"
+
+# NEXTSTEP
+#KILL_ROUTE="/usr/etc/route add $TARGET$ 127.0.0.1 1"
+
+# FreeBSD
+#KILL_ROUTE="route add -net $TARGET$ -netmask 255.255.255.255 127.0.0.1 -blackhole"
+
+# Digital UNIX 4.0D (OSF/1 / Compaq Tru64 UNIX)
+#KILL_ROUTE="/sbin/route add -host -blackhole $TARGET$ 127.0.0.1"
+
+# Generic HP-UX
+#KILL_ROUTE="/usr/sbin/route add net $TARGET$ netmask 255.255.255.0 127.0.0.1"
+
+##
+# Using a packet filter is the PREFERRED. The below lines
+# work well on many OS's. Remember, you can only uncomment *one*
+# KILL_ROUTE option.
+##
+
+# ipfwadm support for Linux
+#KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$ -o"
+#
+# ipfwadm support for Linux (no logging of denied packets)
+#KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$"
+#
+# ipchain support for Linux
+#KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l"
+#
+# ipchain support for Linux (no logging of denied packets)
+#KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY"
+#
+# iptables support for Linux
+KILL_ROUTE="/sbin/iptables -A INPUT -s $TARGET$ -j DROP"
+#
+# For those of you running FreeBSD (and compatible) you can
+# use their built in firewalling as well.
+#
+#KILL_ROUTE="/sbin/ipfw add 1 deny all from $TARGET$:255.255.255.255 to any"
+#
+#
+# For those running ipfilt (OpenBSD, etc.)
+# NOTE THAT YOU NEED TO CHANGE external_interface TO A VALID INTERFACE!!
+#
+#KILL_ROUTE="/bin/echo 'block in log on external_interface from $TARGET$/32 to any' | /sbin/ipf -f -"
+
+
+###############
+# TCP Wrappers#
+###############
+# This text will be dropped into the hosts.deny file for wrappers
+# to use. There are two formats for TCP wrappers:
+#
+# Format One: Old Style - The default when extended host processing
+# options are not enabled.
+#
+KILL_HOSTS_DENY="ALL: $TARGET$"
+
+# Format Two: New Style - The format used when extended option
+# processing is enabled. You can drop in extended processing
+# options, but be sure you escape all '%' symbols with a backslash
+# to prevent problems writing out (i.e. \%c \%h )
+#
+#KILL_HOSTS_DENY="ALL: $TARGET$ : DENY"
+
+###################
+# External Command#
+###################
+# This is a command that is run when a host connects, it can be whatever
+# you want it to be (pager, etc.). This command is executed before the
+# route is dropped or after depending on the KILL_RUN_CMD_FIRST option below
+#
+#
+# I NEVER RECOMMEND YOU PUT IN RETALIATORY ACTIONS AGAINST THE HOST SCANNING
+# YOU!
+#
+# TCP/IP is an *unauthenticated protocol* and people can make scans appear out
+# of thin air. The only time it is reasonably safe (and I *never* think it is
+# reasonable) to run reverse probe scripts is when using the "classic" -tcp mode.
+# This mode requires a full connect and is very hard to spoof.
+#
+# The KILL_RUN_CMD_FIRST value should be set to "1" to force the command
+# to run *before* the blocking occurs and should be set to "0" to make the
+# command run *after* the blocking has occurred.
+#
+#KILL_RUN_CMD_FIRST = "0"
+#
+#
+#KILL_RUN_CMD="/some/path/here/script $TARGET$ $PORT$"
+
+
+#####################
+# Scan trigger value#
+#####################
+# Enter in the number of port connects you will allow before an
+# alarm is given. The default is 0 which will react immediately.
+# A value of 1 or 2 will reduce false alarms. Anything higher is
+# probably not necessary. This value must always be specified, but
+# generally can be left at 0.
+#
+# NOTE: If you are using the advanced detection option you need to
+# be careful that you don't make a hair trigger situation. Because
+# Advanced mode will react for *any* host connecting to a non-used
+# below your specified range, you have the opportunity to really
+# break things. (i.e someone innocently tries to connect to you via
+# SSL [TCP port 443] and you immediately block them). Some of you
+# may even want this though. Just be careful.
+#
+SCAN_TRIGGER="0"
+
+######################
+# Port Banner Section#
+######################
+#
+# Enter text in here you want displayed to a person tripping the Kalasag.
+# I *don't* recommend taunting the person as this will aggravate them.
+# Leave this commented out to disable the feature
+#
+# Stealth scan detection modes don't use this feature
+#
+#PORT_BANNER="** UNAUTHORIZED ACCESS PROHIBITED *** YOUR CONNECTION ATTEMPT HAS BEEN LOGGED. GO AWAY."
+
+# EOF
diff --git a/kalasag.h b/kalasag.h
new file mode 100644
index 0000000..99abca2
--- /dev/null
+++ b/kalasag.h
@@ -0,0 +1,74 @@
+#define VERSION "1.0"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <netdb.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#ifndef _LINUX_C_LIB_VERSION
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "kalasag_config.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+#ifdef SUPPORT_STEALTH
+#ifdef LINUX
+#include "kalasag_tcpip.h"
+#include <netinet/in_systm.h>
+#endif
+
+#define TCPPACKETLEN 80
+#define UDPPACKETLEN 68
+#endif /* SUPPORT_STEALTH */
+
+#ifdef NEXT
+#include <ansi.h>
+#endif
+
+#define ERROR -1
+#define TRUE 1
+#define FALSE 0
+#define MAXBUF 1024
+/* max size of an IP address plus NULL */
+#define IPMAXBUF 16
+/* max sockets we can open */
+#define MAXSOCKS 64
+
+/* Really is about 1025, but we don't need the length for our purposes */
+#define DNSMAXBUF 255
+
+
+/* prototypes */
+int KalasagModeTCP(void);
+int KalasagModeUDP(void);
+int DisposeUDP(char *, int);
+int DisposeTCP(char *, int);
+int CheckStateEngine(char *);
+int InitConfig(void);
+void Usage(void);
+int SmartVerifyTCP(struct sockaddr_in, struct sockaddr_in, int);
+int SmartVerifyUDP(struct sockaddr_in, struct sockaddr_in, int);
+
+#ifdef SUPPORT_STEALTH
+int KalasagStealthModeTCP(void);
+int KalasagAdvancedStealthModeTCP(void);
+int KalasagStealthModeUDP(void);
+int KalasagAdvancedStealthModeUDP(void);
+char *ReportPacketType(struct tcphdr);
+int PacketReadTCP(int, struct iphdr *, struct tcphdr *);
+int PacketReadUDP(int, struct iphdr *, struct udphdr *);
+#endif
diff --git a/kalasag.ignore b/kalasag.ignore
new file mode 100644
index 0000000..fa804ed
--- /dev/null
+++ b/kalasag.ignore
@@ -0,0 +1,2 @@
+127.0.0.1/32
+0.0.0.0
diff --git a/kalasag_config.h b/kalasag_config.h
new file mode 100644
index 0000000..6d5c418
--- /dev/null
+++ b/kalasag_config.h
@@ -0,0 +1,15 @@
+/* These are probably ok. Be sure you change the Makefile if you */
+/* change the path */
+#define CONFIG_FILE "/opt/kalasag/kalasag.conf"
+
+/* The location of Wietse Venema's TCP Wrapper hosts.deny file */
+#define WRAPPER_HOSTS_DENY "/etc/hosts.deny"
+
+/* The default syslog is as daemon.notice. You can also use */
+/* any of the facilities from syslog.h to send messages to (LOCAL0, etc) */
+#define SYSLOG_FACILITY LOG_DAEMON
+#define SYSLOG_LEVEL LOG_NOTICE
+
+
+/* the maximum number of hosts to keep in a "previous connect" state engine*/
+#define MAXSTATE 50
diff --git a/kalasag_io.c b/kalasag_io.c
new file mode 100644
index 0000000..f181bc4
--- /dev/null
+++ b/kalasag_io.c
@@ -0,0 +1,752 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* Main logging function to surrogate syslog */
+void Log(char *logentry, ...)
+{
+ char logbuffer[MAXBUF];
+
+ va_list argsPtr;
+ va_start(argsPtr, logentry);
+
+ vsnprintf(logbuffer, MAXBUF, logentry, argsPtr);
+
+ va_end(argsPtr);
+
+ openlog("kalasag", LOG_PID, SYSLOG_FACILITY);
+ syslog(SYSLOG_LEVEL, "%s", logbuffer);
+ closelog();
+}
+
+
+void Exit(int status)
+{
+ Log("securityalert: Kalasag is shutting down\n");
+ Log("adminalert: Kalasag is shutting down\n");
+ exit(status);
+}
+
+
+void Start(void)
+{
+ Log("adminalert: Kalasag %s is starting.\n", VERSION);
+#ifdef DEBUG
+ printf("Compiled: " __DATE__ " at " __TIME__ "\n");
+#endif
+}
+
+
+
+/* The daemonizing code copied from Advanced Programming */
+/* in the UNIX Environment by W. Richard Stevens with minor changes */
+int DaemonSeed(void)
+{
+ int childpid;
+
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, Exit);
+ signal(SIGABRT, Exit);
+ signal(SIGURG, Exit);
+ signal(SIGKILL, Exit);
+
+ if ((childpid = fork()) < 0)
+ return (ERROR);
+ else if (childpid > 0)
+ exit(0);
+
+ setsid();
+ chdir("/");
+ umask(077);
+
+ /* close stdout, stdin, stderr */
+ close(0);
+ close(1);
+ close(2);
+
+ return (TRUE);
+}
+
+
+/* Compares an IP address against a listed address and its netmask*/
+int CompareIPs(char *target, char *ignoreAddr, int ignoreNetmaskBits)
+{
+ unsigned long int netmaskAddr, ipAddr, targetAddr;
+
+ ipAddr = inet_addr(ignoreAddr);
+ targetAddr = inet_addr(target);
+ netmaskAddr = htonl(0xFFFFFFFF << (32 - ignoreNetmaskBits));
+
+
+#ifdef DEBUG
+ Log("debug: target %s\n", target);
+ Log("debug: ignoreAddr %s\n", ignoreAddr);
+ Log("debug: ignoreNetmaskBits %d\n", ignoreNetmaskBits);
+ Log("debug: ipAddr %lu\n", ipAddr);
+ Log("debug: targetAddr %lu\n", targetAddr);
+ Log("debug: netmask %x\n", netmaskAddr);
+ Log("debug: mix ipAddr %lu\n", (ipAddr & netmaskAddr));
+ Log("debug: mix target %lu\n", (targetAddr & netmaskAddr));
+#endif
+
+ /* Network portion mask & op and return */
+ if ((ipAddr & netmaskAddr) == (targetAddr & netmaskAddr))
+ return (TRUE);
+ else
+ return (FALSE);
+}
+
+
+
+/* check hosts that should never be blocked */
+int NeverBlock(char *target, char *filename)
+{
+ FILE *input;
+ char buffer[MAXBUF], tempBuffer[MAXBUF], netmaskBuffer[MAXBUF];
+ char *slashPos;
+ int count = 0, dest = 0, netmaskBits = 0;
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Opening ignore file: %s \n", filename);
+#endif
+ if ((input = fopen(filename, "r")) == NULL)
+ return (ERROR);
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Doing lookup for host: %s \n", target);
+#endif
+
+ while (fgets(buffer, MAXBUF, input) != NULL) {
+ /* Reset destination counter */
+ dest = 0;
+
+ if ((buffer[0] == '#') || (buffer[0] == '\n'))
+ continue;
+
+ for (count = 0; count < strlen(buffer); count++) {
+ /* Parse out digits, colons, and slashes. Everything else rejected */
+ if ((isdigit(buffer[count])) ||
+ (buffer[count] == '.') || (buffer[count] == ':')
+ || (buffer[count] == '/')) {
+ tempBuffer[dest++] = buffer[count];
+ } else {
+ tempBuffer[dest] = '\0';
+ break;
+ }
+ }
+
+ /* Return pointer to slash if it exists and copy data to buffer */
+ slashPos = strchr(tempBuffer, '/');
+ if (slashPos) {
+ SafeStrncpy(netmaskBuffer, slashPos + 1, MAXBUF);
+ /* Terminate tempBuffer string at delimeter for later use */
+ *slashPos = '\0';
+ } else
+ /* Copy in a 32 bit netmask if none given */
+ SafeStrncpy(netmaskBuffer, "32", MAXBUF);
+
+
+ /* Convert netmaskBuffer to bits in netmask */
+ netmaskBits = atoi(netmaskBuffer);
+ if ((netmaskBits < 0) || (netmaskBits > 32)) {
+ Log("adminalert: Invalid netmask in config file: %s Ignoring entry.\n", buffer);
+ continue;
+ }
+
+ if (CompareIPs(target, tempBuffer, netmaskBits)) {
+#ifdef DEBUG
+ Log("debug: NeverBlock: Host: %s found in ignore file with netmask %s\n", target, netmaskBuffer);
+#endif
+
+ fclose(input);
+ return (TRUE);
+ }
+
+ } /* end while() */
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Host: %s NOT found in ignore file\n", target);
+#endif
+
+ fclose(input);
+ return (FALSE);
+}
+
+
+/* Make sure the config file is available */
+int CheckConfig(void)
+{
+ FILE *input;
+
+ if ((input = fopen(CONFIG_FILE, "r")) == NULL) {
+ Log("adminalert: Cannot open config file: %s. Exiting\n",
+ CONFIG_FILE);
+ return (FALSE);
+ } else
+ fclose(input);
+
+ return (TRUE);
+}
+
+
+/* This writes out blocked hosts to the blocked file. It adds the hostname */
+/* time stamp, and port connection that was acted on */
+int
+WriteBlocked(char *target, char *resolvedHost, int port,
+ char *blockedFilename, char *historyFilename, char *portType)
+{
+ FILE *output;
+ int blockedStatus = TRUE, historyStatus = TRUE;
+
+ struct tm *tmptr;
+
+ time_t current_time;
+ current_time = time(0);
+ tmptr = localtime(¤t_time);
+
+
+#ifdef DEBUG
+ Log("debug: WriteBlocked: Opening block file: %s \n", blockedFilename);
+#endif
+
+
+ if ((output = fopen(blockedFilename, "a")) == NULL) {
+ Log("adminalert: ERROR: Cannot open blocked file: %s.\n",
+ blockedFilename);
+ blockedStatus = FALSE;
+ } else {
+ fprintf(output,
+ "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n",
+ current_time, tmptr->tm_mon + 1, tmptr->tm_mday,
+ tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min,
+ tmptr->tm_sec, resolvedHost, target, port, portType);
+ fclose(output);
+ blockedStatus = TRUE;
+ }
+
+#ifdef DEBUG
+ Log("debug: WriteBlocked: Opening history file: %s \n",
+ historyFilename);
+#endif
+ if ((output = fopen(historyFilename, "a")) == NULL) {
+ Log("adminalert: ERROR: Cannot open history file: %s.\n",
+ historyFilename);
+ historyStatus = FALSE;
+ } else {
+ fprintf(output,
+ "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n",
+ current_time, tmptr->tm_mon + 1, tmptr->tm_mday,
+ tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min,
+ tmptr->tm_sec, resolvedHost, target, port, portType);
+ fclose(output);
+ historyStatus = TRUE;
+ }
+
+ if (historyStatus || blockedStatus == FALSE)
+ return (FALSE);
+ else
+ return (TRUE);
+}
+
+
+
+
+/* This reads a token from the config file up to the "=" and returns the string */
+/* up to the first space or NULL */
+int ConfigTokenRetrieve(char *token, char *configToken)
+{
+ FILE *config;
+ char buffer[MAXBUF], tokenBuffer[MAXBUF];
+ int count = 0;
+
+ if ((config = fopen(CONFIG_FILE, "r")) == NULL) {
+ Log("adminalert: ERROR: Cannot open config file: %s.\n",
+ CONFIG_FILE);
+ return (ERROR);
+ } else {
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieve: checking for token %s", token);
+#endif
+ while ((fgets(buffer, MAXBUF, config)) != NULL) {
+ /* this skips comments */
+ if (buffer[0] != '#') {
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieve: data: %s", buffer);
+#endif
+ /* search for the token and make sure the trailing character */
+ /* is a " " or "=" to make sure the entire token was found */
+ if ((strstr(buffer, token) != (char) NULL) &&
+ ((buffer[strlen(token)] == '=')
+ || (buffer[strlen(token)] == ' '))) { /* cut off the '=' and send it back */
+ if (strstr(buffer, "\"") == (char) NULL) {
+ Log("adminalert: Quotes missing from %s token. Option skipped\n", token);
+ fclose(config);
+ return (FALSE);
+ }
+
+ SafeStrncpy(tokenBuffer, strstr(buffer, "\"") + 1,
+ MAXBUF);
+
+ /* strip off unprintables/linefeeds (if any) */
+ count = 0;
+ while (count < MAXBUF - 1) {
+ if ((isprint(tokenBuffer[count]))
+ && tokenBuffer[count] != '"')
+ configToken[count] = tokenBuffer[count];
+ else {
+ configToken[count] = '\0';
+ break;
+ }
+ count++;
+ }
+
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieved token: %s\n",
+ configToken);
+#endif
+ configToken[MAXBUF - 1] = '\0';
+ fclose(config);
+ return (TRUE);
+ }
+ }
+ }
+ fclose(config);
+ return (FALSE);
+ }
+
+}
+
+
+
+
+/* This will bind a socket to a port. It works for UDP/TCP */
+int
+BindSocket(int sockfd, struct sockaddr_in client,
+ struct sockaddr_in server, int port)
+{
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding to port: %d\n", port);
+#endif
+
+ bzero((char *) &server, sizeof(server));
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = htonl(INADDR_ANY);
+ server.sin_port = htons(port);
+
+ if (bind(sockfd, (struct sockaddr *) &server, sizeof(server)) < 0) {
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding failed\n");
+#endif
+ return (ERROR);
+ } else {
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding successful. Doing listen\n");
+#endif
+ listen(sockfd, 5);
+ return (TRUE);
+ }
+}
+
+
+/* Open a TCP Socket */
+int OpenTCPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenTCPSocket: opening TCP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+
+/* Open a UDP Socket */
+int OpenUDPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: openUDPSocket opening UDP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+#ifdef SUPPORT_STEALTH
+/* Open a RAW TCPSocket */
+int OpenRAWTCPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenRAWTCPSocket: opening RAW TCP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+/* Open a RAW UDP Socket */
+int OpenRAWUDPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenRAWUDPSocket: opening RAW UDP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+#endif
+
+/* This will use a system() call to change the route of the target host to */
+/* a dead IP address on your LOCAL SUBNET. */
+int
+KillRoute(char *target, int port, char *killString, char *detectionType)
+{
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int killStatus = ERROR, substStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+ snprintf(portString, MAXBUF, "%d", port);
+
+ substStatus =
+ SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp);
+ if (substStatus == 0) {
+ Log("adminalert: No target variable specified in KILL_ROUTE option. Skipping.\n");
+ return (ERROR);
+ } else if (substStatus == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+#ifdef DEBUG
+ Log("debug: KillRoute: running route command: %s\n",
+ commandStringFinal);
+#endif
+
+ /* Kill the bastard and report a status */
+ killStatus = system(commandStringFinal);
+
+ if (killStatus == 127) {
+ Log("adminalert: ERROR: There was an error trying to block host (exec fail) %s", target);
+ return (ERROR);
+ } else if (killStatus < 0) {
+ Log("adminalert: ERROR: There was an error trying to block host (system fail) %s", target);
+ return (ERROR);
+ } else {
+ Log("attackalert: Host %s has been blocked via dropped route using command: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+
+/* This will run a specified command with TARGET as the option if one is given. */
+int
+KillRunCmd(char *target, int port, char *killString, char *detectionType)
+{
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int killStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+ snprintf(portString, MAXBUF, "%d", port);
+
+ /* Tokens are not required, but we check for an error anyway */
+ if (SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp)
+ == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+
+ /* Kill the bastard and report a status */
+ killStatus = system(commandStringFinal);
+
+ if (killStatus == 127) {
+ Log("adminalert: ERROR: There was an error trying to run command (exec fail) %s", target);
+ return (ERROR);
+ } else if (killStatus < 0) {
+ Log("adminalert: ERROR: There was an error trying to run command (system fail) %s", target);
+ return (ERROR);
+ } else {
+ /* report success */
+ Log("attackalert: External command run for host: %s using command: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+/* this function will drop the host into the TCP wrappers hosts.deny file to deny */
+/* all access. The drop route method is preferred as this stops UDP attacks as well */
+/* as TCP. You may find though that host.deny will be a more permanent home.. */
+int
+KillHostsDeny(char *target, int port, char *killString,
+ char *detectionType)
+{
+
+ FILE *output;
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int substStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+
+ snprintf(portString, MAXBUF, "%d", port);
+
+#ifdef DEBUG
+ Log("debug: KillHostsDeny: parsing string for block: %s\n",
+ killString);
+#endif
+
+ substStatus =
+ SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp);
+ if (substStatus == 0) {
+ Log("adminalert: No target variable specified in KILL_HOSTS_DENY option. Skipping.\n");
+ return (ERROR);
+ } else if (substStatus == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+#ifdef DEBUG
+ Log("debug: KillHostsDeny: result string for block: %s\n",
+ commandStringFinal);
+#endif
+
+ if ((output = fopen(WRAPPER_HOSTS_DENY, "a")) == NULL) {
+ Log("adminalert: cannot open hosts.deny file: %s for blocking.",
+ WRAPPER_HOSTS_DENY);
+ Log("securityalert: ERROR: There was an error trying to block host %s", target);
+ return (FALSE);
+ } else {
+ fprintf(output, "%s\n", commandStringFinal);
+ fclose(output);
+ Log("attackalert: Host %s has been blocked via wrappers with string: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+/* check if the host is already blocked */
+int IsBlocked(char *target, char *filename)
+{
+ FILE *input;
+ char buffer[MAXBUF], tempBuffer[MAXBUF];
+ char *ipOffset;
+ int count;
+
+
+#ifdef DEBUG
+ Log("debug: IsBlocked: Opening block file: %s \n", filename);
+#endif
+ if ((input = fopen(filename, "r")) == NULL) {
+ Log("adminalert: ERROR: Cannot open blocked file: %s for reading. Will create.\n", filename);
+ return (FALSE);
+ }
+
+ while (fgets(buffer, MAXBUF, input) != NULL) {
+ if ((ipOffset = strstr(buffer, target)) != (char) NULL) {
+ for (count = 0; count < strlen(ipOffset); count++) {
+ if ((isdigit(ipOffset[count])) || (ipOffset[count] == '.')) {
+ tempBuffer[count] = ipOffset[count];
+ } else {
+ tempBuffer[count] = '\0';
+ break;
+ }
+ }
+ if (strcmp(target, tempBuffer) == 0) {
+#ifdef DEBUG
+ Log("debug: isBlocked: Host: %s found in blocked file\n",
+ target);
+#endif
+ fclose(input);
+ return (TRUE);
+ }
+ }
+
+ }
+#ifdef DEBUG
+ Log("debug: IsBlocked: Host: %s NOT found in blocked file\n", target);
+#endif
+ fclose(input);
+ return (FALSE);
+}
+
+/*********************************************************************************
+* String substitute function
+*
+* This function takes:
+*
+* 1) A token to use for replacement.
+* 2) A token to find.
+* 3) A string with the tokens in it.
+* 4) A string to write the replaced result.
+*
+* It returns the number of substitutions made during the operation.
+**********************************************************************************/
+int
+SubstString(const char *replace, const char *find, const char *target,
+ char *result)
+{
+ int replaceCount = 0, count = 0, findCount = 0, findLen =
+ 0, numberOfSubst = 0;
+ char tempString[MAXBUF], *tempStringPtr;
+
+#ifdef DEBUG
+ Log("debug: SubstString: Processing string: %s %d", target,
+ strlen(target));
+ Log("debug: SubstString: Processing search text: %s %d", replace,
+ strlen(replace));
+ Log("debug: SubstString: Processing replace text: %s %d", find,
+ strlen(find));
+#endif
+
+ /* string not found in target */
+ if (strstr(target, find) == (char) NULL) {
+ strncpy(result, target, MAXBUF);
+#ifdef DEBUG
+ Log("debug: SubstString: Result string: %s", result);
+#endif
+ return (numberOfSubst);
+ }
+ /* String/victim/target too long */
+ else if ((strlen(target)) + (strlen(replace)) + (strlen(find)) >
+ MAXBUF)
+ return (ERROR);
+
+ memset(tempString, '\0', MAXBUF);
+ memset(result, '\0', MAXBUF);
+ findLen = strlen(find);
+ tempStringPtr = tempString;
+
+ for (count = 0; count < MAXBUF; count++) {
+ if (*target == '\0')
+ break;
+ else if ((strncmp(target, find, findLen)) != 0)
+ *tempStringPtr++ = *target++;
+ else {
+ numberOfSubst++;
+ for (replaceCount = 0; replaceCount < strlen(replace);
+ replaceCount++)
+ *tempStringPtr++ = replace[replaceCount];
+ for (findCount = 0; findCount < findLen; findCount++)
+ target++;
+ }
+ }
+
+ strncpy(result, tempString, MAXBUF);
+#ifdef DEBUG
+ Log("debug: SubstString: Result string: %s", result);
+#endif
+ return (numberOfSubst);
+}
+
+
+
+/* This function checks a config variable for a numerical flag and returns it */
+int CheckFlag(char *flagName)
+{
+ char configToken[MAXBUF];
+
+ if ((ConfigTokenRetrieve(flagName, configToken)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: CheckFlag: found %s string.\n", flagName);
+#endif
+ return (atoi(configToken));
+ } else {
+#ifdef DEBUG
+ Log("debug: CheckFlag: %s option not found. Assuming FALSE.\n",
+ flagName);
+#endif
+ return (FALSE);
+ }
+}
+
+
+/* snprintf for NEXTSTEP (others??) */
+/* I don't know where this code came from and I don't */
+/* warrant its effectiveness. CHR */
+
+#ifdef HAS_NO_SNPRINTF
+int snprintf(char *str, size_t n, char const *fmt, ...)
+{
+ va_list ap;
+ FILE f;
+
+ if (n > MAXBUF) {
+ n = MAXBUF;
+ }
+ va_start(ap, fmt);
+ f._file = EOF;
+ f._flag = _IOWRT | _IOSTRG;
+ f._base = f._ptr = str;
+ f._bufsiz = f._cnt = n ? n - 1 : 0;
+ (void) vfprintf(&f, fmt, ap);
+ va_end(ap);
+ if (n) {
+ *f._ptr = '\0';
+ }
+ return (f._ptr - str);
+}
+#endif
diff --git a/kalasag_io.h b/kalasag_io.h
new file mode 100644
index 0000000..bb83ec8
--- /dev/null
+++ b/kalasag_io.h
@@ -0,0 +1,23 @@
+/* prototypes */
+int WriteBlocked(char *, char *, int, char *, char *, char *);
+void Log(char *, ...);
+void Exit(int);
+void Start(void);
+int DaemonSeed(void);
+int NeverBlock(char *, char *);
+int CheckConfig(void);
+int OpenTCPSocket(void);
+int OpenUDPSocket(void);
+#ifdef SUPPORT_STEALTH
+int OpenRAWTCPSocket(void);
+int OpenRAWUDPSocket(void);
+#endif
+int BindSocket(int, struct sockaddr_in, struct sockaddr_in, int);
+int KillRoute(char *, int, char *, char *);
+int KillHostsDeny(char *, int, char *, char *);
+int KillRunCmd(char *, int, char *, char *);
+int ConfigTokenRetrieve(char *, char *);
+int IsBlocked(char *, char *);
+int SubstString(const char *, const char *, const char *, char *);
+int CheckFlag(char *);
+int CompareIPs(char *, char *, int);
diff --git a/kalasag_tcpip.h b/kalasag_tcpip.h
new file mode 100644
index 0000000..b697b1f
--- /dev/null
+++ b/kalasag_tcpip.h
@@ -0,0 +1,159 @@
+/* Versions of Linux are not consistent in how the TCP/UDP/IP headers
+* defined. This file contains the Linux/BSD headers from RedHat 5.0 and
+* should clear up compile problems. CHR
+*/
+
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET_TCP_H
+#define _NETINET_TCP_H 1
+
+#include <features.h>
+
+__BEGIN_DECLS struct tcphdr {
+ u_int16_t source;
+ u_int16_t dest;
+ u_int32_t seq;
+ u_int32_t ack_seq;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int16_t res1:4;
+ u_int16_t doff:4;
+ u_int16_t fin:1;
+ u_int16_t syn:1;
+ u_int16_t rst:1;
+ u_int16_t psh:1;
+ u_int16_t ack:1;
+ u_int16_t urg:1;
+ u_int16_t res2:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int16_t doff:4;
+ u_int16_t res1:4;
+ u_int16_t res2:2;
+ u_int16_t urg:1;
+ u_int16_t ack:1;
+ u_int16_t psh:1;
+ u_int16_t rst:1;
+ u_int16_t syn:1;
+ u_int16_t fin:1;
+#else
+#error "Adjust your <bits/endian.h> defines"
+#endif
+ u_int16_t window;
+ u_int16_t check;
+ u_int16_t urg_ptr;
+};
+
+#endif /* tcp.h */
+
+
+
+#ifndef __NETINET_IP_H
+#define __NETINET_IP_H 1
+
+__BEGIN_DECLS struct timestamp {
+ u_int8_t len;
+ u_int8_t ptr;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t flags:4;
+ u_int8_t overflow:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t overflow:4;
+ u_int8_t flags:4;
+#else
+#error "Please fix <bytesex.h>"
+#endif
+ u_int32_t data[9];
+};
+
+struct ip_options {
+ u_int32_t faddr; /* Saved first hop address */
+ u_int8_t optlen;
+ u_int8_t srr;
+ u_int8_t rr;
+ u_int8_t ts;
+ u_int8_t is_setbyuser:1; /* Set by setsockopt? */
+ u_int8_t is_data:1; /* Options in __data, rather than skb */
+ u_int8_t is_strictroute:1; /* Strict source route */
+ u_int8_t srr_is_hit:1; /* Packet destination addr was our one */
+ u_int8_t is_changed:1; /* IP checksum more not valid */
+ u_int8_t rr_needaddr:1; /* Need to record addr of outgoing dev */
+ u_int8_t ts_needtime:1; /* Need to record timestamp */
+ u_int8_t ts_needaddr:1; /* Need to record addr of outgoing dev */
+ u_int8_t router_alert;
+ u_int8_t __pad1;
+ u_int8_t __pad2;
+ u_int8_t __data[0];
+};
+
+struct iphdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t ihl:4;
+ u_int8_t version:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t version:4;
+ u_int8_t ihl:4;
+#else
+#error "Please fix <bytesex.h>"
+#endif
+ u_int8_t tos;
+ u_int16_t tot_len;
+ u_int16_t id;
+ u_int16_t frag_off;
+ u_int8_t ttl;
+ u_int8_t protocol;
+ u_int16_t check;
+ u_int32_t saddr;
+ u_int32_t daddr;
+ /*The options start here. */
+};
+
+#endif
+
+#ifndef __NETINET_UDP_H
+#define __NETINET_UDP_H 1
+
+__BEGIN_DECLS
+/* UDP header as specified by RFC 768, August 1980. */
+ struct udphdr {
+ u_int16_t source;
+ u_int16_t dest;
+ u_int16_t len;
+ u_int16_t check;
+};
+
+__END_DECLS
+#endif /* netinet/udp.h */
diff --git a/kalasag_util.c b/kalasag_util.c
new file mode 100644
index 0000000..42cb133
--- /dev/null
+++ b/kalasag_util.c
@@ -0,0 +1,114 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* A replacement for strncpy that covers mistakes a little better */
+char *SafeStrncpy(char *dest, const char *src, size_t size)
+{
+ if (!dest) {
+ dest = NULL;
+ return (NULL);
+ } else if (size < 1) {
+ dest = NULL;
+ return (NULL);
+ }
+
+ /* Null terminate string. Why the hell strncpy doesn't do this */
+ /* for you is mystery to me. God I hate C. */
+ memset(dest, '\0', size);
+ strncpy(dest, src, size - 1);
+
+ return (dest);
+}
+
+
+/************************************************************************/
+/* Generic safety function to process an IP address and remove anything */
+/* that is: */
+/* 1) Not a number. */
+/* 2) Not a period. */
+/* 3) Greater than IPMAXBUF (15) */
+/************************************************************************/
+char *CleanIpAddr(char *cleanAddr, const char *dirtyAddr)
+{
+ int count = 0, maxdot = 0, maxoctet = 0;
+
+#ifdef DEBUG
+ Log("debug: cleanAddr: Cleaning Ip address: %s", dirtyAddr);
+#endif
+
+ memset(cleanAddr, '\0', IPMAXBUF);
+ /* dirtyAddr must be valid */
+ if (dirtyAddr == NULL)
+ return (cleanAddr);
+
+ for (count = 0; count < IPMAXBUF - 1; count++) {
+ if (isdigit(dirtyAddr[count])) {
+ if (++maxoctet > 3) {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ cleanAddr[count] = dirtyAddr[count];
+ } else if (dirtyAddr[count] == '.') {
+ if (++maxdot > 3) {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ maxoctet = 0;
+ cleanAddr[count] = dirtyAddr[count];
+ } else {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ Log("debug: cleanAddr: Cleaned IpAddress: %s Dirty IpAddress: %s",
+ cleanAddr, dirtyAddr);
+#endif
+
+ return (cleanAddr);
+}
+
+
+/************************************************************************/
+/* Generic safety function to process an unresolved address and remove */
+/* anything that is: */
+/* 1) Not a number. */
+/* 2) Not a period. */
+/* 3) Greater than DNSMAXBUF (255) */
+/* 4) Not a legal DNS character (a-z, A-Z, 0-9, - ) */
+/* */
+/* XXX THIS FUNCTION IS NOT COMPLETE */
+/************************************************************************/
+int CleanAndResolve(char *resolvedHost, const char *unresolvedHost)
+{
+ struct hostent *hostPtr = NULL;
+ struct in_addr addr;
+
+#ifdef DEBUG
+ Log("debug: CleanAndResolv: Resolving address: %s", unresolvedHost);
+#endif
+
+ memset(resolvedHost, '\0', DNSMAXBUF);
+ /* unresolvedHost must be valid */
+ if (unresolvedHost == NULL)
+ return (ERROR);
+
+ /* Not a valid address */
+ if ((inet_aton(unresolvedHost, &addr)) == 0)
+ return (ERROR);
+
+ hostPtr =
+ gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr), AF_INET);
+ if (hostPtr != NULL)
+ snprintf(resolvedHost, DNSMAXBUF, "%s", hostPtr->h_name);
+ else
+ snprintf(resolvedHost, DNSMAXBUF, "%s", unresolvedHost);
+
+#ifdef DEBUG
+ Log("debug: CleanAndResolve: Cleaned Resolved: %s Dirty Unresolved: %s", resolvedHost, unresolvedHost);
+#endif
+
+ return (TRUE);
+}
diff --git a/kalasag_util.h b/kalasag_util.h
new file mode 100644
index 0000000..5f95e2f
--- /dev/null
+++ b/kalasag_util.h
@@ -0,0 +1,6 @@
+/* IP address length plus null */
+#define IPMAXBUF 16
+
+char *SafeStrncpy(char *, const char *, size_t);
+char *CleanIpAddr(char *, const char *);
+int CleanAndResolve(char *, const char *);
tree acdc640ff4b70fddf7a8731fc71b089804d57217
author Engels Antonio <engels@majcms.org> 1314301223 +0800
committer Engels Antonio <engels@majcms.org> 1314301223 +0800
Initial commit, warts and all
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..ca83f7c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,191 @@
+# Makefile for Kalasag
+#
+# STEALTH MODE: Only works on Linux systems right now.
+#
+# The snprintf included with the package is for use with NEXTSTEP only,
+# (Thanks Timothy <tjl@luomat.org>) although it may work elsewhere.
+# We've not tried it under any other OS to date. It shouldn't be needed
+# by any modern OS.
+#
+# Others have used the snprintf from:
+#
+# http://www.ijs.si/software/snprintf/
+#
+# We've not tried this yet but others have had good success. Our only
+# piece of advice for those running an OS without built in snprintf()
+# is to upgrade. :)
+#
+#
+# Generic compiler (usually linked to gcc on most platforms)
+CC = cc
+
+# GNU..
+#CC = gcc
+
+# Normal systems flags
+CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -Wall
+
+# Debug mode for kalasag
+#CFLAGS = -Wall -g -DNODAEMON -DDEBUG
+#CFLAGS = -Wall -g -DNODAEMON
+#CFLAGS = -Wall -g -DDEBUG
+
+# Profiler mode for kalasag
+#CFLAGS = -pg -O -Wall -DNODAEMON
+#LIBS = /usr/lib/libefence.a
+
+INSTALLDIR = /opt
+CHILDDIR=/kalasag
+
+all:
+ @echo "Usage: make <systype>"
+ @echo "<systype> is one of: linux, debian-linux, bsd, solaris, hpux, hpux-gcc,"
+ @echo "freebsd, osx, openbsd, netbsd, bsdi, aix, osf, irix, generic"
+ @echo ""
+ @echo "This code requires snprintf()/vsnprintf() system calls"
+ @echo "to work. If you run a modern OS it should work on"
+ @echo "your system with 'make generic'. If you get it to"
+ @echo "work on an unlisted OS please write us with the"
+ @echo "changes."
+ @echo ""
+ @echo "Install: make install"
+ @echo ""
+ @echo "NOTE: This will install the package in this"
+ @echo " directory: $(INSTALLDIR)"
+ @echo ""
+ @echo "Edit the makefile if you wish to change these paths."
+ @echo "Any existing files will be overwritten."
+ @echo ""
+
+clean:
+ /bin/rm ./kalasag
+
+uninstall:
+ /bin/rm $(INSTALLDIR)$(CHILDDIR)/*
+ /bin/rmdir $(INSTALLDIR)$(CHILDDIR)
+
+install:
+ @echo "Creating directory $(INSTALLDIR)"
+ @if [ ! -d $(INSTALLDIR) ]; then /bin/mkdir $(INSTALLDIR); fi
+ @echo "Setting directory permissions"
+ @echo "Creating kalasag directory $(INSTALLDIR)$(CHILDDIR)"
+ @if [ ! -d $(INSTALLDIR)$(CHILDDIR) ]; then /bin/mkdir $(INSTALLDIR)$(CHILDDIR); fi
+ @echo "Setting directory permissions"
+ chmod 700 $(INSTALLDIR)$(CHILDDIR)
+ @echo "Copying files"
+ cp ./kalasag.conf $(INSTALLDIR)$(CHILDDIR)
+ cp ./kalasag.ignore $(INSTALLDIR)$(CHILDDIR)
+ cp ./kalasag $(INSTALLDIR)$(CHILDDIR)
+ @echo "Setting permissions"
+ chmod 600 $(INSTALLDIR)$(CHILDDIR)/kalasag.ignore
+ chmod 600 $(INSTALLDIR)$(CHILDDIR)/kalasag.conf
+ chmod 700 $(INSTALLDIR)$(CHILDDIR)/kalasag
+ @echo ""
+ @echo ""
+ @echo "Edit $(INSTALLDIR)$(CHILDDIR)/kalasag.conf and change"
+ @echo "your settings if you haven't already. (route, etc)"
+ @echo ""
+
+linux:
+ SYSTYPE=linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DLINUX -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+linux-x86_64:
+ SYSTYPE=linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -m64 -DLINUX -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+debian-linux:
+ SYSTYPE=debian-linux
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DLINUX -DDEBIAN -DSUPPORT_STEALTH -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c $(LIBS)
+
+
+bsd:
+ SYSTYPE=bsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+openbsd:
+ SYSTYPE=openbsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+freebsd:
+ SYSTYPE=freebsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+osx:
+ SYSTYPE=osx
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+netbsd:
+ SYSTYPE=netbsd
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+bsdi:
+ SYSTYPE=bsdi
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DBSD44 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+generic:
+ SYSTYPE=generic
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+hpux:
+ SYSTYPE=hpux
+ @echo "Making $(SYSTYPE)"
+ $(CC) -Ae -DHPUX -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+hpux-gcc:
+ SYSTYPE=hpux-gcc
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -DHPUX -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+solaris:
+ SYSTYPE=solaris
+ @echo "Making $(SYSTYPE)"
+ $(CC) -lnsl -lsocket -lresolv -lc -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+aix:
+ SYSTYPE=aix
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+osf:
+ SYSTYPE=osf
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -taso -ldb -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+irix:
+ SYSTYPE=irix
+ @echo "Making $(SYSTYPE)"
+ $(CC) $(CFLAGS) -O -n32 -mips3 -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
+
+
+# NeXTSTEP Users. NeXT used to work, but we changed the log function and
+# it now uses vsnprintf() to format strings. This means that this
+# version does not work under NeXTSTEP until we can find a workable
+# vsnprintf() call to put in the program. Sorry. If you have some good
+# vsnprintf() code to use under NeXT please send it to us and we'll
+# include it on the next update.
+#next:
+# SYSTYPE=next
+# @echo "Making $(SYSTYPE)"
+# $(CC) $(CFLAGS) -DNEXT -DHAS_NO_SNPRINTF -posix -o ./kalasag ./kalasag.c ./kalasag_io.c ./kalasag_util.c
diff --git a/kalasag.c b/kalasag.c
new file mode 100644
index 0000000..2145b43
--- /dev/null
+++ b/kalasag.c
@@ -0,0 +1,1536 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* Global variables */
+char gblScanDetectHost[MAXSTATE][IPMAXBUF];
+char gblKillRoute[MAXBUF];
+char gblKillHostsDeny[MAXBUF];
+char gblKillRunCmd[MAXBUF];
+char gblBlockedFile[MAXBUF];
+char gblHistoryFile[MAXBUF];
+char gblIgnoreFile[MAXBUF];
+char gblDetectionType[MAXBUF];
+
+int gblScanDetectCount = 0;
+int gblBlockTCP = 0;
+int gblBlockUDP = 0;
+int gblRunCmdFirst = 0;
+int gblResolveHost = 0;
+int gblConfigTriggerCount = 0;
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ Usage();
+ Exit(ERROR);
+ }
+
+ if ((geteuid()) && (getuid()) != 0) {
+ printf("You need to be root to run this.\n");
+ Exit(ERROR);
+ }
+
+
+ /* Cheesy arg parser. Some systems don't support getopt and I don't want to port it. */
+ if ((strcmp(argv[1], "-tcp")) && (strcmp(argv[1], "-udp"))
+ && (strcmp(argv[1], "-stcp")) && (strcmp(argv[1], "-atcp"))
+ && (strcmp(argv[1], "-sudp")) && (strcmp(argv[1], "-audp")) != 0) {
+ Usage();
+ Exit(ERROR);
+ } else {
+ Start();
+ /* This copies the startup type to a global for later use */
+ if ((SafeStrncpy
+ (gblDetectionType, strstr(argv[1], "-") + 1, MAXBUF))
+ == NULL) {
+ Log("adminalert: ERROR: Error setting internal scan detection type.\n");
+ printf("ERROR: Error setting internal scan detection type.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ } else if (CheckConfig() == FALSE) {
+ Log("adminalert: ERROR: Configuration files are missing/corrupted. Shutting down.\n");
+ printf("ERROR: Configuration files are missing/corrupted.\n");
+ printf
+ ("ERROR: Check your syslog for a more detailed error message.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ } else if (InitConfig() == FALSE) {
+ Log("adminalert: ERROR: Your config file is corrupted/missing mandatory option! Shutting down.\n");
+ printf
+ ("ERROR: Your config file is corrupted/missing mandatory option!\n");
+ printf
+ ("ERROR: Check your syslog for a more detailed error message.\n");
+ printf("ERROR: Kalasag is shutting down!\n");
+ Exit(ERROR);
+ }
+#ifndef NODAEMON
+ else if (DaemonSeed() == ERROR) {
+ Log("adminalert: ERROR: could not go into daemon mode. Shutting down.\n");
+ printf
+ ("ERROR: could not go into daemon mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+#endif
+ }
+
+
+ if (strcmp(argv[1], "-tcp") == 0) {
+ if (KalasagModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+#ifdef SUPPORT_STEALTH
+ else if (strcmp(argv[1], "-stcp") == 0) {
+ if (KalasagStealthModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-atcp") == 0) {
+ if (KalasagAdvancedStealthModeTCP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-sudp") == 0) {
+ if (KalasagStealthModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ } else if (strcmp(argv[1], "-audp") == 0) {
+ if (KalasagAdvancedStealthModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+#endif
+ else if (strcmp(argv[1], "-udp") == 0) {
+ if (KalasagModeUDP() == ERROR) {
+ Log("adminalert: ERROR: could not go into Kalasag mode. Shutting down.\n");
+ Exit(ERROR);
+ }
+ }
+
+ Exit(TRUE);
+ /* shuts up compiler warning */
+ return (0);
+}
+
+/****************************************************************/
+/* Reads generic config options into global variables */
+/****************************************************************/
+int InitConfig(void)
+{
+ FILE *input;
+ char configToken[MAXBUF];
+
+ gblBlockTCP = CheckFlag("BLOCK_TCP");
+ gblBlockUDP = CheckFlag("BLOCK_UDP");
+ gblResolveHost = CheckFlag("RESOLVE_HOST");
+
+ memset(gblKillRoute, '\0', MAXBUF);
+ memset(gblKillHostsDeny, '\0', MAXBUF);
+ memset(gblKillRunCmd, '\0', MAXBUF);
+
+ if ((ConfigTokenRetrieve("SCAN_TRIGGER", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read SCAN_TRIGGER option from config file. Disabling SCAN DETECTION TRIGGER");
+ gblConfigTriggerCount = 0;
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved SCAN_TRIGGER option: %s \n",
+ configToken);
+#endif
+ gblConfigTriggerCount = atoi(configToken);
+ }
+
+ if ((ConfigTokenRetrieve("KILL_ROUTE", gblKillRoute)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_ROUTE option: %s \n",
+ gblKillRoute);
+#endif
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_ROUTE option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("KILL_HOSTS_DENY", gblKillHostsDeny)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_HOSTS_DENY option: %s \n",
+ gblKillHostsDeny);
+#endif
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_HOSTS_DENY option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("KILL_RUN_CMD", gblKillRunCmd)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved KILL_RUN_CMD option: %s \n",
+ gblKillRunCmd);
+#endif
+ /* Check the order we should run the KILL_RUN_CMD */
+ /* Default is to run the command after blocking */
+ gblRunCmdFirst = CheckFlag("KILL_RUN_CMD_FIRST");
+ } else {
+#ifdef DEBUG
+ Log("debug: InitConfig: KILL_RUN_CMD option NOT FOUND.\n");
+#endif
+ }
+
+ if ((ConfigTokenRetrieve("BLOCKED_FILE", gblBlockedFile)) == TRUE) {
+ if (strlen(gblBlockedFile) < MAXBUF - 5) {
+ strncat(gblBlockedFile, ".", 1);
+ strncat(gblBlockedFile, gblDetectionType, 4);
+ } else {
+ Log("adminalert: ERROR: Blocked filename is too long to append detection type file extension: %s.\n", gblBlockedFile);
+ return (FALSE);
+ }
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved BLOCKED_FILE option: %s \n",
+ gblBlockedFile);
+ Log("debug: CheckConfig: Removing old block file: %s \n",
+ gblBlockedFile);
+#endif
+
+ if ((input = fopen(gblBlockedFile, "w")) == NULL) {
+ Log("adminalert: ERROR: Cannot delete blocked file on startup: %s.\n", gblBlockedFile);
+ return (FALSE);
+ } else
+ fclose(input);
+ } else {
+ Log("InitConfig: Cannot retrieve BLOCKED_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+
+ if ((ConfigTokenRetrieve("HISTORY_FILE", gblHistoryFile)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved HISTORY_FILE option: %s \n",
+ gblHistoryFile);
+#endif
+ } else {
+ Log("InitConfig: Cannot retrieve HISTORY_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+ if ((ConfigTokenRetrieve("IGNORE_FILE", gblIgnoreFile)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: InitConfig: retrieved IGNORE_FILE option: %s \n",
+ gblIgnoreFile);
+#endif
+ } else {
+ Log("InitConfig: Cannot retrieve IGNORE_FILE option! Aborting\n");
+ return (FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+#ifdef SUPPORT_STEALTH
+
+/* Read in a TCP packet taking into account IP options and other */
+/* errors */
+int PacketReadTCP(int socket, struct iphdr *ipPtr, struct tcphdr *tcpPtr)
+{
+ char packetBuffer[TCPPACKETLEN];
+ struct in_addr addr;
+
+ bzero(ipPtr, sizeof(struct iphdr));
+ bzero(tcpPtr, sizeof(struct tcphdr));
+
+ if (read(socket, packetBuffer, TCPPACKETLEN) == ERROR)
+ return (ERROR);
+
+ memcpy(ipPtr, (struct iphdr *) packetBuffer, sizeof(struct iphdr));
+
+ if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) {
+ addr.s_addr = (u_int) ipPtr->saddr;
+ Log("attackalert: Illegal IP header length detected in TCP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa(addr));
+ return (FALSE);
+ } else {
+ memcpy(tcpPtr,
+ (struct tcphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
+ sizeof(struct tcphdr));
+ return (TRUE);
+ }
+
+}
+
+/* Read in a UDP packet taking into account IP options and other */
+/* errors */
+int PacketReadUDP(int socket, struct iphdr *ipPtr, struct udphdr *udpPtr)
+{
+ char packetBuffer[UDPPACKETLEN];
+ struct in_addr addr;
+
+ bzero(ipPtr, sizeof(struct iphdr));
+ bzero(udpPtr, sizeof(struct udphdr));
+
+ if (read(socket, packetBuffer, UDPPACKETLEN) == ERROR)
+ return (ERROR);
+
+ memcpy(ipPtr, (struct iphdr *) packetBuffer, sizeof(struct iphdr));
+
+ if ((ipPtr->ihl < 5) || (ipPtr->ihl > 15)) {
+ addr.s_addr = (u_int) ipPtr->saddr;
+ Log("attackalert: Illegal IP header length detected in UDP packet: %d from (possible) host: %s\n", ipPtr->ihl, inet_ntoa(addr));
+ return (FALSE);
+ } else {
+ memcpy(udpPtr,
+ (struct udphdr *) (packetBuffer + ((ipPtr->ihl) * 4)),
+ sizeof(struct udphdr));
+ return (TRUE);
+ }
+
+}
+
+/****************************************************************/
+/* Stealth scan detection Mode One */
+/* */
+/* This mode will read in a list of ports to monitor and will */
+/* then open a raw socket to look for packets matching the port. */
+/* */
+/****************************************************************/
+int KalasagStealthModeTCP(void)
+{
+ struct sockaddr_in client, server;
+ int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS];
+ int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE, result =
+ TRUE;
+ int openSockfd = 0, incomingPort = 0;
+ char *temp, target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *packetType;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct tcphdr tcp;
+
+ if ((ConfigTokenRetrieve("TCP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read TCP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* ok, now check if they have a network daemon on the socket already, if they do */
+ /* then skip that port because it will cause false alarms */
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into stealth listen mode on TCP port: %d\n",
+ ports[count]);
+ if ((openSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket(openSockfd, client, server, ports[count]) == ERROR)
+ Log("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]);
+ else { /* well we at least bound to one socket so we'll continue */
+
+ gotBound = TRUE;
+ ports2[portCount2++] = ports[count];
+ }
+ close(openSockfd);
+ }
+
+ /* if we didn't bind to anything then abort */
+ if (gotBound == FALSE) {
+ Log("adminalert: ERROR: All supplied TCP sockets are in use and will not be listened to. Shutting down.\n");
+ return (ERROR);
+ }
+
+ /* Open our raw socket for network IO */
+ if ((openSockfd = OpenRAWTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadTCP(openSockfd, &ip, &tcp) != TRUE)
+ continue;
+
+
+ incomingPort = ntohs(tcp.dest);
+
+ /* check for an ACK/RST to weed out established connections in case the user */
+ /* is monitoring high ephemeral port numbers */
+ if ((tcp.ack != 1) && (tcp.rst != 1)) {
+ /* this iterates the list of ports looking for a match */
+ for (count = 0; count < portCount; count++) {
+ if (incomingPort == ports2[count]) {
+ if (SmartVerifyTCP(client, server, incomingPort) ==
+ TRUE)
+ break;
+
+ /* copy the clients address into our buffer for nuking */
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ packetType = ReportPacketType(tcp);
+ Log("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, ports2[count]);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, ports2[count]);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ /* toast the prick */
+ if (DisposeTCP(target, ports2[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s !!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports2[count],
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ break; /* get out of for(count) loop above */
+ } /* end if(incoming port) == protected port */
+ } /* end for( check for protected port loop ) loop */
+ } /* end if(TH_ACK) check */
+ } /* end for( ; ; ) loop */
+
+} /* end KalasagStealthModeTCP */
+
+
+/****************************************************************/
+/* Advanced Stealth scan detection Mode One */
+/* */
+/* This mode will see what ports are listening below 1024 */
+/* and will then monitor all the rest. This is very sensitive */
+/* and will react on any packet hitting any monitored port, */
+/* regardless of TCP flags set */
+/* */
+/****************************************************************/
+int KalasagAdvancedStealthModeTCP(void)
+{
+ struct sockaddr_in client, server;
+ int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
+ int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
+ unsigned int advancedPorts = 1024;
+ unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
+ char target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *temp, *packetType;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct tcphdr tcp;
+
+ if ((ConfigTokenRetrieve("ADVANCED_PORTS_TCP", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read ADVANCED_PORTS_TCP option from config file. Assuming 1024.");
+ advancedPorts = 1024;
+ } else
+ advancedPorts = atoi(configToken);
+
+ Log("adminalert: Advanced mode will monitor first %d ports",
+ advancedPorts);
+
+ /* try to bind to all ports below 1024, any that are taken we exclude later */
+ for (count = 0; count < advancedPorts; count++) {
+ if ((openSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+ if (BindSocket(openSockfd, client, server, count) == ERROR)
+ inUsePorts[portCount++] = count;
+
+ close(openSockfd);
+ }
+
+ if ((ConfigTokenRetrieve("ADVANCED_EXCLUDE_TCP", configToken)) !=
+ FALSE) {
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ for (count = 0; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ } else
+ break;
+ }
+ }
+ } else
+ Log("adminalert: Advanced mode will manually exclude no ports");
+
+
+ for (count = 0; count < portCount; count++)
+ Log("adminalert: Advanced Stealth scan detection mode activated. Ignored TCP port: %d\n", inUsePorts[count]);
+
+ /* open raw socket for reading */
+ if ((openSockfd = OpenRAWTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadTCP(openSockfd, &ip, &tcp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(tcp.dest);
+
+ /* don't monitor packets with ACK set (established) or RST */
+ /* This could be a hole in some cases */
+ if ((tcp.ack != 1) && (tcp.rst != 1)) {
+ /* check if we should ignore this connection to this port */
+ for (count = 0; count < portCount; count++) {
+ if ((incomingPort == inUsePorts[count])
+ || (incomingPort >= advancedPorts)) {
+ hotPort = FALSE;
+ break;
+ } else
+ hotPort = TRUE;
+ }
+
+ if (hotPort) {
+ smartVerify = SmartVerifyTCP(client, server, incomingPort);
+
+ if (smartVerify != TRUE) {
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ packetType = ReportPacketType(tcp);
+ Log("attackalert: %s from host: %s/%s to TCP port: %d", packetType, resolvedHost, target, incomingPort);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to TCP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ /* toast the prick */
+ if (DisposeTCP(target, incomingPort) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ incomingPort,
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ } /* end if(smartVerify) */
+ } /* end if(hotPort) */
+ } /* end if(TH_ACK) */
+ } /* end for( ; ; ) loop */
+}
+
+/* end KalasagAdvancedStealthModeTCP */
+
+
+
+/****************************************************************/
+/* UDP "stealth" scan detection */
+/* */
+/* This mode will read in a list of ports to monitor and will */
+/* then open a raw socket to look for packets matching the port. */
+/* */
+/****************************************************************/
+int KalasagStealthModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int portCount = 0, portCount2 = 0, ports[MAXSOCKS], ports2[MAXSOCKS],
+ result = TRUE;
+ int count = 0, scanDetectTrigger = TRUE, gotBound = FALSE;
+ int openSockfd = 0, incomingPort = 0;
+ char *temp, target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ struct in_addr addr;
+ struct iphdr ip;
+ struct udphdr udp;
+
+
+ if ((ConfigTokenRetrieve("UDP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read UDP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* ok, now check if they have a network daemon on the socket already, if they do */
+ /* then skip that port because it will cause false alarms */
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into stealth listen mode on UDP port: %d\n",
+ ports[count]);
+ if ((openSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket(openSockfd, client, server, ports[count]) == ERROR)
+ Log("adminalert: ERROR: Socket %d is in use and will not be monitored. Attempting to continue\n", ports[count]);
+ else {
+ gotBound = TRUE;
+ ports2[portCount2++] = ports[count];
+ }
+ close(openSockfd);
+ }
+
+ if (gotBound == FALSE) {
+ Log("adminalert: ERROR: All supplied UDP sockets are in use and will not be listened to. Shutting down.\n");
+ return (ERROR);
+ }
+
+ if ((openSockfd = OpenRAWUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadUDP(openSockfd, &ip, &udp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(udp.dest);
+
+ /* this iterates the list of ports looking for a match */
+ for (count = 0; count < portCount; count++) {
+ if (incomingPort == ports2[count]) {
+ if (SmartVerifyUDP(client, server, incomingPort) == TRUE)
+ break;
+
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr), IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target) !=
+ TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, ports2[count]);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, ports2[count]) != TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports2[count], gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } /* end IsBlocked check */
+ else {
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ }
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ break; /* get out of for(count) loop above */
+ } /* end if(incoming port) == protected port */
+ } /* end for( check for protected port loop ) loop */
+ } /* end for( ; ; ) loop */
+
+} /* end KalasagStealthModeUDP */
+
+
+/****************************************************************/
+/* Advanced Stealth scan detection mode for UDP */
+/* */
+/* This mode will see what ports are listening below 1024 */
+/* and will then monitor all the rest. This is very sensitive */
+/* and will react on any packet hitting any monitored port. */
+/* This is a very dangerous option and is for advanced users */
+/* */
+/****************************************************************/
+int KalasagAdvancedStealthModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int result = TRUE, scanDetectTrigger = TRUE, hotPort = TRUE;
+ int openSockfd = 0, incomingPort = 0, smartVerify = FALSE;
+ unsigned int advancedPorts = 1024;
+ unsigned int count = 0, inUsePorts[MAXSOCKS], portCount = 0;
+ char target[IPMAXBUF], configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF], *temp;
+ struct in_addr addr;
+ struct iphdr ip;
+ struct udphdr udp;
+
+
+ if ((ConfigTokenRetrieve("ADVANCED_PORTS_UDP", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read ADVANCED_PORTS_UDP option from config file. Assuming 1024.");
+ advancedPorts = 1024;
+ } else
+ advancedPorts = atoi(configToken);
+
+ Log("adminalert: Advanced mode will monitor first %d ports",
+ advancedPorts);
+
+ /* try to bind to all ports below 1024, any that are taken we exclude later */
+ for (count = 0; count < advancedPorts; count++) {
+ if ((openSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+ if (BindSocket(openSockfd, client, server, count) == ERROR)
+ inUsePorts[portCount++] = count;
+
+ close(openSockfd);
+ }
+
+ if ((ConfigTokenRetrieve("ADVANCED_EXCLUDE_UDP", configToken)) !=
+ FALSE) {
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ for (count = 0; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL) {
+ inUsePorts[portCount++] = atoi(temp);
+ Log("adminalert: Advanced mode will manually exclude port: %d ", inUsePorts[portCount - 1]);
+ } else
+ break;
+ }
+ }
+ } else
+ Log("adminalert: Advanced mode will manually exclude no ports");
+
+
+ for (count = 0; count < portCount; count++)
+ Log("adminalert: Advanced Stealth scan detection mode activated. Ignored UDP port: %d\n", inUsePorts[count]);
+
+ if ((openSockfd = OpenRAWUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open RAW UDP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main detection loop */
+ for (;;) {
+ if (PacketReadUDP(openSockfd, &ip, &udp) != TRUE)
+ continue;
+
+ incomingPort = ntohs(udp.dest);
+
+ /* check if we should ignore this connection to this port */
+ for (count = 0; count < portCount; count++) {
+ if ((incomingPort == inUsePorts[count])
+ || (incomingPort >= advancedPorts)) {
+ hotPort = FALSE;
+ break;
+ } else
+ hotPort = TRUE;
+ }
+
+ if (hotPort) {
+ smartVerify = SmartVerifyUDP(client, server, incomingPort);
+
+ if (smartVerify != TRUE) {
+ /* copy the clients address into our buffer for nuking */
+ addr.s_addr = (u_int) ip.saddr;
+ SafeStrncpy(target, (char *) inet_ntoa(addr), IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target) !=
+ TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: UDP scan from host: %s/%s to UDP port: %d", resolvedHost, target, incomingPort);
+ /* Report on options present */
+ if (ip.ihl > 5)
+ Log("attackalert: Packet from host: %s/%s to UDP port: %d has IP options set (detection avoidance technique).", resolvedHost, target, incomingPort);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, incomingPort) != TRUE)
+ Log("attackalert: ERROR: Could not block host %s/%s!!", resolvedHost, target);
+ else
+ WriteBlocked(target, resolvedHost,
+ incomingPort, gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } /* end IsBlocked check */
+ else
+ Log("attackalert: Host: %s/%s is already blocked Ignoring", resolvedHost, target);
+ } /* end if(scanDetectTrigger) */
+ } /* end if(never block) check */
+ } /* end if (smartVerify) */
+ } /* end if(hotPort) */
+ } /* end for( ; ; ) loop */
+}
+
+/* end KalasagAdvancedStealthModeUDP */
+
+#endif
+
+
+
+
+/****************************************************************/
+/* Classic detection Mode */
+/* */
+/* This mode will bind to a list of TCP sockets and wait for */
+/* connections to happen. Although the least prone to false */
+/* alarms, it also won't detect stealth scans */
+/* */
+/****************************************************************/
+int KalasagModeTCP(void)
+{
+
+ struct sockaddr_in client, server;
+ int length, portCount = 0, ports[MAXSOCKS];
+ int openSockfd[MAXSOCKS], incomingSockfd, result = TRUE;
+ int count = 0, scanDetectTrigger = TRUE, showBanner =
+ FALSE, boundPortCount = 0;
+ int selectResult = 0;
+ char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF],
+ configToken[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ fd_set selectFds;
+
+ if ((ConfigTokenRetrieve("TCP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read TCP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No TCP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* read in the banner if one is given */
+ if ((ConfigTokenRetrieve("PORT_BANNER", configToken)) == TRUE) {
+ showBanner = TRUE;
+ SafeStrncpy(bannerBuffer, configToken, MAXBUF);
+ }
+
+
+ /* setup select call */
+ FD_ZERO(&selectFds);
+
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into listen mode on TCP port: %d\n",
+ ports[count]);
+ if ((openSockfd[boundPortCount] = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket. Aborting.\n");
+ return (ERROR);
+ }
+
+ if (BindSocket
+ (openSockfd[boundPortCount], client, server,
+ ports[count]) == ERROR) {
+ Log("adminalert: ERROR: could not bind TCP socket: %d. Attempting to continue\n", ports[count]);
+ } else /* well we at least bound to one socket so we'll continue */
+ boundPortCount++;
+ }
+
+
+ /* if we didn't bind to anything then abort */
+ if (boundPortCount == 0) {
+ Log("adminalert: ERROR: could not bind ANY TCP sockets. Shutting down.\n");
+ return (ERROR);
+ }
+
+ length = sizeof(client);
+
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+ /* main loop for multiplexing/resetting */
+ for (;;) {
+ /* set up select call */
+ for (count = 0; count < boundPortCount; count++)
+ FD_SET(openSockfd[count], &selectFds);
+ selectResult =
+ select(MAXSOCKS, &selectFds, NULL, NULL,
+ (struct timeval *) NULL);
+
+ /* something blew up */
+ if (selectResult < 0) {
+ Log("adminalert: ERROR: select call failed. Shutting down.\n");
+ return (ERROR);
+ } else if (selectResult == 0) {
+#ifdef DEBUG
+ Log("Select timeout");
+#endif
+ }
+
+ /* select is reporting a waiting socket. Poll them all to find out which */
+ else if (selectResult > 0) {
+ for (count = 0; count < boundPortCount; count++) {
+ if (FD_ISSET(openSockfd[count], &selectFds)) {
+ incomingSockfd =
+ accept(openSockfd[count],
+ (struct sockaddr *) &client, &length);
+ if (incomingSockfd < 0) {
+ Log("attackalert: Possible stealth scan from unknown host to TCP port: %d (accept failed)", ports[count]);
+ break;
+ }
+
+ /* copy the clients address into our buffer for nuking */
+ SafeStrncpy(target,
+ (char *) inet_ntoa(client.sin_addr),
+ IPMAXBUF);
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+
+ if (scanDetectTrigger == TRUE) {
+ /* show the banner if one was selected */
+ if (showBanner == TRUE)
+ write(incomingSockfd, bannerBuffer,
+ strlen(bannerBuffer));
+ /* we don't need the bonehead anymore */
+ close(incomingSockfd);
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: Connect from host: %s/%s to TCP port: %d", resolvedHost, target, ports[count]);
+
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeTCP(target, ports[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s !!", target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports[count],
+ gblBlockedFile,
+ gblHistoryFile, "TCP");
+ } else
+ Log("attackalert: Host: %s is already blocked. Ignoring", target);
+ }
+ }
+ close(incomingSockfd);
+ break;
+ } /* end if(FD_ISSET) */
+ } /* end for() */
+ } /* end else (selectResult > 0) */
+ } /* end main for(; ; ) loop */
+
+/* not reached */
+ close(incomingSockfd);
+}
+
+
+
+
+
+/****************************************************************/
+/* Classic detection Mode */
+/* */
+/* This mode will bind to a list of UDP sockets and wait for */
+/* connections to happen. Stealth scanning really doesn't apply */
+/* here. */
+/* */
+/****************************************************************/
+int KalasagModeUDP(void)
+{
+ struct sockaddr_in client, server;
+ int length, ports[MAXSOCKS], openSockfd[MAXSOCKS], result = TRUE;
+ int count = 0, portCount = 0, selectResult = 0, scanDetectTrigger = 0;
+ int boundPortCount = 0, showBanner = FALSE;
+ char *temp, target[IPMAXBUF], bannerBuffer[MAXBUF],
+ configToken[MAXBUF];
+ char buffer[MAXBUF];
+ char resolvedHost[DNSMAXBUF];
+ fd_set selectFds;
+
+ if ((ConfigTokenRetrieve("UDP_PORTS", configToken)) == FALSE) {
+ Log("adminalert: ERROR: Could not read UDP_PORTS option from config file");
+ return (ERROR);
+ }
+
+ /* break out the ports */
+ if ((temp = (char *) strtok(configToken, ",")) != NULL) {
+ ports[0] = atoi(temp);
+ for (count = 1; count < MAXSOCKS; count++) {
+ if ((temp = (char *) strtok(NULL, ",")) != NULL)
+ ports[count] = atoi(temp);
+ else
+ break;
+ }
+ portCount = count;
+ } else {
+ Log("adminalert: ERROR: No UDP ports supplied in config file. Aborting");
+ return (ERROR);
+ }
+
+ /* read in the banner if one is given */
+ if ((ConfigTokenRetrieve("PORT_BANNER", configToken)) == TRUE) {
+ showBanner = TRUE;
+ SafeStrncpy(bannerBuffer, configToken, MAXBUF);
+ }
+
+ /* setup select call */
+ FD_ZERO(&selectFds);
+
+ for (count = 0; count < portCount; count++) {
+ Log("adminalert: Going into listen mode on UDP port: %d\n",
+ ports[count]);
+ if ((openSockfd[boundPortCount] = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket. Aborting\n");
+ return (ERROR);
+ }
+ if (BindSocket
+ (openSockfd[boundPortCount], client, server,
+ ports[count]) == ERROR) {
+ Log("adminalert: ERROR: could not bind UDP socket: %d. Attempting to continue\n", ports[count]);
+ } else /* well we at least bound to one socket so we'll continue */
+ boundPortCount++;
+ }
+
+/* if we didn't bind to anything then abort */
+ if (boundPortCount == 0) {
+ Log("adminalert: ERROR: could not bind ANY UDP sockets. Shutting down.\n");
+ return (ERROR);
+ }
+
+
+ length = sizeof(client);
+ Log("adminalert: Kalasag is now active and listening.\n");
+
+/* main loop for multiplexing/resetting */
+ for (;;) {
+ /* set up select call */
+ for (count = 0; count < boundPortCount; count++)
+ FD_SET(openSockfd[count], &selectFds);
+ /* setup the select multiplexing (blocking mode) */
+ selectResult =
+ select(MAXSOCKS, &selectFds, NULL, NULL,
+ (struct timeval *) NULL);
+
+ if (selectResult < 0) {
+ Log("adminalert: ERROR: select call failed. Shutting down.\n");
+ return (ERROR);
+ } else if (selectResult == 0) {
+#ifdef DEBUG
+ Log("Select timeout");
+#endif
+ }
+
+ /* select is reporting a waiting socket. Poll them all to find out which */
+ else if (selectResult > 0) {
+ for (count = 0; count < portCount; count++) {
+ if (FD_ISSET(openSockfd[count], &selectFds)) {
+ /* here just read in one byte from the UDP socket, that's all we need to */
+ /* know that this person is a jerk */
+ if (recvfrom(openSockfd[count], buffer, 1, 0,
+ (struct sockaddr *) &client, &length) < 0)
+ {
+ Log("adminalert: ERROR: could not accept incoming socket for UDP port: %d\n", ports[count]);
+ break;
+ }
+
+ /* copy the clients address into our buffer for nuking */
+ SafeStrncpy(target,
+ (char *) inet_ntoa(client.sin_addr),
+ IPMAXBUF);
+#ifdef DEBUG
+ Log("debug: KalasagModeUDP: accepted UDP connection from: %s\n", target);
+#endif
+ /* check if we should ignore this IP */
+ result = NeverBlock(target, gblIgnoreFile);
+ if (result == ERROR) {
+ Log("attackalert: ERROR: cannot open ignore file. Blocking host anyway.\n");
+ result = FALSE;
+ }
+ if (result == FALSE) {
+ /* check if they've visited before */
+ scanDetectTrigger = CheckStateEngine(target);
+ if (scanDetectTrigger == TRUE) {
+ /* show the banner if one was selected */
+ if (showBanner == TRUE)
+ sendto(openSockfd[count], bannerBuffer,
+ strlen(bannerBuffer), 0,
+ (struct sockaddr *) &client,
+ length);
+
+ if (gblResolveHost) { /* Do they want DNS resolution? */
+ if (CleanAndResolve(resolvedHost, target)
+ != TRUE) {
+ Log("attackalert: ERROR: Error resolving host. \
+ resolving disabled for this host.\n");
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+ } else {
+ snprintf(resolvedHost, DNSMAXBUF, "%s",
+ target);
+ }
+
+ Log("attackalert: Connect from host: %s/%s to UDP port: %d", resolvedHost, target, ports[count]);
+ /* check if this target is already blocked */
+ if (IsBlocked(target, gblBlockedFile) == FALSE) {
+ if (DisposeUDP(target, ports[count]) !=
+ TRUE)
+ Log("attackalert: ERROR: Could not block host %s !!", target);
+ else
+ WriteBlocked(target, resolvedHost,
+ ports[count],
+ gblBlockedFile,
+ gblHistoryFile, "UDP");
+ } else
+ Log("attackalert: Host: %s is already blocked. Ignoring", target);
+ }
+ }
+ break;
+ } /* end if(FD_ISSET) */
+ } /* end for() */
+ } /* end else (selectResult > 0) */
+ } /* end main for(; ; ) loop */
+
+} /* end UDP Kalasag */
+
+
+
+
+/* kill the TCP connection depending on config option */
+int DisposeTCP(char *target, int port)
+{
+ int status = TRUE;
+
+#ifdef DEBUG
+ Log("debug: DisposeTCP: disposing of host %s on port %d with option: %d", target, port, gblBlockTCP);
+ Log("debug: DisposeTCP: killRunCmd: %s", gblKillRunCmd);
+ Log("debug: DisposeTCP: gblRunCmdFirst: %d", gblRunCmdFirst);
+ Log("debug: DisposeTCP: killHostsDeny: %s", gblKillHostsDeny);
+ Log("debug: DisposeTCP: killRoute: %s %d", gblKillRoute,
+ strlen(gblKillRoute));
+#endif
+/* Should we ignore TCP from active response? */
+ if (gblBlockTCP == 1) {
+ /* run external command first, hosts.deny second, dead route last */
+ if (gblRunCmdFirst) {
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ }
+ /* run hosts.deny first, dead route second, external command last */
+ else {
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ }
+ } else if (gblBlockTCP == 2) {
+ /* run external command only */
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd(target, port, gblKillRunCmd, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ } else
+ Log("attackalert: Ignoring TCP response per configuration file setting.");
+
+ return (status);
+}
+
+
+/* kill the UDP connection depending on config option */
+int DisposeUDP(char *target, int port)
+{
+ int status = TRUE;
+
+#ifdef DEBUG
+ Log("debug: DisposeUDP: disposing of host %s on port %d with option: %d", target, port, gblBlockUDP);
+ Log("debug: DisposeUDP: killRunCmd: %d", gblKillRunCmd);
+ Log("debug: DisposeUDP: gblRunCmdFirst: %s", gblRunCmdFirst);
+ Log("debug: DisposeUDP: killHostsDeny: %s", gblKillHostsDeny);
+ Log("debug: DisposeUDP: killRoute: %s %d", gblKillRoute,
+ strlen(gblKillRoute));
+#endif
+/* Should we ignore TCP from active response? */
+ if (gblBlockUDP == 1) {
+ /* run external command first, hosts.deny second, dead route last */
+ if (gblRunCmdFirst) {
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ }
+ /* run hosts.deny first, dead route second, external command last */
+ else {
+ if (strlen(gblKillHostsDeny) > 0)
+ if (KillHostsDeny
+ (target, port, gblKillHostsDeny,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRoute) > 0)
+ if (KillRoute(target, port, gblKillRoute, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd
+ (target, port, gblKillRunCmd,
+ gblDetectionType) != TRUE)
+ status = FALSE;
+ }
+ } else if (gblBlockUDP == 2) {
+ /* run external command only */
+ if (strlen(gblKillRunCmd) > 0)
+ if (KillRunCmd(target, port, gblKillRunCmd, gblDetectionType)
+ != TRUE)
+ status = FALSE;
+ } else
+ Log("attackalert: Ignoring UDP response per configuration file setting.");
+
+ return (status);
+}
+
+
+/* duh */
+void Usage(void)
+{
+ printf("Kalasag Port Scan Detector\n");
+ printf("Version: %s\n\n", VERSION);
+#ifdef SUPPORT_STEALTH
+ printf("usage: kalasag [-tcp -udp -stcp -atcp -sudp -audp]\n\n");
+#else
+ printf("Stealth scan detection not supported on this platform\n");
+ printf("usage: kalasag [-tcp -udp]\n\n");
+#endif
+/*
+ printf ("*** PLEASE READ THE DOCS BEFORE USING *** \n\n");
+*/
+}
+
+
+
+/* our cheesy state engine to monitor who has connected here before */
+int CheckStateEngine(char *target)
+{
+ int count = 0, scanDetectTrigger = TRUE;
+ int gotOne = 0;
+
+/* This is the rather basic scan state engine. It maintains */
+/* an array of past hosts who triggered a connection on a port */
+/* when a new host arrives it is compared against the array */
+/* if it is found in the array it increments a state counter by */
+/* one and checks the remainder of the array. It does this until */
+/* the end is reached or the trigger value has been exceeded */
+/* This would probably be better as a linked list/hash table, */
+/* but for the number of hosts we are tracking this is just as good. */
+/* This will probably change in the future */
+
+ gotOne = 1; /* our flag counter if we get a match */
+ scanDetectTrigger = TRUE; /* set to TRUE until set otherwise */
+
+ if (gblConfigTriggerCount > 0) {
+ for (count = 0; count < MAXSTATE; count++) {
+ /* if the array has the IP address then increment the gotOne counter and */
+ /* check the trigger value. If it is exceeded break out of the loop and */
+ /* set the detecttrigger to TRUE */
+ if (strcmp(gblScanDetectHost[count], target) == 0) {
+ /* compare the number of matches to the configured trigger value */
+ /* if we've exceeded we can stop this noise */
+ if (++gotOne >= gblConfigTriggerCount) {
+ scanDetectTrigger = TRUE;
+#ifdef DEBUG
+ Log("debug: CheckStateEngine: host: %s has exceeded trigger value: %d\n", gblScanDetectHost[count], gblConfigTriggerCount);
+#endif
+ break;
+ }
+ } else
+ scanDetectTrigger = FALSE;
+ }
+
+ /* now add the fresh meat into the state engine */
+ /* if our array is still less than MAXSTATE large add it to the end */
+ if (gblScanDetectCount < MAXSTATE) {
+ SafeStrncpy(gblScanDetectHost[gblScanDetectCount], target,
+ IPMAXBUF);
+ gblScanDetectCount++;
+ } else {
+ /* otherwise tack it to the beginning and start overwriting older ones */
+ gblScanDetectCount = 0;
+ SafeStrncpy(gblScanDetectHost[gblScanDetectCount], target,
+ IPMAXBUF);
+ gblScanDetectCount++;
+ }
+
+#ifdef DEBUG
+ for (count = 0; count < MAXSTATE; count++)
+ Log("debug: CheckStateEngine: state engine host: %s -> position: %d Detected: %d\n", gblScanDetectHost[count], count, scanDetectTrigger);
+#endif
+ /* end catch to set state if gblConfigTriggerCount == 0 */
+ if (gotOne >= gblConfigTriggerCount)
+ scanDetectTrigger = TRUE;
+ }
+
+
+ if (gblConfigTriggerCount > MAXSTATE) {
+ Log("securityalert: WARNING: Trigger value %d is larger than state engine capacity of %d.\n", gblConfigTriggerCount);
+ Log("Adjust the value lower or recompile with a larger state engine value.\n", MAXSTATE);
+ Log("securityalert: Blocking host anyway because of invalid trigger value");
+ scanDetectTrigger = TRUE;
+ }
+ return (scanDetectTrigger);
+}
+
+
+#ifdef SUPPORT_STEALTH
+/* This takes a tcp packet and reports what type of scan it is */
+char *ReportPacketType(struct tcphdr tcpPkt)
+{
+ static char packetDesc[MAXBUF];
+ static char *packetDescPtr = packetDesc;
+
+ if ((tcpPkt.syn == 0) && (tcpPkt.fin == 0) && (tcpPkt.ack == 0)
+ && (tcpPkt.psh == 0) && (tcpPkt.rst == 0) && (tcpPkt.urg == 0))
+ snprintf(packetDesc, MAXBUF, " TCP NULL scan");
+ else if ((tcpPkt.fin == 1) && (tcpPkt.urg == 1) && (tcpPkt.psh == 1))
+ snprintf(packetDesc, MAXBUF, "TCP XMAS scan");
+ else if ((tcpPkt.fin == 1) && (tcpPkt.syn != 1) && (tcpPkt.ack != 1)
+ && (tcpPkt.psh != 1) && (tcpPkt.rst != 1)
+ && (tcpPkt.urg != 1))
+ snprintf(packetDesc, MAXBUF, "TCP FIN scan");
+ else if ((tcpPkt.syn == 1) && (tcpPkt.fin != 1) && (tcpPkt.ack != 1)
+ && (tcpPkt.psh != 1) && (tcpPkt.rst != 1)
+ && (tcpPkt.urg != 1))
+ snprintf(packetDesc, MAXBUF, "TCP SYN/Normal scan");
+ else
+ snprintf(packetDesc, MAXBUF,
+ "Unknown Type: TCP Packet Flags: SYN: %d FIN: %d ACK: %d PSH: %d URG: %d RST: %d",
+ tcpPkt.syn, tcpPkt.fin, tcpPkt.ack, tcpPkt.psh,
+ tcpPkt.urg, tcpPkt.rst);
+
+ return (packetDescPtr);
+}
+
+int
+SmartVerifyTCP(struct sockaddr_in client, struct sockaddr_in server,
+ int port)
+{
+
+ int testSockfd;
+
+/* Ok here is where we "Smart-Verify" the socket. If the port was previously */
+/* unbound, but now appears to have someone there, then we will skip responding */
+/* to this inbound packet. This a basic "stateful" inspection of the */
+/* the connection */
+
+ if ((testSockfd = OpenTCPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open TCP socket to smart-verify.\n");
+ return (FALSE);
+ } else {
+ if (BindSocket(testSockfd, client, server, port) == ERROR) {
+#ifdef DEBUG
+ Log("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
+#endif
+ close(testSockfd);
+ return (TRUE);
+ }
+ }
+
+ close(testSockfd);
+ return (FALSE);
+}
+
+int
+SmartVerifyUDP(struct sockaddr_in client, struct sockaddr_in server,
+ int port)
+{
+ int testSockfd;
+
+/* Ok here is where we "Smart-Verify" the socket. If the port was previously */
+/* unbound, but now appears to have someone there, then we will skip responding */
+/* to this inbound packet. This essentially is a "stateful" inspection of the */
+/* the connection */
+
+ if ((testSockfd = OpenUDPSocket()) == ERROR) {
+ Log("adminalert: ERROR: could not open UDP socket to smart-verify.\n");
+ return (FALSE);
+ } else {
+ if (BindSocket(testSockfd, client, server, port) == ERROR) {
+#ifdef DEBUG
+ Log("debug: SmartVerify: Smart-Verify Port In Use: %d", port);
+#endif
+ close(testSockfd);
+ return (TRUE);
+ }
+ }
+
+ close(testSockfd);
+ return (FALSE);
+}
+
+#endif /* SUPPORT_STEALTH */
diff --git a/kalasag.conf b/kalasag.conf
new file mode 100644
index 0000000..aaa111b
--- /dev/null
+++ b/kalasag.conf
@@ -0,0 +1,293 @@
+# Kalasag Configuration
+#
+# IMPORTANT NOTE: You CAN NOT put spaces between your port arguments.
+#
+# The default ports will catch a large number of common probes
+#
+# All entries must be in quotes.
+
+
+#######################
+# Port Configurations #
+#######################
+#
+#
+# Some example port configs for classic and basic Stealth modes
+#
+# I like to always keep some ports at the "low" end of the spectrum.
+# This will detect a sequential port sweep really quickly and usually
+# these ports are not in use (i.e. tcpmux port 1)
+#
+# ** X-Windows Users **: If you are running X on your box, you need to be sure
+# you are not binding Kalasag to port 6000 (or port 2000 for OpenWindows users).
+# Doing so will prevent the X-client from starting properly.
+#
+# These port bindings are *ignored* for Advanced Stealth Scan Detection Mode.
+#
+
+# Un-comment these if you are really anal:
+#TCP_PORTS="1,7,9,11,15,70,79,80,109,110,111,119,138,139,143,512,513,514,515,540,635,1080,1524,2000,2001,4000,4001,5742,6000,6001,6667,12345,12346,20034,27665,30303,32771,32772,32773,32774,31337,40421,40425,49724,54320"
+#UDP_PORTS="1,7,9,66,67,68,69,111,137,138,161,162,474,513,517,518,635,640,641,666,700,2049,31335,27444,34555,32770,32771,32772,32773,32774,31337,54321"
+#
+# Use these if you just want to be aware:
+TCP_PORTS="1,11,15,79,111,119,143,540,635,1080,1524,2000,5742,6667,12345,12346,20034,27665,31337,32771,32772,32773,32774,40421,49724,54320"
+UDP_PORTS="1,7,9,69,161,162,513,635,640,641,700,37444,34555,31335,32770,32771,32772,32773,32774,31337,54321"
+#
+# Use these for just bare-bones
+#TCP_PORTS="1,11,15,110,111,143,540,635,1080,1524,2000,12345,12346,20034,32771,32772,32773,32774,49724,54320"
+#UDP_PORTS="1,7,9,69,161,162,513,640,700,32770,32771,32772,32773,32774,31337,54321"
+
+###########################################
+# Advanced Stealth Scan Detection Options #
+###########################################
+#
+# This is the number of ports you want Kalasag to monitor in Advanced mode.
+# Any port *below* this number will be monitored. Right now it watches
+# everything below 1024.
+#
+# On many Linux systems you cannot bind above port 61000. This is because
+# these ports are used as part of IP masquerading. I don't recommend you
+# bind over this number of ports. Realistically: I DON'T RECOMMEND YOU MONITOR
+# OVER 1024 PORTS AS YOUR FALSE ALARM RATE WILL ALMOST CERTAINLY RISE. You've been
+# warned! Don't write me if you have have a problem because I'll only tell
+# you to RTFM and don't run above the first 1024 ports.
+#
+#
+ADVANCED_PORTS_TCP="1024"
+ADVANCED_PORTS_UDP="1024"
+#
+# This field tells Kalasag what ports (besides listening daemons) to
+# ignore. This is helpful for services like ident that services such
+# as FTP, SMTP, and wrappers look for but you may not run (and probably
+# *shouldn't* IMHO).
+#
+# By specifying ports here Kalasag will simply not respond to
+# incoming requests, in effect Kalasag treats them as if they are
+# actual bound daemons. The default ports are ones reported as
+# problematic false alarms and should probably be left alone for
+# all but the most isolated systems/networks.
+#
+# Default TCP ident and NetBIOS service
+ADVANCED_EXCLUDE_TCP="113,139"
+# Default UDP route (RIP), NetBIOS, bootp broadcasts.
+ADVANCED_EXCLUDE_UDP="520,138,137,67"
+
+
+######################
+# Configuration Files#
+######################
+#
+# Hosts to ignore
+IGNORE_FILE="/opt/kalasag/kalasag.ignore"
+# Hosts that have been denied (running history)
+HISTORY_FILE="/opt/kalasag/kalasag.history"
+# Hosts that have been denied this session only (temporary until next restart)
+BLOCKED_FILE="/opt/kalasag/kalasag.blocked"
+
+##############################
+# Misc. Configuration Options#
+##############################
+#
+# DNS Name resolution - Setting this to "1" will turn on DNS lookups
+# for attacking hosts. Setting it to "0" (or any other value) will shut
+# it off.
+RESOLVE_HOST = "1"
+
+###################
+# Response Options#
+###################
+# Options to dispose of attacker. Each is an action that will
+# be run if an attack is detected. If you don't want a particular
+# option then comment it out and it will be skipped.
+#
+# The variable $TARGET$ will be substituted with the target attacking
+# host when an attack is detected. The variable $PORT$ will be substituted
+# with the port that was scanned.
+#
+##################
+# Ignore Options #
+##################
+# These options allow you to enable automatic response
+# options for UDP/TCP. This is useful if you just want
+# warnings for connections, but don't want to react for
+# a particular protocol (i.e. you want to block TCP, but
+# not UDP). To prevent a possible Denial of service attack
+# against UDP and stealth scan detection for TCP, you may
+# want to disable blocking, but leave the warning enabled.
+# I personally would wait for this to become a problem before
+# doing though as most attackers really aren't doing this.
+# The third option allows you to run just the external command
+# in case of a scan to have a pager script or such execute
+# but not drop the route. This may be useful for some admins
+# who want to block TCP, but only want pager/e-mail warnings
+# on UDP, etc.
+#
+#
+# 0 = Do not block UDP/TCP scans.
+# 1 = Block UDP/TCP scans.
+# 2 = Run external command only (KILL_RUN_CMD)
+
+BLOCK_UDP="1"
+BLOCK_TCP="1"
+
+###################
+# Dropping Routes:#
+###################
+# This command is used to drop the route or add the host into
+# a local filter table.
+#
+# The gateway (333.444.555.666) should ideally be a dead host on
+# the *local* subnet. On some hosts you can also point this at
+# localhost (127.0.0.1) and get the same effect. NOTE THAT
+# 333.444.555.66 WILL *NOT* WORK. YOU NEED TO CHANGE IT!!
+#
+# ALL KILL ROUTE OPTIONS ARE COMMENTED OUT INITIALLY. Make sure you
+# uncomment the correct line for your OS. If you OS is not listed
+# here and you have a route drop command that works then please
+# mail it to me so I can include it. ONLY ONE KILL_ROUTE OPTION
+# CAN BE USED AT A TIME SO DON'T UNCOMMENT MULTIPLE LINES.
+#
+# NOTE: The route commands are the least optimal way of blocking
+# and do not provide complete protection against UDP attacks and
+# will still generate alarms for both UDP and stealth scans. I
+# always recommend you use a packet filter because they are made
+# for this purpose.
+#
+
+# Generic
+#KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666"
+
+# Generic Linux
+#KILL_ROUTE="/sbin/route add -host $TARGET$ gw 333.444.555.666"
+
+# Newer versions of Linux support the reject flag now. This
+# is cleaner than the above option.
+#KILL_ROUTE="/sbin/route add -host $TARGET$ reject"
+
+# Generic BSD (BSDI, OpenBSD, NetBSD, FreeBSD)
+#KILL_ROUTE="/sbin/route add $TARGET$ 333.444.555.666"
+
+# Generic Sun
+#KILL_ROUTE="/usr/sbin/route add $TARGET$ 333.444.555.666 1"
+
+# NEXTSTEP
+#KILL_ROUTE="/usr/etc/route add $TARGET$ 127.0.0.1 1"
+
+# FreeBSD
+#KILL_ROUTE="route add -net $TARGET$ -netmask 255.255.255.255 127.0.0.1 -blackhole"
+
+# Digital UNIX 4.0D (OSF/1 / Compaq Tru64 UNIX)
+#KILL_ROUTE="/sbin/route add -host -blackhole $TARGET$ 127.0.0.1"
+
+# Generic HP-UX
+#KILL_ROUTE="/usr/sbin/route add net $TARGET$ netmask 255.255.255.0 127.0.0.1"
+
+##
+# Using a packet filter is the PREFERRED. The below lines
+# work well on many OS's. Remember, you can only uncomment *one*
+# KILL_ROUTE option.
+##
+
+# ipfwadm support for Linux
+#KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$ -o"
+#
+# ipfwadm support for Linux (no logging of denied packets)
+#KILL_ROUTE="/sbin/ipfwadm -I -i deny -S $TARGET$"
+#
+# ipchain support for Linux
+#KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY -l"
+#
+# ipchain support for Linux (no logging of denied packets)
+#KILL_ROUTE="/sbin/ipchains -I input -s $TARGET$ -j DENY"
+#
+# iptables support for Linux
+KILL_ROUTE="/sbin/iptables -A INPUT -s $TARGET$ -j DROP"
+#
+# For those of you running FreeBSD (and compatible) you can
+# use their built in firewalling as well.
+#
+#KILL_ROUTE="/sbin/ipfw add 1 deny all from $TARGET$:255.255.255.255 to any"
+#
+#
+# For those running ipfilt (OpenBSD, etc.)
+# NOTE THAT YOU NEED TO CHANGE external_interface TO A VALID INTERFACE!!
+#
+#KILL_ROUTE="/bin/echo 'block in log on external_interface from $TARGET$/32 to any' | /sbin/ipf -f -"
+
+
+###############
+# TCP Wrappers#
+###############
+# This text will be dropped into the hosts.deny file for wrappers
+# to use. There are two formats for TCP wrappers:
+#
+# Format One: Old Style - The default when extended host processing
+# options are not enabled.
+#
+KILL_HOSTS_DENY="ALL: $TARGET$"
+
+# Format Two: New Style - The format used when extended option
+# processing is enabled. You can drop in extended processing
+# options, but be sure you escape all '%' symbols with a backslash
+# to prevent problems writing out (i.e. \%c \%h )
+#
+#KILL_HOSTS_DENY="ALL: $TARGET$ : DENY"
+
+###################
+# External Command#
+###################
+# This is a command that is run when a host connects, it can be whatever
+# you want it to be (pager, etc.). This command is executed before the
+# route is dropped or after depending on the KILL_RUN_CMD_FIRST option below
+#
+#
+# I NEVER RECOMMEND YOU PUT IN RETALIATORY ACTIONS AGAINST THE HOST SCANNING
+# YOU!
+#
+# TCP/IP is an *unauthenticated protocol* and people can make scans appear out
+# of thin air. The only time it is reasonably safe (and I *never* think it is
+# reasonable) to run reverse probe scripts is when using the "classic" -tcp mode.
+# This mode requires a full connect and is very hard to spoof.
+#
+# The KILL_RUN_CMD_FIRST value should be set to "1" to force the command
+# to run *before* the blocking occurs and should be set to "0" to make the
+# command run *after* the blocking has occurred.
+#
+#KILL_RUN_CMD_FIRST = "0"
+#
+#
+#KILL_RUN_CMD="/some/path/here/script $TARGET$ $PORT$"
+
+
+#####################
+# Scan trigger value#
+#####################
+# Enter in the number of port connects you will allow before an
+# alarm is given. The default is 0 which will react immediately.
+# A value of 1 or 2 will reduce false alarms. Anything higher is
+# probably not necessary. This value must always be specified, but
+# generally can be left at 0.
+#
+# NOTE: If you are using the advanced detection option you need to
+# be careful that you don't make a hair trigger situation. Because
+# Advanced mode will react for *any* host connecting to a non-used
+# below your specified range, you have the opportunity to really
+# break things. (i.e someone innocently tries to connect to you via
+# SSL [TCP port 443] and you immediately block them). Some of you
+# may even want this though. Just be careful.
+#
+SCAN_TRIGGER="0"
+
+######################
+# Port Banner Section#
+######################
+#
+# Enter text in here you want displayed to a person tripping the Kalasag.
+# I *don't* recommend taunting the person as this will aggravate them.
+# Leave this commented out to disable the feature
+#
+# Stealth scan detection modes don't use this feature
+#
+#PORT_BANNER="** UNAUTHORIZED ACCESS PROHIBITED *** YOUR CONNECTION ATTEMPT HAS BEEN LOGGED. GO AWAY."
+
+# EOF
diff --git a/kalasag.h b/kalasag.h
new file mode 100644
index 0000000..99abca2
--- /dev/null
+++ b/kalasag.h
@@ -0,0 +1,74 @@
+#define VERSION "1.0"
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <netdb.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#ifndef _LINUX_C_LIB_VERSION
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "kalasag_config.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+#ifdef SUPPORT_STEALTH
+#ifdef LINUX
+#include "kalasag_tcpip.h"
+#include <netinet/in_systm.h>
+#endif
+
+#define TCPPACKETLEN 80
+#define UDPPACKETLEN 68
+#endif /* SUPPORT_STEALTH */
+
+#ifdef NEXT
+#include <ansi.h>
+#endif
+
+#define ERROR -1
+#define TRUE 1
+#define FALSE 0
+#define MAXBUF 1024
+/* max size of an IP address plus NULL */
+#define IPMAXBUF 16
+/* max sockets we can open */
+#define MAXSOCKS 64
+
+/* Really is about 1025, but we don't need the length for our purposes */
+#define DNSMAXBUF 255
+
+
+/* prototypes */
+int KalasagModeTCP(void);
+int KalasagModeUDP(void);
+int DisposeUDP(char *, int);
+int DisposeTCP(char *, int);
+int CheckStateEngine(char *);
+int InitConfig(void);
+void Usage(void);
+int SmartVerifyTCP(struct sockaddr_in, struct sockaddr_in, int);
+int SmartVerifyUDP(struct sockaddr_in, struct sockaddr_in, int);
+
+#ifdef SUPPORT_STEALTH
+int KalasagStealthModeTCP(void);
+int KalasagAdvancedStealthModeTCP(void);
+int KalasagStealthModeUDP(void);
+int KalasagAdvancedStealthModeUDP(void);
+char *ReportPacketType(struct tcphdr);
+int PacketReadTCP(int, struct iphdr *, struct tcphdr *);
+int PacketReadUDP(int, struct iphdr *, struct udphdr *);
+#endif
diff --git a/kalasag.ignore b/kalasag.ignore
new file mode 100644
index 0000000..fa804ed
--- /dev/null
+++ b/kalasag.ignore
@@ -0,0 +1,2 @@
+127.0.0.1/32
+0.0.0.0
diff --git a/kalasag_config.h b/kalasag_config.h
new file mode 100644
index 0000000..6d5c418
--- /dev/null
+++ b/kalasag_config.h
@@ -0,0 +1,15 @@
+/* These are probably ok. Be sure you change the Makefile if you */
+/* change the path */
+#define CONFIG_FILE "/opt/kalasag/kalasag.conf"
+
+/* The location of Wietse Venema's TCP Wrapper hosts.deny file */
+#define WRAPPER_HOSTS_DENY "/etc/hosts.deny"
+
+/* The default syslog is as daemon.notice. You can also use */
+/* any of the facilities from syslog.h to send messages to (LOCAL0, etc) */
+#define SYSLOG_FACILITY LOG_DAEMON
+#define SYSLOG_LEVEL LOG_NOTICE
+
+
+/* the maximum number of hosts to keep in a "previous connect" state engine*/
+#define MAXSTATE 50
diff --git a/kalasag_io.c b/kalasag_io.c
new file mode 100644
index 0000000..f181bc4
--- /dev/null
+++ b/kalasag_io.c
@@ -0,0 +1,752 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* Main logging function to surrogate syslog */
+void Log(char *logentry, ...)
+{
+ char logbuffer[MAXBUF];
+
+ va_list argsPtr;
+ va_start(argsPtr, logentry);
+
+ vsnprintf(logbuffer, MAXBUF, logentry, argsPtr);
+
+ va_end(argsPtr);
+
+ openlog("kalasag", LOG_PID, SYSLOG_FACILITY);
+ syslog(SYSLOG_LEVEL, "%s", logbuffer);
+ closelog();
+}
+
+
+void Exit(int status)
+{
+ Log("securityalert: Kalasag is shutting down\n");
+ Log("adminalert: Kalasag is shutting down\n");
+ exit(status);
+}
+
+
+void Start(void)
+{
+ Log("adminalert: Kalasag %s is starting.\n", VERSION);
+#ifdef DEBUG
+ printf("Compiled: " __DATE__ " at " __TIME__ "\n");
+#endif
+}
+
+
+
+/* The daemonizing code copied from Advanced Programming */
+/* in the UNIX Environment by W. Richard Stevens with minor changes */
+int DaemonSeed(void)
+{
+ int childpid;
+
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGTERM, Exit);
+ signal(SIGABRT, Exit);
+ signal(SIGURG, Exit);
+ signal(SIGKILL, Exit);
+
+ if ((childpid = fork()) < 0)
+ return (ERROR);
+ else if (childpid > 0)
+ exit(0);
+
+ setsid();
+ chdir("/");
+ umask(077);
+
+ /* close stdout, stdin, stderr */
+ close(0);
+ close(1);
+ close(2);
+
+ return (TRUE);
+}
+
+
+/* Compares an IP address against a listed address and its netmask*/
+int CompareIPs(char *target, char *ignoreAddr, int ignoreNetmaskBits)
+{
+ unsigned long int netmaskAddr, ipAddr, targetAddr;
+
+ ipAddr = inet_addr(ignoreAddr);
+ targetAddr = inet_addr(target);
+ netmaskAddr = htonl(0xFFFFFFFF << (32 - ignoreNetmaskBits));
+
+
+#ifdef DEBUG
+ Log("debug: target %s\n", target);
+ Log("debug: ignoreAddr %s\n", ignoreAddr);
+ Log("debug: ignoreNetmaskBits %d\n", ignoreNetmaskBits);
+ Log("debug: ipAddr %lu\n", ipAddr);
+ Log("debug: targetAddr %lu\n", targetAddr);
+ Log("debug: netmask %x\n", netmaskAddr);
+ Log("debug: mix ipAddr %lu\n", (ipAddr & netmaskAddr));
+ Log("debug: mix target %lu\n", (targetAddr & netmaskAddr));
+#endif
+
+ /* Network portion mask & op and return */
+ if ((ipAddr & netmaskAddr) == (targetAddr & netmaskAddr))
+ return (TRUE);
+ else
+ return (FALSE);
+}
+
+
+
+/* check hosts that should never be blocked */
+int NeverBlock(char *target, char *filename)
+{
+ FILE *input;
+ char buffer[MAXBUF], tempBuffer[MAXBUF], netmaskBuffer[MAXBUF];
+ char *slashPos;
+ int count = 0, dest = 0, netmaskBits = 0;
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Opening ignore file: %s \n", filename);
+#endif
+ if ((input = fopen(filename, "r")) == NULL)
+ return (ERROR);
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Doing lookup for host: %s \n", target);
+#endif
+
+ while (fgets(buffer, MAXBUF, input) != NULL) {
+ /* Reset destination counter */
+ dest = 0;
+
+ if ((buffer[0] == '#') || (buffer[0] == '\n'))
+ continue;
+
+ for (count = 0; count < strlen(buffer); count++) {
+ /* Parse out digits, colons, and slashes. Everything else rejected */
+ if ((isdigit(buffer[count])) ||
+ (buffer[count] == '.') || (buffer[count] == ':')
+ || (buffer[count] == '/')) {
+ tempBuffer[dest++] = buffer[count];
+ } else {
+ tempBuffer[dest] = '\0';
+ break;
+ }
+ }
+
+ /* Return pointer to slash if it exists and copy data to buffer */
+ slashPos = strchr(tempBuffer, '/');
+ if (slashPos) {
+ SafeStrncpy(netmaskBuffer, slashPos + 1, MAXBUF);
+ /* Terminate tempBuffer string at delimeter for later use */
+ *slashPos = '\0';
+ } else
+ /* Copy in a 32 bit netmask if none given */
+ SafeStrncpy(netmaskBuffer, "32", MAXBUF);
+
+
+ /* Convert netmaskBuffer to bits in netmask */
+ netmaskBits = atoi(netmaskBuffer);
+ if ((netmaskBits < 0) || (netmaskBits > 32)) {
+ Log("adminalert: Invalid netmask in config file: %s Ignoring entry.\n", buffer);
+ continue;
+ }
+
+ if (CompareIPs(target, tempBuffer, netmaskBits)) {
+#ifdef DEBUG
+ Log("debug: NeverBlock: Host: %s found in ignore file with netmask %s\n", target, netmaskBuffer);
+#endif
+
+ fclose(input);
+ return (TRUE);
+ }
+
+ } /* end while() */
+
+#ifdef DEBUG
+ Log("debug: NeverBlock: Host: %s NOT found in ignore file\n", target);
+#endif
+
+ fclose(input);
+ return (FALSE);
+}
+
+
+/* Make sure the config file is available */
+int CheckConfig(void)
+{
+ FILE *input;
+
+ if ((input = fopen(CONFIG_FILE, "r")) == NULL) {
+ Log("adminalert: Cannot open config file: %s. Exiting\n",
+ CONFIG_FILE);
+ return (FALSE);
+ } else
+ fclose(input);
+
+ return (TRUE);
+}
+
+
+/* This writes out blocked hosts to the blocked file. It adds the hostname */
+/* time stamp, and port connection that was acted on */
+int
+WriteBlocked(char *target, char *resolvedHost, int port,
+ char *blockedFilename, char *historyFilename, char *portType)
+{
+ FILE *output;
+ int blockedStatus = TRUE, historyStatus = TRUE;
+
+ struct tm *tmptr;
+
+ time_t current_time;
+ current_time = time(0);
+ tmptr = localtime(¤t_time);
+
+
+#ifdef DEBUG
+ Log("debug: WriteBlocked: Opening block file: %s \n", blockedFilename);
+#endif
+
+
+ if ((output = fopen(blockedFilename, "a")) == NULL) {
+ Log("adminalert: ERROR: Cannot open blocked file: %s.\n",
+ blockedFilename);
+ blockedStatus = FALSE;
+ } else {
+ fprintf(output,
+ "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n",
+ current_time, tmptr->tm_mon + 1, tmptr->tm_mday,
+ tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min,
+ tmptr->tm_sec, resolvedHost, target, port, portType);
+ fclose(output);
+ blockedStatus = TRUE;
+ }
+
+#ifdef DEBUG
+ Log("debug: WriteBlocked: Opening history file: %s \n",
+ historyFilename);
+#endif
+ if ((output = fopen(historyFilename, "a")) == NULL) {
+ Log("adminalert: ERROR: Cannot open history file: %s.\n",
+ historyFilename);
+ historyStatus = FALSE;
+ } else {
+ fprintf(output,
+ "%ld - %02d/%02d/%04d %02d:%02d:%02d Host: %s/%s Port: %d %s Blocked\n",
+ current_time, tmptr->tm_mon + 1, tmptr->tm_mday,
+ tmptr->tm_year + 1900, tmptr->tm_hour, tmptr->tm_min,
+ tmptr->tm_sec, resolvedHost, target, port, portType);
+ fclose(output);
+ historyStatus = TRUE;
+ }
+
+ if (historyStatus || blockedStatus == FALSE)
+ return (FALSE);
+ else
+ return (TRUE);
+}
+
+
+
+
+/* This reads a token from the config file up to the "=" and returns the string */
+/* up to the first space or NULL */
+int ConfigTokenRetrieve(char *token, char *configToken)
+{
+ FILE *config;
+ char buffer[MAXBUF], tokenBuffer[MAXBUF];
+ int count = 0;
+
+ if ((config = fopen(CONFIG_FILE, "r")) == NULL) {
+ Log("adminalert: ERROR: Cannot open config file: %s.\n",
+ CONFIG_FILE);
+ return (ERROR);
+ } else {
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieve: checking for token %s", token);
+#endif
+ while ((fgets(buffer, MAXBUF, config)) != NULL) {
+ /* this skips comments */
+ if (buffer[0] != '#') {
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieve: data: %s", buffer);
+#endif
+ /* search for the token and make sure the trailing character */
+ /* is a " " or "=" to make sure the entire token was found */
+ if ((strstr(buffer, token) != (char) NULL) &&
+ ((buffer[strlen(token)] == '=')
+ || (buffer[strlen(token)] == ' '))) { /* cut off the '=' and send it back */
+ if (strstr(buffer, "\"") == (char) NULL) {
+ Log("adminalert: Quotes missing from %s token. Option skipped\n", token);
+ fclose(config);
+ return (FALSE);
+ }
+
+ SafeStrncpy(tokenBuffer, strstr(buffer, "\"") + 1,
+ MAXBUF);
+
+ /* strip off unprintables/linefeeds (if any) */
+ count = 0;
+ while (count < MAXBUF - 1) {
+ if ((isprint(tokenBuffer[count]))
+ && tokenBuffer[count] != '"')
+ configToken[count] = tokenBuffer[count];
+ else {
+ configToken[count] = '\0';
+ break;
+ }
+ count++;
+ }
+
+#ifdef DEBUG
+ Log("debug: ConfigTokenRetrieved token: %s\n",
+ configToken);
+#endif
+ configToken[MAXBUF - 1] = '\0';
+ fclose(config);
+ return (TRUE);
+ }
+ }
+ }
+ fclose(config);
+ return (FALSE);
+ }
+
+}
+
+
+
+
+/* This will bind a socket to a port. It works for UDP/TCP */
+int
+BindSocket(int sockfd, struct sockaddr_in client,
+ struct sockaddr_in server, int port)
+{
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding to port: %d\n", port);
+#endif
+
+ bzero((char *) &server, sizeof(server));
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = htonl(INADDR_ANY);
+ server.sin_port = htons(port);
+
+ if (bind(sockfd, (struct sockaddr *) &server, sizeof(server)) < 0) {
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding failed\n");
+#endif
+ return (ERROR);
+ } else {
+#ifdef DEBUG
+ Log("debug: BindSocket: Binding successful. Doing listen\n");
+#endif
+ listen(sockfd, 5);
+ return (TRUE);
+ }
+}
+
+
+/* Open a TCP Socket */
+int OpenTCPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenTCPSocket: opening TCP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+
+/* Open a UDP Socket */
+int OpenUDPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: openUDPSocket opening UDP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+#ifdef SUPPORT_STEALTH
+/* Open a RAW TCPSocket */
+int OpenRAWTCPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenRAWTCPSocket: opening RAW TCP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+
+/* Open a RAW UDP Socket */
+int OpenRAWUDPSocket(void)
+{
+ int sockfd;
+
+#ifdef DEBUG
+ Log("debug: OpenRAWUDPSocket: opening RAW UDP socket\n");
+#endif
+
+ if ((sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0)
+ return (ERROR);
+ else
+ return (sockfd);
+}
+#endif
+
+/* This will use a system() call to change the route of the target host to */
+/* a dead IP address on your LOCAL SUBNET. */
+int
+KillRoute(char *target, int port, char *killString, char *detectionType)
+{
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int killStatus = ERROR, substStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+ snprintf(portString, MAXBUF, "%d", port);
+
+ substStatus =
+ SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp);
+ if (substStatus == 0) {
+ Log("adminalert: No target variable specified in KILL_ROUTE option. Skipping.\n");
+ return (ERROR);
+ } else if (substStatus == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_ROUTE. Skipping.\n");
+ return (ERROR);
+ }
+
+#ifdef DEBUG
+ Log("debug: KillRoute: running route command: %s\n",
+ commandStringFinal);
+#endif
+
+ /* Kill the bastard and report a status */
+ killStatus = system(commandStringFinal);
+
+ if (killStatus == 127) {
+ Log("adminalert: ERROR: There was an error trying to block host (exec fail) %s", target);
+ return (ERROR);
+ } else if (killStatus < 0) {
+ Log("adminalert: ERROR: There was an error trying to block host (system fail) %s", target);
+ return (ERROR);
+ } else {
+ Log("attackalert: Host %s has been blocked via dropped route using command: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+
+/* This will run a specified command with TARGET as the option if one is given. */
+int
+KillRunCmd(char *target, int port, char *killString, char *detectionType)
+{
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int killStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+ snprintf(portString, MAXBUF, "%d", port);
+
+ /* Tokens are not required, but we check for an error anyway */
+ if (SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp)
+ == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_RUN_CMD. Skipping.\n");
+ return (ERROR);
+ }
+
+
+ /* Kill the bastard and report a status */
+ killStatus = system(commandStringFinal);
+
+ if (killStatus == 127) {
+ Log("adminalert: ERROR: There was an error trying to run command (exec fail) %s", target);
+ return (ERROR);
+ } else if (killStatus < 0) {
+ Log("adminalert: ERROR: There was an error trying to run command (system fail) %s", target);
+ return (ERROR);
+ } else {
+ /* report success */
+ Log("attackalert: External command run for host: %s using command: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+/* this function will drop the host into the TCP wrappers hosts.deny file to deny */
+/* all access. The drop route method is preferred as this stops UDP attacks as well */
+/* as TCP. You may find though that host.deny will be a more permanent home.. */
+int
+KillHostsDeny(char *target, int port, char *killString,
+ char *detectionType)
+{
+
+ FILE *output;
+ char cleanAddr[MAXBUF], commandStringTemp[MAXBUF];
+ char commandStringTemp2[MAXBUF], commandStringFinal[MAXBUF];
+ char portString[MAXBUF];
+ int substStatus = ERROR;
+
+ CleanIpAddr(cleanAddr, target);
+
+ snprintf(portString, MAXBUF, "%d", port);
+
+#ifdef DEBUG
+ Log("debug: KillHostsDeny: parsing string for block: %s\n",
+ killString);
+#endif
+
+ substStatus =
+ SubstString(cleanAddr, "$TARGET$", killString, commandStringTemp);
+ if (substStatus == 0) {
+ Log("adminalert: No target variable specified in KILL_HOSTS_DENY option. Skipping.\n");
+ return (ERROR);
+ } else if (substStatus == ERROR) {
+ Log("adminalert: Error trying to parse $TARGET$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (portString, "$PORT$", commandStringTemp,
+ commandStringTemp2) == ERROR) {
+ Log("adminalert: Error trying to parse $PORT$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+
+ if (SubstString
+ (detectionType, "$MODE$", commandStringTemp2,
+ commandStringFinal) == ERROR) {
+ Log("adminalert: Error trying to parse $MODE$ Token for KILL_HOSTS_DENY. Skipping.\n");
+ return (ERROR);
+ }
+#ifdef DEBUG
+ Log("debug: KillHostsDeny: result string for block: %s\n",
+ commandStringFinal);
+#endif
+
+ if ((output = fopen(WRAPPER_HOSTS_DENY, "a")) == NULL) {
+ Log("adminalert: cannot open hosts.deny file: %s for blocking.",
+ WRAPPER_HOSTS_DENY);
+ Log("securityalert: ERROR: There was an error trying to block host %s", target);
+ return (FALSE);
+ } else {
+ fprintf(output, "%s\n", commandStringFinal);
+ fclose(output);
+ Log("attackalert: Host %s has been blocked via wrappers with string: \"%s\"", target, commandStringFinal);
+ return (TRUE);
+ }
+}
+
+
+/* check if the host is already blocked */
+int IsBlocked(char *target, char *filename)
+{
+ FILE *input;
+ char buffer[MAXBUF], tempBuffer[MAXBUF];
+ char *ipOffset;
+ int count;
+
+
+#ifdef DEBUG
+ Log("debug: IsBlocked: Opening block file: %s \n", filename);
+#endif
+ if ((input = fopen(filename, "r")) == NULL) {
+ Log("adminalert: ERROR: Cannot open blocked file: %s for reading. Will create.\n", filename);
+ return (FALSE);
+ }
+
+ while (fgets(buffer, MAXBUF, input) != NULL) {
+ if ((ipOffset = strstr(buffer, target)) != (char) NULL) {
+ for (count = 0; count < strlen(ipOffset); count++) {
+ if ((isdigit(ipOffset[count])) || (ipOffset[count] == '.')) {
+ tempBuffer[count] = ipOffset[count];
+ } else {
+ tempBuffer[count] = '\0';
+ break;
+ }
+ }
+ if (strcmp(target, tempBuffer) == 0) {
+#ifdef DEBUG
+ Log("debug: isBlocked: Host: %s found in blocked file\n",
+ target);
+#endif
+ fclose(input);
+ return (TRUE);
+ }
+ }
+
+ }
+#ifdef DEBUG
+ Log("debug: IsBlocked: Host: %s NOT found in blocked file\n", target);
+#endif
+ fclose(input);
+ return (FALSE);
+}
+
+/*********************************************************************************
+* String substitute function
+*
+* This function takes:
+*
+* 1) A token to use for replacement.
+* 2) A token to find.
+* 3) A string with the tokens in it.
+* 4) A string to write the replaced result.
+*
+* It returns the number of substitutions made during the operation.
+**********************************************************************************/
+int
+SubstString(const char *replace, const char *find, const char *target,
+ char *result)
+{
+ int replaceCount = 0, count = 0, findCount = 0, findLen =
+ 0, numberOfSubst = 0;
+ char tempString[MAXBUF], *tempStringPtr;
+
+#ifdef DEBUG
+ Log("debug: SubstString: Processing string: %s %d", target,
+ strlen(target));
+ Log("debug: SubstString: Processing search text: %s %d", replace,
+ strlen(replace));
+ Log("debug: SubstString: Processing replace text: %s %d", find,
+ strlen(find));
+#endif
+
+ /* string not found in target */
+ if (strstr(target, find) == (char) NULL) {
+ strncpy(result, target, MAXBUF);
+#ifdef DEBUG
+ Log("debug: SubstString: Result string: %s", result);
+#endif
+ return (numberOfSubst);
+ }
+ /* String/victim/target too long */
+ else if ((strlen(target)) + (strlen(replace)) + (strlen(find)) >
+ MAXBUF)
+ return (ERROR);
+
+ memset(tempString, '\0', MAXBUF);
+ memset(result, '\0', MAXBUF);
+ findLen = strlen(find);
+ tempStringPtr = tempString;
+
+ for (count = 0; count < MAXBUF; count++) {
+ if (*target == '\0')
+ break;
+ else if ((strncmp(target, find, findLen)) != 0)
+ *tempStringPtr++ = *target++;
+ else {
+ numberOfSubst++;
+ for (replaceCount = 0; replaceCount < strlen(replace);
+ replaceCount++)
+ *tempStringPtr++ = replace[replaceCount];
+ for (findCount = 0; findCount < findLen; findCount++)
+ target++;
+ }
+ }
+
+ strncpy(result, tempString, MAXBUF);
+#ifdef DEBUG
+ Log("debug: SubstString: Result string: %s", result);
+#endif
+ return (numberOfSubst);
+}
+
+
+
+/* This function checks a config variable for a numerical flag and returns it */
+int CheckFlag(char *flagName)
+{
+ char configToken[MAXBUF];
+
+ if ((ConfigTokenRetrieve(flagName, configToken)) == TRUE) {
+#ifdef DEBUG
+ Log("debug: CheckFlag: found %s string.\n", flagName);
+#endif
+ return (atoi(configToken));
+ } else {
+#ifdef DEBUG
+ Log("debug: CheckFlag: %s option not found. Assuming FALSE.\n",
+ flagName);
+#endif
+ return (FALSE);
+ }
+}
+
+
+/* snprintf for NEXTSTEP (others??) */
+/* I don't know where this code came from and I don't */
+/* warrant its effectiveness. CHR */
+
+#ifdef HAS_NO_SNPRINTF
+int snprintf(char *str, size_t n, char const *fmt, ...)
+{
+ va_list ap;
+ FILE f;
+
+ if (n > MAXBUF) {
+ n = MAXBUF;
+ }
+ va_start(ap, fmt);
+ f._file = EOF;
+ f._flag = _IOWRT | _IOSTRG;
+ f._base = f._ptr = str;
+ f._bufsiz = f._cnt = n ? n - 1 : 0;
+ (void) vfprintf(&f, fmt, ap);
+ va_end(ap);
+ if (n) {
+ *f._ptr = '\0';
+ }
+ return (f._ptr - str);
+}
+#endif
diff --git a/kalasag_io.h b/kalasag_io.h
new file mode 100644
index 0000000..bb83ec8
--- /dev/null
+++ b/kalasag_io.h
@@ -0,0 +1,23 @@
+/* prototypes */
+int WriteBlocked(char *, char *, int, char *, char *, char *);
+void Log(char *, ...);
+void Exit(int);
+void Start(void);
+int DaemonSeed(void);
+int NeverBlock(char *, char *);
+int CheckConfig(void);
+int OpenTCPSocket(void);
+int OpenUDPSocket(void);
+#ifdef SUPPORT_STEALTH
+int OpenRAWTCPSocket(void);
+int OpenRAWUDPSocket(void);
+#endif
+int BindSocket(int, struct sockaddr_in, struct sockaddr_in, int);
+int KillRoute(char *, int, char *, char *);
+int KillHostsDeny(char *, int, char *, char *);
+int KillRunCmd(char *, int, char *, char *);
+int ConfigTokenRetrieve(char *, char *);
+int IsBlocked(char *, char *);
+int SubstString(const char *, const char *, const char *, char *);
+int CheckFlag(char *);
+int CompareIPs(char *, char *, int);
diff --git a/kalasag_tcpip.h b/kalasag_tcpip.h
new file mode 100644
index 0000000..b697b1f
--- /dev/null
+++ b/kalasag_tcpip.h
@@ -0,0 +1,159 @@
+/* Versions of Linux are not consistent in how the TCP/UDP/IP headers
+* defined. This file contains the Linux/BSD headers from RedHat 5.0 and
+* should clear up compile problems. CHR
+*/
+
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef _NETINET_TCP_H
+#define _NETINET_TCP_H 1
+
+#include <features.h>
+
+__BEGIN_DECLS struct tcphdr {
+ u_int16_t source;
+ u_int16_t dest;
+ u_int32_t seq;
+ u_int32_t ack_seq;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int16_t res1:4;
+ u_int16_t doff:4;
+ u_int16_t fin:1;
+ u_int16_t syn:1;
+ u_int16_t rst:1;
+ u_int16_t psh:1;
+ u_int16_t ack:1;
+ u_int16_t urg:1;
+ u_int16_t res2:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int16_t doff:4;
+ u_int16_t res1:4;
+ u_int16_t res2:2;
+ u_int16_t urg:1;
+ u_int16_t ack:1;
+ u_int16_t psh:1;
+ u_int16_t rst:1;
+ u_int16_t syn:1;
+ u_int16_t fin:1;
+#else
+#error "Adjust your <bits/endian.h> defines"
+#endif
+ u_int16_t window;
+ u_int16_t check;
+ u_int16_t urg_ptr;
+};
+
+#endif /* tcp.h */
+
+
+
+#ifndef __NETINET_IP_H
+#define __NETINET_IP_H 1
+
+__BEGIN_DECLS struct timestamp {
+ u_int8_t len;
+ u_int8_t ptr;
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t flags:4;
+ u_int8_t overflow:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t overflow:4;
+ u_int8_t flags:4;
+#else
+#error "Please fix <bytesex.h>"
+#endif
+ u_int32_t data[9];
+};
+
+struct ip_options {
+ u_int32_t faddr; /* Saved first hop address */
+ u_int8_t optlen;
+ u_int8_t srr;
+ u_int8_t rr;
+ u_int8_t ts;
+ u_int8_t is_setbyuser:1; /* Set by setsockopt? */
+ u_int8_t is_data:1; /* Options in __data, rather than skb */
+ u_int8_t is_strictroute:1; /* Strict source route */
+ u_int8_t srr_is_hit:1; /* Packet destination addr was our one */
+ u_int8_t is_changed:1; /* IP checksum more not valid */
+ u_int8_t rr_needaddr:1; /* Need to record addr of outgoing dev */
+ u_int8_t ts_needtime:1; /* Need to record timestamp */
+ u_int8_t ts_needaddr:1; /* Need to record addr of outgoing dev */
+ u_int8_t router_alert;
+ u_int8_t __pad1;
+ u_int8_t __pad2;
+ u_int8_t __data[0];
+};
+
+struct iphdr {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ u_int8_t ihl:4;
+ u_int8_t version:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ u_int8_t version:4;
+ u_int8_t ihl:4;
+#else
+#error "Please fix <bytesex.h>"
+#endif
+ u_int8_t tos;
+ u_int16_t tot_len;
+ u_int16_t id;
+ u_int16_t frag_off;
+ u_int8_t ttl;
+ u_int8_t protocol;
+ u_int16_t check;
+ u_int32_t saddr;
+ u_int32_t daddr;
+ /*The options start here. */
+};
+
+#endif
+
+#ifndef __NETINET_UDP_H
+#define __NETINET_UDP_H 1
+
+__BEGIN_DECLS
+/* UDP header as specified by RFC 768, August 1980. */
+ struct udphdr {
+ u_int16_t source;
+ u_int16_t dest;
+ u_int16_t len;
+ u_int16_t check;
+};
+
+__END_DECLS
+#endif /* netinet/udp.h */
diff --git a/kalasag_util.c b/kalasag_util.c
new file mode 100644
index 0000000..42cb133
--- /dev/null
+++ b/kalasag_util.c
@@ -0,0 +1,114 @@
+#include "kalasag.h"
+#include "kalasag_io.h"
+#include "kalasag_util.h"
+
+/* A replacement for strncpy that covers mistakes a little better */
+char *SafeStrncpy(char *dest, const char *src, size_t size)
+{
+ if (!dest) {
+ dest = NULL;
+ return (NULL);
+ } else if (size < 1) {
+ dest = NULL;
+ return (NULL);
+ }
+
+ /* Null terminate string. Why the hell strncpy doesn't do this */
+ /* for you is mystery to me. God I hate C. */
+ memset(dest, '\0', size);
+ strncpy(dest, src, size - 1);
+
+ return (dest);
+}
+
+
+/************************************************************************/
+/* Generic safety function to process an IP address and remove anything */
+/* that is: */
+/* 1) Not a number. */
+/* 2) Not a period. */
+/* 3) Greater than IPMAXBUF (15) */
+/************************************************************************/
+char *CleanIpAddr(char *cleanAddr, const char *dirtyAddr)
+{
+ int count = 0, maxdot = 0, maxoctet = 0;
+
+#ifdef DEBUG
+ Log("debug: cleanAddr: Cleaning Ip address: %s", dirtyAddr);
+#endif
+
+ memset(cleanAddr, '\0', IPMAXBUF);
+ /* dirtyAddr must be valid */
+ if (dirtyAddr == NULL)
+ return (cleanAddr);
+
+ for (count = 0; count < IPMAXBUF - 1; count++) {
+ if (isdigit(dirtyAddr[count])) {
+ if (++maxoctet > 3) {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ cleanAddr[count] = dirtyAddr[count];
+ } else if (dirtyAddr[count] == '.') {
+ if (++maxdot > 3) {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ maxoctet = 0;
+ cleanAddr[count] = dirtyAddr[count];
+ } else {
+ cleanAddr[count] = '\0';
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ Log("debug: cleanAddr: Cleaned IpAddress: %s Dirty IpAddress: %s",
+ cleanAddr, dirtyAddr);
+#endif
+
+ return (cleanAddr);
+}
+
+
+/************************************************************************/
+/* Generic safety function to process an unresolved address and remove */
+/* anything that is: */
+/* 1) Not a number. */
+/* 2) Not a period. */
+/* 3) Greater than DNSMAXBUF (255) */
+/* 4) Not a legal DNS character (a-z, A-Z, 0-9, - ) */
+/* */
+/* XXX THIS FUNCTION IS NOT COMPLETE */
+/************************************************************************/
+int CleanAndResolve(char *resolvedHost, const char *unresolvedHost)
+{
+ struct hostent *hostPtr = NULL;
+ struct in_addr addr;
+
+#ifdef DEBUG
+ Log("debug: CleanAndResolv: Resolving address: %s", unresolvedHost);
+#endif
+
+ memset(resolvedHost, '\0', DNSMAXBUF);
+ /* unresolvedHost must be valid */
+ if (unresolvedHost == NULL)
+ return (ERROR);
+
+ /* Not a valid address */
+ if ((inet_aton(unresolvedHost, &addr)) == 0)
+ return (ERROR);
+
+ hostPtr =
+ gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr), AF_INET);
+ if (hostPtr != NULL)
+ snprintf(resolvedHost, DNSMAXBUF, "%s", hostPtr->h_name);
+ else
+ snprintf(resolvedHost, DNSMAXBUF, "%s", unresolvedHost);
+
+#ifdef DEBUG
+ Log("debug: CleanAndResolve: Cleaned Resolved: %s Dirty Unresolved: %s", resolvedHost, unresolvedHost);
+#endif
+
+ return (TRUE);
+}
diff --git a/kalasag_util.h b/kalasag_util.h
new file mode 100644
index 0000000..5f95e2f
--- /dev/null
+++ b/kalasag_util.h
@@ -0,0 +1,6 @@
+/* IP address length plus null */
+#define IPMAXBUF 16
+
+char *SafeStrncpy(char *, const char *, size_t);
+char *CleanIpAddr(char *, const char *);
+int CleanAndResolve(char *, const char *);