/*
**
** Copyright(C) 1997, 1998 Network Flight Recorder, Inc.
** All rights reserved.
**
** Use and distribution of this software and its source code
** are governed by the terms and conditions of the 
** ** Network Flight Recorder Software License ("LICENSE.TXT")
**
*/

/*
BackOfficer Friendly. Marcus J. Ranum, 1999.
*/

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


#ifdef WORDS_BIGENDIAN
#define __EL_LONG(x) ((((x) >> 24) & 0x000000FF) | \
                     (((x) >> 8) & 0x0000FF00)  | \
                     (((x) << 8) & 0x00FF0000)  | \
                     (((x) << 24) & 0xFF000000))
#else
#define __EL_LONG(x) (x)
#endif


#define PIDFILE	"/var/run/backofficer.pid"


extern char	*optarg;	/* getopt */


static char	magic[] = "*!*QWTY?";
static long	holdrand = 1L;
static FILE	*logf = (FILE *)0;


static void	BOcrypt(unsigned char *,int);
static	char	*typedecode(int);
static	char	*printarg(unsigned char *);
static	char	*printshort(unsigned char *);
static	void	pingpong(int, struct sockaddr_in *);
static	void	goaway(int, struct sockaddr_in *);
static	void	naughty(int, struct sockaddr_in *);
static	void	direrror(int, struct sockaddr_in *,char *);
static	void	viwerror(int, struct sockaddr_in *,char *);
static	void	neterror(int, struct sockaddr_in *);
static	void	experror(int, struct sockaddr_in *);
static	void	usage();
static	int	daemoniz();
static	char	*ntime();




int
main(int ac,char *av[])
{
	struct sockaddr_in	saddr;
	int			quiet = 0;
	int			demon = 1;
	int			usesyslog = 1;
	int			fd;
	int			psiz;
	int			pid;
	int			typ;
	unsigned char		buf[512];
	unsigned long		*pdw;
	unsigned char		*ptr;

	while((fd = getopt(ac,av,"ql:tn")) != -1) {
		switch(fd) {
		case 'q':
			quiet = 1;
			break;
		case 'n':
			usesyslog = 0;
			break;
		case 't':
			demon = 0;
			break;
		case 'l':
			logf = fopen(optarg,"a");
			if(logf == (FILE *)0) {
				perror(optarg);
				exit(1);
			}
			(void)setlinebuf(logf);
			break;
		default:
			usage();
		}
	}

	/* listen */
	saddr.sin_family = AF_INET;
	saddr.sin_addr.s_addr = INADDR_ANY;
	saddr.sin_port = htons(31337);
	if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		exit(-1);
	}
	if(bind(fd,(struct sockaddr *)&saddr,sizeof saddr) < 0) {
		perror("bind");
		exit(-1);
	}

	/* fork ourselves */
	if(demon)
		daemoniz();

	if(usesyslog)
		(void)openlog("backofficer",LOG_PID,LOG_USER);


	/* server loop */
	while(1) {
		int cc;
		struct sockaddr_in from;
		int len = sizeof(struct sockaddr_in);

		if((cc = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&from,&len)) < 0) {
			perror("recvfrom");
			continue;
		}

		/* doh! */
		buf[cc] = '\0';

		/* try to decrypt the request */
		BOcrypt(buf,cc);


		/* does it look like BO? */
		if(cc < sizeof(magic) || strncmp((char *)buf,magic,sizeof(magic) - 1)) {
			if(logf)
				fprintf(logf,"%s: %d bytes from %s\n",ntime(),cc,inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%d bytes from %s",cc,inet_ntoa(from.sin_addr));
			continue;
		}


		/* what did they ask us to do? */
		pdw = (unsigned long *)buf;
		pdw += 2;
		psiz = *pdw++;
		psiz = __EL_LONG(psiz);
		pid = *pdw++;
		pid = __EL_LONG(pid);
		ptr = (unsigned char *)pdw;
		typ = *ptr++;


		switch(typ) {
		case 0x1:
			if(logf)
				fprintf(logf,"%s: %s PING sweep\n",ntime(),inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%s PING sweep",inet_ntoa(from.sin_addr));
			if(!quiet)
				pingpong(fd,&from);
			break;
		case 0x2:
			if(logf)
				fprintf(logf,"%s: %s REBOOT\n",ntime(),inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%s REBOOT",inet_ntoa(from.sin_addr));
			if(!quiet)
				naughty(fd,&from);
			break;
		case 0x3:
			if(logf)
				fprintf(logf,"%s: %s LOCKUP\n",ntime(),inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%s LOCKUP",inet_ntoa(from.sin_addr));
			if(!quiet)
				naughty(fd,&from);
			break;
		case 0x31:
			if(logf)
				fprintf(logf,"%s: %s DIR %s\n",ntime(),inet_ntoa(from.sin_addr),printarg(ptr));
			if(usesyslog)
				syslog(LOG_INFO,"%s DIR %s",inet_ntoa(from.sin_addr),printshort(ptr));
			if(!quiet)
				direrror(fd,&from,(char *)ptr);
			break;
		case 0x36:
			if(logf)
				fprintf(logf,"%s: %s FILEVIEW %s\n",ntime(),inet_ntoa(from.sin_addr),printarg(ptr));
			if(usesyslog)
				syslog(LOG_INFO,"%s FILEVIEW %s",inet_ntoa(from.sin_addr),printshort(ptr));
			if(!quiet)
				viwerror(fd,&from,(char *)ptr);
			break;
		case 0x12:
			if(logf)
				fprintf(logf,"%s: %s EXPORTLIST\n",ntime(),inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%s EXPORTLIST",inet_ntoa(from.sin_addr));
			if(!quiet)
				experror(fd,&from);
			break;
		case 0x39:
			if(logf)
				fprintf(logf,"%s: %s NETVIEW\n",ntime(),inet_ntoa(from.sin_addr));
			if(usesyslog)
				syslog(LOG_INFO,"%s NETVIEW",inet_ntoa(from.sin_addr));
			if(!quiet)
				neterror(fd,&from);
			break;
		default:
			if(logf)
				fprintf(logf,"%s: %s %s\n",ntime(),inet_ntoa(from.sin_addr),typedecode(typ));
			if(usesyslog)
				syslog(LOG_INFO,"%s %s",inet_ntoa(from.sin_addr),typedecode(typ));
			if(!quiet)
				goaway(fd,&from);
			break;
		}
	}
}



static	char	*
printarg(unsigned char *p)
{
	return((char *)p);
}


/* for syslog - how much is safe to put in? */
static	char	*
printshort(unsigned char *p)
{
	p[10] = '.';
	p[11] = '.';
	p[12] = '.';
	p[13] = '\0';
	return((char *)p);
}



/* reply to a sweep */
static void
pingpong(int fd, struct sockaddr_in *from)
{
	unsigned char	buf[512];
	char		hostbuf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;
	char		*p;

	/* make up host name */
	(void)gethostname(hostbuf,sizeof(hostbuf));
	if((p = index(hostbuf,'.')) != (char *)0)
		*p = '\0';
	/* mash to uppercase so we look like a PC */
	for(p = hostbuf; *p != '\0'; p++)
		if(islower(*p))
			*p = toupper(*p);

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(hostbuf) + 15;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x01;
	strcpy((char *)ptr,"  !PONG!1.20!");
	ptr += 13;
	strcpy((char *)ptr,hostbuf);
	ptr += strlen(hostbuf);
	strcpy((char *)ptr,"!");

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
neterror(int fd, struct sockaddr_in *from)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;

	/* or a reasonable facsimile... */
	char		*message =
"Network resources:\r\n"
"(null)'(null)' - Microsoft Network - UNKNOWN! (Network root?): CONTAINER\r\n"
"(null)'WKGROUP - (null) - DOMAIN: CONTAINER\r\n"
"(null)'\\\\DESKTOP' - Desktop - SERVER:CONTAINER\r\n"
"(null)'\\\\DESKTOP\\C' - - SHARE:DISK\r\n"
"(null)'\\\\DESKTOP\\HP' - - SHARE:PRINT\r\n"
"(null)'\\\\SERVER' - Samba 1.9.16p2 - SERVER:CONTAINER\r\n"
"(null)'\\\\SERVER\\users' - - SHARE:DISK\r\n"
"End of resource list"
;

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
experror(int fd, struct sockaddr_in *from)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;

	/* or a reasonable facsimile... */
	char		*message =
"Shares as returned by system:\r\n"
"End of shares"
;

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
direrror(int fd, struct sockaddr_in *from,char *dir)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;
	static int	sw = 0;	
	char		*message;

	/* one of 3 realistic messages! */
	static char	*mess[] = {
	"Error 53:The network path was not found opening file ",
	"Error 3:The system cannot find the path specified opening file ",
	"Error 2:The system cannot find the file specified opening file "
	};

	message = mess[sw++ % 3];

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + strlen(dir) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);
  	ptr += strlen(message);
	strcpy((char *)ptr,dir);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
viwerror(int fd, struct sockaddr_in *from,char *path)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;
	static int	sw = 0;	
	char		*message;

	/* one of 2 realistic messages! */
	static char	*mess[] = {
	"Error 13:Permission denied opening file ",
	"Error 2:No such file or directory opening file "
	};

	message = mess[sw++ % 2];

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + strlen(path) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);
  	ptr += strlen(message);
	strcpy((char *)ptr,path);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
naughty(int fd, struct sockaddr_in *from)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;
	static char	*message = "Naughty, naughty. Bad hacker! No donut!";

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



static void
goaway(int fd, struct sockaddr_in *from)
{
	unsigned char	buf[512];
	unsigned long	*pdw;
	unsigned char	*ptr;
	int		x;
	static char	*message = "Thanks. We've logged your attempt to access our system. Now go play elsewhere.";

	x = sizeof(magic) + (sizeof(unsigned long) * 2) + strlen(message) + 2;
  	strcpy((char *)buf,magic);
  	pdw = (unsigned long *)(buf + sizeof(magic) - 1);
  	*pdw++ = __EL_LONG(x);
  	*pdw++ = __EL_LONG((unsigned long)-1);
  	ptr = (unsigned char *)pdw;
  	*ptr++ = 0x00;
	strcpy((char *)ptr,message);

  	BOcrypt(buf,(int)x);
	from->sin_family = AF_INET;
	sendto(fd,buf,x,0,(struct sockaddr *)from,sizeof(struct sockaddr));
}



/*
this code from bounix.c; reformatted somewhat.
these hacker kiddies can't code for sh**.
*/
void
msrand(unsigned int seed)
{
	holdrand = (long)seed;
}



int
mrand(void)
{ 
	return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
}



static void
BOcrypt(unsigned char *buff, int len)
{
	int y;

	if(len < 0)
		return;

	msrand(31337);
	for(y = 0; y < len; y++)
	buff[y] = buff[y] ^ (mrand() % 256);
}



static	char *
typedecode(int t)
{
	static struct	typ	{
		char	*nam;
		int	code;
	} types[] = {
		{ "TYPE_ERROR", 0x00 },
		{ "TYPE_PING", 0x01 },
		{ "TYPE_SYSREBOOT", 0x02 },
		{ "TYPE_SYSLOCKUP", 0x03 },
		{ "TYPE_SYSLISTPASSWORDS", 0x04 },
		{ "TYPE_SYSVIEWCONSOLE", 0x05 },
		{ "TYPE_SYSINFO", 0x06 },
		{ "TYPE_SYSLOGKEYS", 0x07 },
		{ "TYPE_SYSENDKEYLOG", 0x08 },
		{ "TYPE_SYSDIALOGBOX", 0x09 },
		{ "TYPE_PACKETRESEND", 0x13 },
		{ "TYPE_REDIRADD", 0x0B },
		{ "TYPE_REDIRDEL", 0x0C },
		{ "TYPE_REDIRLIST", 0x0D },
		{ "TYPE_APPADD", 0x0E },
		{ "TYPE_APPDEL", 0x0F },
		{ "TYPE_APPLIST", 0x3F },
		{ "TYPE_NETEXPORTADD", 0x10 },
		{ "TYPE_NETEXPORTDELETE", 0x11 },
		{ "TYPE_NETEXPORTLIST", 0x12 },
		{ "TYPE_NETVIEW", 0x39 },
		{ "TYPE_NETUSE", 0x3A },
		{ "TYPE_NETDELETE", 0x3B },
		{ "TYPE_NETCONNECTIONS", 0x3C },
		{ "TYPE_PROCESSLIST", 0x20 },
		{ "TYPE_PROCESSKILL", 0x21 },
		{ "TYPE_PROCESSSPAWN", 0x22 },
		{ "TYPE_REGISTRYCREATEKEY", 0x23 },
		{ "TYPE_REGISTRYSETVALUE", 0x24 },
		{ "TYPE_REGISTRYDELETEKEY", 0x25 },
		{ "TYPE_REGISTRYDELETEVALUE", 0x0A },
		{ "TYPE_REGISTRYENUMKEYS", 0x26 },
		{ "TYPE_REGISTRYENUMVALS", 0x27 },
		{ "TYPE_MMCAPFRAME", 0x28 },
		{ "TYPE_MMCAPAVI",   0x29 },
		{ "TYPE_MMPLAYSOUND",  0x2A },
		{ "TYPE_MMLISTCAPS", 0x2B },
		{ "TYPE_MMCAPSCREEN", 0x2C },
		{ "TYPE_DIRECTORYLIST", 0x31 },
		{ "TYPE_FILEFIND", 0x34 },
		{ "TYPE_FILEDELETE", 0x35 },
		{ "TYPE_FILEVIEW", 0x36 },
		{ "TYPE_FILERENAME", 0x37 },
		{ "TYPE_FILECOPY", 0x38 },
		{ "TYPE_DIRECTORYMAKE", 0x3D },
		{ "TYPE_DIRECTORYDELETE", 0x3E },
		{ "TYPE_FILEFREEZE", 0x17 },
		{ "TYPE_FILEMELT", 0x18 },
		{ "TYPE_HTTPENABLE", 0x14 },
		{ "TYPE_HTTPDISABLE", 0x15 },
		{ "TYPE_TCPFILESEND", 0x2d },
		{ "TYPE_TCPFILERECEIVE", 0x2e },
		{ "TYPE_RESOLVEHOST", 0x16 },
		{ "TYPE_PLUGINEXECUTE", 0x19 },
		{ "TYPE_PLUGINLIST", 0x2f },
		{ "TYPE_PLUGINKILL", 0x30 },
		{ 0, 0 }
	};
	struct	typ	*p;

	for(p = types; p->nam != (char *)0; p++)
		if(t == p->code)
			return(p->nam);
	return("unknown");
}



static	void
usage()
{
	fprintf(stderr,"usage: backofficer [-q] [-n] [-t] [-l logfile]\n");
	fprintf(stderr,"\t[-q] disables fake replies\n");
	fprintf(stderr,"\t[-l logfile] appends logs to specified file\n");
	fprintf(stderr,"\t[-t] do not run as a daemon\n");
	fprintf(stderr,"\t[-n] do not use syslog\n");
	exit(1);
}



static	int
daemoniz()
{
	int	nul;
	FILE	*pidf;

	switch(fork()) {
	case -1:
		return(-1);
	case 0:
		break;
	default:
		exit(0);
	}

	/* disassociate */
	(void)setsid();

#ifdef	PIDFILE
	if((pidf = fopen(PIDFILE,"w")) != (FILE *)0) {
		fprintf(pidf,"%d\n",getpid());
		fclose(pidf);
	}

#endif

	/* now /dev/null */
	if((nul = open("/dev/null",O_RDWR,0)) < 0) {
		perror("/dev/null");
		exit(1);
	}

	(void)dup2(nul,0);
	(void)dup2(nul,1);
	(void)dup2(nul,2);
	if(nul > 2)
		(void)close(nul);
	return(0);
}



static	char	*
ntime()
{
	time_t	now;
	char	*p;

	time(&now);
	p = ctime(&now);
	p[24] = '\0';
	return(p);
}
