[36] in Intrusion Detection Systems
Re: Yet Another Intro
daemon@ATHENA.MIT.EDU (Dana Nowell)
Fri Mar 31 06:21:04 1995
Date: Thu, 30 Mar 1995 21:29:05 -0500
To: ids@uow.edu.au
From: DanaNowell@corsof.com (Dana Nowell)
Reply-To: ids@uow.edu.au
--=====================_796642145==_
Content-Type: text/plain; charset="us-ascii"
Attached below. It is pretty trivial.
>Dana:
>
>Can you send me the tcpalert program please? I prefer text or uuencode. My
>platform is Sun Sparc 2 running SunOS 4.1.3.
>
>Thanks.
>
>Sanjiv
>-----
>
>
--=====================_796642145==_
Content-Type: text/plain; charset="us-ascii"
/*
* TcpAlert
* Copyright (c) 1995 - Dana Nowell
*
* This code is provided AS-IS, no warranty of any type is granted
* or implied (including fitness of use), USE AT YOUR OWN RISK.
*
* Redistribution and use in source forms, with and without
* modification, are permitted provided that this entire comment
* appears intact.
*
* Redistribution in binary form may occur without restriction.
* Obviously, it would be nice if you gave credit where it is due.
*
* If you modify this code PLEASE add a comment after this one
* describing your modifications, we should all leave footprints
* for the next guy. (See the change log comment)
*
*
* General design notes:
* This code ASSUMES AN ANSI COMPILER! If your compiler is not ANSI,
* the function prototypes and declarations will give you fits, sorry.
*
* This is NOT designed to be fast, small, and elegant. It IS designed
* to be easy to read, easy to modify, and 'obviously' correct.
*
* Logging to syslog is accomplished via four functions LogDebug,
* LogInfo, LogWarn, and LogError. This allows you a place to make simple
* changes if you dislike my choices of log levels. Currently, LogInfo
* and LogWarn use the same log level, LOG_WARNING. This is because
* of some of the machines I use it on, they only log warnings and above.
* You may want to change LogInfo to use LOG_NOTICE (I probably will in
* the future). The LogDebug function is provided for debugging purposes.
* It uses LOG_DEBUG as the logging level. These functions were provided
* as function calls and not macros to allow people to customize at any
* level, even to the point of not using syslog.
*
* Architecture:
* Very simple. We create a socket, bind to a port and sit on an accept.
* When the accept completes, we spin off a child to handle it and go
* back to sitting on an accept. The child does all the real work in
* function HandleConnection(). The basic info about the connection
* is logged to syslog in function LogPartnerInfo().
*
* Porting Notes:
* This was coded for use on LINUX and compiled with gcc, if you port
* it you need to look at the following areas.
* Includes - your mileage my vary and your compiler may bitch
* cleanchild - specifically the wait (may be wait3 or waitpid)
* - signal call to reset signal (may not need it)
*
* This was coded for AF_INET sockets, if you want AF_ISO, AF_UNIX
* or AF_CCITT you are on your own. The best I can say is you will
* not be using a sockaddr_in structure and at a minimum all references
* to it (and its elements) will need to be changed. I carry the size
* of the socket structure around in socksize just for people like you.
*
* If you port it to a different platform and want to send me the changes,
* I will incorporate them in new versions (assuming there are some).
*
*/
/*
* Change Log:
*
* 03/24/95 Dana Nowell Initial version
* 03/27/95 Dana Nowell Ported to HP and SGI
*
*
*/
/*
* defines for compile time code inclusion
*
* I put them here because I was too lazy to make a makefile, plus it
* improves documentation (yeah, sure it does), you do whatever you want
*
*/
/*
* Which type of wait is used in function cleanchild.
* check only one PLEASE
* note WAIT3 and WAITPID are not implemented yet.
* I have not ported to a box that requires them yet.
*/
#define WAIT_IS_WAIT 1
#define WAIT_IS_WAIT3 0
#define WAIT_IS_WAITPID 0
/*
* causes cleanchild to reset the signal handler. To see if you
* need this: set it to zero, run alert and 'tickle it' two or three
* times. Check the log to ensure you actually did 'tickle it'.
* Run ps and see if you have any zombies, if so set it to 1 and
* try again. Most System V R4 systems should be 1.
*/
#define SIGNAL_RESET 1
/*
* I have included a few lines of debugging info in the code.
* set this to one to log them to syslog via LogDebug
*/
#define DEBUG_ME 0
/*
* Set to 1 to use Bind/Named as the name resolver
* Set to 0 to use only the local host file
* Why anyone would ever set this to 0 is beyond me, but you can
*/
#define USE_NAMED 1 /* use resolver and not host file for lookup */
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <signal.h>
#include <syslog.h>
#if USE_NAMED
#include <arpa/nameser.h>
#include <resolv.h>
#endif
/*
* defines, typedefs, and structure defs used by the program
*/
#define DEFAULT_PORT 1099 /* mainly for testing */
#define MAX_NAME 100
#define MAX_LISTENS 5
#define MAX_SYSLOG_TEXT 200
#define LOGBUF_SIZE 100
/* various defines for exit states */
#define EXIT_SUCCESS 0
#define EXIT_FAILED_UNBLOCK 1
#define EXIT_FAILED_SOCKET 2
#define EXIT_FAILED_CMDLINE 3
#define EXIT_FAILED_INIT 4
#define EXIT_NO_WAY 99
/*
*
* I still do not like the definition of MAX_SYSLOG_BUFFER,
* the magic 100 is annoying but it allows for the PID, port number,
* NULL terminator, and the spacing in the sprintf format string in
* function LogIt(). If you mess with the format string, be sure the
* buffer is still big enough (I have been liberal in the allocation)!
*
*/
#define MAX_SYSLOG_BUFFER (MAX_NAME + 100 + MAX_SYSLOG_TEXT)
/* ANSI function prototypes */
void LogPartnerInfo( struct sockaddr_in *HostAddr );
int GetConnection( int size );
int OpenMySocket( void );
char *GetPartnerName( struct sockaddr_in *sap );
void cleanchild( int s );
void LogDebug( char *p );
void LogInfo( char *p );
void LogWarn( char *p );
void LogError( char *p );
void LogIt( int level, char *p );
int HandleConnection( int *NewSock );
int HandleAccept( int NewSock );
int UnblockTerminal( void );
int ParseCommandLine( int argc, char *argv[] );
int PostListen( int socket );
/* data elements */
/* this has more globals than I would like, but then it is a quick hack */
char copyright[] = "Sections of this program are copyright 1995 by Dana Nowell";
char version[] = "Version 1.0";
int MySocket = -1; /* fd describing socket */
struct sockaddr_in SockAddr; /* My side of the connection */
struct sockaddr_in RealHostAddr; /* His side of the connection */
char msgbuf[LOGBUF_SIZE+1]; /* buffer to format log messages */
int MyPid;
int MyPort = DEFAULT_PORT;
char MyName[MAX_NAME+1];
/***********************************************************************
*
* Main - this is where it all starts
*
***********************************************************************/
int main( int argc, char *argv[] )
{
int NewSock;
int socksize;
printf( "%s, %s - Port Alarm Application\n%s\n\n",
argv[0], version, copyright );
/* get PID for use in error messages */
MyPid = getpid();
/*
* Handle command line junk,
* copy to internal variables to avoid later buffer overrun,
* after the copy we KNOW it will fit because we fixed the max length
*/
if ( ParseCommandLine( argc, argv ) )
{
puts( "Failed to parse the command line properly" );
exit( EXIT_FAILED_CMDLINE );
}
#if DEBUG_ME
LogDebug( "In main after ParseCmdLine" );
#endif
/* spin off a child and die to unblock the terminal/shell */
if ( UnblockTerminal() )
{
exit(EXIT_FAILED_UNBLOCK); /* failed to unblock terminal */
}
/* initialize any global data structures that need it */
if ( InitDataStructs() )
exit(EXIT_FAILED_INIT);
/* write the startup message */
LogInfo( "Started Alert" );
/* setup child signal handler to avoid zombies */
(void) signal(SIGCHLD, cleanchild);
/*
** Try to actually open the socket
*/
/* get a socket for the connection */
socksize = OpenMySocket();
if ( socksize < 0 ) /* if can not get socket, die */
exit(EXIT_FAILED_SOCKET);
/*
* loop forever, spinning off children to handle each connection
*/
while ( 1 )
{
NewSock = GetConnection( socksize );
if ( NewSock < 0 )
continue;
if ( HandleAccept( NewSock ) )
LogError( "Unable to handle the accept" );
(void) close( NewSock ); /* close the 'connection' socket */
NewSock = -1;
} /* back to the forever while loop */
return EXIT_NO_WAY; /* just to make the compiler happy, not used */
}
/***********************************************************************
*
* InitDataStructs - return 0 on success 1 on failure
*
***********************************************************************/
int InitDataStructs( void )
{
int retval = 0;
/* initialize the socket structure */
if (SockAddr.sin_family == 0)
SockAddr.sin_family = AF_INET;
if (SockAddr.sin_addr.s_addr == 0)
SockAddr.sin_addr.s_addr = INADDR_ANY;
if ( SockAddr.sin_port == 0 )
SockAddr.sin_port = htons(MyPort);
#if 0
RealHostAddr.sin_family = AF_INET;
RealHostAddr.sin_addr.s_addr = INADDR_ANY;
#endif
return retval;
}
/***********************************************************************
*
* ParseCommandLine - return 0 on success 1 on failure
*
***********************************************************************/
int ParseCommandLine( int argc, char *argv[] )
{
char *p;
int temp;
int retval = 0;
/* save off my name, size adjusted, for logging to syslogd */
p = argv[0];
temp = strlen( p );
if ( temp > MAX_NAME )
p[MAX_NAME] = '\0';
strcpy( MyName, p );
/* get port number if specified */
if ( argc > 1 )
MyPort = atoi( argv[1] );
return retval;
}
/***********************************************************************
*
* UnblockTerminal - return 0 on success 1 on failure
*
***********************************************************************/
int UnblockTerminal()
{
int pid;
/*
* spin off a child and die so we unblock the terminal
*/
pid = fork();
if ( pid < 0 )
{
LogError( "Can not create child" );
return 1;
}
if ( pid != 0 ) /* if parent, die to unblock term, child continues on */
{
exit(EXIT_SUCCESS);
}
/* now a new process (child process) so reset PID */
MyPid = getpid();
return 0;
}
/***********************************************************************
*
* HandleAccept - where the accept is processed
* returns 0 on success and 1 on failure
*
***********************************************************************/
int HandleAccept( int NewSock )
{
int retval = 0;
int pid;
/*
** Create a subprocess to process the request.
*/
#if DEBUG_ME
sprintf( msgbuf, "Forking (fd = %d)\n", NewSock );
LogDebug( msgbuf );
#endif
pid = fork();
if (pid == 0) /* Child process, log info and die */
{
if ( HandleConnection( &NewSock ) )
LogError( "Failed to handle this connection completely" );
exit(EXIT_SUCCESS); /* child has done its job, die */
}
if (pid < 0) /* fork failed */
{
LogError( "Cannot fork" );
sleep(10);
retval = 1;
}
return retval;
}
/***********************************************************************
*
* HandleConnection - where the connection is actually processed
* returns 0 on success and 1 on failure
*
***********************************************************************/
int HandleConnection( int *NewSock )
{
int retval = 0;
/* clean up stuff left over from the parent */
(void) close( MySocket ); /* close to old 'listen' socket*/
MySocket = -1;
(void) signal(SIGCHLD, SIG_DFL); /* reset the signal handler */
/* process the connection here */
LogPartnerInfo( &RealHostAddr ); /* log info about the connection */
/* add other things here later (IDENTD, FINGER, etc.) */
/*
* NOTE, beware 'recursion wars'. If you include finger here
* and then run this on your finger socket while the
* 'other site' has done the same, it can get REAL interesting.
*/
/* clean up the new connect as we are done */
(void) close( *NewSock ); /* cleanup the 'connection' socket */
*NewSock = -1;
return retval;
}
/***********************************************************************
*
* LogPartnerInfo - logs info about HostAddr to syslog
*
***********************************************************************/
void LogPartnerInfo( struct sockaddr_in *HostAddr )
{
/* log the name of the host connecting to us */
sprintf( msgbuf, "Connected to %s", GetPartnerName( HostAddr ) );
LogWarn( msgbuf );
return;
}
/***********************************************************************
*
* GetConnection - Posts accept and waits for connection
* returns new socket for the connection
*
***********************************************************************/
int GetConnection( int socksize )
{
int NewSock;
int size;
while( 1 ) /* forever is a long time */
{
do
{
errno = 0;
size = socksize;
NewSock = accept( MySocket, (struct sockaddr *) &RealHostAddr, &size );
} while ( NewSock < 0 && errno == EINTR );
if ( NewSock >= 0 ) /* got a live one */
break;
sprintf( msgbuf, "Accept failure, %d", errno );
LogError( msgbuf );
sleep(10);
}
return NewSock;
}
/***********************************************************************
*
* OpenMySocket
* Opens socket, does bind and posts listen
* returns size of socket on success and -1 on failure
*
***********************************************************************/
int OpenMySocket()
{
int on = 1;
int socksize;
socksize = sizeof(SockAddr);
if ( MySocket < 0 ) /* already open ??? */
{
/* get the socket */
MySocket = socket(SockAddr.sin_family, SOCK_STREAM, 0);
if (MySocket < 0)
{ /* probably something already running ??? */
sprintf( msgbuf, "Cannot create socket, errno %d", errno );
LogError( msgbuf );
return -1;
}
(void) setsockopt( MySocket, SOL_SOCKET,
SO_REUSEADDR, (char *)&on, sizeof(on) );
(void) setsockopt( MySocket, SOL_SOCKET,
SO_KEEPALIVE, (char *)&on, sizeof(on) );
/* bind to the socket */
if ( bind( MySocket, (struct sockaddr *) &SockAddr, socksize ) < 0 )
{
sprintf( msgbuf, "Cannot bind, errno %d", errno );
LogError( msgbuf );
socksize = -1;
}
}
if ( PostListen( MySocket ) )
socksize = -1;
/* if error shut it down */
if ( socksize < 0 && MySocket >= 0 )
{
sprintf( msgbuf, "Error on socket %d, shutting it down", MySocket );
LogError( msgbuf );
(void) close( MySocket );
MySocket = -1;
}
return socksize;
}
/***********************************************************************
*
* PostListen - post a listen on socket
* returns 0 on success or 1 on failure
*
***********************************************************************/
int PostListen( int socket )
{
int retval = 0;
if ( listen( socket, MAX_LISTENS ) < 0 )
{
sprintf( msgbuf, "Cannot listen, errno %d", errno );
LogError( msgbuf );
retval = 1;
}
return retval;
}
/***********************************************************************
*
* GetPartnerName -
* returns pointer to buffer containing 'name' of sap
*
***********************************************************************/
char *GetPartnerName( struct sockaddr_in *sap )
{
static char NameBuf[ (3*MAX_NAME) ]; /* bigger than actually needed */
struct hostent *hostinfo;
char *retval;
char *quad = NULL;
char temp[MAX_NAME+1];
int size;
int family;
NameBuf[0] = '\0'; /* safety first, no old data returned by mistake */
family = sap->sin_family;
switch ( family )
{
case AF_INET:
quad = inet_ntoa( sap->sin_addr );
hostinfo = gethostbyaddr((char *) &sap->sin_addr,
sizeof(sap->sin_addr), AF_INET);
break;
default:
hostinfo = NULL;
strcpy( NameBuf, "Unknown socket family" );
sprintf( msgbuf, "In GetPartnerName, bad family %d (%xH)",
family, family );
LogError( msgbuf );
break;
}
/* if we have info, format a proper reply */
if ( hostinfo != NULL )
{
/* make it fit, buffer overflow is a BAD thing */
size = strlen( hostinfo->h_name );
if ( size > MAX_NAME )
{
memcpy( temp, hostinfo->h_name, MAX_NAME );
temp[ MAX_NAME ] = '\0';
strcpy( NameBuf, temp );
}
else
strcpy( NameBuf, hostinfo->h_name );
}
/* if we have a quad format ID add it */
if ( quad != NULL )
{
/* make it fit, buffer overflow is a BAD thing */
size = strlen( quad );
if ( size > MAX_NAME )
quad[ MAX_NAME ] = '\0';
strcat( NameBuf, " " );
strcat( NameBuf, quad );
}
return NameBuf;
}
/***********************************************************************
*
* cleanchild - called by sigchild signal handler to cleanup children
*
***********************************************************************/
void cleanchild( int s )
{
int status;
#if DEBUG_ME
LogDebug( "In cleanchild, just starting" );
#endif
#if WAIT_IS_WAIT
while( wait( &status ) > 0 ) /* catch sig child to avoid zombies */
{
sleep(3);
}
#endif
#if WAIT_IS_WAIT3
#endif
#if WAIT_IS_WAITPID
#endif
#if DEBUG_ME
LogDebug( "In cleanchild, all done" );
#endif
#ifdef SIGNAL_RESET
(void) signal(SIGCHLD, cleanchild); /* reset signal */
#endif
}
/***********************************************************************
*
* LogDebug - writes text p to syslogd at DEBUG level
*
***********************************************************************/
void LogDebug( char *p )
{
LogIt( LOG_DEBUG, p );
}
/***********************************************************************
*
* LogInfo - writes text p to syslogd at WARNING level
*
***********************************************************************/
void LogInfo( char *p )
{
LogIt( LOG_WARNING, p );
}
/***********************************************************************
*
* LogWarn - writes text p to syslogd at WARNING level
*
***********************************************************************/
void LogWarn( char *p )
{
LogIt( LOG_WARNING, p );
}
/***********************************************************************
*
* LogError - writes text p to syslogd at ERROR level
*
***********************************************************************/
void LogError( char *p )
{
LogIt( LOG_ERR, p );
}
/***********************************************************************
*
* LogIt - writes text p to syslogd at the specified error level
*
***********************************************************************/
void LogIt( int level, char *p )
{
static char text[MAX_SYSLOG_BUFFER+1];
int size;
/* make it fit, buffer overflow is a BAD thing */
size = strlen(p);
if ( size > MAX_SYSLOG_TEXT )
p[MAX_SYSLOG_TEXT] = '\0';
/* format the actual syslog message and log it */
/*
* WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
*
* if you modify this format string, see the define for
* MAX_SYSLOG_BUFFER.
*/
sprintf( text, "%s [%d] (port %d) - %s", MyName, MyPid, MyPort, p );
syslog( level, text );
}
--=====================_796642145==_
Content-Type: text/plain; charset="us-ascii"
Dana Nowell Work: DanaNowell@corsof.com
Cornerstone Software Inc. Home: dnowell@mv.mv.com
I don't even believe myself, why should you! (Standard disclaimer in force).
--=====================_796642145==_--