diff options
author | Roger Dingledine <arma@torproject.org> | 2002-06-26 22:45:49 +0000 |
---|---|---|
committer | Roger Dingledine <arma@torproject.org> | 2002-06-26 22:45:49 +0000 |
commit | 9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a (patch) | |
tree | fac560bf2dce8a8d2b82e296b71ff24f59ab1a7a | |
parent | 766a465a6043ac4e643c398feb14f708fd0d863f (diff) | |
download | tor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar tor-9a928eeb1215f0d7c9b6d0bb9e4571d0a16ed79a.tar.gz |
Initial revision
svn:r2
67 files changed, 12287 insertions, 0 deletions
@@ -0,0 +1,124 @@ + +Obvious things I'd like to do that won't break anything: + +* Abstract out crypto calls, with the eventual goal of moving + from openssl to something with a more flexible license. + +* Test suite. We need one. + +* Switch the "return -1" cases that really mean "you've got a bug" + into calls to assert(). + +* Since my OR can handle multiple circuits through a given OP, + I think it's clear that the OP should pass new create cells through the + same channel. Thus we can take advantage of the padding we're already + getting. Does that mean the choose_onion functions should be changed + to always pick a favorite OR first, so the OP can minimize the number + of outgoing connections it must sustain? + +* Rewrite the OP to be non-blocking single-process. + +* Add autoconf support. + Figure out what .h files we're actually using, and how portable + those are. + +* Since we're using a stream cipher, an adversary's cell arriving with the + same aci will forever trash our circuit. Since each side picks half + the aci, for each cell the adversary has a 1/256 chance of trashing a + circuit. This is really nasty. We want to make ACIs something reasonably + hard to collide with, such as 20 bytes. + + While we're at it, I'd like more than 4 bits for Version. :) + +* Exit policies. Since we don't really know what protocol is being spoken, + it really comes down to an IP range and port range that we + allow/disallow. The 'application' connection can evaluate it and make + a decision. + +* We currently block on gethostbyname in OR. This is poor. The complex + solution is to have a separate process that we talk to. There are some + free software versions we can use, but they'll still be tricky. The + better answer is to realize that the OP can do the resolution and + simply hand the OR an IP directly. + A) This prevents us from doing sneaky things like having the name resolve + differently at the OR than at the OP. I'm ok with that. + B) It actually just shunts the "dns lookups block" problem back onto the + OP. But that's ok too, because the OP doesn't have to be as robust. + (Heck, can we have the application proxy resolve it, even?) + +* I'd like a cleaner interface for the configuration files, keys, etc. + Perhaps the next step is a central repository where we download router + lists? Something that takes the human more out of the loop. + + We should look into a 'topology communication protocol'; there's one + mentioned in the spec that Paul has, but I haven't looked at it to + know how complete it is or how well it would work. This would also + allow us to add new ORs on the fly. Directory servers, a la the ones + we're developing for Mixminion (see http://mixminion.net/), are also + a very nice approach to consider. + +* Should ORs rotate their link keys periodically? + +* We probably want OAEP padding for RSA. + +* The parts of the code that say 'FIXME' + +Non-obvious things I'd like to do: + +(Many of these topics are inter-related. It's clear that we need more +analysis before we can guess which approaches are good.) + +* Padding between ORs, and correct padding between OPs. Currently + the OP seems to send padding at a steady rate, but data cells can + come more quickly than that. This doesn't provide much protection + at all. I'd like to investigate a synchronous mixing approach, where + cells are sent at fixed intervals. We need to investigate the effects + of this on DoS resistance -- what do we do when we have too many + packets? One approach is to do traffic shaping rather than traffic + padding -- we gain a bit more resistance to DoS at the expense of some + anonymity. Can we compare this analysis to that of the Cottrell Mix, + and learn something new? We'll need to decide on exactly how the + traffic shaping algorithm works. + +* Make the connection buf's grow dynamically as needed. This won't + really solve the fundamental problem above, though, that a buffer + can be given an adversary-controlled number of cells. + +* I'd like to add a scheduler of some sort. Currently we only need one + for sending out padding cells, and if these events are periodic and + synchronized, we don't yet need a scheduler per se, but rather we just + need to have poll return every so often and avoid sending cells onto + the sockets except at the appointed time. We're nearly ready to do + that as it is, with the separation of write_to_buf() and flush_buf(). + + Edge case: what do we do with circuits that receive a destroy + cell before all data has been sent out? Currently there's only one + (outgoing) buffer per connection, so since it's crypted, a circuit + can't recognize its own packet once it's been queued. We could mark + the circuits for destruction, and go through and cull them once the + buffer is entirely flushed; but with the synchronous approach above, + the buffer may never become empty. Perhaps I should implement a callback + system, so a function can get called when a particular cell gets sent + out. That sounds very flexible, but might also be overkill. + +* Currently when a connection goes down, it generates a destroy cell + (either in both directions or just the appropriate one). When a + destroy cell arrives to an OR (and it gets read after all previous + cells have arrived), it delivers a destroy cell for the "other side" + of the circuit: if the other side is an OP or APP, it closes the entire + connection as well. + + But by "a connection going down", I mean "I read eof from it". Yet + reading an eof simply means that it promises not to send any more + data. It may still be perfectly fine receiving data (read "man 2 + shutdown"). In fact, some webservers work that way -- the client sends + his entire request, and when the webserver reads an eof it begins + its response. We currently don't support that sort of protocol; we + may want to switch to some sort of a two-way-destry-ripple technique + (where a destroy makes its way all the way to the end of the circuit + before being echoed back, and data stops flowing only when a destroy + has been received from both sides of the circuit); this extends the + one-hop-ack approach that Matej used. + +* Reply onions. Hrm. + diff --git a/src/common/Makefile b/src/common/Makefile new file mode 100644 index 000000000..3b0e45159 --- /dev/null +++ b/src/common/Makefile @@ -0,0 +1,228 @@ +SRC=log.c config.c utils.c key.c onion.c cell.c routent.c scheduler.c +OBJ=${SRC:.c=.o} +INCLUDE=-I/usr/local/ssl/include + +CFLAGS=${INCLUDE} -Wall -Wpointer-arith -O2 + +all: ${OBJ} + +clean: + rm -f $(OBJ) + +depend: + makedepend -- $(CFLAGS) -- $(SRC) + +# DO NOT DELETE + + +cell.o: cell.h log.h /usr/include/alloca.h /usr/include/asm/socket.h +cell.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +cell.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +cell.o: /usr/include/bits/environments.h /usr/include/bits/in.h +cell.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h +cell.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +cell.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +cell.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +cell.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +cell.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +cell.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +cell.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +cell.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +cell.o: /usr/include/endian.h /usr/include/features.h /usr/include/getopt.h +cell.o: /usr/include/gnu/stubs.h /usr/include/limits.h +cell.o: /usr/include/linux/limits.h /usr/include/netinet/in.h +cell.o: /usr/include/openssl/rand.h /usr/include/stdint.h +cell.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h +cell.o: /usr/include/syslog.h /usr/include/sys/select.h +cell.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +cell.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/unistd.h +cell.o: /usr/include/xlocale.h +cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +cell.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h +config.o: config.h log.h /usr/include/alloca.h /usr/include/asm/errno.h +config.o: /usr/include/bits/endian.h /usr/include/bits/errno.h +config.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +config.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +config.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +config.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +config.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +config.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h +config.o: /usr/include/features.h /usr/include/_G_config.h +config.o: /usr/include/gconv.h /usr/include/gnu/stubs.h /usr/include/libio.h +config.o: /usr/include/linux/errno.h /usr/include/stdio.h +config.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h +config.o: /usr/include/syslog.h /usr/include/sys/select.h +config.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +config.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/wchar.h +config.o: /usr/include/xlocale.h +config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +key.o: config.h key.h log.h /usr/include/alloca.h /usr/include/asm/errno.h +key.o: /usr/include/bits/endian.h /usr/include/bits/errno.h +key.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +key.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +key.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +key.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +key.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +key.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +key.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/gnu/stubs.h +key.o: /usr/include/libio.h /usr/include/linux/errno.h +key.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +key.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +key.o: /usr/include/openssl/buffer.h /usr/include/openssl/cast.h +key.o: /usr/include/openssl/crypto.h /usr/include/openssl/des.h +key.o: /usr/include/openssl/dh.h /usr/include/openssl/dsa.h +key.o: /usr/include/openssl/e_os2.h /usr/include/openssl/err.h +key.o: /usr/include/openssl/evp.h /usr/include/openssl/lhash.h +key.o: /usr/include/openssl/md2.h /usr/include/openssl/md4.h +key.o: /usr/include/openssl/md5.h /usr/include/openssl/objects.h +key.o: /usr/include/openssl/obj_mac.h /usr/include/openssl/opensslconf.h +key.o: /usr/include/openssl/opensslv.h /usr/include/openssl/pem2.h +key.o: /usr/include/openssl/pem.h /usr/include/openssl/pkcs7.h +key.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +key.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +key.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +key.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +key.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h +key.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +key.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +key.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +key.o: /usr/include/sys/sysmacros.h /usr/include/sys/types.h +key.o: /usr/include/time.h /usr/include/wchar.h /usr/include/xlocale.h +key.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +key.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +log.o: log.h /usr/include/asm/errno.h /usr/include/bits/errno.h +log.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +log.o: /usr/include/bits/stdio_lim.h /usr/include/bits/types.h +log.o: /usr/include/bits/wchar.h /usr/include/errno.h /usr/include/features.h +log.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/gnu/stubs.h +log.o: /usr/include/libio.h /usr/include/linux/errno.h /usr/include/stdio.h +log.o: /usr/include/string.h /usr/include/sys/cdefs.h /usr/include/syslog.h +log.o: /usr/include/sys/syslog.h /usr/include/wchar.h /usr/include/xlocale.h +log.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +log.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +onion.o: cell.h log.h onion.h routent.h /usr/include/alloca.h +onion.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h +onion.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h +onion.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h +onion.o: /usr/include/bits/endian.h /usr/include/bits/environments.h +onion.o: /usr/include/bits/errno.h /usr/include/bits/in.h +onion.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h +onion.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +onion.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +onion.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +onion.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +onion.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +onion.o: /usr/include/bits/types.h /usr/include/bits/uio.h +onion.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +onion.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +onion.o: /usr/include/bits/xopen_lim.h /usr/include/endian.h +onion.o: /usr/include/errno.h /usr/include/features.h /usr/include/_G_config.h +onion.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h +onion.o: /usr/include/libio.h /usr/include/limits.h /usr/include/linux/errno.h +onion.o: /usr/include/linux/limits.h /usr/include/netinet/in.h +onion.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +onion.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +onion.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +onion.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +onion.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +onion.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h +onion.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h +onion.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +onion.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +onion.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +onion.o: /usr/include/openssl/rand.h /usr/include/openssl/rc2.h +onion.o: /usr/include/openssl/rc4.h /usr/include/openssl/ripemd.h +onion.o: /usr/include/openssl/rsa.h /usr/include/openssl/safestack.h +onion.o: /usr/include/openssl/sha.h /usr/include/openssl/stack.h +onion.o: /usr/include/openssl/symhacks.h /usr/include/stdint.h +onion.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +onion.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +onion.o: /usr/include/sys/select.h /usr/include/sys/socket.h +onion.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +onion.o: /usr/include/sys/timeb.h /usr/include/sys/time.h +onion.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/time.h +onion.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h +onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +onion.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h version.h +routent.o: cell.h routent.h /usr/include/alloca.h /usr/include/bits/confname.h +routent.o: /usr/include/bits/endian.h /usr/include/bits/environments.h +routent.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h +routent.o: /usr/include/bits/sched.h /usr/include/bits/select.h +routent.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h +routent.o: /usr/include/bits/time.h /usr/include/bits/types.h +routent.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +routent.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +routent.o: /usr/include/endian.h /usr/include/features.h +routent.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +routent.o: /usr/include/gnu/stubs.h /usr/include/libio.h +routent.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +routent.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +routent.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +routent.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +routent.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +routent.o: /usr/include/openssl/evp.h /usr/include/openssl/md2.h +routent.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +routent.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +routent.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +routent.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +routent.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +routent.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +routent.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +routent.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h +routent.o: /usr/include/sys/cdefs.h /usr/include/sys/select.h +routent.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h +routent.o: /usr/include/sys/time.h /usr/include/sys/types.h +routent.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h +routent.o: /usr/include/xlocale.h +routent.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +routent.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +scheduler.o: log.h scheduler.h /usr/include/bits/confname.h +scheduler.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h +scheduler.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +scheduler.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +scheduler.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +scheduler.o: /usr/include/bits/types.h /usr/include/bits/wchar.h +scheduler.o: /usr/include/bits/wordsize.h /usr/include/features.h +scheduler.o: /usr/include/_G_config.h /usr/include/gconv.h +scheduler.o: /usr/include/getopt.h /usr/include/gnu/stubs.h +scheduler.o: /usr/include/libio.h /usr/include/malloc.h /usr/include/stdio.h +scheduler.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +scheduler.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +scheduler.o: /usr/include/sys/time.h /usr/include/time.h /usr/include/unistd.h +scheduler.o: /usr/include/wchar.h +scheduler.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +scheduler.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +utils.o: log.h /usr/include/alloca.h /usr/include/asm/errno.h +utils.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h +utils.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +utils.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +utils.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h +utils.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +utils.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +utils.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +utils.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +utils.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +utils.o: /usr/include/bits/types.h /usr/include/bits/uio.h +utils.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +utils.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +utils.o: /usr/include/bits/xopen_lim.h /usr/include/ctype.h +utils.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +utils.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +utils.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h +utils.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +utils.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +utils.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +utils.o: /usr/include/sys/select.h /usr/include/sys/socket.h +utils.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +utils.o: /usr/include/sys/time.h /usr/include/sys/types.h +utils.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/time.h +utils.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h +utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +utils.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h utils.h diff --git a/src/common/cell.c b/src/common/cell.c new file mode 100644 index 000000000..e742a80f5 --- /dev/null +++ b/src/common/cell.c @@ -0,0 +1,355 @@ +/** + * cell.c + * Cell manipulation. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.16 2002/06/14 20:41:19 mp292 + * Parameter checking error - thanks Roger. + * + * Revision 1.15 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.14 2002/04/02 10:19:37 badbytes + * Stricter parameter checking. + * + * Revision 1.13 2002/03/12 23:30:19 mp292 + * Removed some memory overruns. + * + * Revision 1.12 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.11 2002/02/03 22:41:45 mp292 + * Changes to cell size. + * + * Revision 1.10 2002/01/21 20:57:19 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.9 2002/01/17 15:00:43 mp292 + * Fixed a bug which caused malloc() generate a seg fault. + * + * Revision 1.8 2002/01/16 23:01:54 mp292 + * First phase of system testing completed (main functionality). + * + * Revision 1.7 2002/01/14 13:05:37 badbytes + * System testing in progress. + * + * Revision 1.6 2002/01/10 13:15:54 badbytes + * Fixed ACI size from 32bits to 16bits. + * + * Revision 1.5 2002/01/07 13:06:06 badbytes + * cell.ACI is now cell.aci + * + * Revision 1.4 2002/01/07 09:26:00 badbytes + * Added pack_create() and pack_data(). + * + * Revision 1.3 2002/01/07 07:48:34 badbytes + * fixed new_create_cell() + * + * Revision 1.2 2002/01/04 12:11:54 badbytes + * Syntax errors fixed. + * + * Revision 1.1 2002/01/04 12:08:34 badbytes + * Added functions for cell creation. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include <openssl/rand.h> + +#include "cell.h" +#include "log.h" + +cell_t *new_padding_cell() +{ + cell_t *c = NULL; + int retval; + + c = malloc(sizeof(cell_t)); + if (!c) /* malloc() error */ + return NULL; + + retval = RAND_pseudo_bytes((unsigned char *)c,sizeof(cell_t)); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + c->command = CELL_PADDING; + + return c; +} + +cell_t *new_destroy_cell(uint16_t aci) +{ + cell_t *c = NULL; + int retval; + + if (aci) /* valid ACI */ + { + c = (cell_t *)malloc(sizeof(cell_t)); + if (!c) /* malloc error */ + return NULL; + + retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + c->aci = aci; + c->command = CELL_DESTROY; + + return c; + } /* valid ACI */ + else /* invalid ACI */ + return NULL; +} + +cell_t *new_ack_cell(uint16_t aci) +{ + cell_t *c = NULL; + int retval; + + if (aci) /* valid ACI */ + { + c = (cell_t *)malloc(sizeof(cell_t)); + if (!c) /* malloc error */ + return NULL; + + retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + c->aci = aci; + c->command = CELL_ACK; + + return c; + } /* valid ACI */ + else /* invalid ACI */ + return NULL; +} + +cell_t *new_nack_cell(uint16_t aci) +{ + cell_t *c = NULL; + int retval; + + if (aci) /* valid ACI */ + { + c = (cell_t *)malloc(sizeof(cell_t)); + if (!c) /* malloc error */ + return NULL; + + retval = RAND_pseudo_bytes((unsigned char *)c+3,sizeof(cell_t)-3); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + c->aci = aci; + c->command = CELL_NACK; + + return c; + } /* valid ACI */ + else /* invalid ACI */ + return NULL; +} + +cell_t *new_create_cell(uint16_t aci, unsigned char length, unsigned char *buf) +{ + cell_t *c = NULL; + int retval; + + if ((aci) && (buf) && (length <= CELL_PAYLOAD_SIZE)) /* valid parameters */ + { + c = (cell_t *)malloc(sizeof(cell_t)); + if (!c) /* malloc() error */ + return NULL; + + c->command = CELL_CREATE; + c->aci = aci; + c->length = length; + c->seq = 0; + + memcpy((void *)c->payload, (void *)buf, length); + retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),CELL_PAYLOAD_SIZE-length); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + return c; + } /* valid parameters */ + else /* invalid parameters */ + return NULL; +} + + +cell_t *new_data_cell(uint16_t aci, unsigned char length, unsigned char *buf) +{ + cell_t *c = NULL; + int retval; + + if ((aci) && (buf) && (length <= CELL_PAYLOAD_SIZE)) /* valid parameters */ + { + c = malloc(sizeof(cell_t)); + if (!c) /* malloc() error */ + return NULL; + + c->command = CELL_DATA; + c->aci = aci; + c->length = length; + c->seq = 0; + + memcpy((void *)c->payload, (void *)buf, length); + retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),CELL_PAYLOAD_SIZE-length); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + return c; + } /* valid parameters */ + else /* invalid parameters */ + return NULL; +} + +int pack_create(uint16_t aci, unsigned char *onion, uint32_t onionlen, unsigned char **cellbuf, unsigned int *cellbuflen) +{ + cell_t *c; + unsigned char *buf; + unsigned int buflen; + unsigned int cells; + unsigned int dataleft; + unsigned int i; + + if ((aci) && (onion) && (cellbuf) && (cellbuflen) && (onionlen)) /* valid parameters */ + { + /* copy the onion into a buffer, prepend with onion length */ + buflen = onionlen+4; + buf = (unsigned char *)malloc(buflen); + if (!buf) /* malloc() error */ + return -1; + + log(LOG_DEBUG,"pack_create() : Setting onion length to %u.",onionlen); + onionlen=htonl(onionlen); + memcpy((void *)buf,(void *)&onionlen,4); + onionlen=ntohl(onionlen); + memcpy((void *)(buf+4),(void *)onion,onionlen); + + /* calculate number of cells required */ + if (buflen%CELL_PAYLOAD_SIZE == 0) + cells = buflen/CELL_PAYLOAD_SIZE; + else + cells = buflen/CELL_PAYLOAD_SIZE+1; + + /* allocate memory for the cells */ + *cellbuflen = cells * sizeof(cell_t); + *cellbuf = malloc(*cellbuflen); + if (!*cellbuf) /* malloc() error */ + return -1; + + log(LOG_DEBUG,"pack_create() : Allocated memory for %u cells.",cells); + + /* create cells one by one */ + dataleft = buflen; + for(i=0; i<cells; i++) + { + log(LOG_DEBUG,"pack_create() : Packing %u bytes of data.",dataleft); + if (dataleft >= CELL_PAYLOAD_SIZE) + { + c = new_create_cell(aci,CELL_PAYLOAD_SIZE,buf+i*CELL_PAYLOAD_SIZE); + dataleft -= CELL_PAYLOAD_SIZE; + } + else + c = new_create_cell(aci,dataleft,buf+i*CELL_PAYLOAD_SIZE); + + if (!c) /* cell creation failed */ + { + free((void *)*cellbuf); + return -1; + } /* cell creation failed */ + + log(LOG_DEBUG,"pack_create() : new_create_cell succeeded; copying the cell into output buffer"); + /* cell has been created, now copy into buffer */ + memcpy((void *)(*cellbuf+i*sizeof(cell_t)),(void *)c,sizeof(cell_t)); + free((void *)c); + } + + free(buf); + return 0; + } /* valid parameters */ + else /* invalid parameters */ + return -1; +} + +int pack_data(uint16_t aci,unsigned char *buf, size_t buflen, unsigned char **cellbuf, unsigned int *cellbuflen) +{ + cell_t *c; + unsigned int cells; + unsigned int dataleft; + unsigned int i; + + if ((aci) && (buf) && (cellbuf) && (cellbuflen) && (buflen)) /* valid parameters */ + { + /* calculate number of cells required */ + if (buflen%CELL_PAYLOAD_SIZE == 0) + cells = buflen/CELL_PAYLOAD_SIZE; + else + cells = buflen/CELL_PAYLOAD_SIZE+1; + + /* allocate memory for the cells */ + *cellbuf = malloc(cells * sizeof(cell_t)); + if (!*cellbuf) /* malloc() error */ + return -1; + + log(LOG_DEBUG,"pack_data() : Allocated memory for %u cells.",cells); + /* create cells one by one */ + dataleft = buflen; + for(i=0; i<cells; i++) + { + log(LOG_DEBUG,"pack_data() : Packing %u bytes of data.",dataleft); + if (dataleft >= CELL_PAYLOAD_SIZE) + { + c = new_data_cell(aci,CELL_PAYLOAD_SIZE,buf+i*CELL_PAYLOAD_SIZE); + dataleft -= CELL_PAYLOAD_SIZE; + } + else + c = new_data_cell(aci,dataleft,buf+i*CELL_PAYLOAD_SIZE); + + if (!c) /* cell creation failed */ + { + free((void *)*cellbuf); + return -1; + } /* cell creation failed */ + + /* cell has been created, now copy into buffer */ + memcpy((void *)(*cellbuf+i*sizeof(cell_t)),(void *)c,sizeof(cell_t)); + free((void *)c); + } + + *cellbuflen = cells * sizeof(cell_t); + return 0; + } /* valid parameters */ + else /* invalid parameters */ + return -1; +} diff --git a/src/common/cell.h b/src/common/cell.h new file mode 100644 index 000000000..726c01a7f --- /dev/null +++ b/src/common/cell.h @@ -0,0 +1,95 @@ +/** + * cell.h + * Cell definition. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.14 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.13 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.12 2002/02/09 17:51:52 mp292 + * CELL_ACK should be 4 not 3 + * + * Revision 1.11 2002/02/03 22:41:45 mp292 + * Changes to cell size. + * + * Revision 1.10 2002/01/21 20:57:19 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.9 2002/01/17 15:00:43 mp292 + * Fixed a bug which caused malloc() generate a seg fault. + * + * Revision 1.8 2002/01/14 13:05:37 badbytes + * System testing in progress. + * + * Revision 1.7 2002/01/10 13:15:54 badbytes + * Fixed ACI size from 32bits to 16bits. + * + * Revision 1.6 2002/01/09 08:10:32 badbytes + * *** empty log message *** + * + * Revision 1.5 2002/01/07 13:03:28 badbytes + * cell.ACI is now cell.aci + * + * Revision 1.4 2002/01/07 09:26:00 badbytes + * Added pack_create() and pack_data(). + * + * Revision 1.3 2002/01/07 07:48:34 badbytes + * fixed new_create_cell() + * + * Revision 1.2 2002/01/04 12:08:34 badbytes + * Added functions for cell creation. + * + * Revision 1.1 2002/01/04 10:02:07 badbytes + * Added cell definition. + * + */ + +#ifndef __CELL_H + +#include <unistd.h> +#include <stdint.h> + +/* cell commands */ +#define CELL_PADDING 0 +#define CELL_CREATE 1 +#define CELL_DATA 2 +#define CELL_DESTROY 3 +#define CELL_ACK 4 +#define CELL_NACK 5 + +#define CELL_PAYLOAD_SIZE 120 + +/* cell definition */ +typedef struct +{ + uint16_t aci; /* Anonymous Connection Identifier */ + unsigned char command; + unsigned char length; /* of payload */ + uint32_t seq; /* sequence number */ + unsigned char payload[120]; +} cell_t; + +cell_t *new_padding_cell(void); +cell_t *new_create_cell(uint16_t aci, unsigned char length, unsigned char *buf); +cell_t *new_destroy_cell(uint16_t aci); +cell_t *new_data_cell(uint16_t aci, unsigned char length, unsigned char *buf); +cell_t *new_ack_cell(uint16_t aci); +cell_t *new_nack_cell(uint16_t aci); + +int pack_create(uint16_t aci, unsigned char *onion, uint32_t onionlen, unsigned char **cellbuf, unsigned int *cellbuflen); +int pack_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **cellbuf, unsigned int *cellbuflen); + +#define __CELL_H +#endif + diff --git a/src/common/config.c b/src/common/config.c new file mode 100644 index 000000000..99961f147 --- /dev/null +++ b/src/common/config.c @@ -0,0 +1,347 @@ +/* + * config.c + * Functions for the manipulation of configuration files. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.7 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.6 2002/01/27 19:23:03 mp292 + * Fixed a bug in parameter checking. + * + * Revision 1.5 2002/01/26 18:42:15 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.4 2002/01/21 21:07:56 mp292 + * Parameter checking was missing in some functions. + * + * Revision 1.3 2001/12/07 09:38:03 badbytes + * Tested. + * + * Revision 1.2 2001/12/06 15:43:50 badbytes + * config.c compiles. Proceeding to test it. + * + * Revision 1.1 2001/11/22 01:20:27 mp292 + * Functions for dealing with configuration files. + * + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "config.h" +#include "log.h" + +/* open configuration file for reading */ +FILE *open_config(const unsigned char *filename) +{ + FILE *f; + + if (filename) /* non-NULL filename */ + { + if (strspn(filename,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(filename)) /* filename consists of legal characters only */ + { + f = fopen(filename, "r"); + + return f; + } /* filename consists of legal characters only */ + else /* illegal values in filename */ + { + return NULL; + } /* illegal values in filename */ + } /* non-NULL filename */ + else /* NULL filename */ + return NULL; +} + +/* close configuration file */ +int close_config(FILE *f) +{ + int retval = 0; + + if (f) /* valid file descriptor */ + { + retval = fclose(f); + + return retval; + } /* valid file descriptor */ + else + return -1; +} + +/* parse the config file and obtain the required option values */ +int parse_config(FILE *f, config_opt_t *option) +{ + unsigned char keyword[CONFIG_KEYWORD_MAXLEN+1]; /* for storing the option keyword */ + + unsigned char *buffer = NULL; /* option value */ + size_t buflen = 0; + + char *errtest = NULL; /* used for testing correctness of strtol() etc. */ + + unsigned int i_keyword = 0; /* current position within keyword */ + unsigned int i_buf = 0; /* current position within buffer */ + + char c=0; /* input char */ + + unsigned int state=0; /* internal state + * 0 - trying to find a keyword + * 1 - reading a keyword + * 2 - keyword read and recognized, looking for the option value + * 3 - reading the option value + * 4 - option value read + * 5 - inside a comment + */ + + int retval=0; /* return value */ + + int lineno=1; /* current line number */ + int curopt=-1; /* current option, as an indexed in config_opt_t */ + int i; + + if ( (f==NULL) || (option==NULL) ) /* invalid parameters */ + return -1; + + fseek(f,0,SEEK_SET); /* make sure we start at the beginning of file */ + + for (;;) /* infinite loop */ + { + c = getc(f); + + if ((c == '\n') || (c == EOF)) + { + if (state == 1) /* reading a keyboard */ + { + log(LOG_ERR,"Error parsing the configuration file on line %d.", lineno); + i_keyword = 0; + state = 0; + retval = -1; + break; + } /* reading a keyboard */ + else if (state == 2) /* keyword read and recognized */ + { + log(LOG_ERR,"Error parsing option %s on line %d.",option[curopt].keyword, lineno); + i_keyword = 0; + state = 0; + option[curopt].err=-1; + retval = -1; + break; + } /* keyboard read and recognized */ + else if (state == 3) /* reading the option value */ + { + buffer[i_buf++] = 0; /* add NULL character to terminate the string */ + state = 4; + /* conversion and copying the value into config_opt_t is done later on */ + } /* reading the option value */ + else if (state == 5) /* reached end of comment */ + state = 0; + + if (c == EOF) + { + log(LOG_DEBUG,"parse_config() : Reached eof on line %d.",lineno); + break; + } + else + { + log(LOG_DEBUG,"parse_config() : Reached eol on line %d.", lineno); + lineno++; + } + } + else if ( (state==0) && (c == '#') ) /* lines beginning with # are ignored */ + { + log(LOG_DEBUG,"parse_config() : Line %d begins with #.",lineno); + state = 5; + } + else if ( (state==0) && (isspace(c)) ) /* leading whitespace is ignored */ + ; + else if ( (state==1) && (isspace(c)) ) /* have apparently read in all of the keyword */ + { + keyword[i_keyword++] = 0; + curopt = -1; + for (i=0;option[i].keyword != NULL;i++) /* try and identify the keyword */ + { + if (!strncmp(keyword,option[i].keyword,CONFIG_KEYWORD_MAXLEN)) + { + curopt = i; + break; + } + } /* try and identify the keyword */ + + if (curopt == -1) /* can't recognise the keyword */ + { + log(LOG_ERR,"Error parsing the configuration file. Cannot recognize keyword %s on line %d.",keyword,lineno); + retval=-1; + break; + } + else + state = 2; + } + else if ( (state==2) && (isspace(c)) ) /* whitespace separating keyword and value is ignored */ + ; + else if ( (state==3) && (isspace(c)) ) /* have apparently finished reading the option value */ + { + buffer[i_buf++]=0; + state = 4; + } + else /* all other characters */ + { + if (state == 0) /* first character of the keyword */ + { + log(LOG_DEBUG, "parse_config() : %c is the start of a keyword on line %d.",c,lineno); + state = 1; + i_keyword = 0; + keyword[i_keyword++] = c; + } + else if (state == 1) /* keep on reading the keyword */ + { + log(LOG_DEBUG,"parse_config() : %c is a character in the keyword on line %d.",c,lineno); + if (i_keyword < CONFIG_KEYWORD_MAXLEN) /* check for buffer overflow */ + keyword[i_keyword++] = c; + else + { + log(LOG_ERR,"Error parsing the configuration file. Keyword on line %d exceeds %d characters.",lineno,CONFIG_KEYWORD_MAXLEN); + retval=-1; + break; + } + } + else if (state == 2) /* first character of the value */ + { + log(LOG_DEBUG,"parse_config() : %c is the first character of the option value on line %d.",c,lineno); + state = 3; + i_buf=0; + buflen = CONFIG_VALUE_MAXLEN+1; /* allocate memory for the value buffer */ + buffer = (char *)malloc(buflen); + if (!buffer) + { + log(LOG_ERR,"Could not allocate memory."); + retval=-1; + break; + } else + buffer[i_buf++]=c; + } + else if (state == 3) /* keep on reading the value */ + { + log(LOG_DEBUG,"parse_config() : %c is a character in the value of the keyword on line %d.",c,lineno); + if (i_buf >= buflen) + { + log(LOG_ERR,"Length of keyword value on line %u exceeds the length limit (%u).",lineno, CONFIG_VALUE_MAXLEN); + retval=-1; + break; + } + + buffer[i_buf++]=c; + } + else if (state == 5) + ; /* character is part of a comment, skip */ + else /* unexpected error */ + { + log(LOG_ERR,"Unexpected error while parsing the configuration file."); + log(LOG_DEBUG,"parse_config() : Encountered a non-delimiter character while not in states 0,1,2 or 3!"); + break; + } + } + + if (state==4) /* convert the value of the option to the appropriate type and write into OPT */ + { + switch(option[curopt].r_type) /* consider each type separately */ + { + case CONFIG_TYPE_STRING: + /* resize the buffer to fit the data exactly */ + buffer = (char *)realloc(buffer,i_buf); + if (!buffer) + { + log(LOG_ERR,"Could not allocate memory."); + return -1; + } + option[curopt].r.str = buffer; + option[curopt].err = 1; + break; + + case CONFIG_TYPE_CHAR: + option[curopt].r.c = *buffer; + option[curopt].err = 1; + break; + + case CONFIG_TYPE_INT: + errtest = NULL; + option[curopt].r.i = (int)strtol(buffer,&errtest,0); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + case CONFIG_TYPE_LONG: + errtest = NULL; + option[curopt].r.l = strtol(buffer,&errtest,0); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + case CONFIG_TYPE_DOUBLE: + errtest = NULL; + option[curopt].r.d = strtod(buffer,&errtest); + if ((unsigned char *)errtest == buffer) + { + log(LOG_ERR, "Error parsing configuration file. Option %s on line %d does not seem to be of the required type.\n",option[curopt].keyword,--lineno); + option[curopt].err = -1; + if (buffer) + free(buffer); + return -1; + } + else + option[curopt].err = 1; + break; + + default: /* unexpected type */ + log(LOG_ERR, "Error parsing configuration file. Unrecognized option type!"); + if (buffer) + free(buffer); + return -1; + } + + /* clean up */ + if (option[curopt].r_type != CONFIG_TYPE_STRING) + { + if (buffer) + free(buffer); + buflen=0; + } + + state = 0; + curopt = -1; + i_buf=0; + i_keyword=0; + } + + + } /* infinite loop */ + + return retval; +} diff --git a/src/common/config.h b/src/common/config.h new file mode 100644 index 000000000..15d9b1e5d --- /dev/null +++ b/src/common/config.h @@ -0,0 +1,88 @@ +/* + * config.h + * Functions for the manipulation of configuration files. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.7 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.6 2002/01/26 18:42:15 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.5 2002/01/21 21:07:56 mp292 + * Parameter checking was missing in some functions. + * + * Revision 1.4 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.3 2001/12/07 09:38:03 badbytes + * Tested. + * + * Revision 1.2 2001/12/06 15:43:50 badbytes + * config.c compiles. Proceeding to test it. + * + * Revision 1.1 2001/11/22 01:20:27 mp292 + * Functions for dealing with configuration files. + * + * + */ + +#ifndef __CONFIG_H + +# include <stdio.h> + +/* enumeration of types which option values can take */ +#define CONFIG_TYPE_STRING 0 +#define CONFIG_TYPE_CHAR 1 +#define CONFIG_TYPE_INT 2 +#define CONFIG_TYPE_LONG 3 +#define CONFIG_TYPE_DOUBLE 4 + +/* max. length of an option keyword */ +#define CONFIG_KEYWORD_MAXLEN 255 + +/* max. length (in characters) of an option value */ +#define CONFIG_VALUE_MAXLEN 255 + +/* legal characters in a filename */ +#define CONFIG_LEGAL_FILENAME_CHARACTERS "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_/" + +typedef struct +{ + unsigned char *keyword; /* option keyword */ + + unsigned int r_type; /* return type as defined above */ + + union /* return value */ + { + char *str; + char c; + int i; + long l; + double d; + } r; + + int err; /* 1 OK + * 0 keyword not found + * -1 error while parsing */ +} config_opt_t; + +/* open configuration file for reading */ +FILE *open_config(const unsigned char *filename); + +/* close configuration file */ +int close_config(FILE *f); + +/* parse the config file and obtain required option values */ +int parse_config(FILE *f, config_opt_t *option); + +#define __CONFIG_H +#endif diff --git a/src/common/key.c b/src/common/key.c new file mode 100644 index 000000000..ab91322d7 --- /dev/null +++ b/src/common/key.c @@ -0,0 +1,89 @@ +/** + * key.c + * Key management. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.5 2002/03/12 23:28:26 mp292 + * Removed calls to ERR_load_crypto_strings() (libcrypt). + * + * Revision 1.4 2002/01/27 19:23:03 mp292 + * Fixed a bug in parameter checking. + * + * Revision 1.3 2002/01/26 18:50:11 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2002/01/04 07:19:03 badbytes + * Key generation moved to a separate utility (orkeygen). + * + * Revision 1.1 2001/12/14 12:16:33 badbytes + * Added routine for reading a private key from a file. + * + */ + +#include <string.h> + +#include <openssl/err.h> +#include <openssl/pem.h> + +#include "key.h" +#include "log.h" +#include "config.h" + +RSA *load_prkey(unsigned char *keyfile) +{ + RSA *rsa_private=NULL; + FILE *f_pr; + int retval = 0; + + if (keyfile) /* non-NULL filename */ + { + if (strspn(keyfile,CONFIG_LEGAL_FILENAME_CHARACTERS) == strlen(keyfile)) /* filename contains legal characters only */ + { + /* open the keyfile */ + f_pr=fopen(keyfile,"r"); + if (!f_pr) + { + log(LOG_ERR,"Failed to open keyfile %s.",keyfile); + return NULL; + } + + /* read the private key */ + rsa_private = PEM_read_RSAPrivateKey(f_pr,&rsa_private,NULL,NULL); + fclose(f_pr); + if (!rsa_private) + { + log(LOG_ERR,"Error reading private key : %s",ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + /* check the private key */ + retval = RSA_check_key(rsa_private); + if (retval == 0) + { + log(LOG_ERR,"Private key read but is invalid : %s.", ERR_reason_error_string(ERR_get_error())); + RSA_free(rsa_private); + return NULL; + } + else if (retval == -1) + { + log(LOG_ERR,"Private key read but validity checking failed : %s",ERR_reason_error_string(ERR_get_error())); + RSA_free(rsa_private); + return NULL; + } + else if (retval == 1) + { + return rsa_private; + } + } /* filename contains legal characters only */ + } + + return NULL; /* report error */ +} diff --git a/src/common/key.h b/src/common/key.h new file mode 100644 index 000000000..ad41f1d88 --- /dev/null +++ b/src/common/key.h @@ -0,0 +1,32 @@ +/** + * key.h + * Routines for generating key pairs and loading private keys. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/01/26 18:50:11 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.1 2001/12/14 12:16:33 badbytes + * Added routine for reading a private key from a file. + * + */ + +#ifndef __KEY_H +#include <openssl/rsa.h> + +/* read the private key in keyfile into memory */ +RSA *load_prkey(unsigned char *keyfile); + +#define __KEY_H +#endif diff --git a/src/common/log.c b/src/common/log.c new file mode 100644 index 000000000..e85f2c946 --- /dev/null +++ b/src/common/log.c @@ -0,0 +1,74 @@ +/* + * log.c + * Logging facilities. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.11 2002/06/14 20:44:57 mp292 + * *** empty log message *** + * + * Revision 1.10 2002/03/12 23:31:36 mp292 + * *** empty log message *** + * + * Revision 1.9 2002/03/02 18:55:50 mp292 + * LOG_DEBUG messages don't print the last errno error anymore. + * + * Revision 1.8 2002/01/26 22:46:48 mp292 + * Reviewd according to Secure-Programs-HOWTO. + * + * Revision 1.7 2002/01/17 15:00:43 mp292 + * Fixed a bug which caused malloc() generate a seg fault. + * + * Revision 1.6 2001/12/12 16:02:55 badbytes + * Minor changes in output format. + * + * Revision 1.5 2001/12/12 06:48:07 badbytes + * Correction - last error message now only shown if severity==LOG_DEBUG. + * + * Revision 1.4 2001/12/12 06:28:46 badbytes + * Modified log() to print error message for last error in addition to the user-specified message. + * + * Revision 1.3 2001/12/07 09:38:03 badbytes + * Tested. + * + * Revision 1.2 2001/12/06 15:43:50 badbytes + * config.c compiles. Proceeding to test it. + * + * Revision 1.1 2001/11/21 23:03:41 mp292 + * log function coded and tested. + * Top-level makefile. + * + */ + +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> +#include <string.h> +#include <errno.h> +#include "log.h" + +void log_internal(int severity, const char *format, va_list ap); + +/* Outputs a message to stdout */ +void log(int severity, const char *format, ...) +{ + extern int loglevel; + va_list ap; + + va_start(ap,format); + + if (severity <= loglevel) + { + vprintf(format,ap); + printf("\n"); + } + + va_end(ap); +} diff --git a/src/common/log.h b/src/common/log.h new file mode 100644 index 000000000..e70aa2f5b --- /dev/null +++ b/src/common/log.h @@ -0,0 +1,40 @@ +/* + * log.h + * Logging facilities. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.5 2002/01/26 18:52:00 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.4 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.3 2001/12/07 09:38:03 badbytes + * Tested. + * + * Revision 1.2 2001/12/06 15:43:50 badbytes + * config.c compiles. Proceeding to test it. + * + * Revision 1.1 2001/11/21 23:03:41 mp292 + * log function coded and tested. + * Top-level makefile. + * + */ + +#ifndef __LOG_H + +#include <syslog.h> + +/* Outputs a message to stdout and also logs the same message using syslog. */ +void log(int severity, const char *format, ...); + +# define __LOG_H +#endif diff --git a/src/common/onion.c b/src/common/onion.c new file mode 100644 index 000000000..2b42f3d95 --- /dev/null +++ b/src/common/onion.c @@ -0,0 +1,633 @@ +/** + * onion.c + * Routines for creating/manipulating onions. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.24 2002/06/14 20:42:02 mp292 + * Bug caused infinite loop when router list had <= 2 entries. + * + * Revision 1.23 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.22 2002/04/02 10:20:09 badbytes + * Memory overflow bug. + * + * Revision 1.21 2002/03/25 09:11:23 badbytes + * Added a list of onions being tracked for replay attacks. + * + * Revision 1.20 2002/03/12 23:32:41 mp292 + * *** empty log message *** + * + * Revision 1.19 2002/01/27 19:23:21 mp292 + * Fixed a bug in parameter checking. + * + * Revision 1.18 2002/01/26 19:24:29 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.17 2002/01/18 20:40:40 mp292 + * Fixed a bug in en/decrypt_onion() functions. + * + * Revision 1.16 2002/01/17 23:48:31 mp292 + * Added some extra debugging messages to fix a bug in encrypt_onion() which + * seems to corrupt the routent *route list. + * + * Revision 1.15 2002/01/17 15:00:43 mp292 + * Fixed a bug which caused malloc() generate a seg fault. + * + * Revision 1.14 2002/01/16 23:01:54 mp292 + * First phase of system testing completed (main functionality). + * + * Revision 1.13 2002/01/14 13:05:37 badbytes + * System testing in progress. + * + * Revision 1.12 2002/01/11 15:47:17 badbytes + * *** empty log message *** + * + * Revision 1.11 2002/01/09 07:58:23 badbytes + * Fixed a bug so that backward crypto engines are now initialized with DecryptInit. + * + * Revision 1.10 2002/01/09 07:55:23 badbytes + * Ciphers got out of sync. Hopefully fixed. + * + * Revision 1.9 2002/01/03 11:01:22 badbytes + * *** empty log message *** + * + * Revision 1.8 2001/12/19 08:29:00 badbytes + * Macro DEFAULT_CIPHER now holds the default crypto algorithm + * + * Revision 1.7 2001/12/18 14:12:47 badbytes + * Default cipher is now IDENTITY, for testing purposes. + * + * Revision 1.6 2001/12/18 10:36:51 badbytes + * create_onion() now also computes a crypt_path + * + * Revision 1.5 2001/12/17 13:35:17 badbytes + * Still writing handle_connection() + * + * Revision 1.4 2001/12/14 14:44:37 badbytes + * chooselen() tested + * + * Revision 1.3 2001/12/14 13:30:48 badbytes + * peel_onion() was redundant, removed it + * + * Revision 1.2 2001/12/14 13:14:03 badbytes + * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies. + * + * Revision 1.1 2001/12/14 12:41:12 badbytes + * Moved from op/ as it will be reused by other modules. + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ + +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <string.h> +#include <time.h> + +#include <openssl/rand.h> +#include <openssl/err.h> +#include <openssl/sha.h> +#include <openssl/evp.h> + +#include "onion.h" +#include "log.h" + +/* uses a weighted coin with weight cw to choose a route length */ +int chooselen(double cw) +{ + int len = 2; + int retval = 0; + unsigned char coin; + + if ((cw < 0) || (cw >= 1)) /* invalid parameter */ + return -1; + + while(1) + { + retval = RAND_pseudo_bytes(&coin,1); + if (retval == -1) + return -1; + + if (coin > cw*255) /* don't extend */ + break; + else + len++; + } + + return len; +} + +/* returns an array of pointers to routent that define a new route through the OR network + * int cw is the coin weight to use when choosing the route + * order of routers is from last to first + */ +unsigned int *new_route(double cw, routent_t **rarray, size_t rarray_len, size_t *rlen) +{ + int routelen = 0; + int i = 0; + int retval = 0; + unsigned int *route = NULL; + unsigned int oldchoice, choice; + + if ( (cw >= 0) && (cw < 1) && (rarray) && (rlen) ) /* valid parameters */ + { + routelen = chooselen(cw); + if (routelen == -1) + { + log(LOG_ERR,"Choosing route length failed."); + return NULL; + } + log(LOG_DEBUG,"new_route(): Chosen route length %u.",routelen); + + /* allocate memory for the new route */ + route = (unsigned int *)malloc(routelen * sizeof(unsigned int)); + if (!route) + { + log(LOG_ERR,"Memory allocation failed."); + return NULL; + } + + oldchoice = rarray_len; + for(i=0;i<routelen;i++) + { + log(LOG_DEBUG,"new_route() : Choosing hop %u.",i); + retval = RAND_pseudo_bytes((unsigned char *)&choice,sizeof(unsigned int)); + if (retval == -1) + { + free((void *)route); + return NULL; + } + + choice = choice % (rarray_len); + log(LOG_DEBUG,"new_route() : Chosen router %u.",choice); + if (choice == oldchoice) /* same router */ + { + /* try again */ + i--; + continue; + } + oldchoice = choice; + route[i] = choice; + } + + *rlen = routelen; + return route; + } /* valid parameters */ + else /* invalid parameters */ + return NULL; +} + +/* creates a new onion from route, stores it and its length into bufp and lenp respectively */ +unsigned char *create_onion(routent_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp) +{ + int i,j; + int retval = 0; + onion_layer_t *layer = NULL; + crypt_path_t *hop = NULL; + unsigned char *retbuf = NULL; + unsigned char *bufp; + routent_t *router; + + if ( (rarray) && (route) && (lenp) ) /* valid parameters */ + { + /* calculate the size of the onion */ + *lenp = routelen * 28 + 100; /* 28 bytes per layer + 100 bytes padding for the innermost layer */ + log(LOG_DEBUG,"create_onion() : Size of the onion is %u.",*lenp); + + /* allocate memory for the onion */ + bufp = (unsigned char *)malloc(*lenp); + if (!bufp) + { + log(LOG_ERR,"Error allocating memory."); + return NULL; + } + log(LOG_DEBUG,"create_onion() : Allocated memory for the onion."); + + for (retval=0; retval<routelen;retval++) + { + log(LOG_DEBUG,"create_onion() : %u : %s:%u, %u/%u",routelen-retval,inet_ntoa(*((struct in_addr *)&((rarray[route[retval]])->addr))),ntohs((rarray[route[retval]])->port),(rarray[route[retval]])->pkey,RSA_size((rarray[route[retval]])->pkey)); + } + + layer = (onion_layer_t *)(bufp + *lenp - 128); /* pointer to innermost layer */ + /* create the onion layer by layer, starting with the innermost */ + for (i=0;i<routelen;i++) + { + router = rarray[route[i]]; + + log(LOG_DEBUG,"create_onion() : %u",router); + log(LOG_DEBUG,"create_onion() : This router is %s:%u",inet_ntoa(*((struct in_addr *)&router->addr)),ntohs(router->port)); + log(LOG_DEBUG,"create_onion() : Key pointer = %u.",router->pkey); + log(LOG_DEBUG,"create_onion() : Key size = %u.",RSA_size(router->pkey)); + + /* 0 bit */ + layer->zero = 0; + /* version */ + layer->version = VERSION; + /* Back F + Forw F both use DES OFB*/ + layer->backf = ONION_DEFAULT_CIPHER; + layer->forwf = ONION_DEFAULT_CIPHER; + /* Dest Port */ + if (i) /* not last hop */ + layer->port = rarray[route[i-1]]->port; + else + layer->port = 0; + /* Dest Addr */ + if (i) /* not last hop */ + layer->addr = rarray[route[i-1]]->addr; + else + layer->addr = 0; + /* Expiration Time */ + layer->expire = time(NULL) + 3600; /* NOW + 1 hour */ + /* Key Seed Material */ + retval = RAND_bytes(layer->keyseed,16); + if (retval < 1) /* error */ + { + log(LOG_ERR,"Error generating random data."); + free((void *)bufp); + if (cpathp) + { + for (j=0;j<i;j++) + free((void *)cpathp[i]); + } + return NULL; + } + log(LOG_DEBUG,"create_onion() : Onion layer %u built : %u, %u, %u, %s, %u.",i+1,layer->zero,layer->backf,layer->forwf,inet_ntoa(*((struct in_addr *)&layer->addr)),ntohs(layer->port)); + + /* build up the crypt_path */ + if (cpathp) + { + cpathp[i] = (crypt_path_t *)malloc(sizeof(crypt_path_t)); + if (!cpathp[i]) + { + log(LOG_ERR,"Error allocating memory."); + free((void *)bufp); + for (j=0;j<i;j++) + free((void *)cpathp[i]); + } + + log(LOG_DEBUG,"create_onion() : Building hop %u of crypt path.",i+1); + hop = cpathp[i]; + /* set crypto functions */ + hop->backf = layer->backf; + hop->forwf = layer->forwf; + + /* calculate keys */ + SHA1(layer->keyseed,16,hop->digest3); + log(LOG_DEBUG,"create_onion() : First SHA pass performed."); + SHA1(hop->digest3,20,hop->digest2); + log(LOG_DEBUG,"create_onion() : Second SHA pass performed."); + SHA1(hop->digest2,20,hop->digest3); + log(LOG_DEBUG,"create_onion() : Third SHA pass performed."); + log(LOG_DEBUG,"create_onion() : Keys generated."); + /* set IVs */ + memset((void *)hop->f_iv,0,16); + memset((void *)hop->b_iv,0,16); + + /* initialize cipher contexts */ + EVP_CIPHER_CTX_init(&hop->f_ctx); + EVP_CIPHER_CTX_init(&hop->b_ctx); + + /* initialize cipher engines */ + switch(layer->forwf) + { + case ONION_CIPHER_DES : + retval = EVP_EncryptInit(&hop->f_ctx, EVP_des_ofb(), hop->digest3, hop->f_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_EncryptInit(&hop->f_ctx, EVP_rc4(), hop->digest3, hop->f_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_EncryptInit(&hop->f_ctx, EVP_enc_null(), hop->digest3, hop->f_iv); + break; + } + if (!retval) /* cipher initialization failed */ + { + log(LOG_ERR,"Could not initialize crypto engines."); + free((void *)bufp); + for (j=0;j<i;j++) + free((void *)cpathp[i]); + return NULL; + } + switch(layer->backf) + { + case ONION_CIPHER_DES : + retval = EVP_DecryptInit(&hop->b_ctx, EVP_des_ofb(), hop->digest2, hop->b_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_DecryptInit(&hop->b_ctx, EVP_rc4(), hop->digest2, hop->b_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_DecryptInit(&hop->b_ctx, EVP_enc_null(), hop->digest2, hop->b_iv); + break; + } + if (!retval) /* cipher initialization failed */ + { + log(LOG_ERR,"Could not initialize crypto engines."); + free((void *)bufp); + for (j=0;j<i;j++) + free((void *)cpathp[i]); + return NULL; + } + + log(LOG_DEBUG,"create_onion() : Built corresponding crypt path hop."); + } + + /* padding if this is the innermost layer */ + if (!i) + { + retval=RAND_pseudo_bytes((unsigned char *)layer + 28,100); + if (retval == -1) /* error */ + { + log(LOG_ERR,"Error generating pseudo-random data."); + free((void *)bufp); + if (cpathp) + { + for (j=0;j<i;j++) + free((void *)cpathp[i]); + } + return NULL; + } + log(LOG_DEBUG,"create_onion() : This is the innermost layer. Adding 100 bytes of padding."); + } + + /* encrypt */ + retbuf = encrypt_onion(layer,128+(i*28),router->pkey); + if (!retbuf) + { + log(LOG_ERR,"Error encrypting onion layer."); + free((void *)bufp); + if (cpathp) + { + for (j=0;j<i;j++) + free((void *)cpathp[i]); + } + return NULL; + } + log(LOG_DEBUG,"create_onion() : Encrypted layer."); + + /* calculate pointer to next layer */ + layer = (onion_layer_t *)bufp + (routelen-i-2)*sizeof(onion_layer_t); + } + + return bufp; + } /* valid parameters */ + else + return NULL; +} + +/* encrypts 128 bytes of the onion with the specified public key, the rest with + * DES OFB with the key as defined in the outter layer */ +unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey) +{ + unsigned char *tmpbuf = NULL; /* temporary buffer for crypto operations */ + unsigned char digest[20]; /* stores SHA1 output - 160 bits */ + unsigned char *retbuf = NULL; + unsigned char iv[8]; + int retval = 0; + int outlen = 0; + + EVP_CIPHER_CTX ctx; /* cipher context */ + + if ( (onion) && (pkey) ) /* valid parameters */ + { + memset((void *)iv,0,8); + + log(LOG_DEBUG,"Onion layer : %u, %u, %u, %s, %u.",onion->zero,onion->backf,onion->forwf,inet_ntoa(*((struct in_addr *)&onion->addr)),ntohs(onion->port)); + /* allocate space for tmpbuf */ + tmpbuf = (unsigned char *)malloc(onionlen); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + return NULL; + } + log(LOG_DEBUG,"encrypt_onion() : allocated %u bytes of memory for the encrypted onion (at %u).",onionlen,tmpbuf); + + /* get key1 = SHA1(KeySeed) */ + retbuf = SHA1(((onion_layer_t *)onion)->keyseed,16,digest); + if (!retbuf) + { + log(LOG_ERR,"Error computing SHA1 digest."); + free((void *)tmpbuf); + return NULL; + } + log(LOG_DEBUG,"encrypt_onion() : Computed DES key."); + + log(LOG_DEBUG,"encrypt_onion() : Trying to RSA encrypt."); + /* encrypt 128 bytes with RSA *pkey */ + retval = RSA_public_encrypt(128, (unsigned char *)onion, tmpbuf, pkey, RSA_NO_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Error RSA-encrypting data :%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + + log(LOG_DEBUG,"encrypt_onion() : RSA encrypted first 128 bytes of the onion."); + + /* now encrypt the rest with DES OFB */ + + EVP_CIPHER_CTX_init(&ctx); + retval = EVP_EncryptInit(&ctx,EVP_des_ofb(),digest,iv); + if (!retval) /* error */ + { + log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + + retval = EVP_EncryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128); + if (!retval) /* error */ + { + log(LOG_ERR,"Error performing DES encryption:%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + log(LOG_DEBUG,"encrypt_onion() : DES OFB encrypted the rest of the onion."); + + EVP_CIPHER_CTX_cleanup(&ctx); + + /* now copy tmpbuf to onion */ + memcpy((void *)onion,(void *)tmpbuf,onionlen); + log(LOG_DEBUG,"encrypt_onion() : Copied cipher to original onion buffer."); + free((void *)tmpbuf); + return (unsigned char *)onion; + } /* valid parameters */ + else + return NULL; +} + +/* decrypts the first 128 bytes using RSA and prkey, decrypts the rest with DES OFB with key1 */ +unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey) +{ + void *tmpbuf = NULL; /* temporary buffer for crypto operations */ + unsigned char digest[20]; /* stores SHA1 output - 160 bits */ + unsigned char *retbuf = NULL; + unsigned char iv[8]; + int retval = 0; + int outlen = 0; + + EVP_CIPHER_CTX ctx; /* cipher context */ + + if ( (onion) && (prkey) ) /* valid parameters */ + { + memset((void *)iv,0,8); + + /* allocate space for tmpbuf */ + tmpbuf = malloc(onionlen); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + return NULL; + } + log(LOG_DEBUG,"decrypt_onion() : Allocated memory for the temporary buffer."); + + /* decrypt 128 bytes with RSA *prkey */ + retval = RSA_private_decrypt(128, (unsigned char*)onion, (unsigned char *)tmpbuf, prkey, RSA_NO_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Error RSA-decrypting data :%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + log(LOG_DEBUG,"decrypt_onion() : RSA decryption complete."); + + /* get key1 = SHA1(KeySeed) */ + retbuf = SHA1(((onion_layer_t *)tmpbuf)->keyseed,16,digest); + if (!retbuf) + { + log(LOG_ERR,"Error computing SHA1 digest."); + free((void *)tmpbuf); + return NULL; + } + log(LOG_DEBUG,"decrypt_onion() : Computed DES key."); + + /* now decrypt the rest with DES OFB */ + EVP_CIPHER_CTX_init(&ctx); + retval = EVP_DecryptInit(&ctx,EVP_des_ofb(),digest,iv); + if (!retval) /* error */ + { + log(LOG_ERR,"Error initializing DES engine:%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + retval = EVP_DecryptUpdate(&ctx,(unsigned char *)tmpbuf+128,&outlen,(unsigned char *)onion+128,onionlen-128); + if (!retval) /* error */ + { + log(LOG_ERR,"Error performing DES decryption:%s",ERR_reason_error_string(ERR_get_error())); + free((void *)tmpbuf); + return NULL; + } + + EVP_CIPHER_CTX_cleanup(&ctx); + log(LOG_DEBUG,"decrypt_onion() : DES decryption complete."); + + /* now copy tmpbuf to onion */ + memcpy((void *)onion,(void *)tmpbuf,onionlen); + free((void *)tmpbuf); + return (unsigned char *)onion; + } /* valid parameters */ + else + return NULL; +} + +/* delete first n bytes of the onion and pads the end with n bytes of random data */ +void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n) +{ + if (onion) /* valid parameter */ + { + memmove((void *)onion,(void *)(onion+n),onionlen-n); + RAND_pseudo_bytes(onion+onionlen-n,n); + } +} + +/* create a new tracked_onion entry */ +tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion) +{ + tracked_onion_t *to = NULL; + + if (!onion || !tracked_onions || !last_tracked_onion) /* invalid parameters */ + return NULL; + + to = (tracked_onion_t *)malloc(sizeof(tracked_onion_t)); + if (!to) + return NULL; + + to->expire = ((onion_layer_t *)onion)->expire; /* set the expiration date */ + /* compute the SHA digest */ + SHA1(onion, onionlen, to->digest); + if (!to->digest) + { + log(LOG_DEBUG,"new_tracked_onion() : Failed to compute a SHA1 digest of the onion."); + free((void *)to); + return NULL; + } + + to->next = NULL; + + if (!*tracked_onions) + { + to->prev = NULL; + *tracked_onions = to; + } + else + { + to->prev = (void *)*last_tracked_onion; + (*last_tracked_onion)->next = (void *)to; + } + *last_tracked_onion = to; + + return to; +} + +/* delete a tracked onion entry */ +void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion) +{ + if (!*tracked_onions || !*last_tracked_onion || !to) + return; + + if (to->prev) + ((tracked_onion_t *)to->prev)->next = to->next; + if (to->next) + ((tracked_onion_t *)to->next)->prev = to->prev; + + if (to == *tracked_onions) + *tracked_onions = (tracked_onion_t *)to->next; + + if (to == *last_tracked_onion) + *last_tracked_onion = (tracked_onion_t *)to->prev; + + free((void *)to); + + return; +} + +/* find a tracked onion in the linked list of tracked onions */ +tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions) +{ + tracked_onion_t *to = tracked_onions; + unsigned char digest[20]; + + /* compute the SHA digest of the onion */ + SHA1(onion,onionlen, digest); + + while(to) + { + if (!memcmp((void *)digest, (void *)to->digest, 20)) + return to; + to = (tracked_onion_t *)to->next; + } + + return NULL; +} + diff --git a/src/common/onion.h b/src/common/onion.h new file mode 100644 index 000000000..41e6d286c --- /dev/null +++ b/src/common/onion.h @@ -0,0 +1,156 @@ +/** + * onion.h + * Routines for creating/manipulating onions. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.17 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.16 2002/03/25 09:11:23 badbytes + * Added a list of onions being tracked for replay attacks. + * + * Revision 1.15 2002/01/26 19:24:29 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.14 2002/01/18 20:40:40 mp292 + * Fixed a bug in en/decrypt_onion() functions. + * + * Revision 1.13 2002/01/17 23:48:31 mp292 + * Added some extra debugging messages to fix a bug in encrypt_onion() which + * seems to corrupt the routent *route list. + * + * Revision 1.12 2002/01/11 15:47:17 badbytes + * *** empty log message *** + * + * Revision 1.11 2002/01/09 07:55:23 badbytes + * Ciphers got out of sync. Hopefully fixed. + * + * Revision 1.10 2002/01/04 13:48:54 badbytes + * Changed unsigned short/long to uint16_t and uint32_t respectively. + * + * Revision 1.9 2001/12/19 08:29:00 badbytes + * Macro DEFAULT_CIPHER now holds the default crypto algorithm + * + * Revision 1.8 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.7 2001/12/18 07:26:47 badbytes + * Added a new definition of onion_layer_t, depending on the byte order. + * + * Revision 1.6 2001/12/17 13:35:17 badbytes + * Still writing handle_connection() + * + * Revision 1.5 2001/12/14 14:44:37 badbytes + * chooselen() tested + * + * Revision 1.4 2001/12/14 13:31:08 badbytes + * peel_onion() was redundant, removed it + * + * Revision 1.3 2001/12/14 13:14:03 badbytes + * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies. + * + * Revision 1.2 2001/12/14 12:44:47 badbytes + * Minor modifications to reflect new paths ... + * + * Revision 1.1 2001/12/14 12:41:12 badbytes + * Moved from op/ as it will be reused by other modules. + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ + +#ifndef __ONION_H + +#include <endian.h> +#include <stdint.h> + +#include <openssl/rsa.h> +#include <openssl/evp.h> + +#include "routent.h" +#include "version.h" + +/* available cipher functions */ +#define ONION_CIPHER_IDENTITY 0 +#define ONION_CIPHER_DES 1 +#define ONION_CIPHER_RC4 2 + +/* default cipher function */ +#define ONION_DEFAULT_CIPHER ONION_CIPHER_DES + +typedef struct +{ + int zero:1; + int version:7; + int backf:4; + int forwf:4; + uint16_t port; + uint32_t addr; + time_t expire; + unsigned char keyseed[16]; +} onion_layer_t; + +typedef struct +{ + unsigned int forwf; + unsigned int backf; + char digest2[20]; /* second SHA output for onion_layer_t.keyseed */ + char digest3[20]; /* third SHA output for onion_layer_t.keyseed */ + + /* IVs */ + char f_iv[16]; + char b_iv[16]; + + /* cipher contexts */ + EVP_CIPHER_CTX f_ctx; + EVP_CIPHER_CTX b_ctx; + +} crypt_path_t; + +typedef struct +{ + time_t expire; + char digest[20]; /* SHA digest of the onion */ + void *prev; + void *next; +} tracked_onion_t; + +/* returns an array of indexes into a router array that define a new route through the OR network + * int cw is the coin weight to use when choosing the route + * order of routers is from last to first */ +unsigned int *new_route(double cw, routent_t **rarray, size_t rarray_len, size_t *rlen); + +/* creates a new onion from route, stores it and its length into bufp and lenp respectively */ +/* if cpathp not NULL then also compute the corresponding crypt_path */ +unsigned char *create_onion(routent_t **rarray, size_t rarray_len, unsigned int *route, size_t routelen, size_t *lenp, crypt_path_t **cpathp); + +/* encrypts 128 bytes of the onion with the specified public key, the rest with + * DES OFB with the key as defined in the outter layer */ +unsigned char *encrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *pkey); + +/* decrypts the onion */ +unsigned char *decrypt_onion(onion_layer_t *onion, uint32_t onionlen, RSA *prkey); + +/* deletes the first n bytes of the onion and pads the end with n bytes of random data */ +void pad_onion(unsigned char *onion, uint32_t onionlen, size_t n); + +/* create a new tracked_onion entry */ +tracked_onion_t *new_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion); + +/* delete a tracked onion entry */ +void remove_tracked_onion(tracked_onion_t *to, tracked_onion_t **tracked_onions, tracked_onion_t **last_tracked_onion); + +/* find a tracked onion in the linked list of tracked onions */ +tracked_onion_t *id_tracked_onion(unsigned char *onion, uint32_t onionlen, tracked_onion_t *tracked_onions); + +#define __ONION_H +#endif diff --git a/src/common/opcell.c b/src/common/opcell.c new file mode 100644 index 000000000..288a355ce --- /dev/null +++ b/src/common/opcell.c @@ -0,0 +1,77 @@ +/** + * opcell.c + * Onion Proxy Cell + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/03/03 12:08:18 mp292 + * Added a new type of cell - used for data going between the onion proxy and + * the first or hop. Payload size identical to that of a normal cell. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include <openssl/rand.h> + +#include "opcell.h" +#include "log.h" + +opcell_t *new_padding_opcell() +{ + opcell_t *c = NULL; + int retval; + + c = malloc(sizeof(opcell_t)); + if (!c) /* malloc() error */ + return NULL; + + retval = RAND_pseudo_bytes((unsigned char *)c,sizeof(opcell_t)); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + c->command = OPCELL_PADDING; + + return c; +} + +opcell_t *new_data_opcell(unsigned char length, unsigned char *buf) +{ + opcell_t *c = NULL; + int retval; + + if ((length <= OPCELL_PAYLOAD_SIZE) && (buf)) /* valid parameters */ + { + c = malloc(sizeof(opcell_t)); + if (!c) /* malloc() error */ + return NULL; + + c->command = OPCELL_DATA; + c->length = length; + + memcpy((void *)c->payload, (void *)buf, length); + retval = RAND_pseudo_bytes((unsigned char *)(c->payload+length),OPCELL_PAYLOAD_SIZE-length); + if (retval == -1) /* RAND_pseudo_bytes() error */ + { + free((void *)c); + return NULL; + } /* RAND_pseudo_bytes() error */ + + return c; + } /* valid parameters */ + else /* invalid parameters */ + return NULL; +} + diff --git a/src/common/opcell.h b/src/common/opcell.h new file mode 100644 index 000000000..5eca59bd5 --- /dev/null +++ b/src/common/opcell.h @@ -0,0 +1,44 @@ +/** + * opcell.h + * Onion Proxy Cell. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/03/03 12:08:18 mp292 + * Added a new type of cell - used for data going between the onion proxy and + * the first or hop. Payload size identical to that of a normal cell. + * + */ + +#ifndef __OPCELL_H + +#include <stdint.h> + +#include "cell.h" + +#define OPCELL_PAYLOAD_SIZE CELL_PAYLOAD_SIZE + +#define OPCELL_PADDING 0 +#define OPCELL_DATA 1 + +/* cell definition */ +typedef struct +{ + unsigned char command; + unsigned char length; /* of payload */ + unsigned char payload[OPCELL_PAYLOAD_SIZE]; +} opcell_t; + +opcell_t *new_data_opcell(unsigned char length, unsigned char *buf); +opcell_t *new_padding_opcell(); + +#define __OPCELL_H +#endif + diff --git a/src/common/policies.h b/src/common/policies.h new file mode 100644 index 000000000..78ba20c50 --- /dev/null +++ b/src/common/policies.h @@ -0,0 +1,39 @@ +/** + * policies.h + * Traffic shaping policies for the network funnel. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.2 2002/03/12 23:42:37 mp292 + * Various bugfixes. + * + * Revision 1.1 2002/03/03 00:03:49 mp292 + * Moved from or/network (merged core and network funnel into a single thread). + * + * Revision 1.3 2002/02/09 17:00:42 mp292 + * Added core_sock to list of parameters for comms with the router core. + * + * Revision 1.2 2002/02/03 22:40:44 mp292 + * Changes to cell size. + * + * Revision 1.1 2002/02/03 20:34:38 mp292 + * Traffic shaping policies for the network funnel. + * + */ + + +/* traffic shaping policies */ +#define POLICY_DROP_CONNECTIONS 0 /* buffer data and drop the connections that cannot be allocated resources */ +#define POLICY_DROP_CELLS 1 /* buffer data and drop cells, which can't be bufered, do re-transmission */ + +#define DEFAULT_POLICY POLICY_DROP_CONNECTIONS + +#define DEFAULT_ACK_TIMEOUT 3000 /* ms */ +#define DEFAULT_WINDOW_SIZE 5 /* cells */ diff --git a/src/common/routent.c b/src/common/routent.c new file mode 100644 index 000000000..0f5f3e7da --- /dev/null +++ b/src/common/routent.c @@ -0,0 +1,147 @@ +/* + * routent.c + * Onion Router and related definitions. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.6 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.5 2002/03/12 23:38:54 mp292 + * Being pedantic about some pointer conversions. + * + * Revision 1.4 2002/03/03 00:24:26 mp292 + * Corrected paths to some #include files. + * + * Revision 1.3 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.2 2002/01/26 19:26:55 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.1 2002/01/10 08:28:33 badbytes + * routent and routentEX related routines + * + */ + +#include "policies.h" + +#include "routent.h" + +routentEX_t *id_router(routentEX_t **routerarray, size_t rarray_len, uint32_t addr, uint16_t port) +{ + routentEX_t *router; + int i; + + if (!routerarray) + return NULL; + + for(i=0;i<rarray_len;i++) + { + router = routerarray[i]; + if ((router->addr == addr) && (router->port == port)) + return router; + } + + return NULL; +} + +routentEX_t *id_routerbys(routentEX_t **routerarray, size_t rarray_len, int s) +{ + routentEX_t *router; + int i; + + if (!routerarray) + return NULL; + + for(i=0;i<rarray_len;i++) + { + router = routerarray[i]; + if (router->s == s) + return router; + } + + return NULL; +} + +conn_buf_t *new_conn_buf(uint16_t aci, int policy, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf) +{ + conn_buf_t *conn_buf; + + if ((!aci) || (!conn_bufs) || (!last_conn_buf)) /* invalid parameters */ + return NULL; + + conn_buf = (conn_buf_t *)malloc(sizeof(conn_buf_t)); + if (!conn_buf) + return NULL; + + memset((void *)conn_buf,0,sizeof(conn_buf_t)); + conn_buf->win_size = DEFAULT_WINDOW_SIZE; + conn_buf->win_avail = DEFAULT_WINDOW_SIZE; + conn_buf->aci = aci; + conn_buf->policy = policy; + + if (!*conn_bufs) + { + *conn_bufs = conn_buf; + } + else + { + (*last_conn_buf)->next=(void *)conn_buf; + conn_buf->prev = (void *)*last_conn_buf; + } + + *last_conn_buf = conn_buf; + + return conn_buf; +} + +int remove_conn_buf(conn_buf_t *conn_buf, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf) +{ + if ( (!conn_buf) || (!*conn_bufs) || (!*last_conn_buf) ) /* invalid parameters */ + return -1; + + if (conn_buf->next) + ((conn_buf_t *)(conn_buf->next))->prev = conn_buf->prev; + if (conn_buf->prev) + ((conn_buf_t *)(conn_buf->prev))->next = conn_buf->next; + + if (conn_buf == *last_conn_buf) + *last_conn_buf = (conn_buf_t *)conn_buf->prev; + + if (conn_buf == *conn_bufs) + *conn_bufs = (conn_buf_t *)conn_buf->next; + + if (conn_buf->buf) + free((void *)conn_buf->buf); + + free((void *)conn_buf); + + return 0; +} + +conn_buf_t *id_conn_buf(conn_buf_t *conn_bufs, uint16_t aci) +{ + conn_buf_t *conn_buf; + + if ( (!aci) || (!conn_bufs) ) + return NULL; + + conn_buf = conn_bufs; + while (conn_buf) + { + if (conn_buf->aci == aci) + return conn_buf; + + conn_buf = conn_buf->next; + } + + return NULL; +} diff --git a/src/common/routent.h b/src/common/routent.h new file mode 100644 index 000000000..6a905a9bb --- /dev/null +++ b/src/common/routent.h @@ -0,0 +1,189 @@ +/* + * routent.h + * Onion Router and related definitions. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.25 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.24 2002/03/29 09:54:19 badbytes + * Fixed type of routentEX.min_interval to struct timeval. + * + * Revision 1.23 2002/03/21 07:20:59 badbytes + * Added a dependency to <sys/time.h>. + * + * Revision 1.22 2002/03/12 23:37:14 mp292 + * Additional flag - destory_buf saying whether the buffer should be destroyed + * when the destroy cell is sent. + * + * Revision 1.21 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.20 2002/02/09 16:58:53 mp292 + * Postponed implementtion of POLICY_DROP_CONNECTIONS due to problems. Need to + * discuss with Andrei first. + * + * Revision 1.19 2002/02/09 16:54:59 mp292 + * routentEX now contains a per anonymous connection packet count + * + * Revision 1.18 2002/01/29 00:59:16 mp292 + * Slight changes in the way timers are kept, c.f. changes in the network funnel. + * + * Revision 1.17 2002/01/28 21:37:36 mp292 + * Router's output buffer is now dynamic. Time of last output to the router + * added to routentEX. + * + * Revision 1.16 2002/01/26 19:26:55 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.15 2002/01/18 22:55:40 mp292 + * Added a cell buffer to struct routent so that a cell can be received in + * several bursts of data. This prevents a DoS attack on the network funnel. + * + * Revision 1.14 2002/01/14 13:05:37 badbytes + * System testing in progress. + * + * Revision 1.13 2002/01/11 15:47:17 badbytes + * *** empty log message *** + * + * Revision 1.12 2002/01/10 08:28:33 badbytes + * routent and routentEX related routines + * + * Revision 1.11 2002/01/08 15:13:30 badbytes + * Added cipher context to routentEX + * + * Revision 1.10 2002/01/08 13:18:48 badbytes + * Added a connection buffer to routentEX + * + * Revision 1.9 2002/01/08 13:02:16 badbytes + * routentEX now contains f_key and b_key, 56-bit DES keys for link encryption + * + * Revision 1.8 2002/01/03 11:17:01 badbytes + * routentEX.max and routentEX.min values changed to 32bit not 64bit. + * + * Revision 1.7 2002/01/03 11:04:16 badbytes + * *** empty log message *** + * + * Revision 1.6 2002/01/03 11:03:14 badbytes + * Added an extended version of routent which includes link utilisation info. + * + * Revision 1.5 2001/12/18 15:26:34 badbytes + * Added #inclusion of <stdint.h> + * + * Revision 1.4 2001/12/18 15:19:41 badbytes + * In struct routent, changed long and short types to uint32_t and uint16_t + * + * Revision 1.3 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.2 2001/12/17 13:35:17 badbytes + * Still writing handle_connection() + * + * Revision 1.1 2001/12/14 13:14:03 badbytes + * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies. + * + */ + +#ifndef __ROUTENT_H +#include <openssl/rsa.h> +#include <openssl/evp.h> +#include <stdint.h> +#include <sys/time.h> +#include <time.h> +#include <sys/timeb.h> + +#include "cell.h" + +/* per-anonymous-connection cell buffer */ +typedef struct +{ + uint16_t aci; + int policy; + unsigned int cells; + unsigned char *buf; + unsigned int buflen; + unsigned int offset; /* offset to the position of the first cell in the buffer */ + cell_t dc; /* static buffer for the destroy cell - so we are always able to destroy a connection */ + unsigned char dc_set; /* flag that signifies presence of a destroy cell */ + unsigned char destroy_buf; /* flag that signifies that the buffer shuld be destroyed when the destroy cell is sent */ + + /* POLICY_DROP_CELLS only */ + unsigned int win_size; /* window size for the connection (number of cells)*/ + unsigned int win_avail; /* available window size */ + uint32_t seq_out; /* next sequence number to use for outgoing cells */ + uint32_t seq_in; /* next expected sequence number */ + uint32_t ack; /* next expected ack/nack */ + struct timeval last_ack; /* time of last ACK/NACK */ + + void *prev; + void *next; +} conn_buf_t; + +/* onion router as seen by the onion proxy */ +typedef struct +{ + char *address; + uint32_t addr; /* address in network byte order */ + uint16_t port; /* network port in network byte order */ + uint16_t entry_port; /* entry port in network byte order */ + RSA *pkey; + void *next; +} routent_t; + +/* onion router as seen by other routers */ +typedef struct +{ + char *address; + + uint32_t addr; + uint16_t port; + + RSA *pkey; /* public RSA key */ + /* 64-bit DES keys for link encryption */ + char f_key[8]; + char b_key[8]; + char f_iv[8]; + char b_iv[8]; + EVP_CIPHER_CTX f_ctx; + EVP_CIPHER_CTX b_ctx; + + /* link info */ + uint32_t min; + uint32_t max; + struct timeval min_interval; + + /* time when last data was sent to that router */ + struct timeval lastsend; + + /* socket */ + int s; + + /* connection buffers */ + conn_buf_t *conn_bufs; /* linked list of connection buffers */ + conn_buf_t *last_conn_buf; /* last item in the list */ + unsigned int next_to_service; /* offset to the connection buffer that is next in turn to be serviced */ + + /* cell buffer */ + unsigned char cellbuf[128]; + unsigned int celllen; + + void *next; +} routentEX_t; + +routentEX_t *id_router(routentEX_t **routerarray, size_t rarray_len, uint32_t addr, uint16_t port); +routentEX_t *id_routerbys(routentEX_t **routerarray, size_t rarray_len, int s); + +conn_buf_t *new_conn_buf(uint16_t aci, int policy, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf); +int remove_conn_buf(conn_buf_t *conn_buf, conn_buf_t **conn_bufs, conn_buf_t **last_conn_buf); +conn_buf_t *id_conn_buf(conn_buf_t *conn_bufs, uint16_t aci); + +#define __ROUTENT_H +#endif diff --git a/src/common/scheduler.c b/src/common/scheduler.c new file mode 100644 index 000000000..3fb02315a --- /dev/null +++ b/src/common/scheduler.c @@ -0,0 +1,260 @@ +/* + * scheduler.c + * Scheduler + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/04/02 10:20:37 badbytes + * Bug fixes. + * + * Revision 1.2 2002/03/28 10:49:07 badbytes + * Renamed get_trigger() to sched_trigger(). + * + * Revision 1.1 2002/03/28 10:36:55 badbytes + * A generic scheduler. + * + */ + +#include <stdio.h> +#include <unistd.h> +#include <malloc.h> +#include "log.h" +#include "scheduler.h" + +/* create a new scheduler */ +sched_t *new_sched() +{ + sched_t *sched; + + sched = (sched_t *)malloc(sizeof(sched_t)); + if (!sched) + return NULL; + + sched->entries = NULL; + return sched; +} + +/* delete a scheduler from memory */ +void free_sched(sched_t *sched) +{ + sched_entry_t *entry; + if (!sched) + return; + + while(sched->entries) + { + entry = (sched_entry_t *)sched->entries->next; + free((void *)sched->entries); + sched->entries = entry; + } +} + +/* add a new item to the scheduler */ +int add_sched_entry(sched_t *sched, struct timeval last, struct timeval interval) +{ + sched_entry_t *new_entry; + sched_entry_t *prev; + sched_entry_t *next; + + if (!sched) /* invalid parameters */ + return -1; + + new_entry = (sched_entry_t *)malloc(sizeof(sched_entry_t)); + if (!new_entry) + return -1; + + new_entry->last = last; + new_entry->interval = interval; + + if (!sched->entries) /* empty list */ + { + sched->entries = new_entry; + new_entry->prev = NULL; + new_entry->next = NULL; + } + else /* maintain a priority queue of items */ + { + /* find the next largest element in the list */ + next = sched->entries; + while(next) + { + if (sched_entry_geq(next->last, next->interval, last, interval)) + { + prev = (sched_entry_t *)next->prev; + break; + } + else + { + prev = next; + next = (sched_entry_t *)next->next; + } + } + + if (prev) + prev->next = (void *)new_entry; + else + sched->entries = new_entry; + + if (next) + next->prev = (void *)new_entry; + + new_entry->prev = (void *)prev; + new_entry->next = (void *)next; + } + + return 0; +} + +int remove_sched_entry(sched_t *sched, struct timeval last, struct timeval interval) +{ + sched_entry_t *entry; + + if (!sched) + return -1; + + if (!sched->entries) + return -1; + + entry = sched->entries; + while(entry) + { + if ((entry->last.tv_sec == last.tv_sec) && (entry->last.tv_usec = last.tv_usec) && (entry->interval.tv_sec == interval.tv_sec) && (entry->interval.tv_usec == interval.tv_usec)) + { + if (entry->prev) + ((sched_entry_t *)(entry->prev))->next = entry->next; + else + sched->entries = (sched_entry_t *)entry->next; + + if (entry->next) + ((sched_entry_t *)(entry->next))->prev = entry->prev; + + free((void *)entry); + break; + } + else + entry = (sched_entry_t *)entry->next; + } + + if (entry) /* found and deleted */ + return 0; + else /* not found */ + return -1; +} + +/* update an existing item with new values */ +int update_sched_entry(sched_t *sched, struct timeval old_last, struct timeval old_interval, struct timeval new_last, struct timeval new_interval) +{ + int retval; + + if (!sched) + return -1; + + /* remove the old entry first */ + retval = remove_sched_entry(sched, old_last, old_interval); + if (!retval) + { + /* add the new one */ + retval = add_sched_entry(sched, new_last, new_interval); + } + + return retval; +} + +/* get the time interval from now until the next time an item needs to be serviced */ +int sched_trigger(sched_t *sched, struct timeval **result) +{ + int retval; + struct timeval *result_val; + struct timeval now; + struct timeval next; + + if (!sched) /* invalid parameters */ + return -1; + + if (!sched->entries) /* no entries */ + { + *result = NULL; + return 0; + } + + /* take the minimum element in the queue and calculate its next service time */ + next.tv_sec = sched->entries->last.tv_sec + sched->entries->interval.tv_sec; + if (sched->entries->last.tv_usec + sched->entries->interval.tv_usec <= 999999) + next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec; + else + { + next.tv_sec++; + next.tv_usec = sched->entries->last.tv_usec + sched->entries->interval.tv_usec - 1000000; + } + + /* get current time */ + retval = gettimeofday(&now,NULL); + if (retval == -1) + return -1; + + /* allocate memory for the result */ + result_val = (struct timeval *)malloc(sizeof(struct timeval)); + if (!result_val) + return -1; + + /* subtract now from next (return zero if negative) */ + if ((next.tv_sec > now.tv_sec) || ((next.tv_sec == now.tv_sec) && (next.tv_usec >= now.tv_usec))) + { + result_val->tv_sec = next.tv_sec - now.tv_sec; + if (next.tv_usec >= now.tv_usec) + result_val->tv_usec = next.tv_usec - now.tv_usec; + else + { + result_val->tv_sec--; + result_val->tv_usec = 1000000 + next.tv_usec - now.tv_usec; + } + } + else /* next service time has already passed, return a timeout of zero */ + { + result_val->tv_sec = 0; + result_val->tv_usec = 0; + } + + *result = result_val; + + return 0; +} + +int sched_entry_geq(struct timeval last1, struct timeval interval1, struct timeval last2, struct timeval interval2) +{ + struct timeval next1; + struct timeval next2; + + /* calculate next service time for entry1 */ + next1.tv_sec = last1.tv_sec + interval1.tv_sec; + if (last1.tv_usec + interval1.tv_usec <= 999999) + next1.tv_usec = last1.tv_usec + interval1.tv_usec; + else + { + next1.tv_sec++; + next1.tv_usec = last1.tv_usec + interval1.tv_usec - 1000000; + } + + /* calculate next service time for entry2 */ + next2.tv_sec = last2.tv_sec + interval2.tv_sec; + if (last2.tv_usec + interval2.tv_usec <= 999999) + next2.tv_usec = last2.tv_usec + interval2.tv_usec; + else + { + next2.tv_sec++; + next2.tv_usec = last2.tv_usec + interval2.tv_usec - 1000000; + } + + /* compare */ + if ((next1.tv_sec > next2.tv_sec) || ((next1.tv_sec == next2.tv_sec) && (next1.tv_usec >= next2.tv_usec))) + return 1; + else + return 0; +} diff --git a/src/common/scheduler.h b/src/common/scheduler.h new file mode 100644 index 000000000..be4d72320 --- /dev/null +++ b/src/common/scheduler.h @@ -0,0 +1,57 @@ +/* + * scheduler.h + * Scheduler + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.2 2002/03/28 10:49:07 badbytes + * Renamed get_trigger() to sched_trigger(). + * + * Revision 1.1 2002/03/28 10:36:55 badbytes + * A generic scheduler. + * + */ + +#ifndef __SCHEDULER_H + +#include <sys/time.h> + +typedef struct +{ + struct timeval last; + struct timeval interval; + void *prev; + void *next; +} sched_entry_t; + +typedef struct +{ + sched_entry_t *entries; +} sched_t; + +/* create a new scheduler */ +sched_t *new_sched(); +/* delete a scheduler from memory */ +void free_sched(sched_t *sched); + +/* add a new item to the scheduler */ +int add_sched_entry(sched_t *sched, struct timeval last, struct timeval interval); +/* remove an item from the scheduler */ +int remove_sched_entry(sched_t *sched, struct timeval last, struct timeval interval); +/* update an existing item with new values */ +int update_sched_entry(sched_t *sched, struct timeval old_last, struct timeval old_interval, struct timeval new_last, struct timeval new_interval); + +/* get the time interval from now until the next time an item needs to be serviced */ +int sched_trigger(sched_t *sched, struct timeval **result); +/* compare two scheduler entries (returns 1 if entry1 >= entry2, 0 otherwise */ +int sched_entry_geq(struct timeval last1, struct timeval interval1, struct timeval last2, struct timeval interval2); + +# define __SCHEDULER_H +#endif diff --git a/src/common/ss.h b/src/common/ss.h new file mode 100644 index 000000000..9298fa1b7 --- /dev/null +++ b/src/common/ss.h @@ -0,0 +1,63 @@ +/* + * ss.h + * Standard structure and related definitions. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.5 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.4 2002/01/26 22:45:34 mp292 + * Added ss-related error codes. + * + * Revision 1.3 2002/01/26 19:30:09 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.1 2001/12/14 13:14:03 badbytes + * Split types.h into routent.h and ss.h. Keeping them all in one file created unnecesary dependencies. + * + * Revision 1.2 2001/12/11 16:31:03 badbytes + * Changed type from ss to SS. + * + * Revision 1.1 2001/12/07 11:15:28 badbytes + * Added the definition for the standard structure. + * + */ + +#ifndef __SS_H + +/* protocol types, as used in the standard structure */ +#define SS_PROTOCOL_TELNET 1 +#define SS_PROTOCOL_HTTP 2 +#define SS_PROTOCOL_SMTP 3 + +/* address format types, as used in the standard structure */ +#define SS_ADDR_FMT_ASCII_HOST_PORT 1 + +/* error codes returned by the onion proxy */ +#define SS_ERROR_SUCCESS 0 +#define SS_ERROR_VERSION_UNSUPPORTED 1 +#define SS_ERROR_ADDR_FMT_UNSUPPORTED 2 +#define SS_ERROR_INVALID_ADDRESS 3 +#define SS_ERROR_INVALID_PORT 4 + +/* standard structure */ +typedef struct +{ + unsigned char version; /* version */ + unsigned char protocol; /* protocol */ + unsigned char retry_count; /* retry count */ + unsigned char addr_fmt; /* address format */ +} ss_t; +#define __SS_H +#endif diff --git a/src/common/utils.c b/src/common/utils.c new file mode 100644 index 000000000..1a6fa6498 --- /dev/null +++ b/src/common/utils.c @@ -0,0 +1,150 @@ +/* + * utils.c + * Miscellaneous utils. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.6 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.5 2002/01/29 02:22:41 mp292 + * Bugfix. + * + * Revision 1.4 2002/01/29 00:58:23 mp292 + * Timeout parametes to read_tout() and write_tout() are now pointers. + * + * Revision 1.3 2002/01/27 19:24:16 mp292 + * Added read_tout(), write_tout() which read/write from a blocking socket but + * impose a timeout on the I/O operation. + * + * Revision 1.2 2002/01/26 19:30:09 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.1 2001/12/14 09:18:00 badbytes + * *** empty log message *** + * + */ + +#include <ctype.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <sys/socket.h> +#include <stdio.h> + +#include "utils.h" +#include "log.h" + +/* converts string to lower case */ +unsigned char *stolower(unsigned char *str) +{ + int i=0; + + if (str) /* valid parameters */ + { + for (i=0; str[i] != 0; i++) + str[i] = tolower(str[i]); + + return str; + } + else return NULL; +} + +/* reads data from a descriptor, just like read(), but imposes a timeout */ +/* the timeout refers to the connection being idle, not to a time limit in which the data + * should be received*/ +int read_tout(int s, unsigned char *buf, size_t buflen, int flags, struct timeval *conn_tout) +{ + int retval=0; + int received = 0; + struct timeval tout; + + fd_set mask,rmask; + + FD_ZERO(&mask); + FD_SET(s,&mask); + + while(1) + { + rmask=mask; + tout = *conn_tout; + retval = select(s+1,&rmask,NULL,NULL,&tout); + if (retval == -1) + { + if (errno == EINTR) + continue; + else + return -1; + } + + if (FD_ISSET(s,&rmask)) + { + retval = read(s,buf+received,buflen-received); + if (retval <= 0) + return -1; + else + { + received += retval; + if ((received < buflen) && (flags == MSG_WAITALL)) + continue; + else + return received; + } + } + else + return -1; + } +} + +/* writes data to a file descriptor, just like write(), but imposes a timeout */ +/* again this refers to the connection being idle, not a time limit in which the data should + * be sent */ +int write_tout(int s, unsigned char *buf, size_t buflen, struct timeval *conn_tout) +{ + int retval = 0; + int sent = 0; + fd_set mask,wmask; + struct timeval tout; + + FD_ZERO(&mask); + FD_SET(s,&mask); + + while(1) + { + wmask = mask; + tout = *conn_tout; + retval = select(s+1,NULL,&wmask,NULL, &tout); + if (retval == -1) + { + if (errno == EINTR) + continue; + else + return -1; + } + + if (FD_ISSET(s,&wmask)) + { + retval = write(s,buf+sent,buflen-sent); + if (retval < 0) + return -1; + else + { + sent += retval; + if (sent < buflen) + continue; + else + return sent; + } + } + else + return -1; + } +} + diff --git a/src/common/utils.h b/src/common/utils.h new file mode 100644 index 000000000..ea836de5c --- /dev/null +++ b/src/common/utils.h @@ -0,0 +1,53 @@ +/* + * utils.h + * Miscellaneous utils. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.8 2002/03/21 07:20:59 badbytes + * Added a dependency to <sys/time.h>. + * + * Revision 1.7 2002/03/03 00:06:45 mp292 + * Modifications to support re-transmission. + * + * Revision 1.6 2002/01/29 02:22:41 mp292 + * Bugfix. + * + * Revision 1.5 2002/01/29 00:58:23 mp292 + * Timeout parametes to read_tout() and write_tout() are now pointers. + * + * Revision 1.4 2002/01/27 19:24:16 mp292 + * Added read_tout(), write_tout() which read/write from a blocking socket but + * impose a timeout on the I/O operation. + * + * Revision 1.3 2002/01/26 19:30:09 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.1 2001/12/14 09:18:00 badbytes + * *** empty log message *** + * + */ + +#ifndef __UTILS_H + +#include <sys/types.h> +#include <sys/un.h> +#include <sys/time.h> + +unsigned char *stolower(unsigned char *str); +int read_tout(int s, unsigned char *buf, size_t buflen, int flags, struct timeval *conn_tout); +int write_tout(int s, unsigned char *buf, size_t buflen, struct timeval *conn_tout); + +#define __UTILS_H + +#endif diff --git a/src/common/version.h b/src/common/version.h new file mode 100644 index 000000000..fa9b87e34 --- /dev/null +++ b/src/common/version.h @@ -0,0 +1,28 @@ +/** + * version.h + * Protocol version. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/04/02 14:27:11 badbytes + * Final finishes. + * + * Revision 1.2 2001/12/18 10:37:47 badbytes + * Header files now only apply if they were not previously included from somewhere else. + * + * Revision 1.1 2001/12/18 09:12:05 badbytes + * Defines VERSION to hold the current protocol version. + * + */ + +#ifndef __VERSION_H +#define VERSION 2 +#define __VERSION_H +#endif diff --git a/src/httpap/Makefile b/src/httpap/Makefile new file mode 100644 index 000000000..bb16e1ad4 --- /dev/null +++ b/src/httpap/Makefile @@ -0,0 +1,99 @@ +SRC=httpap.c http.c +OBJ=${SRC:.c=.o} +PROGS=httpap +LIB= +LIBS= +INCLUDE = + +CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb +LDFLAGS = $(LIB) $(LIBS) + +all: ${PROGS} + +clean: + rm -f *.o ${PROGS} + +depend: + makedepend -- ${CFLAGS} -- ${SRC} + +httpap: ${OBJ} + $(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/utils.o + +# DO NOT DELETE + + +httpap.o: ../common/config.h ../common/log.h ../common/ss.h ../common/utils.h +httpap.o: ../common/version.h httpap.h http.h /usr/include/alloca.h +httpap.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h +httpap.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h +httpap.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +httpap.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +httpap.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +httpap.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h +httpap.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h +httpap.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +httpap.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h +httpap.o: /usr/include/bits/sched.h /usr/include/bits/select.h +httpap.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +httpap.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h +httpap.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h +httpap.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h +httpap.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h +httpap.o: /usr/include/bits/time.h /usr/include/bits/types.h +httpap.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h +httpap.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +httpap.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +httpap.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h +httpap.o: /usr/include/features.h /usr/include/_G_config.h +httpap.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h +httpap.o: /usr/include/libio.h /usr/include/limits.h +httpap.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +httpap.o: /usr/include/netdb.h /usr/include/netinet/in.h +httpap.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h +httpap.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +httpap.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +httpap.o: /usr/include/sys/resource.h /usr/include/sys/select.h +httpap.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h +httpap.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h +httpap.o: /usr/include/sys/types.h /usr/include/sys/ucontext.h +httpap.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/sys/wait.h +httpap.o: /usr/include/time.h /usr/include/ucontext.h /usr/include/unistd.h +httpap.o: /usr/include/wait.h /usr/include/wchar.h /usr/include/xlocale.h +httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +httpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h +http.o: ../common/log.h ../common/utils.h http.h /usr/include/alloca.h +http.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h +http.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h +http.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h +http.o: /usr/include/bits/endian.h /usr/include/bits/environments.h +http.o: /usr/include/bits/errno.h /usr/include/bits/in.h +http.o: /usr/include/bits/local_lim.h /usr/include/bits/netdb.h +http.o: /usr/include/bits/posix1_lim.h /usr/include/bits/posix2_lim.h +http.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h +http.o: /usr/include/bits/sched.h /usr/include/bits/select.h +http.o: /usr/include/bits/siginfo.h /usr/include/bits/sigset.h +http.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +http.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +http.o: /usr/include/bits/types.h /usr/include/bits/uio.h +http.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +http.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +http.o: /usr/include/bits/xopen_lim.h /usr/include/ctype.h +http.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +http.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +http.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h +http.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +http.o: /usr/include/netdb.h /usr/include/netinet/in.h +http.o: /usr/include/rpc/netdb.h /usr/include/stdint.h /usr/include/stdio.h +http.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/sys/cdefs.h +http.o: /usr/include/syslog.h /usr/include/sys/select.h +http.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h +http.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h +http.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/sys/un.h +http.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h +http.o: /usr/include/xlocale.h +http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +http.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h diff --git a/src/httpap/http.c b/src/httpap/http.c new file mode 100644 index 000000000..09116cec7 --- /dev/null +++ b/src/httpap/http.c @@ -0,0 +1,194 @@ +/* + * http.c + * HTTP parsers. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.2 2002/04/02 14:27:33 badbytes + * Final finishes. + * + * Revision 1.1 2002/03/12 23:46:14 mp292 + * HTTP-related routines. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "../common/log.h" +#include "../common/utils.h" + +#include "http.h" + +int http_get_line(int s, unsigned char **line, size_t *len, struct timeval *conn_tout) +{ + int retval =0; /* function return value */ + unsigned char buf[HTTPAP_MAXLEN]; /* line buffer */ + unsigned int buflen = 0; /* length of the received data */ + char got_cr = 0; /* received a CR character and hence expecting a LF */ + unsigned char c; /* input character */ + + if (!line || !len) /* invalid parameters */ + return -1; + + while(1) + { + retval = read_tout(s, &c, 1, MSG_WAITALL, conn_tout); + if (retval < 1) + return -1; + + if (buflen >= HTTPAP_MAXLEN) + return -1; + + buf[buflen++] = c; + + if (got_cr) + { + if (c != HTTPAP_LF) + return -1; + else + break; + } + else + { + if (c == HTTPAP_CR) + got_cr = 1; + } + } + + *len = buflen; + if (buflen) + { + *line = (unsigned char *)malloc(buflen+1); + if (!*line) + return -1; + else + { + memcpy((void *)*line,(void *)buf,buflen); + (*line)[buflen] = 0; /* add the terminating null character */ + } + } + else + *line = NULL; + + return 0; +} + +int http_get_version(unsigned char *rl, unsigned char **http_ver) +{ + unsigned char *start; + unsigned char *end; + + if (!rl || !http_ver) /* invalid parameters */ + return -1; + + start = strrchr(rl, ' '); + if (!start) + return -1; + + end = strrchr(rl, HTTPAP_CR); + if (!end) + return -1; + + start++; + *http_ver = (unsigned char *)malloc(end-start+1); + if (!*http_ver) + return -1; + + strncpy(*http_ver, start, end-start); + (*http_ver)[end-start] = 0; /* terminating NULL character */ + + return 0; +} + +int http_get_dest(unsigned char *rl, unsigned char **addr, unsigned char **port) +{ + unsigned char *start; + unsigned char *end; + unsigned char *colon; + + if (!rl || !addr || !port) /* invalid parameters */ + return -1; + + start = strchr(rl, ' '); + if (!start) + return -1; + start++; + /* make sure this is really an http:// address */ + if (strncmp(start,"http://",7)) + return -1; + + start += 7; + + end = strchr(start,'/'); + if (!end) + return -1; + + /* check for a :port in the address */ + colon = strchr(start,':'); + if (colon) + { + colon++; + *port = (unsigned char *)malloc(end-colon+1); + if (!*port) + return -1; + strncpy(*port,colon, end-colon); + (*port)[end-colon] = 0; /* terminating NULL character */ + end = colon-1; + } + else + *port = NULL; + + /* extract the server address */ + *addr = (unsigned char *)malloc(end-start+1); + if (!*addr) + { + if (*port) + free((void *)*port); + return -1; + } + strncpy(*addr,start, end-start); + (*addr)[end-start] = 0; /* terminating NULL character */ + + return 0; +} + +int http_get_header_name(unsigned char *rl, unsigned char **hname) +{ + unsigned char *end; + + if (!rl || !hname) /* invalid parameters */ + return -1; + + end = strchr(rl, ':'); + if (!end) + return -1; + + *hname = (unsigned char *)malloc(end-rl+1); + if (!*hname) + return -1; + + strncpy(*hname,rl,end-rl); + (*hname)[end-rl] = 0; + + return 0; +} diff --git a/src/httpap/http.h b/src/httpap/http.h new file mode 100644 index 000000000..cc2d20ccf --- /dev/null +++ b/src/httpap/http.h @@ -0,0 +1,46 @@ +/* + * http.h + * HTTP parsers. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.2 2002/04/02 14:27:33 badbytes + * Final finishes. + * + * Revision 1.1 2002/03/12 23:46:14 mp292 + * HTTP-related routines. + * + */ + +#define HTTPAP_MAXLEN 1024 /* maximum length of a line */ + +#define HTTPAP_CR '\015' +#define HTTPAP_LF '\012' +#define HTTPAP_CRLF "\015\012" + +#define HTTPAP_VERSION "HTTP/1.0" + +#define HTTPAP_STATUS_LINE_FORBIDDEN HTTPAP_VERSION " 403 Only local connections are allowed." HTTPAP_CRLF +#define HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED HTTPAP_VERSION " 505 Only HTTP/1.0 is supported." HTTPAP_CRLF +#define HTTPAP_STATUS_LINE_UNAVAILABLE HTTPAP_VERSION " 503 Connection to the server failed." HTTPAP_CRLF +#define HTTPAP_STATUS_LINE_BAD_REQUEST HTTPAP_VERSION " 400 Invalid syntax." HTTPAP_CRLF +#define HTTPAP_STATUS_LINE_UNEXPECTED HTTPAP_VERSION " 500 Internal server error." HTTPAP_CRLF + +#define HTTPAP_HEADER_PROXY_CONNECTION "Proxy-Connection" +#define HTTPAP_HEADER_USER_AGENT "User-Agent" +#define HTTPAP_HEADER_REFERER "Referer" + +int http_get_line(int s, unsigned char **line, size_t *len, struct timeval *conn_tout); + +int http_get_version(unsigned char *rl, unsigned char **http_ver); + +int http_get_dest(unsigned char *rl, unsigned char **addr, unsigned char **port); + +int http_get_header_name(unsigned char *rl, unsigned char **hname); diff --git a/src/httpap/httpap.c b/src/httpap/httpap.c new file mode 100644 index 000000000..dbcb2e630 --- /dev/null +++ b/src/httpap/httpap.c @@ -0,0 +1,702 @@ +/** + * httpap.c + * HTTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.4 2002/06/14 20:45:26 mp292 + * Extra debugging message. + * + * Revision 1.3 2002/04/02 14:27:33 badbytes + * Final finishes. + * + * Revision 1.2 2002/03/12 23:40:58 mp292 + * Tested. + * + * Revision 1.1 2002/03/11 00:21:53 mp292 + * Coding completed. Pending testing. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "../common/log.h" +#include "../common/config.h" +#include "../common/ss.h" +#include "../common/utils.h" +#include "../common/version.h" + +#include "httpap.h" +#include "http.h" + +int loglevel = LOG_ERR; +struct timeval conn_tout; +struct timeval *conn_toutp = &conn_tout; + +/* valid command-line options */ +static const char *args = "hf:p:l:"; + +/* valid config file options */ +static config_opt_t options[] = +{ + {"OnionProxy", CONFIG_TYPE_INT, {0}, 0}, + {"MaxConn", CONFIG_TYPE_INT, {0}, 0}, + {"Anonimize", CONFIG_TYPE_INT, {0}, 0}, + {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0}, + {0} +}; +enum opts { + OnionProxy=0,MaxConn, Anonimize, ConnTimeout +}; + +/* number of open connections */ +int connections=0; + +/* prints help on using httpap */ +void print_usage() +{ + char *program = "httpap"; + + printf("\n%s - HTTP application proxy for Onion Routing.\nUsage : %s -f config [-p port -l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program); +} + +/* used for reaping zombie processes */ +void sigchld_handler(int s) +{ + while (wait(NULL) > 0); + connections--; +} + +int handle_connection(int new_sock, struct hostent *local, struct sockaddr_in remote, uint16_t op_port) +{ + int retval = 0; + int i; + char islocal = 0; /* is the accepted connection local? */ + + char *cp; /* character pointer used for checking whether the connection is local */ + + unsigned char *line; /* one line of input */ + int len; /* length of the line */ + + unsigned char *http_ver; /* HTTP version of the incoming request */ + unsigned char *addr; /* destination address */ + unsigned char *port; /* destination port */ + unsigned char *header_name; /* name of a request header */ + + uint16_t portn; /* destination port converted into an integer */ + char *errtest; /* error check when converting the port into an integer */ + + ss_t ss; /* standard structure */ + unsigned char errcode; /* error code returned by the onion proxy */ + + int sop; /* socket for connecting to the onion proxy */ + struct sockaddr_in op_addr; /* onion proxy address */ + + /* for use with select() */ + fd_set mask,rmask; + int maxfd; + + unsigned char buf[1024]; /* data buffer */ + + log(LOG_DEBUG, "handle_connection() : Local address = %s.", inet_ntoa(*(struct in_addr *)local->h_addr)); + log(LOG_DEBUG, "handle_connection() : Remote address = %s.", inet_ntoa(remote.sin_addr)); + + /* first check that the connection is from the local host, otherwise it will be rejected */ + if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1")) + islocal = 1; + for (i=0; (local->h_addr_list[i] != NULL) && (!islocal); i++) + { + cp = local->h_addr_list[i]; + log(LOG_DEBUG,"handle_connection() : Checking if connection is from address %s.",inet_ntoa(*(struct in_addr *)cp)); + if (!memcmp(&remote.sin_addr, cp, sizeof(struct in_addr))) + islocal = 1; + } + + /* bypass this check for testing purposes */ + islocal = 1; + + /* reject a non-local connection */ + if (!islocal) + { + close(new_sock); + return 0; + } + + /* get the request-line */ + retval = http_get_line(new_sock, &line, &len, conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection : Malformed input or connection lost."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + close(new_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection : Received this from client : %s.", line); + + /* check the HTTP version */ + retval = http_get_version(line, &http_ver); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection : Unable to extract the HTTP version of the incoming request."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + return -1; + } + log(LOG_DEBUG,"handle_connection : Client's version is : %s.",http_ver); + if (strcmp(http_ver, HTTPAP_VERSION)) /* not supported */ + { + log(LOG_DEBUG,"handle_connection : Client's version is %s, I only support HTTP/1.0.",http_ver); + write_tout(new_sock, HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED, strlen(HTTPAP_STATUS_LINE_VERSION_NOT_SUPPORTED), conn_toutp); + return -1; + } + free((void *)http_ver); + + /* extract the destination address and port */ + retval = http_get_dest(line, &addr, &port); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection : Unable to extract destination address and port number."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + return -1; + } + if (!port) /* no destination port specified, assume the default */ + { + port = (unsigned char *)malloc(6); + if (!port) + { + log(LOG_ERR,"Insufficient memory."); + write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp); + return -1; + } + snprintf(port,6,"%u",htons(HTTPAP_DEFAULT_HTTP_PORT)); + } + else + { + log(LOG_DEBUG,"handle_connection() : Destination address is %s.",addr); + log(LOG_DEBUG,"handle_connection() : Destination port is %s.",port); + + /* conver the port to an integer */ + portn = (uint16_t)strtoul(port,&errtest,0); + if ((*port == '\0') || (*errtest != '\0')) /* port conversion was unsuccessful */ + { + log(LOG_DEBUG,"handle_connection : Unable to convert destination port."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + return -1; + } + + /* convert to network order and write back to a string */ + free((void *)port); + port = (unsigned char *)malloc(6); + if (!port) + { + log(LOG_ERR,"Insufficient memory."); + write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp); + return -1; + } + + snprintf(port,6,"%u",htons(portn)); + } + + /* create a standard structure */ + ss.version = VERSION; + ss.protocol = SS_PROTOCOL_HTTP; + ss.retry_count = 0; + ss.addr_fmt = SS_ADDR_FMT_ASCII_HOST_PORT; + + /* open a socket for connecting to the proxy */ + sop = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sop < 0) + { + log(LOG_DEBUG,"handle_connection() : Error opening socket."); + write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp); + return -1; + } + + log(LOG_DEBUG,"handle_connection() : Socket opened."); + memset((void *)&op_addr,0,sizeof(op_addr)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + op_addr.sin_family=AF_INET; + op_addr.sin_port=htons(op_port); + memcpy((void *)&op_addr.sin_addr,local->h_addr,local->h_length); + log(LOG_DEBUG,"handle_connection() : Trying to connect to %s at port %u.",inet_ntoa(*((struct in_addr *)local->h_addr)),op_port); + + /* try to connect */ + retval = connect(sop,(struct sockaddr *)&op_addr,sizeof(op_addr)); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Connection to the onion proxy failed."); + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(sop); + return -1; + } + + /* send the standard structure and the destination address+port */ + retval = write_tout(sop,(unsigned char *)&ss, sizeof(ss), conn_toutp); + if (retval < sizeof(ss)) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(sop); + return -1; + } + retval = write_tout(sop,addr,strlen(addr)+1, conn_toutp); + if (retval < strlen(addr)+1) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(sop); + return -1; + } + retval = write_tout(sop,port,strlen(port)+1, conn_toutp); + if (retval < strlen(port)+1) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(sop); + return -1; + } + + /* wait for a return code */ + retval = read_tout(sop, &errcode, 1, MSG_WAITALL, conn_toutp); + if (retval < 1) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(sop); + return -1; + } + + if (!errcode) /* onion proxy says OK */ + { + /* send the request-line */ + retval = write_tout(sop, line, strlen(line), conn_toutp); + if (retval < strlen(line)) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(new_sock); + return -1; + } + free((void *)line); + + /* read the request headers (if any) and sanitize if necessary */ + while(1) + { + retval = http_get_line(new_sock, &line, &len, conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Malformed input or connection lost."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + close(new_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Received this from client : %s.", line); + + if (len == 2) /* empty line (CRLF only) signifying the end of headers */ + { + log(LOG_DEBUG,"handle_connection() : Empty line received."); + retval = write_tout(sop,line,strlen(line),conn_toutp); + if (retval < strlen(line)) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(new_sock); + return -1; + } + free((void *)line); + break; + } + else /* process the header */ + { + retval = http_get_header_name(line, &header_name); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection : Unable to extract header name."); + write_tout(new_sock, HTTPAP_STATUS_LINE_BAD_REQUEST, strlen(HTTPAP_STATUS_LINE_BAD_REQUEST), conn_toutp); + return -1; + } + log(LOG_DEBUG,"handle_connection : Identified the header as %s.", header_name); + + /* discard the Proxy-Connection header */ + if (!strcmp(header_name,HTTPAP_HEADER_PROXY_CONNECTION)) + free((void *)line); + else if (options[Anonimize].r.i) /* did the user request anonimization? */ + { + if (!strcmp(header_name,HTTPAP_HEADER_USER_AGENT)) + free((void *)line); + else if (!strcmp(header_name, HTTPAP_HEADER_REFERER)) + free((void *)line); + else + { + retval = write_tout(sop, line, strlen(line), conn_toutp); + if (retval < strlen(line)) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(new_sock); + return -1; + } + } + } + else + { + retval = write_tout(sop, line, strlen(line), conn_toutp); + if (retval < strlen(line)) + { + write_tout(new_sock, HTTPAP_STATUS_LINE_UNAVAILABLE, strlen(HTTPAP_STATUS_LINE_UNAVAILABLE), conn_toutp); + close(new_sock); + return -1; + } + } + + free((void *)header_name); + } + } + + /* forward data in both directions until one of the principals closes it */ + /* set up for select() */ + log(LOG_DEBUG,"Header processed, forwarding data in both directions."); + FD_ZERO(&mask); + FD_ZERO(&rmask); + FD_SET(new_sock, &mask); + FD_SET(sop, &mask); + if (sop > new_sock) + maxfd = sop; + else + maxfd = new_sock; + + while(1) + { + rmask = mask; + retval = select(maxfd+1,&rmask,NULL,NULL,NULL); + if (retval < 0) + { + log(LOG_DEBUG,"handle_connection() : select() returned a negative integer"); + break; + } + + if (FD_ISSET(sop,&rmask)) /* data from the onion proxy */ + { + retval = read_tout(sop,buf,1024,0,conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG,"handle_connection : Conection to the onion proxy lost."); + close(sop); + close(new_sock); + break; + } + log(LOG_DEBUG,"handle_connection() : Received %u bytes from the onion proxy.",retval); + + retval = write_tout(new_sock, buf, retval, conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG, "handle_connection : Connection to the client lost."); + close(sop); + close(new_sock); + break; + } + } + + if (FD_ISSET(new_sock, &rmask)) + { + retval = read_tout(new_sock,buf,1024,0,conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG,"handle_connection : Conection to the client lost."); + close(sop); + close(new_sock); + break; + } + log(LOG_DEBUG,"handle_connection() : Received %u bytes from the client.",retval); + + retval = write_tout(sop, buf, retval, conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG, "handle_connection : Connection to the onion proxy lost."); + close(sop); + close(new_sock); + break; + } + } + } + + } + else + { + log(LOG_DEBUG,"handle_connection() : Onion proxy returned a non-zero error code (%d)!", errcode); + write_tout(new_sock, HTTPAP_STATUS_LINE_UNEXPECTED, strlen(HTTPAP_STATUS_LINE_UNEXPECTED), conn_toutp); + close(sop); + return -1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int retval = 0; + + char c; /* command-line option */ + + /* configuration file */ + char *conf_filename = NULL; + FILE *cf = NULL; + + struct hostent *local_host; + char local_hostname[512]; + + struct sockaddr_in local, remote; /* local and remote address info */ + + int request_sock; /* where we listen for connections */ + int new_sock; /* for accepted connections */ + + size_t sin_size; /* for accept() calls */ + + u_short p; /* http proxy port */ + u_short op_port; /* onion proxy port */ + + /* used for reaping zombie processes */ + struct sigaction sa; + + char *errtest = NULL; /* for detecting strtoul() errors */ + + /* set default listening port */ + p = htons(HTTPAP_LISTEN_PORT); + + /* deal with program arguments */ + if ((argc < 2) && (argc > 5)) /* to few or too many arguments*/ + { + print_usage(); + return -1; + } + + opterr = 0; + while ((c = getopt(argc,argv,args)) != -1) + { + switch(c) + { + case 'f': /* config file */ + conf_filename = optarg; + break; + case 'p': + p = htons((u_short)strtoul(optarg,&errtest,0)); + if (errtest == optarg) /* error */ + { + log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value."); + print_usage(); + return -1; + } + break; + case 'h': + print_usage(); + return 0; + break; + case 'l': + if (!strcmp(optarg,"emerg")) + loglevel = LOG_EMERG; + else if (!strcmp(optarg,"alert")) + loglevel = LOG_ALERT; + else if (!strcmp(optarg,"crit")) + loglevel = LOG_CRIT; + else if (!strcmp(optarg,"err")) + loglevel = LOG_ERR; + else if (!strcmp(optarg,"warning")) + loglevel = LOG_WARNING; + else if (!strcmp(optarg,"notice")) + loglevel = LOG_NOTICE; + else if (!strcmp(optarg,"info")) + loglevel = LOG_INFO; + else if (!strcmp(optarg,"debug")) + loglevel = LOG_DEBUG; + else + { + log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug."); + print_usage(); + return -1; + } + break; + case '?': + if (isprint(c)) + log(LOG_ERR,"Missing argument or unknown option '-%c'.",optopt); + else + log(LOG_ERR,"Unknown option character 'x%x'.",optopt); + print_usage(); + return -1; + break; + default: + abort(); + } + } + + /* the -f option is mandatory */ + if (conf_filename == NULL) + { + log(LOG_ERR,"You must specify a config file with the -f option. See help (-h)."); + return -1; + } + + /* load config file */ + cf = open_config(conf_filename); + if (!cf) + { + log(LOG_ERR,"Could not open configuration file %s.",conf_filename); + return -1; + } + retval = parse_config(cf,options); + if (retval) + return -1; + + if (options[OnionProxy].err != 1) + { + log(LOG_ERR,"The OnionProxy option is mandatory."); + return -1; + } + + if (options[MaxConn].err != 1) + { + log(LOG_ERR,"The MaxConn option is mandatory."); + return -1; + } + + if (options[Anonimize].err != 1) + { + log(LOG_ERR,"The Anonimize option is mandatory."); + return -1; + } + else if ((options[Anonimize].r.i != 0) && (options[Anonimize].r.i != 1)) + { + log(LOG_ERR,"The Anonimize option takes the values 1 or 0."); + return -1; + } + + if (options[ConnTimeout].err != 1) + { + conn_tout.tv_sec = HTTPAP_DEFAULT_CONN_TIMEOUT; + } + else + { + if (!options[ConnTimeout].r.i) + conn_toutp = NULL; + else + conn_tout.tv_sec = options[ConnTimeout].r.i; + } + conn_tout.tv_usec = 0; + + op_port = (u_short)options[OnionProxy].r.i; + + /* get local address so that we know where to get the onion proxy when we need it */ + retval = gethostname(local_hostname, (size_t)512); + if (retval < 0) + { + log(LOG_ERR,"Error getting local hostname"); + return -1; + } + local_host = gethostbyname(local_hostname); + if (!local_host) + { + log(LOG_ERR,"Error getting local address."); + return -1; + } + log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname); + + /* get the server up and running */ + request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (request_sock < 0) + { + log(LOG_ERR,"Error opening socket."); + return -1; + } + log(LOG_DEBUG,"Socket opened."); + memset((void *)&local,0,sizeof(local)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + local.sin_family=AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port=p; + /* bind it to the socket */ + retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local)); + if (retval < 0) + { + log(LOG_ERR,"Error binding socket to local port %d.",ntohs(p)); + return retval; + } + log(LOG_DEBUG,"Socket bound to port %d.",ntohs(p)); + /* listen for connections */ + retval = listen(request_sock,SOMAXCONN); + if (retval < 0) + { + log(LOG_ERR,"Could not listen for connections."); + return retval; + } + log(LOG_DEBUG,"Listening for connections."); + /* server should now be up and running */ + + /* install the signal handler for making sure zombie processes are killed */ + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + retval = sigaction(SIGCHLD,&sa,NULL); + if (retval < 0) + { + log(LOG_ERR,"Could not install a signal handler."); + return -1; + } + + /* main server loop */ + /* I use a forking server technique - this isn't the most efficient way to do it, + * but it is simpler. */ + while(1) + { + sin_size = sizeof(struct sockaddr_in); + new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size); + if (new_sock == -1) + { + if (errno != EINTR) + log(LOG_ERR,"Could not accept socket connection."); + else + log(LOG_DEBUG,"Interrupt received."); + continue; + } + if (connections >= options[MaxConn].r.i) + { + log(LOG_NOTICE,"Number of maximum connections reached. Rejecting incoming request."); + close(new_sock); + continue; + } + + log(LOG_DEBUG,"Accepted a connection from %s.",inet_ntoa(remote.sin_addr)); + connections++; + + if (!fork()) /* this is the child process */ + { + close(request_sock); /* the child doesn't need the request socket anymore */ + + /* Main logic of httpap. */ + retval = handle_connection(new_sock, local_host, remote, op_port); + /* End main logic */ + + exit(retval); /* done, exit */ + } + + close(new_sock); /* don't need this anymore */ + } + + return retval; + +} + diff --git a/src/httpap/httpap.h b/src/httpap/httpap.h new file mode 100644 index 000000000..d15516141 --- /dev/null +++ b/src/httpap/httpap.h @@ -0,0 +1,33 @@ +/** + * http.h + * HTTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/03/11 00:21:53 mp292 + * Coding completed. Pending testing. + * + */ + +#ifndef __HTTPAP_H + +#define __HTTPAP_H + +/* default listening port */ +#define HTTPAP_LISTEN_PORT 8080 + +/* default SMTP port */ +#define HTTPAP_DEFAULT_HTTP_PORT 80 + +/* default connection timeout */ +#define HTTPAP_DEFAULT_CONN_TIMEOUT 120; /* 120s */ + +#endif + diff --git a/src/op/Makefile b/src/op/Makefile new file mode 100644 index 000000000..d1732f99e --- /dev/null +++ b/src/op/Makefile @@ -0,0 +1,285 @@ +SRC=args.c config.c op.c routers.c auth.c ss.c buffers.c crypto.c +OBJ=${SRC:.c=.o} +PROGS=op +LIB=-L/usr/local/ssl/lib +LIBS=-lcrypto +INCLUDE = -I/usr/local/ssl/include + +CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb +LDFLAGS = $(LIB) $(LIBS) + +all: ${PROGS} + +clean: + rm -f *.o ${PROGS} + +depend: + makedepend -- ${CFLAGS} -- ${SRC} + +${PROGS}: ${OBJ} + $(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/onion.o ../common/utils.o ../common/cell.o ../common/scheduler.o + +# DO NOT DELETE + + +args.o: args.h ../common/log.h /usr/include/alloca.h +args.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +args.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h +args.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +args.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +args.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +args.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +args.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +args.o: /usr/include/bits/wordsize.h /usr/include/ctype.h +args.o: /usr/include/endian.h /usr/include/features.h /usr/include/_G_config.h +args.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h +args.o: /usr/include/libio.h /usr/include/stdio.h /usr/include/stdlib.h +args.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +args.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +args.o: /usr/include/sys/sysmacros.h /usr/include/sys/types.h +args.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h +args.o: /usr/include/xlocale.h +args.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +args.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +auth.o: auth.h ../common/log.h /usr/include/alloca.h /usr/include/asm/socket.h +auth.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +auth.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +auth.o: /usr/include/bits/environments.h /usr/include/bits/in.h +auth.o: /usr/include/bits/local_lim.h /usr/include/bits/posix1_lim.h +auth.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +auth.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +auth.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +auth.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +auth.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +auth.o: /usr/include/bits/types.h /usr/include/bits/uio.h +auth.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +auth.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +auth.o: /usr/include/bits/xopen_lim.h /usr/include/endian.h +auth.o: /usr/include/features.h /usr/include/_G_config.h /usr/include/gconv.h +auth.o: /usr/include/getopt.h /usr/include/gnu/stubs.h /usr/include/libio.h +auth.o: /usr/include/limits.h /usr/include/linux/limits.h +auth.o: /usr/include/netinet/in.h /usr/include/openssl/bio.h +auth.o: /usr/include/openssl/bn.h /usr/include/openssl/crypto.h +auth.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +auth.o: /usr/include/openssl/rand.h /usr/include/openssl/rsa.h +auth.o: /usr/include/openssl/safestack.h /usr/include/openssl/stack.h +auth.o: /usr/include/openssl/symhacks.h /usr/include/stdint.h +auth.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +auth.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +auth.o: /usr/include/sys/select.h /usr/include/sys/socket.h +auth.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +auth.o: /usr/include/sys/types.h /usr/include/sys/uio.h /usr/include/time.h +auth.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h +auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +auth.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h +buffers.o: buffers.h ../common/cell.h ../common/log.h ../common/onion.h +buffers.o: ../common/routent.h ../common/version.h crypto.h op.h +buffers.o: /usr/include/alloca.h /usr/include/bits/confname.h +buffers.o: /usr/include/bits/endian.h /usr/include/bits/environments.h +buffers.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h +buffers.o: /usr/include/bits/sched.h /usr/include/bits/select.h +buffers.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h +buffers.o: /usr/include/bits/time.h /usr/include/bits/types.h +buffers.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +buffers.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +buffers.o: /usr/include/endian.h /usr/include/features.h +buffers.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +buffers.o: /usr/include/gnu/stubs.h /usr/include/libio.h +buffers.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +buffers.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +buffers.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +buffers.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +buffers.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +buffers.o: /usr/include/openssl/evp.h /usr/include/openssl/md2.h +buffers.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +buffers.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +buffers.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +buffers.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +buffers.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +buffers.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +buffers.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +buffers.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h +buffers.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +buffers.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +buffers.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h +buffers.o: /usr/include/sys/time.h /usr/include/sys/types.h +buffers.o: /usr/include/time.h /usr/include/unistd.h /usr/include/wchar.h +buffers.o: /usr/include/xlocale.h +buffers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +buffers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +config.o: ../common/config.h ../common/log.h config.h +config.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +config.o: /usr/include/bits/stdio_lim.h /usr/include/bits/types.h +config.o: /usr/include/bits/wchar.h /usr/include/features.h +config.o: /usr/include/_G_config.h /usr/include/gconv.h +config.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/stdio.h +config.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +config.o: /usr/include/sys/syslog.h /usr/include/wchar.h +config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +config.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +crypto.o: ../common/cell.h ../common/log.h ../common/onion.h +crypto.o: ../common/routent.h ../common/version.h crypto.h +crypto.o: /usr/include/alloca.h /usr/include/asm/errno.h +crypto.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +crypto.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +crypto.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h +crypto.o: /usr/include/bits/sched.h /usr/include/bits/select.h +crypto.o: /usr/include/bits/sigset.h /usr/include/bits/stdio_lim.h +crypto.o: /usr/include/bits/time.h /usr/include/bits/types.h +crypto.o: /usr/include/bits/waitflags.h /usr/include/bits/waitstatus.h +crypto.o: /usr/include/bits/wchar.h /usr/include/bits/wordsize.h +crypto.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +crypto.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +crypto.o: /usr/include/gnu/stubs.h /usr/include/libio.h +crypto.o: /usr/include/linux/errno.h /usr/include/malloc.h +crypto.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +crypto.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +crypto.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +crypto.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +crypto.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +crypto.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h +crypto.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h +crypto.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +crypto.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +crypto.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +crypto.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +crypto.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +crypto.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +crypto.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +crypto.o: /usr/include/stdint.h /usr/include/stdio.h /usr/include/stdlib.h +crypto.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +crypto.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +crypto.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h +crypto.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/time.h +crypto.o: /usr/include/unistd.h /usr/include/wchar.h /usr/include/xlocale.h +crypto.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +crypto.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +op.o: args.h auth.h buffers.h ../common/cell.h ../common/config.h +op.o: ../common/log.h ../common/onion.h ../common/routent.h +op.o: ../common/scheduler.h ../common/ss.h ../common/utils.h +op.o: ../common/version.h config.h crypto.h op.h routers.h ss.h +op.o: /usr/include/alloca.h /usr/include/arpa/inet.h /usr/include/asm/errno.h +op.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h +op.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +op.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +op.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +op.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h +op.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h +op.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +op.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h +op.o: /usr/include/bits/sched.h /usr/include/bits/select.h +op.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +op.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h +op.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h +op.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h +op.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h +op.o: /usr/include/bits/time.h /usr/include/bits/types.h +op.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h +op.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +op.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +op.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h +op.o: /usr/include/features.h /usr/include/_G_config.h /usr/include/gconv.h +op.o: /usr/include/getopt.h /usr/include/gnu/stubs.h /usr/include/libio.h +op.o: /usr/include/limits.h /usr/include/linux/errno.h +op.o: /usr/include/linux/limits.h /usr/include/netdb.h +op.o: /usr/include/netinet/in.h /usr/include/openssl/asn1.h +op.o: /usr/include/openssl/bio.h /usr/include/openssl/blowfish.h +op.o: /usr/include/openssl/bn.h /usr/include/openssl/buffer.h +op.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +op.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +op.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +op.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h +op.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h +op.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +op.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +op.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +op.o: /usr/include/openssl/pem2.h /usr/include/openssl/pem.h +op.o: /usr/include/openssl/pkcs7.h /usr/include/openssl/rand.h +op.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +op.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +op.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +op.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +op.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h +op.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h +op.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +op.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +op.o: /usr/include/sys/resource.h /usr/include/sys/select.h +op.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h +op.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h +op.o: /usr/include/sys/time.h /usr/include/sys/types.h +op.o: /usr/include/sys/ucontext.h /usr/include/sys/uio.h /usr/include/sys/un.h +op.o: /usr/include/sys/wait.h /usr/include/time.h /usr/include/ucontext.h +op.o: /usr/include/unistd.h /usr/include/wait.h /usr/include/wchar.h +op.o: /usr/include/xlocale.h +op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +op.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h +routers.o: ../common/cell.h ../common/config.h ../common/log.h +routers.o: ../common/routent.h ../common/utils.h routers.h +routers.o: /usr/include/alloca.h /usr/include/asm/errno.h +routers.o: /usr/include/asm/socket.h /usr/include/asm/sockios.h +routers.o: /usr/include/bits/byteswap.h /usr/include/bits/confname.h +routers.o: /usr/include/bits/endian.h /usr/include/bits/environments.h +routers.o: /usr/include/bits/errno.h /usr/include/bits/in.h +routers.o: /usr/include/bits/local_lim.h /usr/include/bits/netdb.h +routers.o: /usr/include/bits/posix1_lim.h /usr/include/bits/posix2_lim.h +routers.o: /usr/include/bits/posix_opt.h /usr/include/bits/pthreadtypes.h +routers.o: /usr/include/bits/sched.h /usr/include/bits/select.h +routers.o: /usr/include/bits/siginfo.h /usr/include/bits/sigset.h +routers.o: /usr/include/bits/sockaddr.h /usr/include/bits/socket.h +routers.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +routers.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +routers.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +routers.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +routers.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +routers.o: /usr/include/_G_config.h /usr/include/gconv.h /usr/include/getopt.h +routers.o: /usr/include/gnu/stubs.h /usr/include/libio.h /usr/include/limits.h +routers.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +routers.o: /usr/include/netdb.h /usr/include/netinet/in.h +routers.o: /usr/include/openssl/asn1.h /usr/include/openssl/bio.h +routers.o: /usr/include/openssl/blowfish.h /usr/include/openssl/bn.h +routers.o: /usr/include/openssl/buffer.h /usr/include/openssl/cast.h +routers.o: /usr/include/openssl/crypto.h /usr/include/openssl/des.h +routers.o: /usr/include/openssl/dh.h /usr/include/openssl/dsa.h +routers.o: /usr/include/openssl/e_os2.h /usr/include/openssl/err.h +routers.o: /usr/include/openssl/evp.h /usr/include/openssl/lhash.h +routers.o: /usr/include/openssl/md2.h /usr/include/openssl/md4.h +routers.o: /usr/include/openssl/md5.h /usr/include/openssl/objects.h +routers.o: /usr/include/openssl/obj_mac.h /usr/include/openssl/opensslconf.h +routers.o: /usr/include/openssl/opensslv.h /usr/include/openssl/pem2.h +routers.o: /usr/include/openssl/pem.h /usr/include/openssl/pkcs7.h +routers.o: /usr/include/openssl/rc2.h /usr/include/openssl/rc4.h +routers.o: /usr/include/openssl/ripemd.h /usr/include/openssl/rsa.h +routers.o: /usr/include/openssl/safestack.h /usr/include/openssl/sha.h +routers.o: /usr/include/openssl/stack.h /usr/include/openssl/symhacks.h +routers.o: /usr/include/openssl/x509.h /usr/include/openssl/x509_vfy.h +routers.o: /usr/include/rpc/netdb.h /usr/include/stdint.h /usr/include/stdio.h +routers.o: /usr/include/stdlib.h /usr/include/string.h +routers.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +routers.o: /usr/include/sys/select.h /usr/include/sys/syslog.h +routers.o: /usr/include/sys/sysmacros.h /usr/include/sys/timeb.h +routers.o: /usr/include/sys/time.h /usr/include/sys/types.h +routers.o: /usr/include/sys/un.h /usr/include/time.h /usr/include/unistd.h +routers.o: /usr/include/wchar.h /usr/include/xlocale.h +routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +routers.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h +ss.o: ../common/log.h ../common/ss.h ../common/utils.h ../common/version.h +ss.o: ss.h /usr/include/bits/confname.h /usr/include/bits/endian.h +ss.o: /usr/include/bits/environments.h /usr/include/bits/posix_opt.h +ss.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +ss.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +ss.o: /usr/include/bits/sockaddr.h /usr/include/bits/time.h +ss.o: /usr/include/bits/types.h /usr/include/bits/wordsize.h +ss.o: /usr/include/endian.h /usr/include/features.h /usr/include/getopt.h +ss.o: /usr/include/gnu/stubs.h /usr/include/malloc.h /usr/include/string.h +ss.o: /usr/include/sys/cdefs.h /usr/include/syslog.h /usr/include/sys/select.h +ss.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +ss.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/sys/un.h +ss.o: /usr/include/time.h /usr/include/unistd.h /usr/include/xlocale.h +ss.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +ss.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h diff --git a/src/op/args.c b/src/op/args.c new file mode 100644 index 000000000..6e3a4600f --- /dev/null +++ b/src/op/args.c @@ -0,0 +1,121 @@ +/** + * args.c + * Routines for processing command-line arguments. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/01/26 22:08:40 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2001/12/14 11:26:23 badbytes + * Tested + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ + +#include <unistd.h> +#include <ctype.h> + +#include "../common/log.h" +#include "args.h" + +/* prints help on using op */ +void print_usage() +{ + char *program = "op"; + + printf("\n%s - Onion Proxy for Onion Routing.\nUsage : %s -f config -p port [-l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program); +} + +/* get command-line arguments */ +int getargs(int argc, char *argv[], char *args, unsigned short *p, char **conf_filename, int *loglevel) +{ + char c; /* next option character */ + char *errtest = NULL; /* for detecting strtoul() errors */ + int gotf=0; int gotp=0; + + if ((!args) || (!conf_filename) || (!loglevel)) /* invalid parameters */ + return -1; + + while ((c = getopt(argc,argv,args)) != -1) + { + switch(c) + { + case 'f': /* config file */ + *conf_filename = optarg; + gotf=1; + break; + case 'p': + *p = (u_short)strtoul(optarg,&errtest,0); + if (errtest == optarg) /* error */ + { + log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value. See help (-h)."); + return -1; + } + gotp=1; + break; + case 'h': + print_usage(); + exit(0); + case 'l': + if (!strcmp(optarg,"emerg")) + *loglevel = LOG_EMERG; + else if (!strcmp(optarg,"alert")) + *loglevel = LOG_ALERT; + else if (!strcmp(optarg,"crit")) + *loglevel = LOG_CRIT; + else if (!strcmp(optarg,"err")) + *loglevel = LOG_ERR; + else if (!strcmp(optarg,"warning")) + *loglevel = LOG_WARNING; + else if (!strcmp(optarg,"notice")) + *loglevel = LOG_NOTICE; + else if (!strcmp(optarg,"info")) + *loglevel = LOG_INFO; + else if (!strcmp(optarg,"debug")) + *loglevel = LOG_DEBUG; + else + { + log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug."); + print_usage(); + return -1; + } + break; + case '?': + if (isprint(c)) + log(LOG_ERR,"Missing argument or unknown option '-%c'. See help (-h).",optopt); + else + log(LOG_ERR,"Unknown option character 'x%x'. See help (-h).",optopt); + print_usage(); + return -1; + break; + default: + return -1; + } + } + + /* the -f option is mandatory */ + if (!gotf) + { + log(LOG_ERR,"You must specify a config file with the -f option. See help (-h)."); + return -1; + } + + /* the -p option is mandatory */ + if (!gotp) + { + log(LOG_ERR,"You must specify a port with the -p option. See help (-h)."); + return -1; + } + + return 0; +} diff --git a/src/op/args.h b/src/op/args.h new file mode 100644 index 000000000..0d693efab --- /dev/null +++ b/src/op/args.h @@ -0,0 +1,41 @@ +/** + * args.h + * Routines for processing command-line arguments. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.4 2002/01/26 22:22:09 mp292 + * Prevented duplicate definitions. + * + * Revision 1.3 2002/01/26 22:08:40 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2001/12/14 11:26:23 badbytes + * Tested + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ + +#ifndef __ARGS_H + +#define __ARGS_H + +#include <stdlib.h> +#include <stdio.h> + +/* print help */ +void print_usage(); + +/* get command-line arguments */ +int getargs(int argc,char *argv[], char *args, unsigned short *p, char **conf_filename, int *loglevel); + +#endif diff --git a/src/op/auth.c b/src/op/auth.c new file mode 100644 index 000000000..3a3f40ce8 --- /dev/null +++ b/src/op/auth.c @@ -0,0 +1,85 @@ +/** + * auth.h + * Key exchange with an onion router. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/03/28 11:00:57 badbytes + * Key exchange with an onion router. + * + */ +#include <openssl/rand.h> +#include <string.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> + +#include "../common/log.h" + +#include "auth.h" + +/* send session keys and bandwidth info to the router */ +int send_auth(int or_sock, uint32_t bandwidth, RSA *pkey, unsigned char *f_session_key, unsigned char *b_session_key) +{ + int retval; + int x; + unsigned char message[20]; /* bandwidth(32bits), forward key(64bits), backward key(64bits) */ + unsigned char cipher[128]; + if ((or_sock <= 0) || (bandwidth <= 0) || !pkey || !f_session_key || !b_session_key) /* invalid parameters */ + return -1; + + bandwidth = htonl(bandwidth); /* convert to network order */ + + /* generate the session keys */ + retval = RAND_bytes(f_session_key, 8); + if (!retval) + { + log(LOG_ERR,"Not enough randomness to generate a session key."); + return -1; + } + retval = RAND_bytes(b_session_key, 8); + if (!retval) + { + log(LOG_ERR,"Not enough randomness to generate a session key."); + return -1; + } + + /* compose the message */ + memcpy((void *)message, (void *)&bandwidth, 4); + memcpy((void *)(message + 4), (void *)f_session_key, 8); + memcpy((void *)(message + 12), (void *)b_session_key, 8); + printf("f_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",f_session_key[x]); + } + printf("\nb_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",b_session_key[x]); + } + printf("\n"); + + /* encrypt with RSA */ + retval = RSA_public_encrypt(20, message, cipher, pkey, RSA_PKCS1_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Public key encryption failed."); + return -1; + } + + /* send the ciphertext */ + retval = send(or_sock, cipher, 128, 0); + if (retval < 128) + { + log(LOG_ERR,"Connection to router lost while exchanging session keys."); + return -1; + } + + return 0; +} diff --git a/src/op/auth.h b/src/op/auth.h new file mode 100644 index 000000000..97be419bf --- /dev/null +++ b/src/op/auth.h @@ -0,0 +1,23 @@ +/** + * auth.h + * Key exchange with an onion router. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/03/28 11:00:57 badbytes + * Key exchange with an onion router. + * + */ + +#include <openssl/rsa.h> +#include <stdint.h> + +/* send session keys and bandwidth info to the router */ +int send_auth(int or_sock, uint32_t bandwidth, RSA *pkey, unsigned char *f_session_key, unsigned char *b_session_key); diff --git a/src/op/buffers.c b/src/op/buffers.c new file mode 100644 index 000000000..39a4e442d --- /dev/null +++ b/src/op/buffers.c @@ -0,0 +1,146 @@ +/** + * buffers.c + * Buffers. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + + +#include <unistd.h> +#include <openssl/evp.h> + +#include "../common/cell.h" +#include "../common/log.h" + +#include "buffers.h" +#include "crypto.h" +#include "op.h" + +int buffer_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen) +{ + int retval; + int i; + cell_t *cellbuf; + cell_t *c; + size_t cellbuflen; + size_t cells; + unsigned char *tmpbuf; /* temporary buffer for realloc() operations */ + + if (!buf || !outbuf || !outbuflen) /* invalid parameters */ + return -1; + + /* split the plaintext into DATA cells */ + retval = pack_data(aci,buf, buflen, (unsigned char **)&cellbuf, &cellbuflen); + if (retval == -1) + { + log(LOG_DEBUG,"buffer_data() : Could not pack data into cells."); + return -1; + } + log(LOG_DEBUG,"buffer_data() : DATA cells created."); + + cells = cellbuflen/(sizeof(cell_t)); + /* encrypt the cells */ + for (i=0; i<cells; i++) + { + c = cellbuf+i; + /* encrypt the payload length */ + retval = crypt_f((unsigned char *)&c->length, 1, cpath, cpathlen); + if (retval == -1) + { + log(LOG_ERR,"Could not encrypt the payload length of a DATA cell."); + free((void *)cellbuf); + return -1; + } + /* encrypt the payload */ + retval = crypt_f((unsigned char *)c->payload, CELL_PAYLOAD_SIZE, cpath, cpathlen); + if (retval == -1) + { + log(LOG_ERR,"Could not encrypt the payload of a DATA cell."); + free((void *)cellbuf); + return -1; + } + } + + /* now copy the cells into the output buffer */ + if (*outbuflen-*outbuf_dataoffset-*outbuf_datalen < cellbuflen) /* increase the buffer size if necessary */ + { + /* allocate a new buffer (in OP_DEFAULT_BUFSIZE chunks)*/ + tmpbuf = (unsigned char *)malloc(((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1)*OP_DEFAULT_BUFSIZE); + if (!tmpbuf) + { + log(LOG_ERR,"Error allocating memory."); + free((void *)cellbuf); + return -1; + } + /* copy old data to the new buffer */ + memcpy((void *)tmpbuf,(void *)(*outbuf+*outbuf_dataoffset),*outbuf_datalen); + /* replace the old buffer with the new one */ + if (*outbuf) + free((void *)*outbuf); + *outbuf = tmpbuf; + *outbuflen = ((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1) * OP_DEFAULT_BUFSIZE; + *outbuf_dataoffset = 0; + } + memcpy((void *)(*outbuf + *outbuf_dataoffset + *outbuf_datalen), (void *)cellbuf, cellbuflen); + *outbuf_datalen += cellbuflen; + + return 0; +} + +int buffer_create(uint16_t aci, unsigned char *onion, size_t onionlen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen) +{ + int retval; + cell_t *cellbuf; + size_t cells; + size_t cellbuflen; + unsigned char *tmpbuf; /* temporary buffer for realloc() operations */ + + if (!onion || !outbuf || !outbuflen || !outbuf_dataoffset || !outbuf_datalen) /* invalid parameters */ + return -1; + + retval = pack_create(aci,onion, onionlen, (unsigned char **)&cellbuf, &cellbuflen); + if (retval == -1) + { + log(LOG_DEBUG,"buffer_create() : Could not pack the onion into cells."); + return -1; + } + log(LOG_DEBUG,"buffer_create() : CREATE cells created."); + + cells = cellbuflen/(sizeof(cell_t)); + + /* now copy the cells into the output buffer */ + if (*outbuflen-*outbuf_dataoffset-*outbuf_datalen < cellbuflen) /* increase the buffer size if necessary */ + { + /* allocate a new buffer (in OP_DEFAULT_BUFSIZE chunks)*/ + tmpbuf = (unsigned char *)malloc(((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1)*OP_DEFAULT_BUFSIZE); + if (!tmpbuf) + { + log(LOG_ERR,"Error allocating memory."); + free((void *)cellbuf); + return -1; + } + /* copy old data to the new buffer */ + memcpy((void *)tmpbuf,(void *)(*outbuf+*outbuf_dataoffset),*outbuf_datalen); + /* replace the old buffer with the new one */ + if (*outbuf) + free((void *)*outbuf); + *outbuf = tmpbuf; + *outbuflen = ((cellbuflen+*outbuf_datalen)/OP_DEFAULT_BUFSIZE+1) * OP_DEFAULT_BUFSIZE; + *outbuf_dataoffset = 0; + } + memcpy((void *)(*outbuf + *outbuf_dataoffset + *outbuf_datalen), (void *)cellbuf, cellbuflen); + *outbuf_datalen += cellbuflen; + + return 0; +} diff --git a/src/op/buffers.h b/src/op/buffers.h new file mode 100644 index 000000000..a161e0810 --- /dev/null +++ b/src/op/buffers.h @@ -0,0 +1,25 @@ +/** + * buffers.h + * Buffers. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + + +#include <stdint.h> + +#include "../common/onion.h" + +int buffer_data(uint16_t aci, unsigned char *buf, size_t buflen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen); +int buffer_create(uint16_t aci, unsigned char *onion, size_t onionlen, unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, crypt_path_t **cpath, size_t cpathlen); diff --git a/src/op/config.c b/src/op/config.c new file mode 100644 index 000000000..33e5a394a --- /dev/null +++ b/src/op/config.c @@ -0,0 +1,49 @@ +/** + * config.c + * Routines for loading the configuration file. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/04/02 14:28:01 badbytes + * Final finishes. + * + * Revision 1.2 2002/01/26 22:09:53 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ + +#include "config.h" +#include "../common/log.h" + +/* loads the configuration file */ +int getconfig(char *conf_filename, config_opt_t *options) +{ + FILE *cf = NULL; + int retval = 0; + + if ((!conf_filename) || (!options)) + return -1; + + /* load config file */ + cf = open_config(conf_filename); + if (!cf) + { + log(LOG_ERR,"Could not open configuration file %s.",conf_filename); + return -1; + } + retval = parse_config(cf,options); + if (retval) + return -1; + + return 0; +} diff --git a/src/op/config.h b/src/op/config.h new file mode 100644 index 000000000..190296e76 --- /dev/null +++ b/src/op/config.h @@ -0,0 +1,30 @@ +/** + * config.h + * Routines for loading the configuration file. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.4 2002/04/02 14:28:01 badbytes + * Final finishes. + * + * Revision 1.3 2002/01/26 22:55:11 mp292 + * *** empty log message *** + * + * Revision 1.2 2002/01/26 22:22:09 mp292 + * Prevented duplicate definitions. + * + * Revision 1.1 2001/12/13 15:15:10 badbytes + * Started coding the onion proxy. + * + */ +#include "../common/config.h" + +/* loads the configuration file */ +int getconfig(char *filename, config_opt_t *options); diff --git a/src/op/crypto.c b/src/op/crypto.c new file mode 100644 index 000000000..effa768b8 --- /dev/null +++ b/src/op/crypto.c @@ -0,0 +1,104 @@ +/** + * crypto.c + * Crypto calls. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + +#include <malloc.h> +#include <unistd.h> +#include <openssl/err.h> +#include <openssl/evp.h> + +#include "../common/log.h" + +#include "crypto.h" + +int crypt_f(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen) +{ + int i=0; + int retval = 0; + unsigned char *ciphertext = NULL; + crypt_path_t *thishop; + + /* allocate the ciphertext buffer */ + ciphertext = (unsigned char *)malloc(buflen); + if (!ciphertext) + { + log(LOG_ERR,"Error allocating memory."); + return -1; + } + + for (i=0; i < cpathlen; i++) /* moving from last to first hop + * Remember : cpath is in reverse order, i.e. last hop first + */ + { + log(LOG_DEBUG,"crypt_f() : Processing hop %u",cpathlen-i); + thishop = cpath[i]; + + /* encrypt */ + retval = EVP_EncryptUpdate(&thishop->f_ctx,ciphertext, &buflen, buf, buflen); + if (!retval) /* error */ + { + log(LOG_ERR,"Error performing encryption:%s",ERR_reason_error_string(ERR_get_error())); + free(ciphertext); + return -1; + } + + /* copy ciphertext back to buf */ + memcpy((void *)buf,(void *)ciphertext,buflen); + } + free((void *)ciphertext); + + return 0; +} + +int crypt_b(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen) +{ + int i=0; + int retval=0; + unsigned char *plaintext=NULL; + crypt_path_t *thishop; + + /* allocate the plaintext buffer */ + plaintext = (unsigned char *)malloc(buflen); + if (!plaintext) + { + log(LOG_ERR,"Error allocating memory."); + return -1; + } + + for (i=cpathlen-1; i >= 0; i--) /* moving from first to last hop + * Remember : cpath is in reverse order, i.e. last hop first + */ + { + thishop = cpath[i]; + + /* encrypt */ + retval = EVP_DecryptUpdate(&thishop->b_ctx,plaintext, &buflen, buf, buflen); + if (!retval) /* error */ + { + log(LOG_ERR,"Error performing decryption:%s",ERR_reason_error_string(ERR_get_error())); + free(plaintext); + return -1; + } + + /* copy plaintext back to buf */ + memcpy((void *)buf,(void *)plaintext,buflen); + } + + free(plaintext); + + return 0; +} diff --git a/src/op/crypto.h b/src/op/crypto.h new file mode 100644 index 000000000..17c61e669 --- /dev/null +++ b/src/op/crypto.h @@ -0,0 +1,23 @@ +/** + * crypto.h + * Crypto calls. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + +#include "../common/onion.h" + +int crypt_f(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen); + +int crypt_b(unsigned char *buf, size_t buflen, crypt_path_t **cpath, size_t cpathlen); diff --git a/src/op/op.c b/src/op/op.c new file mode 100644 index 000000000..06ae99950 --- /dev/null +++ b/src/op/op.c @@ -0,0 +1,916 @@ +/** + * op.c + * Onion Proxy + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.37 2002/06/14 20:45:56 mp292 + * *** empty log message *** + * + * Revision 1.36 2002/04/02 14:28:01 badbytes + * Final finishes. + * + * Revision 1.35 2002/04/02 10:21:07 badbytes + * *** empty log message *** + * + * Revision 1.34 2002/03/29 08:35:12 badbytes + * Link encryption is now done on the entire cell header for simplicity. + * + * Revision 1.33 2002/03/28 17:57:59 badbytes + * Bug fix. + * + * Revision 1.32 2002/03/28 11:01:43 badbytes + * Now does link-encryption and link-padding. + * + * Revision 1.31 2002/03/12 23:40:32 mp292 + * Started on op<->router connection padding. + * + * Revision 1.30 2002/01/29 02:22:58 mp292 + * Put a timeout on all network I/O. + * + * Revision 1.29 2002/01/26 23:01:55 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.28 2002/01/18 20:42:06 mp292 + * Reflects changes to common/onion.c:new_route() + * + * Revision 1.27 2002/01/17 23:49:15 mp292 + * Added size of public key to one of the debugging messages. + * + * Revision 1.26 2002/01/16 23:01:58 mp292 + * First phase of system testing completed (main functionality). + * + * Revision 1.25 2002/01/16 17:01:56 mp292 + * There was a bug in checking whether the incoming connection is local or not. + * + * Revision 1.24 2002/01/16 16:09:32 mp292 + * A pointer cast was missing. Fixed. + * + * Revision 1.23 2002/01/14 13:05:39 badbytes + * System testing in progress. + * + * Revision 1.22 2002/01/11 15:47:25 badbytes + * *** empty log message *** + * + * Revision 1.21 2002/01/09 09:18:35 badbytes + * Now handles EINTR error from accept(). + * + * Revision 1.20 2002/01/09 07:57:18 badbytes + * Ciphers got out of sync, hopefully fixed. + * + * Revision 1.19 2001/12/19 11:15:41 badbytes + * Corrected AF_INET to PF_INET in socket() calls. + * + * Revision 1.18 2001/12/19 08:38:38 badbytes + * Zombie problems hopefully fixed. + * + * Revision 1.17 2001/12/19 08:29:29 badbytes + * Tested. Still some problems with zombies in both op and smtpap. + * + * Revision 1.16 2001/12/18 15:51:58 badbytes + * Connection with onion router established. Will continue testing tomorrow. + * + * Revision 1.15 2001/12/18 14:12:05 badbytes + * Tested up to connect() to onion router. + * + * Revision 1.14 2001/12/18 12:21:11 badbytes + * Forgot to convert port to network order :-) + * + * Revision 1.13 2001/12/18 11:52:27 badbytes + * Coding completed. Proceeding to test. + * + * Revision 1.12 2001/12/17 13:36:15 badbytes + * Writing handle_connection() + * + * Revision 1.11 2001/12/17 08:42:44 badbytes + * getrouters() now returns an array of routers and also writes the length of the array to an int*. + * + * Revision 1.10 2001/12/14 14:45:13 badbytes + * Added range checking for CoinWeight. + * + * Revision 1.9 2001/12/14 14:08:50 badbytes + * getrouters() now returns an array of pointers rather than a linked list + * + * Revision 1.8 2001/12/14 13:31:20 badbytes + * *** empty log message *** + * + * Revision 1.7 2001/12/14 13:17:12 badbytes + * Corrected references to types.h + * + * Revision 1.6 2001/12/14 13:00:30 badbytes + * Changed my mind, routers.c and routers.h stay where they are :-) + * + * Revision 1.5 2001/12/14 12:56:55 badbytes + * Moved routers* to common/ + * + * Revision 1.4 2001/12/14 12:42:50 badbytes + * References to onion.h and onion.o now point to the common/ directory. + * + * Revision 1.3 2001/12/14 12:40:26 badbytes + * Was being stupid - op doesn't need a private key!! Have removed ... + * + * Revision 1.2 2001/12/14 11:27:16 badbytes + * Configuration and server setup completed. + * + * Revision 1.1 2001/12/13 15:15:11 badbytes + * Started coding the onion proxy. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> + +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + +#include "../common/log.h" +#include "../common/version.h" +#include "../common/onion.h" +#include "../common/utils.h" +#include "../common/cell.h" +#include "../common/scheduler.h" + +#include "config.h" +#include "routers.h" +#include "args.h" +#include "auth.h" +#include "op.h" +#include "ss.h" +#include "crypto.h" +#include "buffers.h" + +/* global variables */ + +/* default logging threshold */ +int loglevel = LOG_ERR; +struct timeval conn_tout; +struct timeval *conn_toutp = &conn_tout; + +/* valid command-line options */ +static char *args = "hf:p:l:"; + +/* valid config file options */ +static config_opt_t options[] = +{ + {"RouterFile", CONFIG_TYPE_STRING, {0}, 0}, + {"CoinWeight", CONFIG_TYPE_DOUBLE, {0}, 0}, + {"MaxConn", CONFIG_TYPE_INT, {0}, 0}, + {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0}, + {"Bandwidth", CONFIG_TYPE_INT, {0}, 0}, + {0} +}; +enum opts { + RouterFile=0, CoinWeight, MaxConn, ConnTimeout, Bandwidth +}; + +int connections = 0; /* number of active connections */ + +/* local host info */ +struct hostent *local_host; +char local_hostname[512]; + +struct sockaddr_in local, remote; /* local and remote address info */ +struct sockaddr_in or_addr; /* onion router address */ + +int request_sock; /* where we listen for connections */ +int new_sock; /* for accepted connections */ +int or_sock; /* for connecting to the first onion router */ + +/* router array */ +routent_t **routerarray = NULL; +int rarray_len = 0; + +/* end of global variables */ + +void send_to_router(int s,unsigned char **outbuf, size_t *outbuflen, size_t *outbuf_dataoffset, size_t *outbuf_datalen, struct timeval *lastsend, struct timeval *interval, sched_t *scheduler, EVP_CIPHER_CTX *ctx) +{ + int retval; + int cells; + int datacells; + int paddingcells; + int i; + int x; + char *px; + struct timeval now; + cell_t cipher; + cell_t *padding; + int cipherlen; + unsigned long elapsed; + + /* calculate the number of cells that need to be sent */ + retval = gettimeofday(&now,NULL); + if (retval == -1) + { + log(LOG_ERR,"Could not get current time!"); + return; + } + + elapsed = 1000000*(now.tv_sec-lastsend->tv_sec) + now.tv_usec-lastsend->tv_usec; + + if (elapsed < 1000000) + { + cells = ((options[Bandwidth].r.i) * 512) / /* number of bytes per second, divided by two */ + (1000000/elapsed); /* fractions of second since last send */ + } + else + { + cells = ((options[Bandwidth].r.i) * 512) * /* number of bytes per second, divided by two */ + (elapsed/1000000); /* 1/fractions of second since last send */ + } + cells /= sizeof(cell_t); + + datacells = (*outbuf_datalen)/sizeof(cell_t); /* number of data cells available */ + if (datacells > cells) + datacells = cells; + paddingcells = cells - datacells; + + /* send the data cells first */ + for (i=0; i<datacells; i++) + { + /* link-encrypt the cell header */ + printf("Cell header plaintext: "); + for(x=0;x<8;x++) { + printf("%u ",*(char *)(*outbuf+*outbuf_dataoffset+x)); + } + printf("\n"); + retval = EVP_EncryptUpdate(ctx, (unsigned char *)&cipher, &cipherlen, *outbuf+*outbuf_dataoffset, 8); + if (!retval) + { + log(LOG_ERR,"Link encryption failed. Exiting."); + exit(-1); + } + printf("Cell header crypttext: "); + px = (char *)&cipher; + for(x=0;x<8;x++) { + printf("%u ",px[x]); + } + printf("\n"); + + /* copy the payload */ + memcpy((void *)cipher.payload, (void *)(*outbuf+*outbuf_dataoffset+8), CELL_PAYLOAD_SIZE); + + /* send the cell */ + log(LOG_DEBUG,"send_to_router(): Trying to send a data/create cell to router."); + retval = write_tout(s,(unsigned char *)&cipher, sizeof(cell_t), conn_toutp); + if (retval < sizeof(cell_t)) + { + log(LOG_ERR,"Connection to the router seems to be lost. Exiting."); + exit(-1); + } + *outbuf_dataoffset += sizeof(cell_t); + *outbuf_datalen -= sizeof(cell_t); + + } + + /* send padding */ + for (i=0; i<cells-datacells; i++) + { + padding = new_padding_cell(); + if (!padding) + { + log(LOG_ERR,"Memory allocation error. Exiting."); + exit(-1); + } + + /* link encrypt the cell header */ + retval = EVP_EncryptUpdate(ctx, (unsigned char *)&cipher, &cipherlen, (unsigned char *)padding, 8); + if (!retval) + { + log(LOG_ERR,"Link encryption failed. Exiting."); + exit(-1); + } + + /* copy the payload */ + memcpy((void *)cipher.payload, (void *)((unsigned char *)padding+8), CELL_PAYLOAD_SIZE); + + /* send the cell */ + log(LOG_DEBUG,"send_to_router(): Trying to send a padding cell to router."); + retval = write_tout(s, (unsigned char *)&cipher, sizeof(cell_t), conn_toutp); + if (retval < sizeof(cell_t)) + { + log(LOG_ERR,"Connection to the router seems to be lost. Exiting."); + exit(-1); + } + + free((void *)padding); + } + + /* update scheduler state, if we've sent anything to the router */ + if (cells) + { + retval = update_sched_entry(scheduler, *lastsend, *interval, now, *interval); + if (retval == -1) + { + log(LOG_ERR,"Scheduler error. Exiting."); + exit(-1); + } + memcpy((void *)lastsend,(void *)&now, sizeof(struct timeval)); + } + +} + +/* deal with a client */ +int handle_connection() +{ + int retval = 0; + int routelen = 0; /* length of the route */ + unsigned int *route = NULL; /* hops in the route as an array of indexes into rarray */ + routent_t *firsthop = NULL; + + uint32_t aci; /* ACI for this connection */ + + unsigned char *onion = NULL; /* holds the onion */ + int onionlen = 0; /* onion length in host order */ + + crypt_path_t **cpath = NULL; /* defines the crypt operations that need to be performed on incoming/outgoing data */ + char *dest_addr = NULL; /* destination address in ASCII format */ + + int dest_addrlen = 0; + char *dest_port = NULL; /* destination port in ASCII format */ + int dest_portlen = 0; + ss_t *ss; /* standard structure */ + + uint32_t router_addr_net; /* address of the first onion router in network order */ + + unsigned char inbuf[1024]; /* buffer for forwarding data between ap and or */ + + unsigned char *outbuf = NULL; /* buffer for cells which are to be transmitted to the first core onion router in the route */ + size_t outbuflen = 0; + size_t outbuf_dataoffset = 0; /* offset to the beginning of the data */ + size_t outbuf_datalen = 0; /* length of the data stored in the buffer */ + + cell_t cellbuf; + int cellbuflen = 0; + + struct timeval lastsend; /* time of last transmission to the onion router */ + struct timeval interval; /* transmission interval */ + + /* link encryption */ + unsigned char f_session_key[8]; + unsigned char f_session_iv[8] = {0,0,0,0,0,0,0,0}; + unsigned char b_session_key[8]; + unsigned char b_session_iv[8] = {0,0,0,0,0,0,0,0}; + EVP_CIPHER_CTX f_ctx; + EVP_CIPHER_CTX b_ctx; + + /* scheduler */ + sched_t *scheduler; + + /* for use with select() */ + fd_set rmask, mask; + int maxfd; + struct timeval *timeout; + + /* get the standard structure */ + retval = process_ss(new_sock, conn_toutp, &ss,&dest_addr, &dest_addrlen, &dest_port, &dest_portlen); + if (retval == -1) + { + log(LOG_ERR,"Error processing the standard structure."); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Destination = %s:%s",dest_addr,dest_port); + + /* choose a route */ + route = (unsigned int *)new_route(options[CoinWeight].r.d, routerarray,rarray_len, &routelen); + if (!route) + { + log(LOG_ERR,"Error choosing a route through the OR network."); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Chosen a route of length %u : ",routelen); + for (retval=routelen-1;retval>=0;retval--) + { + log(LOG_DEBUG,"handle_connection() : %u : %s:%u, %u",routelen-retval,(routerarray[route[retval]])->address,ntohs((routerarray[route[retval]])->port),RSA_size((routerarray[route[retval]])->pkey)); + } + + /* allocate memory for the crypt path */ + cpath = malloc(routelen * sizeof(crypt_path_t *)); + if (!cpath) + { + log(LOG_ERR,"Error allocating memory."); + free(route); + return -1; + } + /* create an onion and calculate crypto keys */ + onion = create_onion(routerarray,rarray_len,route,routelen,&onionlen,cpath); + if (!onion) + { + log(LOG_ERR,"Error creating an onion."); + free(route); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Created an onion of size %u bytes.",onionlen); + log(LOG_DEBUG,"handle_connection() : Crypt path :"); + for (retval=0;retval<routelen;retval++) + { + log(LOG_DEBUG,"handle_connection() : %u/%u",(cpath[retval])->forwf, (cpath[retval])->backf); + } + + /* connect to first onion router */ + or_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (or_sock < 0) + { + free(route); + free(onion); + free(cpath); + close(new_sock); + log(LOG_ERR,"Error creating socket."); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Socket created."); + + firsthop = routerarray[route[routelen-1]]; + memset((void *)&or_addr,0,sizeof(or_addr)); + or_addr.sin_family=AF_INET; + or_addr.sin_port=firsthop->entry_port; + router_addr_net = firsthop->addr; + memcpy(&or_addr.sin_addr,&router_addr_net,sizeof(struct sockaddr_in)); + log(LOG_DEBUG,"handle_connection() : Trying to connect to %s:%u",inet_ntoa(or_addr.sin_addr), ntohs(or_addr.sin_port)); + retval = connect(or_sock,(struct sockaddr *)&or_addr, sizeof(or_addr)); + if (retval == -1) + { + log(LOG_ERR,"Could not connect to onion router."); + free(route); + free(onion); + free(cpath); + close(or_sock); + close(new_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Connected to first onion router."); + + /* send session key and bandwidth info */ + retval = send_auth(or_sock, options[Bandwidth].r.i, firsthop->pkey, f_session_key, b_session_key); + if (retval == -1) + { + close(or_sock); + close(new_sock); + log(LOG_ERR,"Lost connection to an onion router. Exiting."); + return -1; + } + /* initialize crypto engines */ + EVP_CIPHER_CTX_init(&f_ctx); + EVP_CIPHER_CTX_init(&b_ctx); + EVP_EncryptInit(&f_ctx, EVP_des_ofb(), f_session_key, f_session_iv); + EVP_DecryptInit(&b_ctx, EVP_des_ofb(), b_session_key, b_session_iv); + + /* chose an ACI */ + do + { + retval = RAND_pseudo_bytes((unsigned char *)&aci, 2); + if (retval==-1) + { + log(LOG_ERR,"Random data generator doesn't seem to work. Exiting."); + return -1; + } + } while(!aci); /* don't allow zero ACIs */ + log(LOG_DEBUG,"handle_connection() : ACI %u chosen.",aci); + + /* initialize last time of transmission to now */ + retval = gettimeofday(&lastsend, NULL); + if (retval == -1) + { + log(LOG_ERR,"Could not get current time."); + return -1; + } + /* calculate the transmission interval */ + interval.tv_sec = 0; + interval.tv_usec = 250000/options[Bandwidth].r.i; + /* initialize the scheduler */ + scheduler = new_sched(); + if (!scheduler) + { + log(LOG_ERR,"Could not initialize scheduler."); + return -1; + } + retval = add_sched_entry(scheduler, lastsend, interval); + if (retval == -1) + { + log(LOG_ERR,"Could not initialize scheduler."); + return -1; + } + timeout = NULL; + + /* write the onion into the output buffer */ + retval = buffer_create(aci, (unsigned char *)onion, onionlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Could not buffer the onion."); + close(or_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Onion buffered for output."); + + /* send standard structure */ + log(LOG_DEBUG,"handle_connection() : Calling send_crypt ... routelen=%u, sizeof(SS) = %u",routelen,sizeof(ss_t)); + retval = buffer_data(aci, (unsigned char *)ss, sizeof(ss_t), &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure for output."); + close(or_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Buffered the standard structure header."); + retval = buffer_data(aci, dest_addr,dest_addrlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure (dest. address) for output."); + close(or_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Buffered the destination address."); + retval = buffer_data(aci, dest_port, dest_portlen, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : Could not buffer the standard structure (dest. port) for output."); + close(or_sock); + return -1; + } + log(LOG_DEBUG,"handle_connection() : Buffered the destination port."); + + + /* forward data in both directions, crypt as necessary */ + /* use select() */ + + FD_ZERO(&mask); + FD_SET(new_sock, &mask); + FD_SET(or_sock, &mask); + if (new_sock > or_sock) + maxfd = new_sock; + else + maxfd = or_sock; + + while(1) + { + rmask = mask; + + /* delete old timeout */ + if (timeout) + free((void *)timeout); + /* get the new one */ + retval = sched_trigger(scheduler, &timeout); + if (retval == -1) + { + log(LOG_DEBUG,"Scheduler error."); + break; + } + retval = select(maxfd+1,&rmask,NULL,NULL,timeout); + if (retval < 0) + { + log(LOG_DEBUG,"handle_connection() : select() returned negative integer"); + break; + } + + if (FD_ISSET(new_sock,&rmask)) + { + log(LOG_DEBUG,"handle_connection() : FD_ISSET(new_sock)"); + retval = read_tout(new_sock, inbuf, 1024, 0, conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG,"handle_connection() : Received EOF on new_sock."); + break; + } + log(LOG_DEBUG,"handle_connection() : Received %u bytes from client.",retval); + retval = buffer_data(aci, inbuf, retval, &outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, cpath, routelen); + + if (retval < 0) + { + log(LOG_DEBUG,"handle_connection() : Could not buffer data for output to OR."); + break; + } + log(LOG_DEBUG,"handle_connection() : Buffered %u bytes for output to the OR.",retval); + } + + if (FD_ISSET(or_sock, &rmask)) + { + log(LOG_DEBUG,"handle_connection() : FD_ISSET(or_sock)"); + /* read the remainder of the cell (or whatever we can get) */ + retval = read_tout(or_sock, ((unsigned char *)&cellbuf)+cellbuflen, sizeof(cell_t) - cellbuflen, 0, conn_toutp); + if (retval <= 0) + { + log(LOG_DEBUG,"handle_connection() : Received EOF on or_sock."); + break; + } + log(LOG_DEBUG,"handle_connection() : Received %u bytes from router.",retval); + cellbuflen += retval; + + if (cellbuflen == sizeof(cell_t)) /* received an entire cell */ + { + /* link decrypt the cell header */ + retval = EVP_DecryptUpdate(&b_ctx, (unsigned char *)inbuf, &cellbuflen, (unsigned char *)&cellbuf, 8); + if (!retval) + { + log(LOG_ERR,"Decryption error. Closing the connection and exiting."); + break; + } + + if (((cell_t *)inbuf)->command == CELL_PADDING) /* padding, discard */ + { + log(LOG_DEBUG,"Received a PADDING cell. Discarding."); + ; /* discard */ + } + else if (((cell_t *)inbuf)->command == CELL_DATA) /* only process DATA cells , discard otherwise */ + { + /* decrypt the payload length */ + retval = crypt_b((unsigned char *)&((cell_t *)inbuf)->length, 1, cpath, routelen); + if (retval == -1) + { + log(LOG_ERR,"Decryption error. Closing the connection and exiting."); + break; + } + + /* decrypt the payload */ + retval = crypt_b((unsigned char *)cellbuf.payload, CELL_PAYLOAD_SIZE, cpath, routelen); + if (retval == -1) + { + log(LOG_ERR,"Decryption error. Closing the connection and exiting."); + break; + } + + /* send the payload to the application proxy */ + retval = write_tout(new_sock, (unsigned char *)cellbuf.payload, ((cell_t *)inbuf)->length, conn_toutp); + if (retval < ((cell_t *)inbuf)->length) + { + log(LOG_ERR,"Connection to the application proxy seems to be lost."); + break; + } + log(LOG_DEBUG,"handle_connection() : Sent %u bytes to client.",retval); + } + else + log(LOG_DEBUG,"handle_connection() : Recived cell has incorrect command or ACI. Discarding."); + + cellbuflen = 0; /* get ready for the next cell */ + } + } + + /* send cells to the router */ + send_to_router(or_sock,&outbuf, &outbuflen, &outbuf_dataoffset, &outbuf_datalen, &lastsend, &interval, scheduler, &f_ctx); + } + + /* clean up */ + log(LOG_DEBUG,"handle_connection() : handle_connection() exiting."); + close(or_sock); + + return 0; +} + +/* used for reaping zombie processes */ +void sigchld_handler(int s) +{ + while (wait(NULL) > 0); + connections--; +} + +int main(int argc, char *argv[]) +{ + int one = 1; + int retval = 0; + + char *cp; /* temporary storage */ + int i=0; /* iteration counter */ + + char *conf_filename = NULL; /* configuration file */ + + size_t sin_size; /* for accept() calls */ + + u_short p; /* onion proxy port */ + + /* used for reaping zombie processes */ + struct sigaction sa; + + int islocal = 0; /* is the incoming connection local? */ + + struct rlimit cd_limit; /* resource limit to prevent core dumps */ + + /* prevent core dump */ + retval = getrlimit(RLIMIT_CORE, &cd_limit); + if (retval == -1) + { + log(LOG_ERR,"Could not tell the OS to prevent core dumps for the process."); + return -1; + } + cd_limit.rlim_cur = 0; + retval = setrlimit(RLIMIT_CORE, &cd_limit); + if (retval == -1) + { + log(LOG_ERR,"Could not tell the OS to prevent core dumps for the process."); + return -1; + } + + /* get command-line arguments */ + retval = getargs(argc,argv,args,&p,&conf_filename,&loglevel); + if (retval == -1) + { + log(LOG_ERR,"Error processing command-line arguments."); + exit(1); + } + + /* load config file */ + retval = getconfig(conf_filename,options); + if (retval == -1) + { + log(LOG_ERR,"Error loading configuration file."); + exit(1); + } + + if (options[RouterFile].err != 1) + { + log(LOG_ERR,"RouterFile option required, but not found."); + exit(1); + } + + if (options[CoinWeight].err == -1) + { + log(LOG_ERR,"Error reading the CoinWeight option."); + exit(1); + } + + if (options[CoinWeight].err == 0) + { + /* this is optional, so if not found, set default value */ + options[CoinWeight].r.d = OP_DEFAULT_COIN_WEIGHT; + } + else if ((options[CoinWeight].r.d < 0) || (options[CoinWeight].r.d >= 1)) + { + /* must be a value in [0,1) */ + log(LOG_ERR,"CoinWeight option must be >= 0 and < 1."); + exit(1); + } + + if (options[Bandwidth].err == 0) + { + /* optional, set to default */ + options[Bandwidth].r.i = OP_DEFAULT_BANDWIDTH; + } + else if (options[Bandwidth].r.i <= 0) + { + log(LOG_ERR,"The Bandwidth option must be an integer greater than zero."); + exit(1); + } + + if (options[ConnTimeout].err != 1) + { + conn_tout.tv_sec = OP_DEFAULT_CONN_TIMEOUT; + conn_tout.tv_usec = 0; + } + else + { + if (!options[ConnTimeout].r.i) + conn_toutp = NULL; + else + conn_tout.tv_sec = options[ConnTimeout].r.i; + conn_tout.tv_usec = 0; + } + + /* load the routers file */ + routerarray = getrouters(options[RouterFile].r.str,&rarray_len); + if (!routerarray) + { + log(LOG_ERR,"Error loading router list."); + exit(1); + } + + /* get local address so that we know where to allow connections from*/ + retval = gethostname(local_hostname, (size_t)512); + if (retval < 0) + { + log(LOG_ERR,"Error getting local hostname."); + return -1; + } + local_host = gethostbyname(local_hostname); + if (!local_host) + { + log(LOG_ERR,"Error getting local address."); + return -1; + } + log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname); + + /* get the server up and running */ + request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (request_sock < 0) + { + log(LOG_ERR,"Error opening socket."); + return -1; + } + setsockopt(request_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + log(LOG_DEBUG,"main() : Socket opened."); + + memset((void *)&local,0,sizeof(local)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + local.sin_family=AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port=htons(p); + /* bind it to the socket */ + retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local)); + if (retval < 0) + { + log(LOG_ERR,"Error binding socket to local port %d.",p); + return retval; + } + log(LOG_DEBUG,"main() : Socket bound to port %d.",p); + /* listen for connections */ + retval = listen(request_sock,SOMAXCONN); + if (retval < 0) + { + log(LOG_ERR,"Could not listen for connections."); + return retval; + } + log(LOG_DEBUG,"main() : Listening for connections."); + /* server should now be up and running */ + + /* install the signal handler for making sure zombie processes are killed */ + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + retval = sigaction(SIGCHLD,&sa,NULL); + if (retval < 0) + { + log(LOG_ERR,"Could not install a signal handler."); + return -1; + } + + /* main server loop */ + /* I use a forking server technique - this isn't the most efficient way to do it, + * but it is simpler. */ + while(1) + { + sin_size = sizeof(struct sockaddr_in); + new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size); + if (new_sock == -1) + { + if (errno != EINTR) + log(LOG_ERR,"Could not accept socket connection."); + else + log(LOG_DEBUG,"main() : Interrupt received."); + continue; + } + + if (connections == options[MaxConn].r.i) + { + close(new_sock); + log(LOG_NOTICE,"Maximum connection limit exceeded. Rejecting incoming request."); + } + connections++; + log(LOG_DEBUG,"main() : Accepted a connection from %s.",inet_ntoa(remote.sin_addr)); + + /* see if the connection is local, otherwise reject */ + /* first check that the connection is from the local host, otherwise reject */ + if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1")) + islocal=1; + for (i=0; (local_host->h_addr_list[i] != NULL) && (!islocal); i++) + { + cp = local_host->h_addr_list[i]; + if (!memcmp(&remote.sin_addr, cp,sizeof(struct in_addr))) + islocal = 1; + } + + if (!islocal) + { + log(LOG_DEBUG,"main() : Incoming connection is not local. Will reject."); + close(new_sock); + } + else + { + log(LOG_DEBUG,"main() : Incoming connection seems to be local. Will accept."); + /* fork a process to deal with the customer */ + if (!fork()) /* this is the child process */ + { + close(request_sock); /* the child doesn't need the request socket anymore */ + + /* Main logic of op. */ + retval = handle_connection(); + log(LOG_DEBUG,"main() : Handle connection returned %d.",retval); + /* End main logic */ + + exit(retval); /* done, exit */ + } + + close(new_sock); /* don't need this anymore */ + } + } + + return retval; + +} + diff --git a/src/op/op.h b/src/op/op.h new file mode 100644 index 000000000..f45d387dd --- /dev/null +++ b/src/op/op.h @@ -0,0 +1,54 @@ +/** + * op.h + * Onion Proxy + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.7 2002/03/28 11:01:43 badbytes + * Now does link-encryption and link-padding. + * + * Revision 1.6 2002/03/12 23:40:32 mp292 + * Started on op<->router connection padding. + * + * Revision 1.5 2002/01/29 02:22:58 mp292 + * Put a timeout on all network I/O. + * + * Revision 1.4 2002/01/26 23:01:55 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.3 2001/12/18 11:52:27 badbytes + * Coding completed. Proceeding to test. + * + * Revision 1.2 2001/12/17 13:36:15 badbytes + * Writing handle_connection() + * + * Revision 1.1 2001/12/13 15:15:11 badbytes + * Started coding the onion proxy. + * + */ + +#ifndef __OP_H + +#define __OP_H + +/* choosing the length of a route uses a weighted coin + * this is the default value for it */ +#define OP_DEFAULT_COIN_WEIGHT 0.8 + +/* default connection timeout */ +#define OP_DEFAULT_CONN_TIMEOUT 120 /* 120s */ + +/* default connection bandwidth */ +#define OP_DEFAULT_BANDWIDTH 1 /* 1kb/s */ + +/* default buffer size per connection */ +#define OP_DEFAULT_BUFSIZE 4096 /* 4kb */ + +#endif diff --git a/src/op/routers.c b/src/op/routers.c new file mode 100644 index 000000000..943b08cf9 --- /dev/null +++ b/src/op/routers.c @@ -0,0 +1,364 @@ +/** + * routers.c + * Routines for loading the list of routers and their public RSA keys. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.16 2002/04/02 14:28:01 badbytes + * Final finishes. + * + * Revision 1.15 2002/03/25 10:48:48 badbytes + * Added explicit dependency on <netinet/in.h>. + * + * Revision 1.14 2002/01/27 19:24:33 mp292 + * Fixed a bug in parameter checking. + * + * Revision 1.13 2002/01/26 22:19:15 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.12 2002/01/18 20:42:25 mp292 + * Slight modification to the way keys are read from the route file. + * + * Revision 1.11 2002/01/14 13:05:39 badbytes + * System testing in progress. + * + * Revision 1.10 2002/01/11 15:47:25 badbytes + * *** empty log message *** + * + * Revision 1.9 2001/12/18 15:51:58 badbytes + * Connection with onion router established. Will continue testing tomorrow. + * + * Revision 1.8 2001/12/17 13:36:15 badbytes + * Writing handle_connection() + * + * Revision 1.7 2001/12/17 08:42:45 badbytes + * getrouters() now returns an array of routers and also writes the length of the array to an int*. + * + * Revision 1.6 2001/12/14 14:08:50 badbytes + * getrouters() now returns an array of pointers rather than a linked list + * + * Revision 1.5 2001/12/14 14:05:56 badbytes + * Added routent_t** make_rarray(routent_t* list); + * + * Revision 1.4 2001/12/14 13:25:17 badbytes + * Moved back from common/ + * + * Revision 1.2 2001/12/14 11:24:57 badbytes + * Tested. + * + * Revision 1.1 2001/12/13 15:15:11 badbytes + * Started coding the onion proxy. + * + */ + +#include <openssl/pem.h> +#include <openssl/err.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> + +#include "routers.h" +#include "../common/log.h" +#include "../common/utils.h" +#include "../common/config.h" + +/* delete a list of routers from memory */ +void delete_routerlist(routent_t *list) +{ + routent_t *tmp = NULL; + + if (!list) + return; + + do + { + tmp=list->next; + free(list->address); + RSA_free(list->pkey); + free(list); + list = tmp; + } + while (list != NULL); + + return; +} + +/* create an NULL-terminated array of pointers pointing to elements of a router list */ +/* this is done in two passes through the list - inefficient but irrelevant as this is + * only done once when op/or start up */ +routent_t **make_rarray(routent_t* list, size_t *len) +{ + routent_t *tmp=NULL; + int listlen = 0; + routent_t **array=NULL; + routent_t **p=NULL; + + if ((!list) || (!len)) + return NULL; + + /* get the length of the list */ + tmp = list; + do + { + listlen++; + tmp = tmp->next; + } + while (tmp != NULL); + + array = malloc((listlen+1)*sizeof(routent_t *)); + if (!array) + { + log(LOG_ERR,"Error allocating memory."); + return NULL; + } + + tmp=list; + p = array; + do + { + *p = tmp; + p++; + tmp = tmp->next; + } + while(tmp != NULL); + *p=NULL; + + *len = listlen; + return array; +} + +/* load the router list */ +routent_t **getrouters(char *routerfile, size_t *lenp) +{ + int retval = 0; + char *retp = NULL; + routent_t *router=NULL, *routerlist=NULL, *lastrouter=NULL; + FILE *rf; /* router file */ + fpos_t fpos; + char line[512]; + char *token; + char *errtest; /* detecting errors in strtoul() calls */ + struct hostent *rent; + + if ((!routerfile) || (!lenp)) /* invalid parameters */ + return NULL; + + if (strspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != strlen(routerfile)) /* invalid filename */ + { + log(LOG_ERR,"Could not open %s because it contains illegal characters.",routerfile); + return NULL; + } + + /* open the router list */ + rf = fopen(routerfile,"r"); + if (!rf) + { + log(LOG_ERR,"Could not open %s.",routerfile); + return NULL; + } + + retp= fgets(line,512,rf); + while (retp) + { + log(LOG_DEBUG,"getrouters() : Line :%s",line); + token = (char *)strtok(line,OP_ROUTERLIST_SEPCHARS); + if (token) + { + log(LOG_DEBUG,"getrouters() : Token : %s",token); + if (token[0] != '#') /* ignore comment lines */ + { + router = malloc(sizeof(routent_t)); + if (!router) + { + log(LOG_ERR,"Could not allocate memory."); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + + /* read the address */ + router->address = malloc(strlen(token)+1); + if (!router->address) + { + log(LOG_ERR,"Could not allocate memory."); + fclose(rf); + free(router); + delete_routerlist(routerlist); + return NULL; + } + strcpy(router->address,token); + + rent = (struct hostent *)gethostbyname(router->address); + if (!rent) + { + log(LOG_ERR,"Could not get address for router %s.",router->address); + fclose(rf); + free(router->address); + free(router); + delete_routerlist(routerlist); + return NULL; + } + + memcpy(&router->addr, rent->h_addr,rent->h_length); + + /* read the network port */ + token = (char *)strtok(NULL,OP_ROUTERLIST_SEPCHARS); + if (token) /* network port */ + { + log(LOG_DEBUG,"getrouters() : Token :%s",token); + router->port = (uint16_t)strtoul(token,&errtest,0); + if ((*token != '\0') && (*errtest == '\0')) /* network port conversion was successful */ + { + router->port = htons(router->port); + /* read the entry port */ + token = (char *)strtok(NULL,OP_ROUTERLIST_SEPCHARS); + if (token) /* entry port */ + { + log(LOG_DEBUG,"getrouters() : Token :%s",token); + router->entry_port = (uint16_t)strtoul(token,&errtest,0); + if ((*token != '\0') && (*errtest == '\0')) /* entry port number conversion was successful */ + { + router->entry_port = htons(router->entry_port); + /* check that there is a public key entry for that router */ + retval = fgetpos(rf, &fpos); /* save the current file position + * we wil return to it later if we find a public key */ + if (retval == -1) + { + log(LOG_ERR,"Could not save position in %s.",routerfile); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + do /* read through to the next non-empty line */ + { + retp=fgets(line,512,rf); + if (!retp) + { + log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + log(LOG_DEBUG,"getrouters() : Line:%s",line); + if ((*line != '#') && (strspn(line,OP_ROUTERLIST_SEPCHARS) != strlen(line) )) + { + break; + } + } while (1); + + if (!strcmp(line,OP_PUBLICKEY_BEGIN_TAG)) /* we've got the public key */ + { + retval = fsetpos(rf,&fpos); /* get us back to where we were otherwise crypto lib won't find the key */ + if (retval == -1) + { + log(LOG_ERR,"Could not set position in %s.",routerfile); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else /* we found something else; this isn't right */ + { + log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + + log(LOG_DEBUG,"getrouters() : Reading the key ..."); + /* read the public key into router->pkey */ + router->pkey=NULL; + router->pkey = PEM_read_RSAPublicKey(rf,NULL,NULL,NULL); + if (!router->pkey) /* something went wrong */ + { + log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + else /* read the key */ + { + log(LOG_DEBUG,"getrouters() : Public key size = %u.", RSA_size(router->pkey)); + if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */ + { + log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port); + free(router->address); + RSA_free(router->pkey); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + router->next = NULL; + /* save the entry into the routerlist linked list */ + if (!routerlist) /* this is the first entry */ + routerlist = router; + else + lastrouter->next = (void *)router; + lastrouter = router; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid entry funnel port.",router->address); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain an entry funnel port.",router->address); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid network funnel port.",router->address); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a network funnel port.",router->address); + free(router->address); + free(router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + } + retp=fgets(line,512,rf); + } + + fclose(rf); + return make_rarray(routerlist, lenp); +} diff --git a/src/op/routers.h b/src/op/routers.h new file mode 100644 index 000000000..7cacb4daf --- /dev/null +++ b/src/op/routers.h @@ -0,0 +1,66 @@ +/** + * routers.h + * Routines for loading the list of routers and their public RSA keys. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.11 2002/04/02 14:28:01 badbytes + * Final finishes. + * + * Revision 1.10 2002/01/26 22:22:09 mp292 + * Prevented duplicate definitions. + * + * Revision 1.9 2002/01/26 22:19:15 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.8 2001/12/17 08:42:45 badbytes + * getrouters() now returns an array of routers and also writes the length of the array to an int*. + * + * Revision 1.7 2001/12/14 14:08:50 badbytes + * getrouters() now returns an array of pointers rather than a linked list + * + * Revision 1.6 2001/12/14 14:05:56 badbytes + * Added routent** make_rarray(routent_t* list); + * + * Revision 1.5 2001/12/14 13:32:18 badbytes + * No longer contains the definition of routent_t. This is now in common/routent_t.h + * + * Revision 1.4 2001/12/14 13:25:17 badbytes + * Moved back from common/ + * + * Revision 1.2 2001/12/14 11:24:57 badbytes + * Tested. + * + * Revision 1.1 2001/12/13 15:15:11 badbytes + * Started coding the onion proxy. + * + */ + +#ifndef __ROUTERS_H + +#define __ROUTERS_H + +#include <openssl/rsa.h> +#include "../common/routent.h" + +#define OP_ROUTERLIST_SEPCHARS " \t\n" + +#define OP_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n" + +/* load the list of routers into memory */ +routent_t **getrouters(char *routerfile, size_t *listlenp); + +/* free the router list pointed to by list */ +void delete_routerlist(routent_t *list); + +/* create an NULL-terminated array of pointers pointing to elements of a router list */ +routent_t **make_rarray(routent_t* list, size_t *listlenp); + +#endif diff --git a/src/op/ss.c b/src/op/ss.c new file mode 100644 index 000000000..3437517e4 --- /dev/null +++ b/src/op/ss.c @@ -0,0 +1,194 @@ +/** + * ss.c + * Standard structure processing. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + + +#include <malloc.h> +#include <unistd.h> + +#include "../common/log.h" +#include "../common/version.h" +#include "../common/utils.h" + +#include "ss.h" + +/* read the standard structure, check if it's acceptable and send an appropriate error code + * Returns : + * -1 processing error + * 0 OK + * 1 no error, but standard structure rejected + */ +int process_ss(int s, struct timeval *conn_toutp, ss_t **ssp, char **addrp, int *addrlenp, char **portp, int *portlenp) +{ + int retval = 0; + int len = 0; /* number of bytes read */ + ss_t *ss; /* standard structure */ + char errcode = SS_ERROR_SUCCESS; /* error code which we send back to the client */ + char inbuf; + char *addr = NULL; /* destination address */ + int addrlen = 0; + char *port = NULL; /* destination port */ + int portlen = 0; + char *tmp = NULL; /* temporary storage */ + + if ((!ssp) || (!addrp) || (!addrlenp) || (!portp) || (!portlenp)) /* invalid parameters */ + return -1; + + /* allocate memory for SS */ + ss = malloc(sizeof(ss_t)); + if (!ss) + { + log(LOG_ERR,"Error allocating memory."); + return -1; + } + + log(LOG_DEBUG,"Allocated memory for ss."); + + len = 0; + while (len < sizeof(ss_t)) /* need to make sure the entire ss is read */ + { + retval = read_tout(s,(char *)ss+len,sizeof(ss_t)-len,0, conn_toutp); + if (retval <= 0) + { + free(ss); + log(LOG_ERR,"Could not receive standard structure."); + return -1; + } + len +=retval; + } + + if ((ss->version == 0) || (ss->version != VERSION)) /* unsupported version */ + { + log(LOG_DEBUG,"Unsupported version."); + free(ss); + errcode = SS_ERROR_VERSION_UNSUPPORTED; + write_tout(s,&errcode,1,conn_toutp); + return -1; + } + + if (ss->addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) /* unrecognized address format */ + { + log(LOG_DEBUG,"Unrecognized address format."); + free(ss); + errcode = SS_ERROR_ADDR_FMT_UNSUPPORTED; + write_tout(s,&errcode,1,conn_toutp); + return -1; + } + + /* allocate memory for the destination address - 512 bytes maximum */ + addrlen=512; + addr = malloc(addrlen); + if (!addr) + { + free(ss); + log(LOG_ERR,"Error allocating memory."); + return -1; + } + + /* now read the destination address */ + len = 0; + do /* need to keep going until the entire string is read in */ + { + if (len == addrlen) /* we've run out of space, abort */ + { + free(ss); + free(addr); + log(LOG_ERR,"Client tried to send address > 512 characters."); + errcode = SS_ERROR_INVALID_ADDRESS; + write_tout(s,&errcode,1,conn_toutp); + return -1; + } + retval = read_tout(s,(void *)&inbuf, 1, 0, conn_toutp); + if (retval <= 0) + { + free(ss); + free(addr); + log(LOG_ERR,"Error receiving destination address."); + return -1; + } + *(addr+len) = inbuf; + len++; + } while (inbuf != 0); + + + /* allocate memory for the destination port - 6 bytes maximum */ + portlen = 6; + port = malloc(portlen); + if (!port) + { + free(ss); + log(LOG_ERR,"Error allocating memory."); + free(addr); + return -1; + } + /* now read the destination port */ + len = 0; + do /* keep going until the entire string is read in */ + { + if (len == portlen) /* no more space, abort */ + { + free(ss); + free(addr); + free(port); + log(LOG_ERR,"Client tried to send port > 6 characters."); + errcode = SS_ERROR_INVALID_PORT; + write_tout(s,&errcode,1,conn_toutp); + return -1; + } + retval = read_tout(s,(void *)&inbuf, 1, 0, conn_toutp); + if (retval <= 0) + { + free(ss); + free(addr); + free(port); + log(LOG_ERR,"Error receiving destination port."); + return -1; + } + *(port+len)=inbuf; + len++; + } while (inbuf != 0); + + /* send a success error code back to the client */ + errcode = SS_ERROR_SUCCESS; + write_tout(s,&errcode,1,conn_toutp); + + /* done, now save */ + addrlen = strlen(addr)+1; + tmp = addr; + addr = realloc(addr,addrlen); + /* if realloc() fails, we just ignore it and use the previously allocated memory, although this may be wasteful */ + if (!addr) + addr=tmp; /* restore previous state */ + else + addr[addrlen-1]=0; + + portlen = strlen(port)+1; + tmp=port; + port = realloc(port,portlen); + if (!port) + port=tmp; + else + port[portlen-1]=0; + + *ssp = ss; + *addrp = addr; + *addrlenp = addrlen; + *portp = port; + *portlenp = portlen; + + return 0; +} diff --git a/src/op/ss.h b/src/op/ss.h new file mode 100644 index 000000000..d6e68240f --- /dev/null +++ b/src/op/ss.h @@ -0,0 +1,22 @@ +/** + * ss.h + * Standard structure processing. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/04/02 14:28:01 badbytes + * Final finishes. + * + */ + + +#include "../common/ss.h" + +int process_ss(int s, struct timeval *conn_toutp, ss_t **ssp, char **addrp, int *addrlenp, char **portp, int *portlenp); diff --git a/src/or/Makefile b/src/or/Makefile new file mode 100644 index 000000000..edf2398b4 --- /dev/null +++ b/src/or/Makefile @@ -0,0 +1,18 @@ +SRC=args.c buffers.c cell.c circuit.c command.c connection.c connection_app.c connection_op.c connection_or.c config.c main.c onion.c routers.c +OBJ=${SRC:.c=.o} +PROGS=or +LIB= +LIBS= +INCLUDE = -I/usr/local/ssl/include + +CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -ggdb +LDFLAGS = $(LIB) $(LIBS) + +all: ${OBJ} ${PROGS} + +or: ${OBJ} + gcc -o or $(CFLAGS) *.o ../common/*.o -lcrypto + +clean: + rm -f *.o ${PROGS} + diff --git a/src/or/args.c b/src/or/args.c new file mode 100644 index 000000000..fc3837ca8 --- /dev/null +++ b/src/or/args.c @@ -0,0 +1,99 @@ +/** + * args.c + * Routines for processing command-line arguments. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/01/27 00:42:50 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.2 2002/01/04 10:05:28 badbytes + * Completed. + * + * Revision 1.1 2002/01/03 10:23:43 badbytes + * Code based on that in op. Needs to be modified. + */ + +#include "or.h" + +/* prints help on using or */ +void print_usage() +{ + char *program = "or"; + printf("\n%s - Onion Router.\nUsage : %s -f config [-l loglevel -h]\n-h : display this help\n-f config : config file\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program); +} + + +/* get command-line arguments */ +int getargs(int argc,char *argv[], char *args, char **conf_filename, int *loglevel) +{ + char c; /* next option character */ + int gotf=0; + + if ((!args) || (!conf_filename) || (!loglevel)) + return -1; + + while ((c = getopt(argc,argv,args)) != -1) + { + switch(c) + { + case 'f': /* config file */ + *conf_filename = optarg; + gotf=1; + break; + case 'h': + print_usage(argv[0]); + exit(0); + case 'l': + if (!strcmp(optarg,"emerg")) + *loglevel = LOG_EMERG; + else if (!strcmp(optarg,"alert")) + *loglevel = LOG_ALERT; + else if (!strcmp(optarg,"crit")) + *loglevel = LOG_CRIT; + else if (!strcmp(optarg,"err")) + *loglevel = LOG_ERR; + else if (!strcmp(optarg,"warning")) + *loglevel = LOG_WARNING; + else if (!strcmp(optarg,"notice")) + *loglevel = LOG_NOTICE; + else if (!strcmp(optarg,"info")) + *loglevel = LOG_INFO; + else if (!strcmp(optarg,"debug")) + *loglevel = LOG_DEBUG; + else + { + log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug."); + print_usage(argv[0]); + return -1; + } + break; + case '?': + if (isprint(c)) + log(LOG_ERR,"Missing argument or unknown option '-%c'. See help (-h).",optopt); + else + log(LOG_ERR,"Unknown option character 'x%x'. See help (-h).",optopt); + print_usage(argv[0]); + return -1; + break; + default: + return -1; + } + } + + /* the -f option is mandatory */ + if (!gotf) + { + log(LOG_ERR,"You must specify a config file with the -f option. See help (-h)."); + return -1; + } + + return 0; +} diff --git a/src/or/buffers.c b/src/or/buffers.c new file mode 100644 index 000000000..6900ca300 --- /dev/null +++ b/src/or/buffers.c @@ -0,0 +1,174 @@ + +/* buffers.c */ + +#include "or.h" + +int buf_new(char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) { + + if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */ + return -1; + + *pbuf = (char *)malloc(MAX_BUF_SIZE); + if(!*pbuf) + return -1; + memset(*pbuf,0,MAX_BUF_SIZE); + *pbuflen = MAX_BUF_SIZE; + *pbuf_datalen = 0; + + return 0; +} + +int buf_free(char *buf) { + + free(buf); + + return 0; +} + +int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof) { + + /* grab from s, put onto buf, return how many bytes read */ + + int read_result; + char *buf; + size_t buflen; + size_t buf_datalen; + + if (!pbuf || !pbuflen || !pbuf_datalen || !preached_eof) /* invalid parameters */ + return -1 ; + + if(s<0) { + log(LOG_DEBUG,"read_to_buf() received negative socket %d.",s); + return -1; + } + + /* this is the point where you would grow the buffer, if you want to */ + buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen; + + if (!buf) /* invalid parameter */ + return -1; + + read_result = read(s, buf+buf_datalen, buflen - buf_datalen); + if (read_result < 0) { + if(errno!=EAGAIN) { /* it's a real error */ + return -1; + } + return 0; + } else if (read_result == 0) { + log(LOG_DEBUG,"read_to_buf(): Encountered eof"); + *preached_eof = 1; + return 0; + } else { /* we read some bytes */ + *pbuf_datalen = buf_datalen + read_result; + log(LOG_DEBUG,"read_to_buf(): Read %d bytes. %d on inbuf.",read_result, *pbuf_datalen); + return read_result; + } + +} + +int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) { + + /* push from buf onto s + * then memmove to front of buf + * return -1 or how many bytes remain on the buf */ + + int write_result; + char *buf; + size_t buflen; + size_t buf_datalen; + + if (!pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */ + return -1; + + if(s<0) { + log(LOG_DEBUG,"flush_buf() received negative socket %d.",s); + return -1; + } + + + if(*pbuf_datalen == 0) /* nothing to flush */ + return 0; + + /* this is the point where you would grow the buffer, if you want to */ + buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen; + + if (!buf) /* invalid parameter */ + return -1; + + write_result = write(s, buf, buf_datalen); + if (write_result < 0) { + if(errno!=EAGAIN) { /* it's a real error */ + return -1; + } + log(LOG_DEBUG,"flush_buf(): write() would block, returning."); + return 0; + } else { + *pbuf_datalen -= write_result; + memmove(buf, buf+write_result, *pbuf_datalen); + log(LOG_DEBUG,"flush_buf(): flushed %d bytes, %d remain.",write_result,*pbuf_datalen); + return *pbuf_datalen; + } + +} + +int write_to_buf(char *string, size_t string_len, + char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) { + + /* append string to buf (growing as needed, return -1 if "too big") + * return total number of bytes on the buf + */ + + char *buf; + size_t buflen; + size_t buf_datalen; + + if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */ + return -1; + + /* this is the point where you would grow the buffer, if you want to */ + buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen; + + if (!buf) /* invalid parameter */ + return -1; + + if (string_len + buf_datalen > buflen) { /* we're out of luck */ + log(LOG_DEBUG, "write_to_buf(): buflen too small. Time to implement growing dynamic bufs."); + return -1; + } + + memcpy(buf+buf_datalen, string, string_len); + *pbuf_datalen += string_len; + log(LOG_DEBUG,"write_to_buf(): added %d bytes to buf (now %d total).",string_len, *pbuf_datalen); + return *pbuf_datalen; + +} + +int fetch_from_buf(char *string, size_t string_len, + char **pbuf, size_t *pbuflen, size_t *pbuf_datalen) { + + /* if there is string_len bytes in buf, write them onto string, + * then memmove buf back (that is, remove them from buf) */ + + char *buf; + size_t buflen; + size_t buf_datalen; + + if (!string || !pbuf || !pbuflen || !pbuf_datalen) /* invalid parameters */ + return -1; + + /* this is the point where you would grow the buffer, if you want to */ + buf = *pbuf, buflen = *pbuflen, buf_datalen = *pbuf_datalen; + + if (!buf) /* invalid parameter */ + return -1; + + if(string_len > buf_datalen) /* we want too much. sorry. */ + return -1; + + memcpy(string,buf,string_len); + *pbuf_datalen -= string_len; + memmove(buf, buf+string_len, *pbuf_datalen); + return *pbuf_datalen; + +} + diff --git a/src/or/cell.c b/src/or/cell.c new file mode 100644 index 000000000..ef53a9eae --- /dev/null +++ b/src/or/cell.c @@ -0,0 +1,23 @@ + +#include "or.h" + +int check_sane_cell(cell_t *cell) { + + if(!cell) + return -1; + + if(cell->aci == 0) { + log(LOG_DEBUG,"check_sane_cell(): Cell has aci=0. Dropping."); + return -1; + } + +#if 0 /* actually, the length is sometimes encrypted. so it's ok. */ + if(cell->length > 120) { + log(LOG_DEBUG,"check_sane_cell(): Cell claims to have payload length %d. Dropping.",cell->length); + return -1; + } +#endif + + return 0; /* looks good */ +} + diff --git a/src/or/circuit.c b/src/or/circuit.c new file mode 100644 index 000000000..b910fd351 --- /dev/null +++ b/src/or/circuit.c @@ -0,0 +1,319 @@ + +#include "or.h" + +/********* START VARIABLES **********/ + +circuit_t *global_circuitlist=NULL; + +/********* END VARIABLES ************/ + +void circuit_add(circuit_t *circ) { + + if(!global_circuitlist) { /* first one */ + global_circuitlist = circ; + circ->next = NULL; + } else { + circ->next = global_circuitlist; + global_circuitlist = circ; + } + +} + +void circuit_remove(circuit_t *circ) { + circuit_t *tmpcirc; + + if(!circ || !global_circuitlist) + return; + + if(global_circuitlist == circ) { + global_circuitlist = global_circuitlist->next; + return; + } + + for(tmpcirc = global_circuitlist;tmpcirc->next;tmpcirc = tmpcirc->next) { + if(tmpcirc->next == circ) { + tmpcirc->next = circ->next; + return; + } + } + +} + +circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn) { + + circuit_t *circ; + + circ = (circuit_t *)malloc(sizeof(circuit_t)); + if(!circ) + return NULL; + memset(circ,0,sizeof(circuit_t)); /* zero it out */ + + circ->p_aci = p_aci; + circ->p_conn = p_conn; + + circ->state = CIRCUIT_STATE_OPEN_WAIT; + circ->onion = NULL; + circ->onionlen=0; + circ->recvlen=0; + + /* ACIs */ + circ->p_aci = p_aci; + circ->n_aci = 0; /* we need to have identified the next hop to choose a correct ACI */ + + circuit_add(circ); + + return circ; +} + +void circuit_free(circuit_t *circ) { + + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + + if(circ->onion) + free(circ->onion); + + free(circ); + +} + +aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type) { + aci_t test_aci; + connection_t *conn; + + log(LOG_DEBUG,"get_unique_aci_by_addr_port() trying to get a unique aci"); + + RAND_pseudo_bytes((unsigned char *)&test_aci, 2); + + if(aci_type == ACI_TYPE_LOWER) + test_aci &= htons(0x00FF); + if(aci_type == ACI_TYPE_HIGHER) + test_aci &= htons(0xFF00); + + if(test_aci == 0) + return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */ + + conn = connection_get_by_addr_port(addr,port); + if(!conn) /* there can't be a conflict -- no connection of that sort yet */ + return test_aci; + + if(circuit_get_by_aci_conn(test_aci, conn)) + return get_unique_aci_by_addr_port(addr, port, aci_type); /* try again */ + + return test_aci; + +} + +int circuit_init(circuit_t *circ, int aci_type) { + onion_layer_t *ol; + int retval = 0; + unsigned char digest1[20]; + unsigned char digest2[20]; + + if (!circ) + return -1; + + ol = (onion_layer_t *)circ->onion; + if (!ol) + return -1; + + log(LOG_DEBUG,"circuit_init(): starting"); + circ->n_addr = ol->addr; + circ->n_port = ol->port; + log(LOG_DEBUG,"circuit_init(): Set port to %u.",ntohs(ol->port)); + circ->p_f = ol->backf; + log(LOG_DEBUG,"circuit_init(): Set BACKF to %u.",ol->backf); + circ->n_f = ol->forwf; + log(LOG_DEBUG,"circuit_init(): Set FORWF to %u.",ol->forwf); + circ->state = CIRCUIT_STATE_OPEN; + + log(LOG_DEBUG,"circuit_init(): aci_type = %u.",aci_type); + + circ->n_aci = get_unique_aci_by_addr_port(circ->n_addr, circ->n_port, aci_type); + + log(LOG_DEBUG,"circuit_init(): Chosen ACI %u.",circ->n_aci); + + /* keys */ + SHA1(ol->keyseed,16,digest1); + SHA1(digest1,20,digest2); + SHA1(digest2,20,digest1); + memcpy(circ->p_key,digest2,16); + memcpy(circ->n_key,digest1,16); + log(LOG_DEBUG,"circuit_init(): Computed keys."); + + /* set IVs to zero */ + memset(circ->n_iv,0,16); + memset(circ->p_iv,0,16); + + /* initialize cipher context */ + EVP_CIPHER_CTX_init(&circ->n_ctx); + EVP_CIPHER_CTX_init(&circ->p_ctx); + + /* initialize crypto engines */ + switch(circ->p_f) + { + case ONION_CIPHER_DES : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_des_ofb(), circ->p_key, circ->p_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_rc4(), circ->p_key,circ->p_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_EncryptInit(&circ->p_ctx, EVP_enc_null(), circ->p_key, circ->p_iv); + break; + default : + log(LOG_ERR,"Onion contains unrecognized cipher(%u) for ACI : %u.",circ->p_f,circ->n_aci); + return -1; + break; + } + + if (!retval) /* EVP_EncryptInit() error */ + { + log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + return -1; + } + switch(circ->n_f) + { + case ONION_CIPHER_DES : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_des_ofb(), circ->n_key, circ->n_iv); + break; + case ONION_CIPHER_RC4 : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_rc4(), circ->n_key,circ->n_iv); + break; + case ONION_CIPHER_IDENTITY : + retval = EVP_DecryptInit(&circ->n_ctx, EVP_enc_null(), circ->n_key, circ->n_iv); + break; + default : + log(LOG_ERR,"Onion contains unrecognized cipher for ACI : %u.",circ->n_aci); + return -1; + break; + } + if (!retval) /* EVP_EncryptInit() error */ + { + log(LOG_ERR,"Cipher initialization failed (ACI %u).",circ->n_aci); + EVP_CIPHER_CTX_cleanup(&circ->n_ctx); + EVP_CIPHER_CTX_cleanup(&circ->p_ctx); + return -1; + } + log(LOG_DEBUG,"circuit_init(): Cipher initialization complete."); + + circ->expire = ol->expire; + + return 0; +} + +circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn && circ->p_aci == aci) + return circ; + if(circ->n_conn == conn && circ->n_aci == aci) + return circ; + } + return NULL; +} + +circuit_t *circuit_get_by_conn(connection_t *conn) { + circuit_t *circ; + + for(circ=global_circuitlist;circ;circ = circ->next) { + if(circ->p_conn == conn) + return circ; + if(circ->n_conn == conn) + return circ; + } + return NULL; +} + +int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type) { + + /* first decrypt cell->length */ + if(circuit_crypt(circ, &(cell->length), 1, crypt_type) < 0) { + log(LOG_DEBUG,"circuit_deliver_data_cell(): length decryption failed. Dropping connection."); + return -1; + } + + /* then decrypt the payload */ + if(circuit_crypt(circ, (char *)&(cell->payload), CELL_PAYLOAD_SIZE, crypt_type) < 0) { + log(LOG_DEBUG,"circuit_deliver_data_cell(): payload decryption failed. Dropping connection."); + return -1; + } + + if(conn->type == CONN_TYPE_APP) { /* send payload directly */ + log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to application."); + if(connection_app_process_data_cell(cell, conn) < 0) { + return -1; + } + } else { /* send it as a cell */ + log(LOG_DEBUG,"circuit_deliver_data_cell(): Sending to connection."); + if(connection_write_cell_to_buf(cell, conn) < 0) { + return -1; + } + } + return 0; /* success */ +} + +int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type) { + char *out; + int outlen; + + if(!circ || !in) + return -1; + + out = malloc(inlen); + if(!out) + return -1; + + if(crypt_type == 'e') { + log(LOG_DEBUG,"circuit_crypt(): Encrypting %d bytes.",inlen); + if(!EVP_EncryptUpdate(&circ->p_ctx,out,&outlen,in,inlen)) { + log(LOG_ERR,"circuit_encrypt(): Encryption failed for ACI : %u (%s).",circ->p_aci, ERR_reason_error_string(ERR_get_error())); + return -1; + } + } else if(crypt_type == 'd') { + log(LOG_DEBUG,"circuit_crypt(): Decrypting %d bytes.",inlen); + if(!EVP_DecryptUpdate(&circ->n_ctx,out,&outlen,in,inlen)) { + log(LOG_ERR,"circuit_crypt(): Decryption failed for ACI : %u (%s).",circ->n_aci, ERR_reason_error_string(ERR_get_error())); + return -1; + } + } + + if(outlen != inlen) { + log(LOG_DEBUG,"circuit_crypt(): %d bytes crypted to %d bytes. Weird.",inlen,outlen); + return -1; + } + + memcpy(in,out,inlen); + free(out); + + return 0; +} + +void circuit_close(circuit_t *circ) { + circuit_remove(circ); + connection_send_destroy(circ->n_aci, circ->n_conn); + connection_send_destroy(circ->p_aci, circ->p_conn); + circuit_free(circ); +} + +void circuit_about_to_close_connection(connection_t *conn) { + /* send destroys for all circuits using conn */ + /* currently, we assume it's too late to flush conn's buf here. + * down the road, maybe we'll consider that eof doesn't mean can't-write + */ + + circuit_t *circ; + + while((circ = circuit_get_by_conn(conn))) { + circuit_remove(circ); + if(circ->n_conn == conn) /* it's closing in front of us */ + connection_send_destroy(circ->p_aci, circ->p_conn); + if(circ->p_conn == conn) /* it's closing behind us */ + connection_send_destroy(circ->n_aci, circ->n_conn); + circuit_free(circ); + } +} + diff --git a/src/or/command.c b/src/or/command.c new file mode 100644 index 000000000..2392a9bf2 --- /dev/null +++ b/src/or/command.c @@ -0,0 +1,212 @@ + +#include "or.h" + +void command_process_cell(cell_t *cell, connection_t *conn) { + + if(check_sane_cell(cell) < 0) + return; + + switch(cell->command) { + case CELL_PADDING: + /* do nothing */ + break; + case CELL_CREATE: + command_process_create_cell(cell, conn); + break; + case CELL_DATA: + command_process_data_cell(cell, conn); + break; + case CELL_DESTROY: + command_process_destroy_cell(cell, conn); + break; + } +} + +void command_process_create_cell(cell_t *cell, connection_t *conn) { + circuit_t *circ; + connection_t *n_conn; + unsigned char *cellbuf; /* array of cells */ + int cellbuflen; /* size of cellbuf in bytes */ + cell_t *tmpcell; /* pointer to an arbitrary cell */ + int retval, i; + + circ = circuit_get_by_aci_conn(cell->aci, conn); + + if(circ && circ->state != CIRCUIT_STATE_OPEN_WAIT) { + log(LOG_DEBUG,"command_process_create_cell(): received CREATE cell, not in open_wait. Dropping."); + return; + } + + if(!circ) { /* if it's not there, create it */ + circ = circuit_new(cell->aci, conn); + circ->state = CIRCUIT_STATE_OPEN_WAIT; + memcpy((void *)&circ->onionlen,(void *)cell->payload, 4); + circ->onionlen = ntohl(circ->onionlen); + log(LOG_DEBUG,"command_process_create_cell(): Onion length is %u.",circ->onionlen); + if(circ->onionlen > 50000) { /* too big */ + log(LOG_DEBUG,"That's ludicrous. Closing."); + circuit_close(circ); + return; + } + circ->onion = (unsigned char *)malloc(circ->onionlen); + if(!circ->onion) { + log(LOG_DEBUG,"command_process_create_cell(): Out of memory. Closing."); + circuit_close(circ); + return; + } + if(circ->onionlen < cell->length-4) { /* protect from buffer overflow */ + log(LOG_DEBUG,"command_process_create_cell(): Onion too small. Closing."); + circuit_close(circ); + return; + } + memcpy((void *)circ->onion,(void *)(cell->payload+4),cell->length-4); + circ->recvlen = cell->length-4; + log(LOG_DEBUG,"command_process_create_cell(): Primary create cell handled, have received %d of %d onion bytes.", + circ->recvlen,circ->onionlen); + + } else { /* pull over as much of the onion as we can */ + if(cell->length + circ->recvlen > circ->onionlen) { /* protect from buffer overflow */ + log(LOG_DEBUG,"command_process_create_cell(): payload too big for onion. Closing."); + circuit_close(circ); + return; + } + memcpy((void *)(circ->onion+circ->recvlen),(void *)cell->payload,cell->length); + circ->recvlen += cell->length; + log(LOG_DEBUG,"command_process_create_cell(): Secondary create cell handled, have received %d of %d onion bytes.", + circ->recvlen,circ->onionlen); + } + + if(circ->recvlen != circ->onionlen) { + log(LOG_DEBUG,"command_process_create_cell(): Onion not all here yet. Ok."); + return; + } + + /* we're all ready to go now. */ + circ->state = CIRCUIT_STATE_OPEN; + + if(process_onion(circ, conn) < 0) { + log(LOG_DEBUG,"command_process_create_cell(): Onion processing failed. Closing."); + circuit_close(circ); + return; + } + + if(circ->n_addr && circ->n_port) { /* must send create cells to the next router */ + n_conn = connection_get_by_addr_port(circ->n_addr,circ->n_port); + if(!n_conn || n_conn->type != CONN_TYPE_OR) { + /* i've disabled making connections through OPs, but it's definitely + * possible here. I'm not sure if it would be a bug or a feature. -RD + */ + log(LOG_DEBUG,"command_process_create_cell(): Next router not connected. Closing."); + circuit_close(circ); + } + circ->n_conn = n_conn; + log(LOG_DEBUG,"command_process_create_cell(): n_conn is %s:%u",n_conn->address,ntohs(n_conn->port)); + + /* send the CREATE cells on to the next hop */ + pad_onion(circ->onion,circ->onionlen, sizeof(onion_layer_t)); + log(LOG_DEBUG,"command_process_create_cell(): Padded the onion with random data."); + + retval = pack_create(circ->n_aci, circ->onion, circ->onionlen, &cellbuf, &cellbuflen); + free((void *)circ->onion); + circ->onion = NULL; + if (retval == -1) /* pack_create() error */ + { + log(LOG_DEBUG,"command_process_create_cell(): Could not pack the onion into CREATE cells. Closing the connection."); + circuit_close(circ); + return; + } + log(LOG_DEBUG,"command_process_create_cell(): Onion packed into CREATE cells. Buffering the cells."); + /* queue the create cells for transmission to the next hop */ + tmpcell = (cell_t *)cellbuf; + for (i=0;i<cellbuflen/sizeof(cell_t);i++) + { + retval = connection_write_cell_to_buf(tmpcell, n_conn); + if (retval == -1) /* buffering failed, drop the connection */ + { + log(LOG_DEBUG,"command_process_create_cell(): Could not buffer new create cells. Closing."); + circuit_close(circ); + return; + } + tmpcell++; + } + free((void *)cellbuf); + return; + + } else { /* this is destined for an app */ + log(LOG_DEBUG,"command_process_create_cell(): Creating new application connection."); + n_conn = connection_new(CONN_TYPE_APP); + if(!n_conn) { + log(LOG_DEBUG,"command_process_create_cell(): connection_new failed. Closing."); + circuit_close(circ); + return; + } + n_conn->state = APP_CONN_STATE_CONNECTING_WAIT; + n_conn->s = -1; /* not yet valid */ + if(connection_add(n_conn) < 0) { /* no space, forget it */ + log(LOG_DEBUG,"command_process_create_cell(): connection_add failed. Closing."); + connection_free(n_conn); + circuit_close(circ); + return; + } + circ->n_conn = n_conn; + return; + } + +} + +void command_process_data_cell(cell_t *cell, connection_t *conn) { + circuit_t *circ; + + /* FIXME do something with 'close' state, here */ + + circ = circuit_get_by_aci_conn(cell->aci, conn); + + if(!circ) { + log(LOG_DEBUG,"command_process_data_cell(): received DATA cell for unknown circuit. Dropping."); + return; + } + + if(circ->state == CIRCUIT_STATE_OPEN_WAIT) { + log(LOG_DEBUG,"command_process_data_cell(): circuit in open_wait. Dropping data cell."); + return; + } + + /* at this point both circ->n_conn and circ->p_conn are guaranteed to be set */ + + if(cell->aci == circ->p_aci) { /* it's an outgoing cell */ + cell->aci = circ->n_aci; /* switch it */ + if(circuit_deliver_data_cell(cell, circ, circ->n_conn, 'd') < 0) { + log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (forward) failed. Closing."); + circuit_close(circ); + return; + } + } else { /* it's an ingoing cell */ + cell->aci = circ->p_aci; /* switch it */ + if(circuit_deliver_data_cell(cell, circ, circ->p_conn, 'e') < 0) { + log(LOG_DEBUG,"command_process_data_cell(): circuit_deliver_data_cell (backward) failed. Closing."); + circuit_close(circ); + return; + } + } +} + +void command_process_destroy_cell(cell_t *cell, connection_t *conn) { + circuit_t *circ; + + circ = circuit_get_by_aci_conn(cell->aci, conn); + + if(!circ) { + log(LOG_DEBUG,"command_process_destroy_cell(): received DESTROY cell for unknown circuit. Dropping."); + return; + } + + log(LOG_DEBUG,"command_process_destroy_cell(): Received for aci %d.",cell->aci); + circuit_remove(circ); + if(cell->aci == circ->p_aci) /* the destroy came from behind */ + connection_send_destroy(circ->n_aci, circ->n_conn); + if(cell->aci == circ->n_aci) /* the destroy came from ahead */ + connection_send_destroy(circ->p_aci, circ->p_conn); + circuit_free(circ); + +} + diff --git a/src/or/config.c b/src/or/config.c new file mode 100644 index 000000000..535f7638d --- /dev/null +++ b/src/or/config.c @@ -0,0 +1,49 @@ +/** + * config.c + * Routines for loading the configuration file. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.3 2002/04/02 14:28:24 badbytes + * Final finishes. + * + * Revision 1.2 2002/01/27 00:42:50 mp292 + * Reviewed according to Secure-Programs-HOWTO. + * + * Revision 1.1 2002/01/03 10:24:05 badbytes + * COde based on that in op. Needs to be modified. + * + */ + +#include "or.h" + +/* loads the configuration file */ +int getconfig(char *conf_filename, config_opt_t *options) +{ + FILE *cf = NULL; + int retval = 0; + + if ((!conf_filename) || (!options)) + return -1; + + /* load config file */ + cf = open_config(conf_filename); + if (!cf) + { + log(LOG_ERR,"Could not open configuration file %s.",conf_filename); + return -1; + } + retval = parse_config(cf,options); + if (retval) + return -1; + + return 0; +} + diff --git a/src/or/connection.c b/src/or/connection.c new file mode 100644 index 000000000..7411852ac --- /dev/null +++ b/src/or/connection.c @@ -0,0 +1,362 @@ + +#include "or.h" + +/********* START VARIABLES **********/ + +char *conn_type_to_string[] = { + "OP listener", /* 0 */ + "OP", /* 1 */ + "OR listener", /* 2 */ + "OR", /* 3 */ + "App" /* 4 */ +}; + +char *conn_state_to_string[][10] = { + { "ready" }, /* op listener, 0 */ + { "awaiting keys", /* op, 0 */ + "open", /* 1 */ + "close", /* 2 */ + "close_wait" }, /* 3 */ + { "ready" }, /* or listener, 0 */ + { "connecting (as client)", /* or, 0 */ + "sending auth (as client)", /* 1 */ + "waiting for auth (as client)", /* 2 */ + "sending nonce (as client)", /* 3 */ + "waiting for auth (as server)", /* 4 */ + "sending auth (as server)", /* 5 */ + "waiting for nonce (as server)",/* 6 */ + "open" }, /* 7 */ + { "connecting", /* app, 0 */ + "open", /* 1 */ + "waiting for dest info", /* 2 */ + "flushing buffer, then will close",/* 3 */ + "close_wait" } /* 4 */ +}; + +/********* END VARIABLES ************/ + +connection_t *connection_new(int type) { + connection_t *conn; + + conn = (connection_t *)malloc(sizeof(connection_t)); + if(!conn) + return NULL; + memset(conn,0,sizeof(connection_t)); /* zero it out to start */ + + conn->type = type; + buf_new(&conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen); + buf_new(&conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); + + return conn; +} + +void connection_free(connection_t *conn) { + assert(conn); + + buf_free(conn->inbuf); + buf_free(conn->outbuf); + if(conn->address) + free(conn->address); + + /* FIXME should we do these for all connections, or just ORs, or what */ + if(conn->type == CONN_TYPE_OR || + conn->type == CONN_TYPE_OP) { +// EVP_CIPHER_CTX_cleanup(&conn->f_ctx); +// EVP_CIPHER_CTX_cleanup(&conn->b_ctx); + } + + if(conn->s > 0) + close(conn->s); + free(conn); +} + +int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type) { + connection_t *conn; + int s; + int one=1; + + s = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) + { + log(LOG_ERR,"connection_create_listener(): Socket creation failed."); + return -1; + } + + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + if(bind(s,(struct sockaddr *)local,sizeof(*local)) < 0) { + perror("bind "); + log(LOG_ERR,"Could not bind to local port %u.",ntohs(local->sin_port)); + return -1; + } + + /* start local server */ + if(listen(s,SOMAXCONN) < 0) { + log(LOG_ERR,"Could not listen on local port %u.",ntohs(local->sin_port)); + return -1; + } + + fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */ + + conn = connection_new(type); + conn->s = s; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return -1; + } + + /* remember things so you can tell the baby sockets */ + memcpy(&conn->local,local,sizeof(struct sockaddr_in)); + conn->prkey = prkey; + + log(LOG_DEBUG,"connection_create_listener(): Listening on local port %u.",ntohs(local->sin_port)); + + conn->state = LISTENER_STATE_READY; + connection_watch_events(conn, POLLIN); + + return 0; +} + +int connection_handle_listener_read(connection_t *conn, int new_type, int new_state) { + + int news; /* the new socket */ + connection_t *newconn; + struct sockaddr_in remote; /* information about the remote peer when connecting to other routers */ + int remotelen = sizeof(struct sockaddr_in); /* length of the remote address */ + + news = accept(conn->s,(struct sockaddr *)&remote,&remotelen); + if (news == -1) { /* accept() error */ + if(errno==EAGAIN) + return 0; /* he hung up before we could accept(). that's fine. */ + /* else there was a real error. */ + log(LOG_ERR,"connection_handle_listener_read(): accept() failed. Closing."); + return -1; + } + log(LOG_DEBUG,"Connection accepted on socket %d.",news); + + newconn = connection_new(new_type); + newconn->s = news; + + /* learn things from parent, so we can perform auth */ + memcpy(&newconn->local,&conn->local,sizeof(struct sockaddr_in)); + newconn->prkey = conn->prkey; +// newconn->address = strdup(get_string_from_remote()) FIXME ; + + if(connection_add(newconn) < 0) { /* no space, forget it */ + connection_free(newconn); + return -1; + } + + log(LOG_DEBUG,"connection_handle_listener_read(): socket %d entered state %d.",newconn->s, new_state); + newconn->state = new_state; + connection_watch_events(newconn, POLLIN); + + return 0; +} + +int retry_all_connections(routerinfo_t **router_array, int rarray_len, + RSA *prkey, uint16_t or_port, uint16_t op_port) { + + /* start all connections that should be up but aren't */ + + routerinfo_t *router; + int i; + + /* local host information */ + char localhostname[512]; + struct hostent *localhost; + struct sockaddr_in local; /* local address */ + + /* obtain local host information */ + if(gethostname(localhostname,512) < 0) { + log(LOG_ERR,"Error obtaining local hostname."); + return -1; + } + localhost = gethostbyname(localhostname); + if (!localhost) { + log(LOG_ERR,"Error obtaining local host info."); + return -1; + } + memset((void *)&local,0,sizeof(local)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port = htons(or_port); + memcpy((void *)&local.sin_addr,(void *)localhost->h_addr,sizeof(struct in_addr)); + + for (i=0;i<rarray_len;i++) { + router = router_array[i]; + if(!connection_get_by_addr_port(router->addr,router->port)) { /* not in the list */ + connect_to_router(router, prkey, &local); + } + } + + if(!connection_get_by_type(CONN_TYPE_OR_LISTENER)) { + connection_or_create_listener(prkey, &local); + } + + local.sin_port = htons(op_port); + if(!connection_get_by_type(CONN_TYPE_OP_LISTENER)) { + connection_op_create_listener(prkey, &local); + } + + return 0; + +} + +int connection_read_to_buf(connection_t *conn) { + return read_to_buf(conn->s, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen, &conn->inbuf_reached_eof); +} + +int connection_fetch_from_buf(char *string, int len, connection_t *conn) { + return fetch_from_buf(string, len, &conn->inbuf, &conn->inbuflen, &conn->inbuf_datalen); +} + +int connection_flush_buf(connection_t *conn) { + return flush_buf(conn->s, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); +} + +int connection_write_to_buf(char *string, int len, connection_t *conn) { + if(!len) + return 0; + connection_watch_events(conn, POLLOUT | POLLIN); + return write_to_buf(string, len, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); +} + +int connection_send_destroy(aci_t aci, connection_t *conn) { + cell_t cell; + + if(!conn) + return -1; + + if(conn->type == CONN_TYPE_OP || + conn->type == CONN_TYPE_APP) { + log(LOG_DEBUG,"connection_send_destroy(): At an edge. Marking connection for close."); + conn->marked_for_close = 1; + return 0; + } + + cell.aci = aci; + cell.command = CELL_DESTROY; + log(LOG_DEBUG,"connection_send_destroy(): Sending destroy (aci %d).",aci); + return connection_write_cell_to_buf(&cell, conn); + +} + +int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn) { + /* FIXME in the future, we should modify windows, etc, here */ + + if(connection_encrypt_cell_header(cellp,conn)<0) { + return -1; + } + + return connection_write_to_buf((char *)cellp, sizeof(cell_t), conn); + +} + +int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn) { + char newheader[8]; + int newsize; + int x; + char *px; + + printf("Sending: Cell header plaintext: "); + px = (char *)cellp; + for(x=0;x<8;x++) { + printf("%u ",px[x]); + } + printf("\n"); + + if(!EVP_EncryptUpdate(&conn->f_ctx, newheader, &newsize, (char *)cellp, 8)) { + log(LOG_ERR,"Could not encrypt data for connection %s:%u.",conn->address,ntohs(conn->port)); + return -1; + } + printf("Sending: Cell header crypttext: "); + for(x=0;x<8;x++) { + printf("%u ",newheader[x]); + } + printf("\n"); + + memcpy(cellp,newheader,8); + return 0; +} + +int connection_process_inbuf(connection_t *conn) { + + assert(conn); + + switch(conn->type) { + case CONN_TYPE_OP: + return connection_op_process_inbuf(conn); + case CONN_TYPE_OR: + return connection_or_process_inbuf(conn); + case CONN_TYPE_APP: + return connection_app_process_inbuf(conn); + default: + log(LOG_DEBUG,"connection_process_inbuf() got unexpected conn->type."); + return -1; + } +} + +int connection_finished_flushing(connection_t *conn) { + + assert(conn); + + log(LOG_DEBUG,"connection_finished_flushing() entered. Socket %u.", conn->s); + + switch(conn->type) { + case CONN_TYPE_OP: + return connection_op_finished_flushing(conn); + case CONN_TYPE_OR: + return connection_or_finished_flushing(conn); + case CONN_TYPE_APP: + return connection_app_finished_flushing(conn); + default: + log(LOG_DEBUG,"connection_finished_flushing() got unexpected conn->type."); + return -1; + } +} + +int connection_process_cell_from_inbuf(connection_t *conn) { + /* check if there's a whole cell there. + * if yes, pull it off, decrypt it, and process it. + */ + char crypted[128]; + char outbuf[1024]; + int outlen; + int x; + cell_t *cellp; + + if(conn->inbuf_datalen < 128) /* entire response available? */ + return 0; /* not yet */ + + if(connection_fetch_from_buf(crypted,128,conn) < 0) { + return -1; + } + + printf("Cell header crypttext: "); + for(x=0;x<8;x++) { + printf("%u ",crypted[x]); + } + printf("\n"); + /* decrypt */ + if(!EVP_DecryptUpdate(&conn->b_ctx,(unsigned char *)outbuf,&outlen,crypted,8)) { + log(LOG_ERR,"connection_process_cell_from_inbuf(): Decryption failed, dropping."); + return connection_process_inbuf(conn); /* process the remainder of the buffer */ + } + log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Cell decrypted (%d bytes).",outlen); + printf("Cell header plaintext: "); + for(x=0;x<8;x++) { + printf("%u ",outbuf[x]); + } + printf("\n"); + + /* copy the rest of the cell */ + memcpy((char *)outbuf+8, (char *)crypted+8, sizeof(cell_t)-8); + cellp = (cell_t *)outbuf; + log(LOG_DEBUG,"connection_process_cell_from_inbuf(): Decrypted cell is of type %u (ACI %u).",cellp->command,cellp->aci); + command_process_cell(cellp, conn); + + return connection_process_inbuf(conn); /* process the remainder of the buffer */ +} + diff --git a/src/or/connection_app.c b/src/or/connection_app.c new file mode 100644 index 000000000..94276fd59 --- /dev/null +++ b/src/or/connection_app.c @@ -0,0 +1,212 @@ + +#include "or.h" + +connection_t *connection_app_new(void) { + return connection_new(CONN_TYPE_APP); +} + +int connection_app_process_inbuf(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_APP); + + if(conn->inbuf_reached_eof) { + /* eof reached, kill it. */ + log(LOG_DEBUG,"connection_app_process_inbuf(): conn reached eof. Closing."); + return -1; + } + + log(LOG_DEBUG,"connection_app_process_inbuf(): state %d.",conn->state); + + switch(conn->state) { + case APP_CONN_STATE_CONNECTING: + log(LOG_DEBUG,"connection_app_process_inbuf(): text from app server while in 'connecting' state. Leaving it on buffer."); + return 0; + case APP_CONN_STATE_OPEN: + return connection_app_package_inbuf(conn); + } + return 0; + +} + +int connection_app_package_inbuf(connection_t *conn) { + int amount_to_process; + cell_t cell; + circuit_t *circ; + + assert(conn && conn->type == CONN_TYPE_APP); + + amount_to_process = conn->inbuf_datalen; + + if(!amount_to_process) + return 0; + + if(amount_to_process > CELL_PAYLOAD_SIZE) { + cell.length = CELL_PAYLOAD_SIZE; + } else { + cell.length = amount_to_process; + } + + if(connection_fetch_from_buf(cell.payload,cell.length,conn) < 0) { + return -1; + } + + circ = circuit_get_by_conn(conn); + if(!circ) { + log(LOG_DEBUG,"connection_app_package_inbuf(): conn has no circuits!"); + return -1; + } + + log(LOG_DEBUG,"connection_app_package_inbuf(): Packaging %d bytes.",cell.length); + cell.aci = circ->p_aci; + cell.command = CELL_DATA; + if(circuit_deliver_data_cell(&cell, circ, circ->p_conn, 'e') < 0) { + log(LOG_DEBUG,"connection_app_package_inbuf(): circuit_deliver_data_cell (backward) failed. Closing."); + circuit_close(circ); + return 0; + } + if(amount_to_process > CELL_PAYLOAD_SIZE) + return(connection_app_package_inbuf(conn)); + return 0; +} + +int connection_app_finished_flushing(connection_t *conn) { + int e, len=sizeof(e); + + assert(conn && conn->type == CONN_TYPE_APP); + + switch(conn->state) { + case APP_CONN_STATE_CONNECTING: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + log(LOG_DEBUG,"connection_app_finished_flushing(): in-progress connect failed. Removing."); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + log(LOG_DEBUG,"connection_app_finished_flushing() : Connection to %s:%u established.", + conn->address,ntohs(conn->port)); + + conn->state = APP_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + return 0; + case APP_CONN_STATE_OPEN: + /* FIXME down the road, we'll clear out circuits that are pending to close */ + connection_watch_events(conn, POLLIN); + return 0; + default: + log(LOG_DEBUG,"Bug: connection_app_finished_flushing() called in unexpected state."); + return 0; + } + + return 0; +} + +int connection_app_process_data_cell(cell_t *cell, connection_t *conn) { + struct hostent *rent; + struct sockaddr_in dest_addr; + int s; + + /* an outgoing data cell has arrived */ + + assert(conn && conn->type == CONN_TYPE_APP); + + switch(conn->state) { + case APP_CONN_STATE_CONNECTING_WAIT: + log(LOG_DEBUG,"connection_app_process_cell(): state is connecting_wait. cell length %d.", cell->length); + if(!conn->ss_received) { /* this cell contains the ss */ + if(cell->length != sizeof(ss_t)) { + log(LOG_DEBUG,"connection_app_process_cell(): Supposed to contain SS but wrong size. Closing."); + return -1; + } + memcpy(&conn->ss, cell->payload, cell->length); + if(conn->ss.addr_fmt != SS_ADDR_FMT_ASCII_HOST_PORT) { /* unrecognized address format */ + log(LOG_DEBUG,"connection_app_process_cell(): SS has unrecognized address format. Closing."); + return -1; + } + conn->ss_received = 1; + log(LOG_DEBUG,"connection_app_process_cell(): SS received."); + } else if (!conn->addr) { /* this cell contains the dest addr */ + if(!memchr(cell->payload,0,cell->length)) { + log(LOG_DEBUG,"connection_app_process_cell(): dest_addr cell has no \\0. Closing."); + return -1; + } + conn->address = strdup(cell->payload); + rent = gethostbyname(cell->payload); + if (!rent) { + log(LOG_ERR,"connection_app_process_cell(): Could not resolve dest addr %s.",cell->payload); + return -1; + } + memcpy(&conn->addr, rent->h_addr,rent->h_length); + log(LOG_DEBUG,"connection_app_process_cell(): addr %s resolves to %d.",cell->payload,conn->addr); + } else if (!conn->port) { /* this cell contains the dest port */ + if(!memchr(cell->payload,'\0',cell->length)) { + log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell has no \\0. Closing."); + return -1; + } + conn->port = atoi(cell->payload); + if(!conn->port) { /* bad port */ + log(LOG_DEBUG,"connection_app_process_cell(): dest_port cell isn't a valid number. Closing."); + return -1; + } + /* all the necessary info is here. Start the connect() */ + s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) + { + log(LOG_ERR,"connection_app_process_cell(): Error creating network socket."); + return -1; + } + fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */ + + memset((void *)&dest_addr,0,sizeof(dest_addr)); + dest_addr.sin_family = AF_INET; + dest_addr.sin_port = conn->port; + memcpy((void *)&dest_addr.sin_addr, &conn->addr, sizeof(uint32_t)); + + log(LOG_DEBUG,"connection_app_process_cell(): Connecting to %s:%u.",conn->address,ntohs(conn->port)); + + if(connect(s,(struct sockaddr *)&dest_addr,sizeof(dest_addr)) < 0){ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + log(LOG_DEBUG,"connection_app_process_cell(): Connect failed."); + return -1; + } else { + /* it's in progress. set state appropriately and return. */ + conn->s = s; + connection_set_poll_socket(conn); + conn->state = APP_CONN_STATE_CONNECTING; + + /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */ + log(LOG_DEBUG,"connection_app_process_cell(): connect in progress, socket %d.",s); + connection_watch_events(conn, POLLOUT | POLLIN); + return 0; + } + } + + /* it succeeded. we're connected. */ + log(LOG_DEBUG,"connection_app_process_cell(): Connection to %s:%u established.",conn->address,ntohs(conn->port)); + + conn->s = s; + connection_set_poll_socket(conn); + conn->state = APP_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + } else { /* i'm not sure what this would be */ + log(LOG_DEBUG,"connection_app_process_cell(): in connecting_wait, not sure why."); + } + return 0; + case APP_CONN_STATE_CONNECTING: + log(LOG_DEBUG,"connection_app_process_cell(): Data receiving while connecting. Queueing."); + /* FIXME kludge. shouldn't call write_to_buf directly. */ + return write_to_buf(cell->payload, cell->length, &conn->outbuf, &conn->outbuflen, &conn->outbuf_datalen); + case APP_CONN_STATE_OPEN: + return connection_write_to_buf(cell->payload, cell->length, conn); + } + + return 0; +} + + + diff --git a/src/or/connection_op.c b/src/or/connection_op.c new file mode 100644 index 000000000..03d36dbe6 --- /dev/null +++ b/src/or/connection_op.c @@ -0,0 +1,130 @@ + +#include "or.h" + +connection_t *connection_op_new(void) { + return connection_new(CONN_TYPE_OP); +} + +connection_t *connection_op_listener_new(void) { + return connection_new(CONN_TYPE_OP_LISTENER); +} + +int connection_op_process_inbuf(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_OP); + + if(conn->inbuf_reached_eof) { + /* eof reached, kill it. */ + log(LOG_DEBUG,"connection_op_process_inbuf(): conn reached eof. Closing."); + return -1; + } + + log(LOG_DEBUG,"connection_op_process_inbuf(): state %d.",conn->state); + + switch(conn->state) { + case OP_CONN_STATE_AWAITING_KEYS: + return op_handshake_process_keys(conn); + case OP_CONN_STATE_OPEN: + return connection_process_cell_from_inbuf(conn); + default: + log(LOG_DEBUG,"connection_op_process_inbuf() called in state where I'm writing. Ignoring buf for now.") +; + } + + return 0; + +} + +int op_handshake_process_keys(connection_t *conn) { + int retval; + int x; + + /* key exchange message */ + unsigned char auth_cipher[128]; + unsigned char auth_plain[128]; + + assert(conn); + + log(LOG_DEBUG,"op_handshake_process_keys() entered."); + + if(conn->inbuf_datalen < 128) /* entire response available? */ + return 0; /* not yet */ + + if(connection_fetch_from_buf(auth_cipher,128,conn) < 0) { + return -1; + } + log(LOG_DEBUG,"op_handshake_process_keys() : Received auth."); + + /* decrypt response */ + retval = RSA_private_decrypt(128,auth_cipher,auth_plain,conn->prkey,RSA_PKCS1_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Decrypting keys from new OP failed."); + log(LOG_DEBUG,"op_handshake_process_keys() : Reason : %s.", + ERR_reason_error_string(ERR_get_error())); + return -1; + } + + log(LOG_DEBUG,"Successfully decrypted keys from new OP."); + + conn->bandwidth = ntohl(*((uint32_t *)auth_plain)); + + memcpy(conn->b_session_key, auth_plain+4, 8); + memcpy(conn->f_session_key, auth_plain+12, 8); + printf("f_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",conn->f_session_key[x]); + } + printf("\nb_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",conn->b_session_key[x]); + } + printf("\n"); + + memset((void *)conn->f_session_iv, 0, 8); + memset((void *)conn->b_session_iv, 0, 8); + + EVP_CIPHER_CTX_init(&conn->f_ctx); + EVP_CIPHER_CTX_init(&conn->b_ctx); + EVP_EncryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv); + EVP_DecryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv); + +#if 0 + /* FIXME must choose conn->aci here? What does it mean for a connection to have an aci? */ + log(LOG_DEBUG,"new_entry_connection : Chosen ACI %u.",conn->aci); +#endif + + conn->state = OP_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + + return 0; +} + +int connection_op_finished_flushing(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_OP); + + switch(conn->state) { + case OP_CONN_STATE_OPEN: + /* FIXME down the road, we'll clear out circuits that are pending to close */ + connection_watch_events(conn, POLLIN); + return 0; + default: + log(LOG_DEBUG,"Bug: connection_op_finished_flushing() called in unexpected state."); + return 0; + } + + return 0; + +} + +int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local) { + log(LOG_DEBUG,"connection_create_op_listener starting"); + return connection_create_listener(prkey, local, CONN_TYPE_OP_LISTENER); +} + +int connection_op_handle_listener_read(connection_t *conn) { + log(LOG_NOTICE,"OP: Received a connection request. Waiting for keys."); + return connection_handle_listener_read(conn, CONN_TYPE_OP, OP_CONN_STATE_AWAITING_KEYS); +} + diff --git a/src/or/connection_or.c b/src/or/connection_or.c new file mode 100644 index 000000000..813cc8975 --- /dev/null +++ b/src/or/connection_or.c @@ -0,0 +1,580 @@ + +#include "or.h" + +/* + * + * these two functions are the main ways 'in' to connection_or + * + */ + +int connection_or_process_inbuf(connection_t *conn) { + + assert(conn && conn->type == CONN_TYPE_OR); + + if(conn->inbuf_reached_eof) { + /* eof reached, kill it. */ + log(LOG_DEBUG,"connection_or_process_inbuf(): conn reached eof. Closing."); + return -1; + } + + log(LOG_DEBUG,"connection_or_process_inbuf(): state %d.",conn->state); + + switch(conn->state) { + case OR_CONN_STATE_CLIENT_AUTH_WAIT: + return or_handshake_client_process_auth(conn); + case OR_CONN_STATE_SERVER_AUTH_WAIT: + return or_handshake_server_process_auth(conn); + case OR_CONN_STATE_SERVER_NONCE_WAIT: + return or_handshake_server_process_nonce(conn); + case OR_CONN_STATE_OPEN: + return connection_process_cell_from_inbuf(conn); + default: + log(LOG_DEBUG,"connection_or_process_inbuf() called in state where I'm writing. Ignoring buf for now."); + } + + return 0; + +} + +int connection_or_finished_flushing(connection_t *conn) { + int e, len=sizeof(e); + + assert(conn && conn->type == CONN_TYPE_OR); + + switch(conn->state) { + case OR_CONN_STATE_CLIENT_CONNECTING: + if (getsockopt(conn->s, SOL_SOCKET, SO_ERROR, &e, &len) < 0) { /* not yet */ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + log(LOG_DEBUG,"connection_or_finished_flushing(): in-progress connect failed. Removing."); + return -1; + } else { + return 0; /* no change, see if next time is better */ + } + } + /* the connect has finished. */ + + log(LOG_DEBUG,"connection_or_finished_flushing() : Connection to router %s:%u established.", + conn->address,ntohs(conn->port)); + + return or_handshake_client_send_auth(conn); + case OR_CONN_STATE_CLIENT_SENDING_AUTH: + log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending auth."); + conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT; + connection_watch_events(conn, POLLIN); + return 0; + case OR_CONN_STATE_CLIENT_SENDING_NONCE: + log(LOG_DEBUG,"connection_or_finished_flushing(): client finished sending nonce."); + conn_or_init_crypto(conn); + conn->state = OR_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + return 0; + case OR_CONN_STATE_SERVER_SENDING_AUTH: + log(LOG_DEBUG,"connection_or_finished_flushing(): server finished sending auth."); + conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT; + connection_watch_events(conn, POLLIN); + return 0; + case OR_CONN_STATE_OPEN: + /* FIXME down the road, we'll clear out circuits that are pending to close */ + connection_watch_events(conn, POLLIN); + return 0; + default: + log(LOG_DEBUG,"Bug: connection_or_finished_flushing() called in unexpected state."); + return 0; + } + + return 0; + +} + +/*********************/ + +connection_t *connection_or_new(void) { + return connection_new(CONN_TYPE_OR); +} + +connection_t *connection_or_new_listener(void) { + return connection_new(CONN_TYPE_OR_LISTENER); +} + +void conn_or_init_crypto(connection_t *conn) { + int x; + + assert(conn); + printf("f_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",conn->f_session_key[x]); + } + printf("\nb_session_key: "); + for(x=0;x<8;x++) { + printf("%d ",conn->b_session_key[x]); + } + printf("\n"); + + memset(conn->f_session_iv,0,8); + memset(conn->b_session_iv,0,8); + EVP_CIPHER_CTX_init(&conn->f_ctx); + EVP_CIPHER_CTX_init(&conn->b_ctx); + EVP_EncryptInit(&conn->f_ctx, EVP_des_ofb(), conn->f_session_key, conn->f_session_iv); + EVP_DecryptInit(&conn->b_ctx, EVP_des_ofb(), conn->b_session_key, conn->b_session_iv); + /* always encrypt with f, always decrypt with b */ + +} + +/* + * + * auth handshake, as performed by OR *initiating* the connection + * + */ + +int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local) { + connection_t *conn; + struct sockaddr_in router_addr; + int s; + + if ((!router) || (!prkey) || (!local)) + return -1; + + if(router->addr == local->sin_addr.s_addr && router->port == local->sin_port) { + /* this is me! don't connect to me. */ + return 0; + } + + conn = connection_or_new(); + + /* set up conn so it's got all the data we need to remember */ + conn->addr = router->addr, conn->port = router->port; + conn->prkey = prkey; + conn->min = router->min, conn->max = router->max; + conn->pkey = router->pkey; + conn->address = strdup(router->address); + memcpy(&conn->local,local,sizeof(struct sockaddr_in)); + + s=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (s < 0) + { + log(LOG_ERR,"Error creating network socket."); + connection_free(conn); + return -1; + } + fcntl(s, F_SETFL, O_NONBLOCK); /* set s to non-blocking */ + + memset((void *)&router_addr,0,sizeof(router_addr)); + router_addr.sin_family = AF_INET; + router_addr.sin_port = router->port; + memcpy((void *)&router_addr.sin_addr, &router->addr, sizeof(uint32_t)); + + log(LOG_DEBUG,"connect_to_router() : Trying to connect to %s:%u.",inet_ntoa(*(struct in_addr *)&router->addr),ntohs(router->port)); + + if(connect(s,(struct sockaddr *)&router_addr,sizeof(router_addr)) < 0){ + if(errno != EINPROGRESS){ + /* yuck. kill it. */ + connection_free(conn); + return -1; + } else { + /* it's in progress. set state appropriately and return. */ + conn->s = s; + + conn->state = OR_CONN_STATE_CLIENT_CONNECTING; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return -1; + } + /* i think only pollout is needed, but i'm curious if pollin ever gets caught -RD */ + log(LOG_DEBUG,"connect_to_router() : connect in progress."); + connection_watch_events(conn, POLLOUT | POLLIN); + return 0; + } + } + + /* it succeeded. we're connected. */ + conn->s = s; + + if(connection_add(conn) < 0) { /* no space, forget it */ + connection_free(conn); + return -1; + } + + log(LOG_DEBUG,"connect_to_router() : Connection to router %s:%u established.",router->address,ntohs(router->port)); + + /* move to the next step in the handshake */ + if(or_handshake_client_send_auth(conn) < 0) { + connection_remove(conn); + connection_free(conn); + return -1; + } + return 0; +} + +int or_handshake_client_send_auth(connection_t *conn) { + int retval; + char keys[16]; + char buf[44]; + char cipher[128]; + + if (!conn) + return -1; + + /* generate random keys */ + retval = RAND_bytes(keys,16); + if (retval != 1) /* error */ + { + log(LOG_ERR,"Cannot generate a secure DES key."); + return -1; + } + log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated DES keys."); + + /* save keys */ + memcpy(conn->f_session_key,keys,8); + memcpy(conn->b_session_key,keys+8,8); + + /* generate first message */ + memcpy(buf,&conn->local.sin_addr,4); /* local address */ + memcpy(buf+4,(void *)&conn->local.sin_port,2); /* local port */ + memcpy(buf+6, (void *)&conn->addr, 4); /* remote address */ + memcpy(buf+10, (void *)&conn->port, 2); /* remote port */ + memcpy(buf+12,keys,16); /* keys */ + *((uint32_t *)(buf+28)) = htonl(conn->min); /* min link utilisation */ + *((uint32_t *)(buf+32)) = htonl(conn->max); /* maximum link utilisation */ + log(LOG_DEBUG,"or_handshake_client_send_auth() : Generated first authentication message."); + + /* encrypt message */ + retval = RSA_public_encrypt(36,buf,cipher,conn->pkey,RSA_PKCS1_PADDING); + if (retval == -1) /* error */ + { + log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_client_send_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error())); + return -1; + } + log(LOG_DEBUG,"or_handshake_client_send_auth() : Encrypted authentication message."); + + /* send message */ + + if(connection_write_to_buf(cipher, 128, conn) < 0) { + log(LOG_DEBUG,"or_handshake_client_send_auth(): my outbuf is full. Oops."); + return -1; + } + retval = connection_flush_buf(conn); + if(retval < 0) { + log(LOG_DEBUG,"or_handshake_client_send_auth(): bad socket while flushing."); + return -1; + } + if(retval > 0) { + /* still stuff on the buffer. */ + conn->state = OR_CONN_STATE_CLIENT_SENDING_AUTH; + connection_watch_events(conn, POLLOUT | POLLIN); + return 0; + } + + /* it finished sending */ + log(LOG_DEBUG,"or_handshake_client_send_auth(): Finished sending authentication message."); + conn->state = OR_CONN_STATE_CLIENT_AUTH_WAIT; + connection_watch_events(conn, POLLIN); + return 0; + +} + +int or_handshake_client_process_auth(connection_t *conn) { + char buf[128]; /* only 44 of this is expected to be used */ + char cipher[128]; + uint32_t min,max; + int retval; + + assert(conn); + + if(conn->inbuf_datalen < 128) /* entire response available? */ + return 0; /* not yet */ + + if(connection_fetch_from_buf(cipher,128,conn) < 0) { + return -1; + } + log(LOG_DEBUG,"or_handshake_client_process_auth() : Received auth."); + + /* decrypt response */ + retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.", + conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.", + ERR_reason_error_string(ERR_get_error())); + return -1; + } + else if (retval != 44) + { + log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.", + conn->address,ntohs(conn->port)); + return -1; + } + log(LOG_DEBUG,"or_handshake_client_process_auth() : Decrypted response."); + /* check validity */ + if ( (memcmp(&conn->local.sin_addr, buf, 4)) || /* local address */ + (memcmp(&conn->local.sin_port, buf+4, 2)) || /* local port */ + (memcmp(&conn->addr, buf+6, 4)) || /* remote address */ + (memcmp(&conn->port, buf+10, 2)) || /* remote port */ + (memcmp(&conn->f_session_key, buf+12, 8)) || /* keys */ + (memcmp(&conn->b_session_key, buf+20, 8))) + { /* incorrect response */ + log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port)); + return -1; + } + + log(LOG_DEBUG,"or_handshake_client_process_auth() : Response valid."); + + /* update link info */ + min = *(uint32_t *)(buf+28); + max = *(uint32_t *)(buf+32); + min = ntohl(min); + max = ntohl(max); + + if (conn->min > min) + conn->min = min; + if (conn->max > max) + conn->max = max; + + /* reply is just local addr/port, remote addr/port, nonce */ + memcpy(buf+12, buf+36, 8); + + /* encrypt reply */ + retval = RSA_public_encrypt(20,buf,cipher,conn->pkey,RSA_PKCS1_PADDING); + if (retval == -1) /* error */ + { + log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_client_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error())); + return -1; + } + + /* send the message */ + + if(connection_write_to_buf(cipher, 128, conn) < 0) { + log(LOG_DEBUG,"or_handshake_client_process_auth(): my outbuf is full. Oops."); + return -1; + } + retval = connection_flush_buf(conn); + if(retval < 0) { + log(LOG_DEBUG,"or_handshake_client_process_auth(): bad socket while flushing."); + return -1; + } + if(retval > 0) { + /* still stuff on the buffer. */ + conn->state = OR_CONN_STATE_CLIENT_SENDING_NONCE; + connection_watch_events(conn, POLLOUT | POLLIN); +/* return(connection_process_inbuf(conn)); process the rest of the inbuf */ + return 0; + } + + /* it finished sending */ + log(LOG_DEBUG,"or_handshake_client_process_auth(): Finished sending nonce."); + conn_or_init_crypto(conn); + conn->state = OR_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + return connection_process_inbuf(conn); /* process the rest of the inbuf */ + +} + +/* + * + * auth handshake, as performed by OR *receiving* the connection + * + */ + +int or_handshake_server_process_auth(connection_t *conn) { + int retval; + + char buf[128]; /* only 42 of this is expected to be used */ + char cipher[128]; + + uint32_t addr; + uint16_t port; + + uint32_t min,max; + routerinfo_t *router; + + assert(conn); + + log(LOG_DEBUG,"or_handshake_server_process_auth() entered."); + + if(conn->inbuf_datalen < 128) /* entire response available? */ + return 0; /* not yet */ + + if(connection_fetch_from_buf(cipher,128,conn) < 0) { + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_auth() : Received auth."); + + /* decrypt response */ + retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.", + conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.", + ERR_reason_error_string(ERR_get_error())); + return -1; + } + else if (retval != 36) + { + log(LOG_ERR,"Received an incorrect authentication request."); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_auth() : Decrypted authentication message."); + + /* identify the router */ + memcpy(&addr,buf,4); /* save the IP address */ + memcpy(&port,buf+4,2); /* save the port */ + + router = router_get_by_addr_port(addr,port); + if (!router) + { + log(LOG_DEBUG,"or_handshake_server_process_auth() : Received a connection from an unknown router. Will drop."); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_auth() : Router identified as %s:%u.", + router->address,ntohs(router->port)); + + if(connection_get_by_addr_port(addr,port)) { + log(LOG_DEBUG,"or_handshake_server_process_auth(): That router is already connected. Dropping."); + return -1; + } + + /* save keys */ + memcpy(conn->b_session_key,buf+12,8); + memcpy(conn->f_session_key,buf+20,8); + + /* update link info */ + min = *(uint32_t *)(buf+28); + max = *(uint32_t *)(buf+32); + min = ntohl(min); + max = ntohl(max); + + conn->min = router->min; + conn->max = router->max; + + if (conn->min > min) + conn->min = min; + if (conn->max > max) + conn->max = max; + + /* copy all relevant info to conn */ + conn->addr = router->addr, conn->port = router->port; + conn->pkey = router->pkey; + conn->address = strdup(router->address); + + /* generate a nonce */ + retval = RAND_pseudo_bytes(conn->nonce,8); + if (retval == -1) /* error */ + { + log(LOG_ERR,"Cannot generate a nonce."); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_auth() : Nonce generated."); + + /* generate message */ + memcpy(buf+36,conn->nonce,8); /* append the nonce to the end of the message */ + *(uint32_t *)(buf+28) = htonl(conn->min); /* send min link utilisation */ + *(uint32_t *)(buf+32) = htonl(conn->max); /* send max link utilisation */ + + /* encrypt message */ + retval = RSA_public_encrypt(44,buf,cipher,conn->pkey,RSA_PKCS1_PADDING); + if (retval == -1) /* error */ + { + log(LOG_ERR,"Public-key encryption failed during authentication to %s:%u.",conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_server_process_auth() : Reason : %s.",ERR_reason_error_string(ERR_get_error())); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_auth() : Reply encrypted."); + + /* send message */ + + if(connection_write_to_buf(cipher, 128, conn) < 0) { + log(LOG_DEBUG,"or_handshake_server_process_auth(): my outbuf is full. Oops."); + return -1; + } + retval = connection_flush_buf(conn); + if(retval < 0) { + log(LOG_DEBUG,"or_handshake_server_process_auth(): bad socket while flushing."); + return -1; + } + if(retval > 0) { + /* still stuff on the buffer. */ + conn->state = OR_CONN_STATE_SERVER_SENDING_AUTH; + connection_watch_events(conn, POLLOUT | POLLIN); + return 0; + } + + /* it finished sending */ + log(LOG_DEBUG,"or_handshake_server_process_auth(): Finished sending auth."); + conn->state = OR_CONN_STATE_SERVER_NONCE_WAIT; + connection_watch_events(conn, POLLIN); + return 0; + +} + +int or_handshake_server_process_nonce(connection_t *conn) { + + char buf[128]; + char cipher[128]; + int retval; + + assert(conn); + + if(conn->inbuf_datalen < 128) /* entire response available? */ + return 0; /* not yet */ + + if(connection_fetch_from_buf(cipher,128,conn) < 0) { + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_nonce() : Received auth."); + + /* decrypt response */ + retval = RSA_private_decrypt(128,cipher,buf,conn->prkey,RSA_PKCS1_PADDING); + if (retval == -1) + { + log(LOG_ERR,"Public-key decryption failed during authentication to %s:%u.", + conn->address,ntohs(conn->port)); + log(LOG_DEBUG,"or_handshake_server_process_nonce() : Reason : %s.", + ERR_reason_error_string(ERR_get_error())); + return -1; + } + else if (retval != 20) + { + log(LOG_ERR,"Received an incorrect response from router %s:%u during authentication.", + conn->address,ntohs(conn->port)); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response decrypted."); + + /* check validity */ + if ((memcmp(&conn->addr,buf, 4)) || /* remote address */ + (memcmp(&conn->port,buf+4,2)) || /* remote port */ + (memcmp(&conn->local.sin_addr,buf+6,4)) || /* local address */ + (memcmp(&conn->local.sin_port,buf+10,2)) || /* local port */ + (memcmp(conn->nonce,buf+12,8))) /* nonce */ + { + log(LOG_ERR,"Router %s:%u failed to authenticate. Either the key I have is obsolete or they're doing something they're not supposed to.",conn->address,ntohs(conn->port)); + return -1; + } + log(LOG_DEBUG,"or_handshake_server_process_nonce() : Response valid. Authentication complete."); + + conn_or_init_crypto(conn); + conn->state = OR_CONN_STATE_OPEN; + connection_watch_events(conn, POLLIN); + return connection_process_inbuf(conn); /* process the rest of the inbuf */ + +} + + +/* ********************************** */ + + +int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local) { + log(LOG_DEBUG,"connection_create_or_listener starting"); + return connection_create_listener(prkey, local, CONN_TYPE_OR_LISTENER); +} + +int connection_or_handle_listener_read(connection_t *conn) { + log(LOG_NOTICE,"OR: Received a connection request from a router. Attempting to authenticate."); + return connection_handle_listener_read(conn, CONN_TYPE_OR, OR_CONN_STATE_SERVER_AUTH_WAIT); +} + diff --git a/src/or/main.c b/src/or/main.c new file mode 100644 index 000000000..85f91ab30 --- /dev/null +++ b/src/or/main.c @@ -0,0 +1,406 @@ + +#include "or.h" + +/********* START VARIABLES **********/ + +/* valid command-line options */ +static char *args = "hf:e:n:l:"; + +int loglevel = LOG_DEBUG; + +/* valid config file options */ +config_opt_t options[] = +{ + {"RouterFile", CONFIG_TYPE_STRING, {0}, 0}, + {"PrivateKeyFile", CONFIG_TYPE_STRING, {0}, 0}, + {"EntryPort", CONFIG_TYPE_INT, {0}, 0}, + {"NetworkPort", CONFIG_TYPE_INT, {0}, 0}, + {"MaxConn", CONFIG_TYPE_INT, {0}, 0}, + {"MaxConnTimeout", CONFIG_TYPE_INT, {0}, 0}, + {"TrafficShaping", CONFIG_TYPE_INT, {0}, 0}, + {0} +}; +enum opts { + RouterFile=0, PrivateKeyFile, EntryPort, NetworkPort, MaxConn, MaxConnTimeout, TrafficShaping +}; + +connection_t *connection_array[MAXCONNECTIONS] = + { NULL }; + +struct pollfd poll_array[MAXCONNECTIONS] = + { [0 ... MAXCONNECTIONS-1] = { -1, 0, 0 } }; + +int nfds=0; /* number of connections currently active */ + +/* default logging threshold */ +extern int loglevel; + +/* private key */ +RSA *prkey = NULL; + +/* router array */ +routerinfo_t **router_array = NULL; +int rarray_len = 0; + +/********* END VARIABLES ************/ + +int connection_add(connection_t *conn) { + + if(nfds >= MAXCONNECTIONS-2) { /* 2, for some breathing room. should count the fenceposts. */ + /* FIXME should use the 'max connections' option */ + log(LOG_DEBUG,"connection_add(): failing because nfds is too high."); + return -1; + } + + conn->poll_index = nfds; + connection_set_poll_socket(conn); + connection_array[nfds] = conn; + + /* zero these out here, because otherwise we'll inherit values from the previously freed one */ + poll_array[nfds].events = 0; + poll_array[nfds].revents = 0; + + nfds++; + + log(LOG_DEBUG,"connection_add(): new conn type %d, socket %d, nfds %d.",conn->type, conn->s, nfds); + + return 0; + +} + +void connection_set_poll_socket(connection_t *conn) { + poll_array[conn->poll_index].fd = conn->s; +} + +int connection_remove(connection_t *conn) { + + int current_index; + + assert(conn); + assert(nfds>0); + + circuit_about_to_close_connection(conn); /* flush and send destroys for all circuits on this conn */ + + current_index = conn->poll_index; + if(current_index == nfds-1) { /* this is the end */ +// connection_free(conn); + nfds--; + log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds); + return 0; + } + + /* we replace this one with the one at the end, then free it */ + nfds--; + poll_array[current_index].fd = poll_array[nfds].fd; + poll_array[current_index].events = poll_array[nfds].events; + poll_array[current_index].revents = poll_array[nfds].revents; + connection_array[current_index] = connection_array[nfds]; + connection_array[current_index]->poll_index = current_index; + + log(LOG_DEBUG,"connection_remove(): nfds now %d.",nfds); + + return 0; +} + +connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port) { + + int i; + connection_t *conn; + + for(i=0;i<nfds;i++) { + conn = connection_array[i]; + assert(conn); + if(conn->addr == addr && conn->port == port) + return conn; + } + + return NULL; + +} + +connection_t *connection_get_by_type(int type) { + + int i; + connection_t *conn; + + for(i=0;i<nfds;i++) { + conn = connection_array[i]; + if(conn->type == type) + return conn; + } + + return NULL; + +} + +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port) { + int i; + routerinfo_t *router; + + if (!router_array) + return NULL; + + for(i=0;i<rarray_len;i++) + { + router = router_array[i]; + if ((router->addr == addr) && (router->port == port)) + return router; + } + + return NULL; + +} + +void connection_watch_events(connection_t *conn, short events) { + + assert(conn && conn->poll_index < nfds); + + poll_array[conn->poll_index].events = events; +} + +void check_conn_read(int i) { + + int retval; + connection_t *conn; + + if(poll_array[i].revents & POLLIN) { /* something to read */ + + conn = connection_array[i]; + assert(conn); + log(LOG_DEBUG,"check_conn_read(): socket %d has something to read.",conn->s); + + if (conn->type == CONN_TYPE_OP_LISTENER) { + retval = connection_op_handle_listener_read(conn); + } else if (conn->type == CONN_TYPE_OR_LISTENER) { + retval = connection_or_handle_listener_read(conn); + } else { + /* else it's an OP, OR, or app */ + retval = connection_read_to_buf(conn); + if (retval >= 0) { /* all still well */ + retval = connection_process_inbuf(conn); + log(LOG_DEBUG,"check_conn_read(): connection_process_inbuf returned %d.",retval); + } + } + + if(retval < 0) { /* this connection is broken. remove it */ + log(LOG_DEBUG,"check_conn_read(): Connection broken, removing."); + connection_remove(conn); + connection_free(conn); + if(i<nfds) { /* we just replaced the one at i with a new one. + process it too. */ + check_conn_read(i); + } + } + } +} + +void check_conn_write(int i) { + + int retval; + connection_t *conn; + + if(poll_array[i].revents & POLLOUT) { /* something to write */ + + conn = connection_array[i]; + log(LOG_DEBUG,"check_conn_write(): socket %d wants to write.",conn->s); + + if(conn->type == CONN_TYPE_OP_LISTENER || + conn->type == CONN_TYPE_OR_LISTENER) { + log(LOG_DEBUG,"check_conn_write(): Got a listener socket. Can't happen!"); + retval = -1; + } else { + /* else it's an OP, OR, or app */ + retval = connection_flush_buf(conn); /* conns in CONNECTING state will fall through... */ + if(retval == 0) { /* it's done flushing */ + retval = connection_finished_flushing(conn); /* ...and get handled here. */ + } + } + + if(retval < 0) { /* this connection is broken. remove it. */ + log(LOG_DEBUG,"check_conn_write(): Connection broken, removing."); + connection_remove(conn); + connection_free(conn); + if(i<nfds) { /* we just replaced the one at i with a new one. + process it too. */ + check_conn_read(i); + } + } + } +} + +void check_conn_marked(int i) { + connection_t *conn; + + conn = connection_array[i]; + assert(conn); + if(conn->marked_for_close) { + log(LOG_DEBUG,"check_conn_marked(): Cleaning up connection."); + connection_flush_buf(conn); /* flush it first */ + connection_remove(conn); + connection_free(conn); + if(i<nfds) { /* we just replaced the one at i with a new one. + process it too. */ + check_conn_read(i); + } + } +} + +#if 0 +void check_conn_hup(int i) { + connection_t *conn; + + if(poll_array[i].revents & POLLHUP) { /* they've hung up */ + conn = connection_array[i]; + log(LOG_DEBUG,"check_conn_hup(): socket %d has hung up.",conn->s); + connection_remove(conn); + connection_free(conn); + + if(i<nfds) { /* we just replaced the one at i with a new one. + process it too. */ + check_conn_hup(i); + } + } +} +#endif + +int do_main_loop(void) { + + int i; + + /* load the routers file */ + router_array = getrouters(options[RouterFile].r.str,&rarray_len); + if (!router_array) + { + log(LOG_ERR,"Error loading router list."); + exit(1); + } + + /* load the private key */ + ERR_load_crypto_strings(); + prkey = load_prkey(options[PrivateKeyFile].r.str); + if (!prkey) + { + log(LOG_ERR,"Error loading private key."); + exit(1); + } + log(LOG_DEBUG,"core : Loaded private key of size %u bytes.",RSA_size(prkey)); + ERR_free_strings(); + + /* try to connect to all the other ORs, and start the listeners */ + retry_all_connections(router_array, rarray_len, prkey, + options[NetworkPort].r.i,options[EntryPort].r.i); + + for(;;) { + poll(poll_array, nfds, -1); /* poll until we have an event */ + + /* do all the reads first, so we can detect closed sockets */ + for(i=0;i<nfds;i++) + check_conn_read(i); + + /* then do the writes */ + for(i=0;i<nfds;i++) + check_conn_write(i); + + /* any of the conns need to be closed now? */ + for(i=0;i<nfds;i++) + check_conn_marked(i); + +#if 0 /* no, check_conn_read() takes care of hups. */ + /* remove the ones that have disconnected */ + for(i=0;i<nfds;i++) + check_conn_hup(i); +#endif + } + +} + +void catch () +{ + errno = 0; /* netcat does this. it looks fun. */ + + log(LOG_DEBUG,"Catching ^c, exiting cleanly."); + + exit(0); + +} + +int main(int argc, char *argv[]) +{ + int retval = 0; + + char *conf_filename = NULL; /* configuration file */ + signal (SIGINT, catch); /* to catch ^c so we can exit cleanly */ + + /* get command-line arguments */ + retval = getargs(argc,argv,args,&conf_filename,&loglevel); + if (retval == -1) + { + log(LOG_ERR,"Error processing command-line arguments."); + exit(1); + } + + /* load config file */ + retval = getconfig(conf_filename,options); + if (retval == -1) + { + log(LOG_ERR,"Error loading configuration file."); + exit(1); + } + else if (options[RouterFile].err != 1) + { + log(LOG_ERR,"RouterFile option required, but not found."); + exit(1); + } + else if (options[PrivateKeyFile].err != 1) + { + log(LOG_ERR,"PrivateKeyFile option required but not found."); + exit(1); + } + else if (options[EntryPort].err != 1) + { + log(LOG_ERR,"EntryPort option required but not found."); + exit(1); + } + else if (options[NetworkPort].err != 1) + { + log(LOG_ERR,"NetworkPort option required but not found."); + exit(1); + } + else if (options[MaxConn].err != 1) + { + log(LOG_ERR,"MaxConn option required but not found."); + exit(1); + } +#if 0 + else if (options[MaxConnTimeout].err != 1) + { + conn_tout.tv_sec = OR_DEFAULT_CONN_TIMEOUT; + } + else + { + if (!options[MaxConnTimeout].r.i) + conn_toutp = NULL; + else + conn_tout.tv_sec = options[MaxConnTimeout].r.i; + } + conn_tout.tv_usec = 0; + + if (!options[TrafficShaping].err) + { + options[TrafficShaping].r.i = DEFAULT_POLICY; + } + else if ((options[TrafficShaping].r.i < 0) || (options[TrafficShaping].r.i > 1)) + { + log(LOG_ERR,"Invalid value for the TrafficShaping option."); + exit(1); + } +#endif + + ERR_load_crypto_strings(); + retval = do_main_loop(); + ERR_free_strings(); + + return retval; + +} + diff --git a/src/or/onion.c b/src/or/onion.c new file mode 100644 index 000000000..d9c8a9c5d --- /dev/null +++ b/src/or/onion.c @@ -0,0 +1,62 @@ + +#include "or.h" + +/********* START VARIABLES **********/ + +tracked_onion_t *tracked_onions = NULL; /* linked list of tracked onions */ +tracked_onion_t *last_tracked_onion = NULL; + +/********* END VARIABLES ************/ + + +int decide_aci_type(uint32_t local_addr, uint16_t local_port, + uint32_t remote_addr, uint16_t remote_port) { + + if(local_addr > remote_addr) + return ACI_TYPE_HIGHER; + if(local_addr < remote_addr) + return ACI_TYPE_LOWER; + if(local_port > remote_port) + return ACI_TYPE_HIGHER; + /* else */ + return ACI_TYPE_LOWER; +} + +int process_onion(circuit_t *circ, connection_t *conn) { + aci_t aci_type; + + if(!decrypt_onion((onion_layer_t *)circ->onion,circ->onionlen,conn->prkey)) { + log(LOG_DEBUG,"command_process_create_cell(): decrypt_onion() failed, closing circuit."); + return -1; + } + log(LOG_DEBUG,"command_process_create_cell(): Onion decrypted."); + + /* check freshness */ + if (((onion_layer_t *)circ->onion)->expire < time(NULL)) /* expired onion */ + { + log(LOG_NOTICE,"I have just received an expired onion. This could be a replay attack."); + return -1; + } + + aci_type = decide_aci_type(conn->local.sin_addr.s_addr, conn->local.sin_port, + ((onion_layer_t *)circ->onion)->addr,((onion_layer_t *)circ->onion)->port); + + if(circuit_init(circ, aci_type) < 0) { + log(LOG_ERR,"process_onion(): init_circuit() failed."); + return -1; + } + + /* check for replay */ + if(id_tracked_onion(circ->onion, circ->onionlen, tracked_onions)) { + log(LOG_NOTICE,"process_onion(): I have just received a replayed onion. This could be a replay attack."); + return -1; + } + + /* track the new onion */ + if(!new_tracked_onion(circ->onion,circ->onionlen, &tracked_onions, &last_tracked_onion)) { + log(LOG_DEBUG,"process_onion(): Onion tracking failed. Will ignore."); + } + + return 0; +} + diff --git a/src/or/or.h b/src/or/or.h new file mode 100644 index 000000000..1d73e123a --- /dev/null +++ b/src/or/or.h @@ -0,0 +1,411 @@ +/* Copyright (c) 2002 Roger Dingledine. See LICENSE for licensing information */ +/* $Id$ */ + +#ifndef __OR_H +#define __OR_H + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <netdb.h> +#include <ctype.h> +#include <sys/poll.h> +#include <sys/types.h> +#include <sys/fcntl.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <assert.h> + +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + +#include "../common/cell.h" +#include "../common/config.h" +#include "../common/key.h" +#include "../common/log.h" +#include "../common/onion.h" +#include "../common/ss.h" + +#define MAXCONNECTIONS 200 /* upper bound on max connections. + can be overridden by config file */ + +#define MAX_BUF_SIZE (64*1024) + +#define ACI_TYPE_LOWER 0 +#define ACI_TYPE_HIGHER 1 + +#define CONN_TYPE_OP_LISTENER 1 +#define CONN_TYPE_OP 2 +#define CONN_TYPE_OR_LISTENER 3 +#define CONN_TYPE_OR 4 +#define CONN_TYPE_APP 5 + +#define LISTENER_STATE_READY 0 + +#define OP_CONN_STATE_AWAITING_KEYS 0 +#define OP_CONN_STATE_OPEN 1 +#if 0 +#define OP_CONN_STATE_CLOSE 2 /* flushing the buffer, then will close */ +#define OP_CONN_STATE_CLOSE_WAIT 3 /* have sent a destroy, awaiting a confirmation */ +#endif + +#define OR_CONN_STATE_CLIENT_CONNECTING 0 +#define OR_CONN_STATE_CLIENT_SENDING_AUTH 1 /* sending address and info */ +#define OR_CONN_STATE_CLIENT_AUTH_WAIT 2 /* have sent address and info, waiting */ +#define OR_CONN_STATE_CLIENT_SENDING_NONCE 3 /* sending nonce, last piece of handshake */ +#define OR_CONN_STATE_SERVER_AUTH_WAIT 4 /* waiting for address and info */ +#define OR_CONN_STATE_SERVER_SENDING_AUTH 5 /* writing auth and nonce */ +#define OR_CONN_STATE_SERVER_NONCE_WAIT 6 /* waiting for confirmation of nonce */ +#define OR_CONN_STATE_OPEN 7 /* ready to send/receive cells. */ + +#define APP_CONN_STATE_CONNECTING_WAIT 0 /* waiting for standard structure or dest info */ +#define APP_CONN_STATE_CONNECTING 1 +#define APP_CONN_STATE_OPEN 2 +#if 0 +#define APP_CONN_STATE_CLOSE 3 /* flushing the buffer, then will close */ +#define APP_CONN_STATE_CLOSE_WAIT 4 /* have sent a destroy, awaiting a confirmation */ +#endif + +#define CIRCUIT_STATE_OPEN_WAIT 0 /* receiving/processing the onion */ +#define CIRCUIT_STATE_OPEN 1 /* onion processed, ready to send data along the connection */ +#define CIRCUIT_STATE_CLOSE_WAIT1 2 /* sent two "destroy" signals, waiting for acks */ +#define CIRCUIT_STATE_CLOSE_WAIT2 3 /* received one ack, waiting for one more + (or if just one was sent, waiting for that one */ +//#define CIRCUIT_STATE_CLOSE 4 /* both acks received, connection is dead */ /* NOT USED */ + +typedef uint16_t aci_t; + +typedef struct +{ + +/* Used by all types: */ + + unsigned char type; + int state; + int s; /* our socket */ + int poll_index; + int marked_for_close; + + char *inbuf; + size_t inbuflen; + size_t inbuf_datalen; + int inbuf_reached_eof; + + char *outbuf; + size_t outbuflen; + size_t outbuf_datalen; + +/* used by OP and App: */ + + uint16_t aci; /* anonymous connection identifier */ + +/* used by OR and OP: */ + + uint32_t bandwidth; /* connection bandwidth */ + int window_sent; /* how many cells can i still send? */ + int window_received; /* how many cells do i still expect to receive? */ + + /* link encryption */ + unsigned char f_session_key[8]; + unsigned char b_session_key[8]; + unsigned char f_session_iv[8]; + unsigned char b_session_iv[8]; + EVP_CIPHER_CTX f_ctx; + EVP_CIPHER_CTX b_ctx; + +// struct timeval lastsend; /* time of last transmission to the client */ +// struct timeval interval; /* transmission interval */ + + uint32_t addr; /* these two uniquely identify a router */ + uint16_t port; + +/* used by app: */ + + ss_t ss; /* standard structure */ + int ss_received; /* size of ss, received so far */ + +/* used by OR, to keep state while connect()ing: Kludge. */ + + RSA *prkey; + struct sockaddr_in local; + + /* link info */ + uint32_t min; + uint32_t max; + + char *address; /* strdup into this, gets free_connection frees it */ + RSA *pkey; /* public RSA key for the other side */ + + char nonce[8]; + +} connection_t; + +/* config stuff we know about the other ORs in the network */ +typedef struct +{ + char *address; + + uint32_t addr; + uint16_t port; + + RSA *pkey; /* public RSA key */ + + /* link info */ + uint32_t min; + uint32_t max; +// struct timeval min_interval; + + /* time when last data was sent to that router */ +// struct timeval lastsend; + + /* socket */ +// int s; + + void *next; +} routerinfo_t; + +/* per-anonymous-connection struct */ +typedef struct +{ +#if 0 + uint32_t p_addr; /* all in network order */ + uint16_t p_port; +#endif + uint32_t n_addr; + uint16_t n_port; + connection_t *p_conn; + connection_t *n_conn; + + aci_t p_aci; /* connection identifiers */ + aci_t n_aci; + + unsigned char p_f; /* crypto functions */ + unsigned char n_f; + + unsigned char p_key[128]; /* crypto keys */ + unsigned char n_key[128]; + + unsigned char p_iv[16]; /* initialization vectors */ + unsigned char n_iv[16]; + + EVP_CIPHER_CTX p_ctx; /* cipher context */ + EVP_CIPHER_CTX n_ctx; + + uint32_t expire; /* expiration time for the corresponding onion */ + + int state; + + unsigned char *onion; /* stores the onion when state is CONN_STATE_OPEN_WAIT */ + uint32_t onionlen; /* total onion length */ + uint32_t recvlen; /* length of the onion so far */ + + void *next; +} circuit_t; + + + + + /* all the function prototypes go here */ + + +/********************************* args.c ***************************/ + +/* print help*/ +void print_usage(); + +/* get command-line arguments */ +int getargs(int argc,char *argv[], char *args,char **conf_filename, int *loglevel); + +/********************************* buffers.c ***************************/ + +int buf_new(char **pbuf, size_t *pbuflen, size_t *pbuf_datalen); + +int buf_free(char *buf); + +int read_to_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen, int *preached_eof); + /* grab from s, put onto buf, return how many bytes read */ + +int flush_buf(int s, char **pbuf, size_t *pbuflen, size_t *pbuf_datalen); + /* push from buf onto s + * then memmove to front of buf + * return -1 or how many bytes remain on the buf */ + +int write_to_buf(char *string, size_t string_len, + char **pbuf, size_t *pbuflen, size_t *pbuf_datalen); + /* append string to buf (growing as needed, return -1 if "too big") + * return total number of bytes on the buf + */ + +int fetch_from_buf(char *string, size_t string_len, + char **pbuf, size_t *pbuflen, size_t *pbuf_datalen); + /* if there is string_len bytes in buf, write them onto string, + * * then memmove buf back (that is, remove them from buf) */ + +/********************************* cell.c ***************************/ + +int check_sane_cell(cell_t *cell); + +/********************************* circuit.c ***************************/ + +void circuit_add(circuit_t *circ); +void circuit_remove(circuit_t *circ); + +circuit_t *circuit_new(aci_t p_aci, connection_t *p_conn); + +/* internal */ +aci_t get_unique_aci_by_addr_port(uint32_t addr, uint16_t port, int aci_type); + +circuit_t *circuit_get_by_aci_conn(aci_t aci, connection_t *conn); +circuit_t *circuit_get_by_conn(connection_t *conn); + +int circuit_deliver_data_cell(cell_t *cell, circuit_t *circ, connection_t *conn, int crypt_type); +int circuit_crypt(circuit_t *circ, char *in, size_t inlen, char crypt_type); + +int circuit_init(circuit_t *circ, int aci_type); +void circuit_free(circuit_t *circ); + +void circuit_close(circuit_t *circ); + +void circuit_about_to_close_connection(connection_t *conn); + /* flush and send destroys for all circuits using conn */ + +/********************************* command.c ***************************/ + +void command_process_cell(cell_t *cell, connection_t *conn); + +void command_process_create_cell(cell_t *cell, connection_t *conn); +void command_process_data_cell(cell_t *cell, connection_t *conn); +void command_process_destroy_cell(cell_t *cell, connection_t *conn); + +/********************************* config.c ***************************/ + +/* loads the configuration file */ +int getconfig(char *filename, config_opt_t *options); + +/********************************* connection.c ***************************/ + +connection_t *connection_new(int type); + +void connection_free(connection_t *conn); + +int connection_create_listener(RSA *prkey, struct sockaddr_in *local, int type); + +int connection_handle_listener_read(connection_t *conn, int new_type, int new_state); + +/* start all connections that should be up but aren't */ +int retry_all_connections(routerinfo_t **router_array, int rarray_len, + RSA *prkey, uint16_t or_port, uint16_t op_port); + +int connection_read_to_buf(connection_t *conn); + +int connection_fetch_from_buf(char *string, int len, connection_t *conn); + +int connection_flush_buf(connection_t *conn); + +int connection_write_to_buf(char *string, int len, connection_t *conn); +int connection_send_destroy(aci_t aci, connection_t *conn); +int connection_encrypt_cell_header(cell_t *cellp, connection_t *conn); +int connection_write_cell_to_buf(cell_t *cellp, connection_t *conn); + +int connection_process_inbuf(connection_t *conn); +int connection_process_cell_from_inbuf(connection_t *conn); + +int connection_finished_flushing(connection_t *conn); + +/********************************* connection_or.c ***************************/ + +int connection_or_process_inbuf(connection_t *conn); +int connection_or_finished_flushing(connection_t *conn); + +connection_t *connection_or_new(void); +connection_t *connection_or_listener_new(void); + +void conn_or_init_crypto(connection_t *conn); + +int or_handshake_client_process_auth(connection_t *conn); +int or_handshake_client_send_auth(connection_t *conn); + +int or_handshake_server_process_auth(connection_t *conn); +int or_handshake_server_process_nonce(connection_t *conn); + +int connect_to_router(routerinfo_t *router, RSA *prkey, struct sockaddr_in *local); + +int connection_or_create_listener(RSA *prkey, struct sockaddr_in *local); +int connection_or_handle_listener_read(connection_t *conn); + +/********************************* connection_op.c ***************************/ + +connection_t *connection_op_new(void); +connection_t *connection_op_listener_new(void); + +int op_handshake_process_keys(connection_t *conn); + +int connection_op_process_inbuf(connection_t *conn); + +int connection_op_finished_flushing(connection_t *conn); + +int connection_op_create_listener(RSA *prkey, struct sockaddr_in *local); + +int connection_op_handle_listener_read(connection_t *conn); + +/********************************* connection_app.c ***************************/ + +connection_t *connection_app_new(void); + +int connection_app_process_inbuf(connection_t *conn); +int connection_app_package_inbuf(connection_t *conn); +int connection_app_process_data_cell(cell_t *cell, connection_t *conn); + +int connection_app_finished_flushing(connection_t *conn); + +/********************************* main.c ***************************/ + +int connection_add(connection_t *conn); +int connection_remove(connection_t *conn); +void connection_set_poll_socket(connection_t *conn); + +connection_t *connection_get_by_addr_port(uint32_t addr, uint16_t port); + +connection_t *connection_get_by_type(int type); + +routerinfo_t *router_get_by_addr_port(uint32_t addr, uint16_t port); + +void connection_watch_events(connection_t *conn, short events); + +void check_conn_read(int i); +void check_conn_marked(int i); +void check_conn_write(int i); + +void check_conn_hup(int i); + +int do_main_loop(void); + +int main(int argc, char *argv[]); + +/********************************* onion.c ***************************/ + +int decide_aci_type(uint32_t local_addr, uint16_t local_port, + uint32_t remote_addr, uint16_t remote_port); + +int process_onion(circuit_t *circ, connection_t *conn); + +/********************************* routers.c ***************************/ + +routerinfo_t **getrouters(char *routerfile, size_t *listlenp); +void delete_routerlist(routerinfo_t *list); +/* create an NULL-terminated array of pointers pointing to elements of a router list */ +routerinfo_t **make_rarray(routerinfo_t* list, size_t *listlenp); + + +#endif diff --git a/src/or/routers.c b/src/or/routers.c new file mode 100644 index 000000000..d24d4d1bd --- /dev/null +++ b/src/or/routers.c @@ -0,0 +1,365 @@ +/** + * routers.c + * Routines for loading the list of routers and their public RSA keys. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +#define OR_ROUTERLIST_SEPCHARS " \t\n" +#define OR_PUBLICKEY_BEGIN_TAG "-----BEGIN RSA PUBLIC KEY-----\n" + +#include "or.h" + +/* delete a list of routers from memory */ +void delete_routerlist(routerinfo_t *list) +{ + routerinfo_t *tmp = NULL; + + if (!list) + return; + + do + { + tmp=list->next; + free((void *)list->address); + RSA_free(list->pkey); + free((void *)list); + list = tmp; + } + while (list != NULL); + + return; +} + +/* create an NULL-terminated array of pointers pointing to elements of a router list */ +/* this is done in two passes through the list - inefficient but irrelevant as this is + * only done once when op/or start up */ +routerinfo_t **make_rarray(routerinfo_t* list, size_t *len) +{ + routerinfo_t *tmp=NULL; + int listlen = 0; + routerinfo_t **array=NULL; + routerinfo_t **p=NULL; + + if ((!list) || (!len)) + return NULL; + + /* get the length of the list */ + tmp = list; + do + { + listlen++; + tmp = tmp->next; + } + while (tmp != NULL); + + array = malloc((listlen+1)*sizeof(routerinfo_t *)); + if (!array) + { + log(LOG_ERR,"Error allocating memory."); + return NULL; + } + + tmp=list; + p = array; + do + { + *p = tmp; + p++; + tmp = tmp->next; + } + while(tmp != NULL); + *p=NULL; + + *len = listlen; + return array; +} + +/* load the router list */ +routerinfo_t **getrouters(char *routerfile, size_t *lenp) +{ + int retval = 0; + char *retp = NULL; + routerinfo_t *router=NULL, *routerlist=NULL, *lastrouter=NULL; + FILE *rf; /* router file */ + fpos_t fpos; + char line[512]; + char *token; + char *errtest; /* detecting errors in strtoul() calls */ + struct hostent *rent; + + if ((!routerfile) || (!lenp)) + return NULL; + + if (strcspn(routerfile,CONFIG_LEGAL_FILENAME_CHARACTERS) != 0) + { + log(LOG_ERR,"Filename %s contains illegal characters.",routerfile); + return NULL; + } + + /* open the router list */ + rf = fopen(routerfile,"r"); + if (!rf) + { + log(LOG_ERR,"Could not open %s.",routerfile); + return NULL; + } + + retp= fgets(line,512,rf); + while (retp) + { + log(LOG_DEBUG,"getrouters():Line :%s",line); + token = (char *)strtok(line,OR_ROUTERLIST_SEPCHARS); + if (token) + { + log(LOG_DEBUG,"getrouters():Token : %s",token); + if (token[0] != '#') /* ignore comment lines */ + { + router = malloc(sizeof(routerinfo_t)); + if (!router) + { + log(LOG_ERR,"Could not allocate memory."); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + +#if 0 + router->conn_bufs = NULL; /* no output buffers */ + router->last_conn_buf = NULL; + router->next_to_service = 0; + + router->s = -1; /* to signify this router is as yet unconnected */ + router->celllen = 0; /* cell buffer is empty */ +#endif + + /* read the address */ + router->address = malloc(strlen(token)+1); + if (!router->address) + { + log(LOG_ERR,"Could not allocate memory."); + fclose(rf); + free((void *)router); + delete_routerlist(routerlist); + return NULL; + } + strcpy(router->address,token); + + rent = (struct hostent *)gethostbyname(router->address); + if (!rent) + { + log(LOG_ERR,"Could not get address for router %s.",router->address); + fclose(rf); + free((void *)router->address); + free((void *)router); + delete_routerlist(routerlist); + return NULL; + } + + memcpy(&router->addr, rent->h_addr,rent->h_length); + + /* read the port */ + token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS); + if (token) + { + log(LOG_DEBUG,"getrouters():Token :%s",token); + router->port = (uint16_t)strtoul(token,&errtest,0); + if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */ + { + /* convert port to network format */ + router->port = htons(router->port); + + /* read min bandwidth */ + token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS); + if (token) /* min bandwidth */ + { + router->min = (uint32_t)strtoul(token,&errtest,0); + if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */ + { + if (router->min) /* must not be zero */ + { + /* read max bandwidth */ + token = (char *)strtok(NULL,OR_ROUTERLIST_SEPCHARS); + if (token) /* max bandwidth */ + { + router->max = (uint32_t)strtoul(token,&errtest,0); + if ((*token != '\0') && (*errtest == '\0')) /* conversion was successful */ + { + if (router->max) /* must not be zero */ + { + /* check that there is a public key entry for that router */ + retval = fgetpos(rf, &fpos); /* save the current file position + * we wil return to it later if we find a public key */ + if (retval == -1) + { + log(LOG_ERR,"Could not save position in %s.",routerfile); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + do /* read through to the next non-empty line */ + { + retp=fgets(line,512,rf); + if (!retp) + { + log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + log(LOG_DEBUG,"getrouters():Line:%s",line); + if ((*line != '#') && ( strspn(line,OR_ROUTERLIST_SEPCHARS) != strlen(line) )) + { + break; + } + } while (1); + + if (!strcmp(line,OR_PUBLICKEY_BEGIN_TAG)) /* we've got the public key */ + { + retval = fsetpos(rf,&fpos); /* get us back to where we were otherwise crypto lib won't find the key */ + if (retval == -1) + { + log(LOG_ERR,"Could not set position in %s.",routerfile); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else /* we found something else; this isn't right */ + { + log(LOG_ERR,"Could not find a public key entry for router %s:%u.",router->address,router->port); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + + log(LOG_DEBUG,"getrouters():Reading the key ..."); + /* read the public key into router->pkey */ + router->pkey=NULL; + router->pkey = PEM_read_RSAPublicKey(rf,&router->pkey,NULL,NULL); + if (!router->pkey) /* something went wrong */ + { + log(LOG_ERR,"Could not read public key for router %s:%u.",router->address,router->port); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + else /* read the key */ + { + log(LOG_DEBUG,"getrouters():Public key size = %u.", RSA_size(router->pkey)); + if (RSA_size(router->pkey) != 128) /* keys MUST be 1024 bits in size */ + { + log(LOG_ERR,"Key for router %s:%u is not 1024 bits. All keys must be exactly 1024 bits long.",router->address,router->port); + free((void *)router->address); + RSA_free(router->pkey); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + router->next = NULL; + /* save the entry into the routerlist linked list */ + if (!routerlist) /* this is the first entry */ + routerlist = router; + else + lastrouter->next = (void *)router; + lastrouter = router; + } + } + else /* maximum link utilisation is zero */ + { + log(LOG_ERR,"Entry for router %s doesn't contain a valid maximum bandwidth entry (must be > 0).",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid maximum bandwidth entry.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a maximum bandwidth entry.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't contain a valid minimum bandwidth entry (must be > 0).",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid minimum bandwidth entry.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a minimum bandwidth entry.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a valid port number.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + else + { + log(LOG_ERR,"Entry for router %s doesn't seem to contain a port number.",router->address); + free((void *)router->address); + free((void *)router); + fclose(rf); + delete_routerlist(routerlist); + return NULL; + } + } + } + retp=fgets(line,512,rf); + } + + fclose(rf); + return make_rarray(routerlist, lenp); +} diff --git a/src/orkeygen/Makefile b/src/orkeygen/Makefile new file mode 100644 index 000000000..20c65c457 --- /dev/null +++ b/src/orkeygen/Makefile @@ -0,0 +1,58 @@ +SRC=orkeygen.c +OBJ=${SRC:.c=.o} +PROGS=orkeygen +LIB=-L/usr/local/ssl/lib +LIBS=-lcrypto +INCLUDE=-I/usr/local/ssl/include + +CFLAGS= $(INCLUDE) +LDFLAGS = $(LIB) ${LIBS} + +all: ${PROGS} + +clean: + rm -f *.o ${PROGS} + +depend: + makedepend -- ${CFLAGS} -- ${SRC} + +${PROGS}: ${OBJ} + $(LINK.c) -o $@ $(OBJ) + +# DO NOT DELETE + + +orkeygen.o: /usr/include/alloca.h /usr/include/asm/errno.h +orkeygen.o: /usr/include/bits/endian.h /usr/include/bits/errno.h +orkeygen.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/sched.h +orkeygen.o: /usr/include/bits/select.h /usr/include/bits/sigset.h +orkeygen.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +orkeygen.o: /usr/include/bits/types.h /usr/include/bits/waitflags.h +orkeygen.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +orkeygen.o: /usr/include/endian.h /usr/include/errno.h /usr/include/features.h +orkeygen.o: /usr/include/_G_config.h /usr/include/gconv.h +orkeygen.o: /usr/include/gnu/stubs.h /usr/include/libio.h +orkeygen.o: /usr/include/linux/errno.h /usr/include/openssl/asn1.h +orkeygen.o: /usr/include/openssl/bio.h /usr/include/openssl/blowfish.h +orkeygen.o: /usr/include/openssl/bn.h /usr/include/openssl/buffer.h +orkeygen.o: /usr/include/openssl/cast.h /usr/include/openssl/crypto.h +orkeygen.o: /usr/include/openssl/des.h /usr/include/openssl/dh.h +orkeygen.o: /usr/include/openssl/dsa.h /usr/include/openssl/e_os2.h +orkeygen.o: /usr/include/openssl/err.h /usr/include/openssl/evp.h +orkeygen.o: /usr/include/openssl/lhash.h /usr/include/openssl/md2.h +orkeygen.o: /usr/include/openssl/md4.h /usr/include/openssl/md5.h +orkeygen.o: /usr/include/openssl/objects.h /usr/include/openssl/obj_mac.h +orkeygen.o: /usr/include/openssl/opensslconf.h /usr/include/openssl/opensslv.h +orkeygen.o: /usr/include/openssl/pem2.h /usr/include/openssl/pem.h +orkeygen.o: /usr/include/openssl/pkcs7.h /usr/include/openssl/rc2.h +orkeygen.o: /usr/include/openssl/rc4.h /usr/include/openssl/ripemd.h +orkeygen.o: /usr/include/openssl/rsa.h /usr/include/openssl/safestack.h +orkeygen.o: /usr/include/openssl/sha.h /usr/include/openssl/stack.h +orkeygen.o: /usr/include/openssl/symhacks.h /usr/include/openssl/x509.h +orkeygen.o: /usr/include/openssl/x509_vfy.h /usr/include/stdio.h +orkeygen.o: /usr/include/stdlib.h /usr/include/sys/cdefs.h +orkeygen.o: /usr/include/sys/select.h /usr/include/sys/sysmacros.h +orkeygen.o: /usr/include/sys/types.h /usr/include/time.h /usr/include/wchar.h +orkeygen.o: /usr/include/xlocale.h +orkeygen.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +orkeygen.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h diff --git a/src/orkeygen/orkeygen.c b/src/orkeygen/orkeygen.c new file mode 100644 index 000000000..718f888f4 --- /dev/null +++ b/src/orkeygen/orkeygen.c @@ -0,0 +1,119 @@ +/** + * orkeygen.c + * Key generation utility. + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.1 2002/01/04 07:19:27 badbytes + * Key generation utility. + * + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <openssl/rsa.h> +#include <openssl/err.h> +#include <openssl/pem.h> + +int main(int argc, char *argv[]) +{ + + char *file_pr = argv[1]; + char *file_pu = argv[2]; + + FILE *f_pr = NULL; + FILE *f_pu = NULL; + + RSA *rsa_key = NULL; + + int retval = 0; + + + if (argc < 3) + { + printf("Need two files, for private and public key in that order.\n"); + exit(1); + } + + /* generate the key */ + rsa_key = RSA_generate_key(1024,65535,NULL,NULL); + if (!rsa_key) /* error has occured */ + { + printf("%s",ERR_reason_error_string(ERR_get_error())); + exit(1); + } + else /* keys generated */ + { + retval = RSA_check_key(rsa_key); + if (retval == 1) + { + printf("Generated key seems to be valid.\n"); + /* open the output files */ + f_pr = fopen(file_pr,"w"); + if (!f_pr) + { + perror("fopen"); + RSA_free(rsa_key); + exit(1); + } + + f_pu = fopen(file_pu,"w"); + if (!f_pu) + { + perror("fopen"); + RSA_free(rsa_key); + exit(1); + } + + /* write the private key */ + retval = PEM_write_RSAPrivateKey(f_pr,rsa_key,NULL,NULL,0,0,NULL); + if (retval == 0) + { + printf("%s",ERR_reason_error_string(ERR_get_error())); + fclose(f_pr); + fclose(f_pu); + RSA_free(rsa_key); + exit(1); + } + + /* write the public key */ + retval = PEM_write_RSAPublicKey(f_pu,rsa_key); + if (retval == 0) + { + printf("%s",ERR_reason_error_string(ERR_get_error())); + fclose(f_pr); + fclose(f_pu); + RSA_free(rsa_key); + exit(1); + } + + printf("Keys written to files %s (public) and %s (private).\n",file_pu,file_pr); + } + else if (retval == 0) + { + printf("Generated key seems to be invalid. Exiting.\n"); + RSA_free(rsa_key); + exit(1); + } + else if (retval == -1) + { + printf("%s",ERR_reason_error_string(ERR_get_error())); + RSA_free(rsa_key); + exit(1); + } + } + + RSA_free(rsa_key); + fclose(f_pu); + fclose(f_pr); + exit(0); + + } diff --git a/src/smtpap/Makefile b/src/smtpap/Makefile new file mode 100644 index 000000000..e10605e0a --- /dev/null +++ b/src/smtpap/Makefile @@ -0,0 +1,80 @@ +SRC=smtpap.c io.c +OBJ=${SRC:.c=.o} +PROGS=smtpap +LIB= +LIBS= +INCLUDE = + +CFLAGS= $(INCLUDE) -Wall -Wpointer-arith -O2 -ggdb +LDFLAGS = $(LIB) $(LIBS) + +all: ${PROGS} + +clean: + rm -f *.o ${PROGS} + +depend: + makedepend -- ${CFLAGS} -- ${SRC} + +${PROGS}: ${OBJ} + $(LINK.c) -o $@ $(OBJ) ../common/log.o ../common/config.o ../common/utils.o + +# DO NOT DELETE + + +io.o: ../common/log.h ../common/utils.h io.h smtpap.h +io.o: /usr/include/bits/endian.h /usr/include/bits/pthreadtypes.h +io.o: /usr/include/bits/sched.h /usr/include/bits/select.h +io.o: /usr/include/bits/sigset.h /usr/include/bits/sockaddr.h +io.o: /usr/include/bits/stdio_lim.h /usr/include/bits/time.h +io.o: /usr/include/bits/types.h /usr/include/bits/wchar.h +io.o: /usr/include/endian.h /usr/include/features.h /usr/include/_G_config.h +io.o: /usr/include/gconv.h /usr/include/gnu/stubs.h /usr/include/libio.h +io.o: /usr/include/malloc.h /usr/include/stdio.h /usr/include/string.h +io.o: /usr/include/sys/cdefs.h /usr/include/syslog.h /usr/include/sys/select.h +io.o: /usr/include/sys/syslog.h /usr/include/sys/sysmacros.h +io.o: /usr/include/sys/time.h /usr/include/sys/types.h /usr/include/sys/un.h +io.o: /usr/include/time.h /usr/include/wchar.h /usr/include/xlocale.h +io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +io.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +smtpap.o: ../common/config.h ../common/log.h ../common/ss.h ../common/utils.h +smtpap.o: ../common/version.h io.h smtpap.h /usr/include/alloca.h +smtpap.o: /usr/include/arpa/inet.h /usr/include/asm/errno.h +smtpap.o: /usr/include/asm/sigcontext.h /usr/include/asm/socket.h +smtpap.o: /usr/include/asm/sockios.h /usr/include/bits/byteswap.h +smtpap.o: /usr/include/bits/confname.h /usr/include/bits/endian.h +smtpap.o: /usr/include/bits/environments.h /usr/include/bits/errno.h +smtpap.o: /usr/include/bits/in.h /usr/include/bits/local_lim.h +smtpap.o: /usr/include/bits/netdb.h /usr/include/bits/posix1_lim.h +smtpap.o: /usr/include/bits/posix2_lim.h /usr/include/bits/posix_opt.h +smtpap.o: /usr/include/bits/pthreadtypes.h /usr/include/bits/resource.h +smtpap.o: /usr/include/bits/sched.h /usr/include/bits/select.h +smtpap.o: /usr/include/bits/sigaction.h /usr/include/bits/sigcontext.h +smtpap.o: /usr/include/bits/siginfo.h /usr/include/bits/signum.h +smtpap.o: /usr/include/bits/sigset.h /usr/include/bits/sigstack.h +smtpap.o: /usr/include/bits/sigthread.h /usr/include/bits/sockaddr.h +smtpap.o: /usr/include/bits/socket.h /usr/include/bits/stdio_lim.h +smtpap.o: /usr/include/bits/time.h /usr/include/bits/types.h +smtpap.o: /usr/include/bits/uio.h /usr/include/bits/waitflags.h +smtpap.o: /usr/include/bits/waitstatus.h /usr/include/bits/wchar.h +smtpap.o: /usr/include/bits/wordsize.h /usr/include/bits/xopen_lim.h +smtpap.o: /usr/include/ctype.h /usr/include/endian.h /usr/include/errno.h +smtpap.o: /usr/include/features.h /usr/include/_G_config.h +smtpap.o: /usr/include/gconv.h /usr/include/getopt.h /usr/include/gnu/stubs.h +smtpap.o: /usr/include/libio.h /usr/include/limits.h +smtpap.o: /usr/include/linux/errno.h /usr/include/linux/limits.h +smtpap.o: /usr/include/netdb.h /usr/include/netinet/in.h +smtpap.o: /usr/include/rpc/netdb.h /usr/include/signal.h /usr/include/stdint.h +smtpap.o: /usr/include/stdio.h /usr/include/stdlib.h /usr/include/string.h +smtpap.o: /usr/include/sys/cdefs.h /usr/include/syslog.h +smtpap.o: /usr/include/sys/resource.h /usr/include/sys/select.h +smtpap.o: /usr/include/sys/socket.h /usr/include/sys/syslog.h +smtpap.o: /usr/include/sys/sysmacros.h /usr/include/sys/time.h +smtpap.o: /usr/include/sys/types.h /usr/include/sys/ucontext.h +smtpap.o: /usr/include/sys/uio.h /usr/include/sys/un.h /usr/include/sys/wait.h +smtpap.o: /usr/include/time.h /usr/include/ucontext.h /usr/include/unistd.h +smtpap.o: /usr/include/wait.h /usr/include/wchar.h /usr/include/xlocale.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/limits.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stdarg.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/stddef.h +smtpap.o: /usr/lib/gcc-lib/i386-redhat-linux/2.96/include/syslimits.h diff --git a/src/smtpap/io.c b/src/smtpap/io.c new file mode 100644 index 000000000..57ea72854 --- /dev/null +++ b/src/smtpap/io.c @@ -0,0 +1,133 @@ +#include <sys/time.h> +#include <stdarg.h> +#include <stdio.h> +#include <malloc.h> + +#include "../common/log.h" +#include "../common/utils.h" + +#include "smtpap.h" +#include "io.h" + +/* connection timeout */ +extern struct timeval *conn_toutp; + +/* printf-like function used to send messages to a socket */ +int sendmessage(int s, char *buf, size_t buflen, const char *format, ...) +{ + int retval = 0; + va_list ap; + + if (!buf) + return -1; + + va_start(ap,format); + retval = vsnprintf(buf,buflen, format, ap); + va_end(ap); + + if (retval < 0) + { + log(LOG_DEBUG,"sendmessage() : could not print to buffer"); + return -1; + } + + log(LOG_DEBUG,"sendmessage() : printed this to buffer : %s",buf); + + retval = write_tout(s,buf,(size_t)retval, conn_toutp); + if (retval < 0) + { + log(LOG_DEBUG,"sendmessage() : could not send"); + return -1; + } + + return retval; +} + +/* receive a response from the recipient SMTP server into *op_in + * Can handle multi-line responses. */ +int receive(int s, char **inbuf,size_t *inbuflen, int flags) +{ + int inputlen = 0; /* running total length of the input */ + int inputerror = 0; /* has an error occured? */ + int retval = 0; /* used for saving function return values */ + + /* for processing multi-line responses */ + int i=0; + + /* storing old values of *inbuf and *inbuflen */ + char *inbuf_old = NULL; + size_t inbuflen_old=0; + + if ((!inbuf) || (!*inbuf) || (!inbuflen)) + return -1; + + /* saving old values in case we need to restore them */ + inbuf_old = *inbuf; + inbuflen_old = *inbuflen; + + do + { + if (inputlen == *inbuflen-1) /* we need to increase the buffer size */ + { + /* increase the size of the buffer */ + *inbuflen += 512; + + *inbuf = (char *)realloc(*inbuf,(size_t)*inbuflen); + if (!*inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + *inbuf = inbuf_old; + *inbuflen = inbuflen_old; + inputerror = 1; + break; + } + } + + retval=read_tout(s,*inbuf+inputlen,(size_t)(*inbuflen-inputlen-1),flags, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */ + if (retval <= 0) + { + log(LOG_ERR,"Error occured while receiving data."); + inputerror = 1; + break; + } + else + { + inputerror = 0; + inputlen += retval; + + /* exit clause if we have received CRLF, otherwise we need to keep reading*/ + /* also keep on reading if it's a multi-line response */ + if (inputlen >= SMTPAP_CRLF_LEN) + { + if (!strncmp(*inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN)) /* entire line received */ + { + /* now check wether we should expect more lines */ + /* find the <CRLF> sequence which occurs one before last */ + for(i=inputlen-SMTPAP_CRLF_LEN-1; i > 0; i--) /* move backwards, start just before the final CRLF */ + { + if ((*inbuf)[i] == SMTPAP_LF) /* got a LF */ + { + /* check for a CR preceding it */ + if ((*inbuf)[i-1] == SMTPAP_CR) /* got a CR */ + break; + } + } + if (i==0) /* correct the offset if no CRLF found */ + i=-1; + + /* check the 4th character after the <CRLF> to see if it is - or <SP> */ + if ((*inbuf)[i+4] != '-') /* no more lines */ + break; + } + } + } + } while(1); + + if (!inputerror) + { + (*inbuf)[inputlen]=0; /* add the terminating NULL character */ + return inputlen; + } + + return -1; +} diff --git a/src/smtpap/io.h b/src/smtpap/io.h new file mode 100644 index 000000000..d5515adb4 --- /dev/null +++ b/src/smtpap/io.h @@ -0,0 +1,2 @@ +int sendmessage(int s, char *buf, size_t buflen, const char *format, ...); +int receive(int s, char **inbuf,size_t *inbuflen, int flags); diff --git a/src/smtpap/smtpap.c b/src/smtpap/smtpap.c new file mode 100644 index 000000000..701dde809 --- /dev/null +++ b/src/smtpap/smtpap.c @@ -0,0 +1,1393 @@ +/** + * smtpap.c + * SMTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.32 2002/04/02 14:29:49 badbytes + * Final finishes. + * + * Revision 1.31 2002/03/25 08:03:17 badbytes + * Added header sanitization. + * + * Revision 1.30 2002/03/02 23:54:06 mp292 + * Fixed missing CRLFs at the end of error messages. + * + * Revision 1.29 2002/01/29 01:00:10 mp292 + * All network operations are now timeoutable. + * + * Revision 1.28 2002/01/28 21:38:18 mp292 + * Fixed bugs in RSET handling. Added Anonimize option which signifies whether + * the router should falsify the identity of the sender or not. + * + * Revision 1.27 2002/01/26 22:45:02 mp292 + * Now handles SS_ERROR_INVALID_PORT. + * + * Revision 1.26 2002/01/26 22:33:21 mp292 + * Removed hard-coded values for onion proxy return codes. Also fixed a bug in + * parameter checking. + * + * Revision 1.25 2002/01/26 21:58:27 mp292 + * Added some missing parameter checking. + * + * Revision 1.24 2002/01/26 21:50:17 mp292 + * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network + * timeouts. + * + * Revision 1.23 2002/01/18 21:07:02 mp292 + * (a) THe form of HELO is now HELO Anonymous.Smtp.Daemon rather than the real + * address. (b) The user *can* now specify a default SMTP daemon to route through + * although this is insecure and not recommended. + * + * Revision 1.22 2002/01/16 23:01:58 mp292 + * First phase of system testing completed (main functionality). + * + * Revision 1.21 2002/01/16 17:04:01 mp292 + * Bug in checking whether incoming connection is local or not. + * + * Revision 1.20 2002/01/09 09:18:22 badbytes + * Now handles EINTR error from accept(). + * + * Revision 1.19 2001/12/19 11:15:27 badbytes + * Corrected AF_INET to PF_INET in socket() calls. + * + * Revision 1.18 2001/12/19 08:36:04 badbytes + * Incorrect error checking in recv() calls caused zombies ... fixed + * + * Revision 1.17 2001/12/18 14:42:46 badbytes + * Variable name op_port_str was incorrectly named, changed to dest_port_str + * + * Revision 1.16 2001/12/18 13:20:16 badbytes + * Some error messages did not include a terminating <CRLF> + * + * Revision 1.15 2001/12/18 12:37:23 badbytes + * Found an overflow bug ... + * + * Revision 1.14 2001/12/18 09:17:31 badbytes + * Corrected a spelling mistake in print_usage() + * + * Revision 1.13 2001/12/14 13:13:24 badbytes + * Changed types.h references to ss.h + * + * Revision 1.12 2001/12/14 09:17:25 badbytes + * Moved function stolower(char *str) from smtpap.c to common/utils.c + * + * Revision 1.11 2001/12/13 13:51:05 badbytes + * Fixed a bug in processing command-line parameters. + * + * Revision 1.10 2001/12/13 13:36:44 badbytes + * Now accepts the -l command-line option which specifies the logging threshold. + * + * Revision 1.9 2001/12/12 16:02:29 badbytes + * Testing completed. + * + * Revision 1.8 2001/12/11 16:30:20 badbytes + * Some bugs removed, still testing though. + * + * Revision 1.7 2001/12/11 14:12:20 badbytes + * Onion Proxy connection setup completed. Proceeding to test. + * + * Revision 1.6 2001/12/11 10:43:21 badbytes + * MAIL and RCPT handling completed. Still coding connection to Onion Proxy. + * + * Revision 1.5 2001/12/10 16:10:35 badbytes + * Wrote a tokenize() function to help with parsing input from SMTP clients. + * + * Revision 1.4 2001/12/07 15:02:43 badbytes + * Server setup code completed. + * + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <wait.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> + +#include "../common/log.h" +#include "../common/config.h" +#include "../common/ss.h" +#include "../common/utils.h" +#include "../common/version.h" + +#include "smtpap.h" +#include "io.h" + +int loglevel = LOG_ERR; +struct timeval conn_tout; +struct timeval *conn_toutp = &conn_tout; + +/* valid command-line options */ +static const char *args = "hf:p:l:"; + +/* valid config file options */ +static config_opt_t options[] = +{ + {"OnionProxy", CONFIG_TYPE_INT, {0}, 0}, + {"MaxConn", CONFIG_TYPE_INT, {0}, 0}, + {"Anonimize", CONFIG_TYPE_INT, {0}, 0}, + {"ConnTimeout", CONFIG_TYPE_INT, {0}, 0}, + {0} +}; +enum opts { + OnionProxy=0,MaxConn, Anonimize, ConnTimeout +}; + +/* number of open connections */ +int connections=0; + +/* prints help on using smtpap */ +void print_usage() +{ + char *program = "smtpap"; + + printf("\n%s - SMTP application proxy for Onion Routing.\nUsage : %s -f config [-p port -l loglevel -h]\n-h : display this help\n-f config : config file\n-p port : port number which %s should bind to\n-l loglevel : logging threshold; one of alert|crit|err|warning|notice|info|debug\n\n", program,program,program); +} + +/* used for reaping zombie processes */ +void sigchld_handler(int s) +{ + while (wait(NULL) > 0); + connections--; +} + +/* takes the contents of a RCPT command in a null-terminated string and retrieves the address + * of the corresponding recipient domain*/ +char *extract_smtp_dest(char *rcptbuf) +{ + char *dest_smtp=NULL; + char *pos1, *pos2; + + if (!rcptbuf) + return NULL; + + pos1 = (char *)strchr(rcptbuf,'@'); + if (pos1 == NULL) + return NULL; + + pos2 = (char *)strpbrk(pos1,SMTPAP_PATH_SEPCHARS); + if (pos2 == NULL) + return NULL; + else + { + dest_smtp = (char *)malloc((size_t)(pos2-pos1)); + if (!dest_smtp) + { + log(LOG_ERR,"Could not allocate memory."); + return NULL; + } + else + { + strncpy(dest_smtp,pos1+1,(size_t)(pos2-pos1-1)); + dest_smtp[pos2-pos1-1] = 0; + } + } + + return dest_smtp; +} + +/* process a DATA stream and remove any e-mail headers */ +int sanitize_data(unsigned char **buf, int *buflen) +{ + unsigned char *offset; /* offset to data after the last header */ + unsigned char *crlf = NULL; + unsigned char *colon = NULL; + unsigned char *line; + unsigned char *newbuf; + int newbuflen; + + if ((!buf) || (!buflen)) /* invalid parameters */ + return -1; + + offset = *buf; + line = *buf; + /* process the data line by line and discard anything that looks like a header */ + while(1) + { + /* find the end of line */ + crlf = strstr(line, SMTPAP_CRLF); + if (crlf) + { + colon = (unsigned char *)memchr((void *)line,(int)':',crlf-line); + if (!colon) + break; /* this doesn't seem to be a header, can stop */ + else + offset = crlf + 2; /* discard this line */ + + line = crlf + 2; /* move on to the next line */ + } + else /* no more CRLFs found, end of data */ + /* NB : there is no need to check the current line at this stage as this will be of the form <CRLF>.<CRLF> */ + /* we should never reach this point in the code anyway, the '.' will be trapped as a non-header line in the above code */ + break; + } + + if (offset != *buf) /* data changed */ + { + newbuflen = *buflen - (offset - *buf); + newbuf = (unsigned char *)malloc(newbuflen+1); /* leave space for a terminating NULL character */ + if (!newbuf) /* malloc() error */ + return -1; + else + { + /* copy into the new buffer */ + memcpy((void *)newbuf, (void *)offset, newbuflen); + newbuf[newbuflen] = 0; + + /* replace the old buffer with the new one */ + free((void *)*buf); + *buf = newbuf; + *buflen = newbuflen; + } + } + + return 0; +} + +/* main logic of smtpap */ +int handle_connection(int s, struct hostent *local, struct sockaddr_in remote, u_short op_port) +{ + int retval = 0; + int state = 0; /* 0 - start / RSET received + * 1 - connection not local, waiting for QUIT + * 2 - connection local, waiting for HELO/EHLO + * 3 - HELO/EHLO received, waiting for MAIL + * 4 - MAIL received, waiting for RCPT + * 5 - waiting for DATA + * 6 - DATA received, accepting data + * - data accepted, back to state 3 + */ + int islocal = 0; + char *cp; + int i=0; + char message[512]; /* for storing outgoing messages */ + char *inbuf = NULL; /* for storing incoming messages */ + char *token = NULL; /* next token in the incoming message */ + char *tmpbuf = NULL; /* temporary buffer for copying data */ + char *mailbuf = NULL; /* storing the MAIL command */ + char **rcptarray = NULL; /* storing a NULL-terminated array of RCPT commands */ + char *rcptbuf = NULL; /* storing a single RCPT command */ + int tmpbuflen = 0; /* length of tmpbuflen in bytes */ + int inbuflen = 0; /* length of inbuf in bytes */ + int inputlen = 0; /* length of actual input in bytes */ + int mailbuflen=0; /* etc ... */ + int rcptbuflen=0; + int inputerror=0; /* error occured when receiving data from the client */ + + /* the following is used for conecting to the SMTP host through the OR network */ + char *dest_addr_str = NULL; /* for storing the ASCII address of the destination SMTP */ + int sop=-1; /* socket for connecting to the onion proxy */ + struct sockaddr_in op_addr; /* stores the address of the onion proxy */ + ss_t ss; /* standard structure */ + char dest_port_str[6]; /* ascii representation of the destination port */ + /* input and output buffers for talking to the onion proxy */ + char *op_out = NULL; + char *op_in = NULL; + int op_outlen = 0; + int op_inlen = 0; + + int partial_dataend = 0; /* used for recognising the <CRLF>.<CRLF> sequence that ends the DATA segment */ + + if (!local) + return -1; + + log(LOG_DEBUG, "handle_connection() : Local address = %s.", inet_ntoa(*(struct in_addr *)local->h_addr)); + log(LOG_DEBUG, "handle_connection() : Remote address = %s.", inet_ntoa(remote.sin_addr)); + + /* first check that the connection is from the local host, otherwise reject */ + if (*(uint32_t *)&remote.sin_addr == inet_addr("127.0.0.1")) + islocal = 1; + for (i=0; (local->h_addr_list[i] != NULL) && (!islocal); i++) + { + cp = local->h_addr_list[i]; + log(LOG_DEBUG,"handle_connection() : Checking if connection is from address %s.",inet_ntoa(*(struct in_addr *)cp)); + if (!memcmp(&remote.sin_addr, cp, sizeof(struct in_addr))) + islocal = 1; + } + + if (islocal) + { + log(LOG_DEBUG,"handle_connection() : Connection seems to be local. Will accept."); + state = 2; + sendmessage(s, (char *)message, (size_t)512, "220 This is smtpap v1.0 running on %s.%s",local->h_name,SMTPAP_CRLF); + } + else + { + log(LOG_DEBUG,"handle_connection() : Connection doesn't seem to be local. Will reject."); + state = 1; + sendmessage(s,(char *)message, (size_t)512,"554 smtpap v1.0 Connection refused. Only local connections allowed.%s",SMTPAP_CRLF); + } + + /* initially allocate 512 bytes for incoming message buffer */ + inbuf = (char *)malloc((size_t)512); + if (!inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + return -1; + } + inbuflen = 512; + + /* initially allocate 512 bytes for the temporary buffer */ + tmpbuf = (char *)malloc((size_t)512); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + free(inbuf); + return -1; + } + tmpbuflen = 512; + + while(1) + { + inputlen = 0; + do + { + if (inputlen == inbuflen-1) /* we need to increase the buffer size */ + { + /* increase the size of the buffers */ + inbuflen += 512; + tmpbuflen += 512; + + inbuf = (char *)realloc(inbuf,(size_t)inbuflen); + if (!inbuf) + { + log(LOG_ERR,"Could not allocate memory."); + inputerror = 1; + break; + } + + tmpbuf = (char *)realloc(tmpbuf,(size_t)tmpbuflen); + if (!tmpbuf) + { + log(LOG_ERR,"Could not allocate memory."); + free(inbuf); + inputerror = 1; + break; + } + } + + retval=read_tout(s,inbuf+inputlen,(size_t)(inbuflen-inputlen-1),0, conn_toutp); /* subtract 1 from inbuflen to leave space for \0 */ + if (retval <= 0) + { + log(LOG_ERR,"Error occured while receiving data."); + inputerror = 1; + break; + } + else + { + inputerror = 0; + inputlen += retval; + + /* exit clause if we have received CRLF or SMTPAP_ENDDATA, otherwise we need to keep reading*/ + if ( (state == 6) && (inputlen >= SMTPAP_ENDDATA_LEN) ) + { + if (!strncmp(inbuf+inputlen-SMTPAP_ENDDATA_LEN,SMTPAP_ENDDATA,SMTPAP_ENDDATA_LEN)) + break; + } + else if ( (state != 6) && (inputlen >= SMTPAP_CRLF_LEN) ) + { + if (!strncmp(inbuf+inputlen-SMTPAP_CRLF_LEN,SMTPAP_CRLF,SMTPAP_CRLF_LEN)) + break; + } + } + } while(1); + + if (inputerror != 0) + break; + + if (*inbuf == EOF) + { + log(LOG_DEBUG,"handle_connection() : Received EOF. Exiting."); + break; + } + + inbuf[inputlen]=0; /* add the terminating NULL character */ + log(LOG_DEBUG, "Received this from client : %s",inbuf); + + /* save a copy of inbuf into tmpbuf, because calls to strtok() will change it */ + strcpy(tmpbuf,inbuf); + + /* now handle input depending on the state */ + + /* first check for a quit */ + token = stolower((char *)strtok(inbuf,SMTPAP_SEPCHARS)); + log(LOG_DEBUG,"handle_connection() : First token is %s.",token); + if ((!strcmp(token,SMTPAP_QUIT)) && (state != 6)) /* QUIT command - but doesn't count in state 6 + * That's when we are receiving DATA input + */ + { + sendmessage(s,(char *)message, (size_t)512,"221 %s closing connection. Goodbye.%s",local->h_name,SMTPAP_CRLF); + break; + } + /* check for a RSET */ + if ((!strcmp(token,SMTPAP_RSET)) && (state !=6)) /* RSET command - again, doesn't count in state 6 */ + { + sendmessage(s,(char *)message,(size_t)512,"250 RSET received.%s",SMTPAP_CRLF); + /* clean up message state */ + if (mailbuf != NULL) + { + free(mailbuf); + mailbuf = NULL; + } + if (rcptarray != NULL) + { + free(rcptarray); + rcptarray = NULL; + } + if (rcptbuf != NULL) + { + free(rcptbuf); + rcptbuf=NULL; + } + + close(sop); + + /* set state to 2/3 (depending on wether we have recieved HELO yet) and loop back and start again */ + if (state != 2) + state=3; + + continue; + } + + if (state == 1) + { + sendmessage(s,(char *)message, (size_t)512,"503 Connection refused. Please QUIT.%s",SMTPAP_CRLF); + } + else if (state == 2) + { + if ((!strcmp(token,SMTPAP_HELO)) || (!strcmp(token,SMTPAP_EHLO))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) /* no more tokens in inbuf */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO without arguments."); + sendmessage(s,(char *)message,(size_t)512,"500 HELO/EHLO requires domain address.%s",SMTPAP_CRLF); + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Received HELO/EHLO with the following domain address : %s.",token); + state =3; + sendmessage(s,(char *)message,(size_t)512,"250 Hello user at %s. Pleased to meet you.%s",inet_ntoa(remote.sin_addr),SMTPAP_CRLF); + } + } + else + sendmessage(s,(char *)message,(size_t)512,"503 Expecting either HELO/EHLO or QUIT.%s",SMTPAP_CRLF); + } + else if (state == 3) + { + int further_check=0; + if ((!strncmp(token,SMTPAP_MAIL,SMTPAP_MAIL_LEN))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) + { + sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address> .%s",SMTPAP_CRLF); + } + else + { + stolower(token); + if (!strcmp(token,"from:")) /* from: separate from the address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* expected another parameter but it's not there */ + { + log(LOG_DEBUG,"handle_connection() : Received MAIL From: without an address."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check = 1; + } + else if (!strcmp(token,"from")) /* probably from : address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received Mail From with no other parameters."); + sendmessage(s,(char *)message,(size_t)512, "500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check=0; + } + else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */ + { + token++; + further_check=1; + } + else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received Mail From : with no other parameters."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL From: requires sender address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check =1; + } + else /* couldn't find a colon (:) */ + { + log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received MAIL command."); + sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF); + further_check = 1; + } + } + else /* probably from:address */ + { + if (!strncmp(token,"from:",5)) /* string starts with from: */ + { + token += 5; /* skip the from: bit */ + further_check = 1; /* continue further checking */ + } + else /* error */ + { + log(LOG_DEBUG,"handle_connection() : MAIL parameters don't start with from: ."); + sendmessage(s,(char *)message,(size_t)512,"500 MAIL requires From:<sender@address>.%s",SMTPAP_CRLF); + further_check=0; + } + } + if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else + * but straightforward <user@host> representation, <> optional + */ + { + if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */ + { + log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a reverse path, which I can't handle."); + sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple return address.%s",SMTPAP_CRLF); + } + else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */ + { + log(LOG_DEBUG,"handle_connection() : The client specified a sender address with no @."); + sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF); + } + else /* the mail command seems to be OK, save it */ + { + if (mailbuf != NULL) + free(mailbuf); + mailbuflen = strlen(tmpbuf) + 1; + mailbuf = (char *)malloc(mailbuflen); + if (!mailbuf) + { + log(LOG_ERR,"Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + } + else + { + strncpy(mailbuf,tmpbuf,mailbuflen); + mailbuf[mailbuflen-1] = '\0'; /* add the terminating NULL character */ + log(LOG_DEBUG,"handle_connection() : MAIL command saved as %s.",mailbuf); + + /* send an OK response to the client */ + sendmessage(s,(char *)message,(size_t)512,"250 Sender address OK.%s",SMTPAP_CRLF); + state=4; + } + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Need MAIL first.%s",SMTPAP_CRLF); + } + else if(state == 4) + { + int further_check=0; + if ((!strcmp(token,SMTPAP_RCPT))) + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (!token) + { + sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address> .%s",SMTPAP_CRLF); + } + else + { + stolower(token); + if (!strcmp(token,"to:")) /* to: separate from the address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* expected another parameter but it's not there */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To: without an address."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check = 1; + } + else if (!strcmp(token,"to")) /* probably to : address or to :address */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To with no other parameters."); + sendmessage(s,(char *)message,(size_t)512, "500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check=0; + } + else if ( (token[0] == ':') && (token[1]!='\0') ) /* contains :address */ + { + token++; + further_check=1; + } + else if ( (token[0] == ':') && (token[1]=='\0') )/* the address is in the next token */ + { + token = (char *)strtok(NULL,SMTPAP_SEPCHARS); + if (token == NULL) /* not enough parameters */ + { + log(LOG_DEBUG,"handle_connection() : Received RCPT To : with no other parameters."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT To: requires recipient address.%s",SMTPAP_CRLF); + further_check = 0; + } + else /* continue further checking */ + further_check =1; + } + else /* couldn't find a colon (:) */ + { + log(LOG_DEBUG,"handle_connection() : Couldn't find a colon in the received RCPT command."); + sendmessage(s,(char *)message,(size_t)512,"500 There is a colon (:) missing in that command.%s",SMTPAP_CRLF); + further_check = 1; + } + } + else /* probably to:address */ + { + if (!strncmp(token,"to:",3)) /* string starts with from: */ + { + token += 3; /* skip the to: bit */ + further_check = 1; /* continue further checking */ + } + else /* error */ + { + log(LOG_DEBUG,"handle_connection() : RCPT parameters don't start with to: ."); + sendmessage(s,(char *)message,(size_t)512,"500 RCPT requires To:<recipient@address>.%s",SMTPAP_CRLF); + further_check=0; + } + } + if (further_check == 1) /* check that this is in the correct, format - we can't handle anything else + * but straightforward <user@host> representation, <> optional + */ + { + if (((cp = (char *)strchr(token,',')) != NULL) || ((cp = (char *)strchr(token,':')) != NULL)) /* path contains , or : - can't cope with that */ + { + log(LOG_DEBUG,"handle_connection() : The client is probably trying to specify a forward path, which I can't handle."); + sendmessage(s,(char *)message,(size_t)512,"500 I can only handle a simple recipient address.%s",SMTPAP_CRLF); + } + else if ((cp = (char *)strchr(token,'@')) == NULL) /* no @, that is most likely a problem :-) */ + { + log(LOG_DEBUG,"handle_connection() : The client specified a recipient address with no @."); + sendmessage(s,(char *)message,(size_t)512,"500 Domain name required.%s",SMTPAP_CRLF); + } + else /* the rcpt command seems to be OK, save it */ + { + if (rcptbuf != NULL) + { + free(rcptbuf); + rcptbuf = NULL; + } + rcptbuflen = strlen(tmpbuf) + 1; + rcptbuf = (char *)malloc(rcptbuflen); + if (!rcptbuf) + { + log(LOG_ERR,"Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + } + else + { + strncpy(rcptbuf,tmpbuf,rcptbuflen); + rcptbuf[rcptbuflen-1] = '\0'; /* add the terminating NULL character */ + log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT command saved."); + + /* attempt to connect to the destination SMTP server through the OR network */ + /* first extract the destination address */ + dest_addr_str = extract_smtp_dest(rcptbuf); + log(LOG_DEBUG,"handle_connection() : handle_connection : called extract_smtp_dest()"); + if (!dest_addr_str) + { + log(LOG_DEBUG,"handle_connection() : Could not extract a destination SMTP address from the specified recipient address."); + sendmessage(s,(char *)message,(size_t)512,"550 Could not extract destination domain.%s",SMTPAP_CRLF); + } + else + { + /* fill in the standard structure */ + ss.version = VERSION; + ss.protocol= SS_PROTOCOL_SMTP; + ss.retry_count = 0; + ss.addr_fmt = SS_ADDR_FMT_ASCII_HOST_PORT; + + /* open a socket for connecting to the proxy */ + sop = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sop < 0) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Error opening socket."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF); + if (dest_addr_str != NULL) { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Socket opened."); + memset((void *)&op_addr,0,sizeof(op_addr)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + op_addr.sin_family=AF_INET; + op_addr.sin_port=htons(op_port); + memcpy((void *)&op_addr.sin_addr,local->h_addr,local->h_length); + log(LOG_DEBUG,"handle_connection() : Trying to connect to %s at port %u.",inet_ntoa(*((struct in_addr *)local->h_addr)),op_port); + + /* try to connect */ + retval = connect(sop,(struct sockaddr *)&op_addr,sizeof(op_addr)); + if (retval == -1) + { + sendmessage(s,(char *)message,(size_t)512,"451 Could not connect to the onion proxy.%s",SMTPAP_CRLF); + close(sop); + if (dest_addr_str != NULL) + { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else /* connection established, now send the standard structure + address and wait for a response */ + { + /* write the message to the op_out buffer */ + snprintf(dest_port_str,6,"%u",htons(SMTPAP_DEFAULT_SMTP_PORT)); + + if (op_out != NULL) + { + free(op_out); + op_out = NULL; + } + + op_outlen = sizeof(ss) /* standard structure */ + + strlen(dest_addr_str) /* destination address */ + + 1 /* terminating NULL character */ + + strlen(dest_port_str) + + 1; /* terminating NULL character */ + op_out = (char *)malloc(op_outlen); + + if (!op_out) /* error */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + if (dest_addr_str != NULL) + { + free(dest_addr_str); + dest_addr_str = NULL; + } + } + else + { + memcpy(op_out,(void *)&ss,sizeof(ss)); + strcpy(op_out+sizeof(ss), dest_addr_str); + strcpy(op_out+sizeof(ss)+strlen(dest_addr_str)+1,dest_port_str); + /* now send the message */ + retval = write_tout(sop,op_out,op_outlen,conn_toutp); + /* now clean up the buffers */ + op_outlen = 0; + free(op_out); + free(dest_addr_str); + if (retval == -1) /* send failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : send() failed."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not send to onion proxy.%s",SMTPAP_CRLF); + close(sop); + } + else /* send seemed to have succeeded */ + { + /* wait for the return code */ + op_inlen = 1; + op_in = (char *)malloc(op_inlen); + if (!op_in) /* memory allocation failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_conection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + } + else + { + retval = read_tout(sop,op_in,1,0, conn_toutp); + if (retval <= 0) /* recv() failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : recv() failed."); + sendmessage(s,(char *)message,(size_t)512,"451 Could not receive data from the onion proxy.%s",SMTPAP_CRLF); + close(sop); + } + else + { + if (!(*op_in)) /* onion proxy says OK */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : received E_SUCCESS from onion proxy"); + /* clean up */ + free(op_in); + op_inlen=0; + + /* allocate both op_in and op_out 512 bytes, the maximum size of an SMTP line */ + op_outlen=512; + op_inlen=512; + op_out = (char *)malloc(512); + op_in = (char *)malloc(512); + if ((!op_out) || (!op_in)) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Could not allocate memory."); + sendmessage(s,(char *)message,(size_t)512,"451 Insufficient memory.%s",SMTPAP_CRLF); + close(sop); + } + else + { + /* receive the greeting message from the recipient */ + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) /* could not receive greeting */ + { + + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving greeting from recipient."); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + } + else /* received greeting */ + { + /* send HELO command */ + retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"HELO ANONYMOUS.smtp.daemon%s",SMTPAP_CRLF); + if (retval == -1) + { + sendmessage(s,(char *)message,(size_t)512,"451 Error sending HELO to the recipient."); + close(sop); + } + else + { + + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving HELO response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + /* send MAIL */ + if (options[Anonimize].r.i) + retval = sendmessage(sop,(char *)op_out,(size_t)op_outlen,"MAIL From:anonymous@anon.net%s",SMTPAP_CRLF); + else + retval = write_tout(sop,mailbuf,mailbuflen-1,conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error sending MAIL to recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending MAIL to the recipient.%s",SMTPAP_CRLF); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + } + else + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving MAIL response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + /* send RCPT */ + retval = write_tout(sop,rcptbuf,rcptbuflen-1,conn_toutp); /* rcptbuflen includes the terminating NULL character but we don't want to send that */ + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error sending RCPT to recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending RCPT to the recipient.%s",SMTPAP_CRLF); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + } + else + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving RCPT response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + close(sop); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"250 Recipient OK.%s",SMTPAP_CRLF); + state = 5; + } + else /* didn't like my RCPT */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : RCPT unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my RCPT.%s",SMTPAP_CRLF); + } + } + } + } + else /* didn't like my MAIL */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : MAIL unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my MAIL.%s",SMTPAP_CRLF); + } + } + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : HELO unsuccessful"); + sendmessage(sop,(char *)op_out,(size_t)op_outlen,"%s%s",SMTPAP_QUIT,SMTPAP_CRLF); + close(sop); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my HELO.%s",SMTPAP_CRLF); + } + } + } + } + } + } + else + { + log(LOG_DEBUG,"handle_connection() : handle_connection : onion proxy returned non-zero error code %d.",*op_in); + close(sop); + switch(*op_in) + { + case SS_ERROR_VERSION_UNSUPPORTED : + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Protocol version not supported).%s",SMTPAP_CRLF); + break; + case SS_ERROR_ADDR_FMT_UNSUPPORTED: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Address format not recognised).%s",SMTPAP_CRLF); + break; + case SS_ERROR_INVALID_ADDRESS: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination address).%s",SMTPAP_CRLF); + break; + case SS_ERROR_INVALID_PORT: + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned an error (Invalid destination port).%s",SMTPAP_CRLF); + break; + default : + sendmessage(s,(char *)message,(size_t)512,"500 Onion proxy returned unexpected error code %d.%s",*op_in,SMTPAP_CRLF); + break; + } + /* clean up */ + free(op_in); + op_inlen=0; + } + } + } + } + } + } + } + } + } + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Need RCPT first.%s",SMTPAP_CRLF); + } + else if (state == 5) + { + if ((!strcmp(token,SMTPAP_DATA))) /* received data */ + { + partial_dataend = 0; + retval = write_tout(sop, tmpbuf, strlen(tmpbuf), conn_toutp); /* send DATA */ + if (retval == -1) /* send(0) failed */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to send DATA to recipient."); + sendmessage(s,(char *)message,(size_t)512,"451 Error sending DATA to the recipient.%s",SMTPAP_CRLF); + } + else /* get response from the recipient */ + { + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving DATA response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Error receiving data from the recipient.%s",SMTPAP_CRLF); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '3') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"354 Enter mail, end with \".\" on a line by itself%s",SMTPAP_CRLF); + state = 6; + } + else /* didn't like my DATA */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful"); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF); + } + } + } + } + else + sendmessage(s,(char *)message, (size_t)512,"503 Expecting DATA.%s",SMTPAP_CRLF); + } + else if (state == 6) + { + /* sanitize the data stream if necessary */ + if (options[Anonimize].r.i == 1) + { + log(LOG_DEBUG,"handle_connection() : Sanitizing headers ..."); + retval = sanitize_data((unsigned char **)&tmpbuf, &inputlen); + } + + if ((!retval) || (!options[Anonimize].r.i)) /* sanitization successsful (or wasn't necessary)? */ + { + log(LOG_DEBUG,"handle_connection() : Attempting to send ..."); + /* forward to recipient */ + retval = write_tout(sop,tmpbuf, inputlen, conn_toutp); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Failed to forward data to recipient."); + sendmessage(sop, (char *)op_out, (size_t)op_outlen,"451 Failed to forward data to the recipient.%s",SMTPAP_CRLF); + } + else + { + /* get the response */ + retval = receive(sop,&op_in,(size_t *)&op_inlen,0); + if (retval == -1) + { + log(LOG_DEBUG,"handle_connection() : handle_connection : error receiving response from recipient"); + sendmessage(s,(char *)message,(size_t)512,"451 Data sent but did not receive a response from the recipient%s.",SMTPAP_CRLF); + } + else + { + op_in[retval]=0; + log(LOG_DEBUG,"handle_connection() : handle_connection : Received this from recipient : %s.",op_in); + if (op_in[0] == '2') /* success */ + { + sendmessage(s,(char *)message,(size_t)512,"250 Message accepted for delivery.%s",SMTPAP_CRLF); + sendmessage(sop, (char *)op_out, (size_t)op_outlen,"QUIT%s",SMTPAP_CRLF); + } + else /* didn't like my DATA */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : DATA unsuccessful"); + sendmessage(s,(char *)message,(size_t)512,"500 Recipient SMTP daemon rejected my DATA.%s",SMTPAP_CRLF); + } + } + } + } + else /* sanitization error */ + { + log(LOG_ERR,"Unable to sanitize an incoming message. Will reject."); + sendmessage(sop,(char *)op_out, (size_t)op_outlen,"400 Failed to sanitize the data stream.%s", SMTPAP_CRLF); + } + + /* after state 6 we go back to state 3, regardless of wether the transfer was succesful or not */ + state = 3; + close(sop); + free(op_in);op_in=NULL; + free(op_out);op_out=NULL; + } + else /* unexpected state */ + { + log(LOG_DEBUG,"handle_connection() : handle_connection : Unexpected state!"); + log(LOG_ERR,"An unexpected error has occured. Closing connection."); + sendmessage(s,(char *)message,(size_t)512,"500 An unexpected error has ocurred. Closing connection.%s",SMTPAP_CRLF); + break; + } + } + + /* clean up */ + if (inbuf != NULL) + free(inbuf); + if (tmpbuf != NULL) + free(tmpbuf); + if (mailbuf != NULL) + free(mailbuf); + if (rcptbuf != NULL) + free(rcptbuf); + if (rcptarray != NULL) + free(rcptarray); + if (dest_addr_str != NULL) + free(dest_addr_str); + if (op_in != NULL) + free(op_in); + if (op_out != NULL) + free(op_out); + close(sop); + close(s); + + return retval; +} + +int main(int argc, char *argv[]) +{ + int retval = 0; + + char c; /* command-line option */ + + /* configuration file */ + char *conf_filename = NULL; + FILE *cf = NULL; + + struct hostent *local_host; + char local_hostname[512]; + + struct sockaddr_in local, remote; /* local and remote address info */ + + int request_sock; /* where we listen for connections */ + int new_sock; /* for accepted connections */ + + size_t sin_size; /* for accept() calls */ + + u_short p; /* smtp proxy port */ + u_short op_port; /* onion proxy port */ + + /* used for reaping zombie processes */ + struct sigaction sa; + + char *errtest = NULL; /* for detecting strtoul() errors */ + + /* set default listening port */ + p = htons(SMTPAP_LISTEN_PORT); + + /* deal with program arguments */ + if ((argc < 2) && (argc > 5)) /* to few or too many arguments*/ + { + print_usage(); + return -1; + } + + opterr = 0; + while ((c = getopt(argc,argv,args)) != -1) + { + switch(c) + { + case 'f': /* config file */ + conf_filename = optarg; + break; + case 'p': + p = htons((u_short)strtoul(optarg,&errtest,0)); + if (errtest == optarg) /* error */ + { + log(LOG_ERR,"Error : -p must be followed by an unsigned positive integer value."); + print_usage(); + return -1; + } + break; + case 'h': + print_usage(); + return 0; + break; + case 'l': + if (!strcmp(optarg,"emerg")) + loglevel = LOG_EMERG; + else if (!strcmp(optarg,"alert")) + loglevel = LOG_ALERT; + else if (!strcmp(optarg,"crit")) + loglevel = LOG_CRIT; + else if (!strcmp(optarg,"err")) + loglevel = LOG_ERR; + else if (!strcmp(optarg,"warning")) + loglevel = LOG_WARNING; + else if (!strcmp(optarg,"notice")) + loglevel = LOG_NOTICE; + else if (!strcmp(optarg,"info")) + loglevel = LOG_INFO; + else if (!strcmp(optarg,"debug")) + loglevel = LOG_DEBUG; + else + { + log(LOG_ERR,"Error : argument to -l must be one of alert|crit|err|warning|notice|info|debug."); + print_usage(); + return -1; + } + break; + case '?': + if (isprint(c)) + log(LOG_ERR,"Missing argument or unknown option '-%c'.",optopt); + else + log(LOG_ERR,"Unknown option character 'x%x'.",optopt); + print_usage(); + return -1; + break; + default: + abort(); + } + } + + /* the -f option is mandatory */ + if (conf_filename == NULL) + { + log(LOG_ERR,"You must specify a config file with the -f option. See help (-h)."); + return -1; + } + + /* load config file */ + cf = open_config(conf_filename); + if (!cf) + { + log(LOG_ERR,"Could not open configuration file %s.",conf_filename); + return -1; + } + retval = parse_config(cf,options); + if (retval) + return -1; + + if (options[OnionProxy].err != 1) + { + log(LOG_ERR,"The OnionProxy option is mandatory."); + return -1; + } + + if (options[MaxConn].err != 1) + { + log(LOG_ERR,"The MaxConn option is mandatory."); + return -1; + } + + if (options[Anonimize].err != 1) + { + log(LOG_ERR,"The Anonimize option is mandatory."); + return -1; + } + else if ((options[Anonimize].r.i != 0) && (options[Anonimize].r.i != 1)) + { + log(LOG_ERR,"The Anonimize option takes the values 1 or 0."); + return -1; + } + + if (options[ConnTimeout].err != 1) + { + conn_tout.tv_sec = SMTPAP_DEFAULT_CONN_TIMEOUT; + } + else + { + if (!options[ConnTimeout].r.i) + conn_toutp = NULL; + else + conn_tout.tv_sec = options[ConnTimeout].r.i; + } + conn_tout.tv_usec = 0; + + op_port = (u_short)options[OnionProxy].r.i; + + /* get local address so that we know where to get the onion proxy when we need it */ + retval = gethostname(local_hostname, (size_t)512); + if (retval < 0) + { + log(LOG_ERR,"Error getting local hostname"); + return -1; + } + local_host = gethostbyname(local_hostname); + if (!local_host) + { + log(LOG_ERR,"Error getting local address."); + return -1; + } + log(LOG_DEBUG,"main() : Got local address : %s.",local_hostname); + + /* get the server up and running */ + request_sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (request_sock < 0) + { + log(LOG_ERR,"Error opening socket."); + return -1; + } + log(LOG_DEBUG,"Socket opened."); + memset((void *)&local,0,sizeof(local)); /* clear the structure first */ + /* set up the sockaddr_in structure */ + local.sin_family=AF_INET; + local.sin_addr.s_addr = INADDR_ANY; + local.sin_port=p; + /* bind it to the socket */ + retval = bind(request_sock,(struct sockaddr *)&local, sizeof(local)); + if (retval < 0) + { + log(LOG_ERR,"Error binding socket to local port %d.",ntohs(p)); + return retval; + } + log(LOG_DEBUG,"Socket bound to port %d.",ntohs(p)); + /* listen for connections */ + retval = listen(request_sock,SOMAXCONN); + if (retval < 0) + { + log(LOG_ERR,"Could not listen for connections."); + return retval; + } + log(LOG_DEBUG,"Listening for connections."); + /* server should now be up and running */ + + /* install the signal handler for making sure zombie processes are killed */ + sa.sa_handler = sigchld_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + retval = sigaction(SIGCHLD,&sa,NULL); + if (retval < 0) + { + log(LOG_ERR,"Could not install a signal handler."); + return -1; + } + + /* main server loop */ + /* I use a forking server technique - this isn't the most efficient way to do it, + * but it is simpler. */ + while(1) + { + sin_size = sizeof(struct sockaddr_in); + new_sock = accept(request_sock,(struct sockaddr *)&remote,&sin_size); + if (new_sock == -1) + { + if (errno != EINTR) + log(LOG_ERR,"Could not accept socket connection."); + else + log(LOG_DEBUG,"Interrupt received."); + continue; + } + if (connections >= options[MaxConn].r.i) + { + log(LOG_NOTICE,"Number of maximum connections reached. Rejecting incoming request."); + close(new_sock); + continue; + } + + log(LOG_DEBUG,"Accepted a connection from %s.",inet_ntoa(remote.sin_addr)); + connections++; + + if (!fork()) /* this is the child process */ + { + close(request_sock); /* the child doesn't need the request socket anymore */ + + /* Main logic of smtpap. */ + retval = handle_connection(new_sock, local_host, remote, op_port); + /* End main logic */ + + exit(retval); /* done, exit */ + } + + close(new_sock); /* don't need this anymore */ + } + + return retval; + +} + diff --git a/src/smtpap/smtpap.h b/src/smtpap/smtpap.h new file mode 100644 index 000000000..3e875f4d1 --- /dev/null +++ b/src/smtpap/smtpap.h @@ -0,0 +1,90 @@ +/** + * smtpap.h + * SMTP Application Proxy for Onion Routing + * + * Matej Pfajfar <mp292@cam.ac.uk> + */ + +/* + * Changes : + * $Log$ + * Revision 1.1 2002/06/26 22:45:50 arma + * Initial revision + * + * Revision 1.12 2002/01/29 01:00:10 mp292 + * All network operations are now timeoutable. + * + * Revision 1.11 2002/01/26 21:50:17 mp292 + * Reviewed according to Secure-Programs-HOWTO. Still need to deal with network + * timeouts. + * + * Revision 1.10 2001/12/18 14:56:29 badbytes + * *** empty log message *** + * + * Revision 1.9 2001/12/18 14:43:19 badbytes + * Added DEFAULT_SMTP_PORT. + * + * Revision 1.8 2001/12/12 16:02:29 badbytes + * Testing completed. + * + * Revision 1.7 2001/12/11 10:43:21 badbytes + * MAIL and RCPT handling completed. Still coding connection to Onion Proxy. + * + * Revision 1.6 2001/12/10 16:10:35 badbytes + * Wrote a tokenize() function to help with parsing input from SMTP clients. + * + * Revision 1.5 2001/12/07 15:02:43 badbytes + * Server setup code completed. + * + */ + +#ifndef __SMTPAP_H + +#define __SMTPAP_H + +#define SMTPAP_CRLF "\015\012" +#define SMTPAP_CRLF_LEN 2 + +#define SMTPAP_CR '\015' +#define SMTPAP_LF '\012' + +/* terminator for DATA input */ +#define SMTPAP_ENDDATA "\015\012.\015\012" +#define SMTPAP_ENDDATA_LEN 5 + +/* characters that separate tokens in SMTPAP commands */ +#define SMTPAP_SEPCHARS " \t\015\012" /* for general commands */ +#define SMTPAP_PATH_SEPCHARS " \t\015\012<>" /* for forward and reverse path */ + +/* default listening port */ +#define SMTPAP_LISTEN_PORT 25 + +/* default SMTP port */ +#define SMTPAP_DEFAULT_SMTP_PORT 25 + +/* default connection timeout */ +#define SMTPAP_DEFAULT_CONN_TIMEOUT 120; /* 120s */ + +/* SMTP commands and their lengths */ +#define SMTPAP_QUIT "quit" +#define SMTPAP_QUIT_LEN 4 + +#define SMTPAP_HELO "helo" +#define SMTPAP_HELO_LEN 4 +#define SMTPAP_EHLO "ehlo" +#define SMTPAP_EHLO_LEN 4 + +#define SMTPAP_MAIL "mail" +#define SMTPAP_MAIL_LEN 4 + +#define SMTPAP_RSET "rset" +#define SMTPAP_RSET_LEN 4 + +#define SMTPAP_RCPT "rcpt" +#define SMTPAP_RCPT_LEN 4 + +#define SMTPAP_DATA "data" +#define SMTPAP_DATA_LEN 4 + +#endif + |