Skip to content

Commit

Permalink
Daemon/service is now self-contained, using libmicrohttpd
Browse files Browse the repository at this point in the history
Cables communication now uses libmicrohttpd instead of depending
on complex nginx/spawn-fcgi/fcgiwrap integration. Files are served
directly from certificate and <msgid> directories, and [service]
requests are also fulfilled directly by the daemon (no process
spawning). Service requests handler has been rewritten to recover
from all possible error conditions; the handler is now also
thread-safe.

The API has not changed -- however, it is now stricter: extra '/'
delimiters are not allowed anymore. Request API does not hide
errors anymore.

Listen address is configured in /etc/profile using CABLE_{HOST,PORT}
variables.

Cables username is read once upon startup; the daemon must be
restarted if username changes.

No mallocs are performed by the code during operation, although
libmicrohttpd and standard library do memory allocation for their
purposes.

Makefile now tracks dependencies, and is more friendly towards
environment CFLAGS, which is appended (instead of prepended)
to the switches in makefile. Feature defines are now only set
in the makefile -- including _FILE_OFFSET_BITS=64, which must
conform to libmicrohttpd's compile-time setting.

Valgrind is used in the testing script to track potential memory
and file descriptor leaks. Testing is now nearly identical to actual
deployment, since nginx shortcut is not necessary anymore. Mockup
script for curl only replaces hostnames with localhost references.
  • Loading branch information
Maxim Kammerer committed Jun 4, 2012
1 parent f72f931 commit 1fdd170
Show file tree
Hide file tree
Showing 17 changed files with 903 additions and 800 deletions.
20 changes: 13 additions & 7 deletions cable/cms
Expand Up @@ -98,13 +98,13 @@ keysdir="${ssldir}"/private
#
# <peer>
#
# out: derive.pem, rpeer.sig
# out: derive.pem, rpeer.sig[atomic]
#
# <--- rpeer.sig
#
# <send>
# in: message, username, {ca,verify}.pem, rpeer.sig
# out: speer.sig, message.enc, {send,recv,ack}.mac
# out: speer.sig[atomic], message.enc[atomic], {send,recv,ack}.mac
#
# ---> speer.sig, message.enc, send.mac
#
Expand All @@ -119,7 +119,8 @@ keysdir="${ssldir}"/private
# ---> ack.mac
case ${cmd} in
peer)
rm -f -- "${msgdir}"/derive.pem "${msgdir}"/rpeer.der "${msgdir}"/rpeer.sig
rm -f -- "${msgdir}"/derive.pem "${msgdir}"/rpeer.der "${msgdir}"/rpeer.sig \
"${msgdir}"/rpeer.sig.tmp

# generate ephemeral peer key
openssl genpkey -paramfile "${modp18}" \
Expand All @@ -134,7 +135,8 @@ peer)
-signer "${certdir}"/verify.pem \
-inkey "${keysdir}"/sign.pem \
-in "${msgdir}"/rpeer.der \
-out "${msgdir}"/rpeer.sig
-out "${msgdir}"/rpeer.sig.tmp
mv -- "${msgdir}"/rpeer.sig.tmp "${msgdir}"/rpeer.sig

rm -- "${msgdir}"/rpeer.der
;;
Expand All @@ -143,7 +145,8 @@ peer)
send)
rm -f -- "${msgdir}"/derive.pem "${msgdir}"/speer.der "${msgdir}"/rpeer.der \
"${msgdir}"/speer.sig "${msgdir}"/shared.key "${msgdir}"/message.enc \
"${msgdir}"/send.mac "${msgdir}"/recv.mac "${msgdir}"/ack.mac
"${msgdir}"/send.mac "${msgdir}"/recv.mac "${msgdir}"/ack.mac \
"${msgdir}"/speer.sig.tmp "${msgdir}"/message.enc.tmp

# verify certificates chain
verify_certs "${msgdir}"
Expand Down Expand Up @@ -175,7 +178,9 @@ send)
-signer "${certdir}"/verify.pem \
-inkey "${keysdir}"/sign.pem \
-in "${msgdir}"/speer.der \
-out "${msgdir}"/speer.sig
-out "${msgdir}"/speer.sig.tmp
mv -- "${msgdir}"/speer.sig.tmp "${msgdir}"/speer.sig


# deterministically derive encryption and MAC keys from shared secret
enckey=`openssl dgst -mac hmac -${enchash} -macopt key:encrypt "${msgdir}"/shared.key | cut -d' ' -f2`
Expand Down Expand Up @@ -204,7 +209,8 @@ send)
openssl cms -EncryptedData_encrypt -binary -${encalg} -outform pem \
-secretkey ${enckey} \
-in "${msgdir}"/message \
-out "${msgdir}"/message.enc
-out "${msgdir}"/message.enc.tmp
mv -- "${msgdir}"/message.enc.tmp "${msgdir}"/message.enc

rm -- "${msgdir}"/derive.pem "${msgdir}"/speer.der "${msgdir}"/rpeer.der \
"${msgdir}"/shared.key
Expand Down
44 changes: 2 additions & 42 deletions cable/comm
@@ -1,7 +1,7 @@
#!/bin/sh -e

if [ $# != 2 ]; then
echo "Format: $0 send|peer|recv|ack|fin <msgid>"
echo "Format: $0 send|recv|ack|fin <msgid>"
exit 1
fi

Expand All @@ -10,8 +10,6 @@ fi
username=`cat ${CABLE_CERTS}/certs/username | tr -cd a-z2-7`
queue=${CABLE_QUEUES}/queue
rqueue=${CABLE_QUEUES}/rqueue
pubqueue=${CABLE_PUB}/"${username}"/queue
pubrqueue=${CABLE_PUB}/"${username}"/rqueue

# Parameters
cmd="$1"
Expand All @@ -35,23 +33,6 @@ urlprefix() {
}


# Atomic no-clobber copy
atomicnccopy() {
local src="$1" dst="$2"

if [ ! -e "${dst}" ]; then
# prevent race condition in possible implementations of max retry-num
if cp -T "${src}" "${dst}".new && [ -e "${src}" ]; then
chmod 640 "${dst}".new
mv -T "${dst}".new "${dst}"
else
rm -f "${dst}".new
error "failed to copy ${src}"
fi
fi
}


# MAC key extractor
getmac() {
local src="$1" mac=
Expand All @@ -66,8 +47,6 @@ getmac() {
# Sanity checks
[ ${#msgid} = 40 ] || error "bad msgid"
[ ${#username} = 32 ] || error "bad own username"
[ -e "${pubqueue}" ] || error "public queue does not exist"
[ -e "${pubrqueue}" ] || error "public rqueue does not exist"

check_userhost() {
[ ${#1} = 32 ] || error "bad username"
Expand All @@ -80,31 +59,18 @@ send)
# <send> [comm loop]
if [ -e ${queue}/"${msgid}"/${cmd}.ok -a ! -e ${queue}/"${msgid}"/ack.ok ]; then
prefix=`urlprefix ${queue}`

atomicnccopy ${queue}/"${msgid}"/speer.sig "${pubqueue}"/"${msgid}".key
atomicnccopy ${queue}/"${msgid}"/message.enc "${pubqueue}"/"${msgid}"

sendmac=`getmac ${queue}/"${msgid}"/send.mac`
curl -sSfg "${prefix}"/snd/"${msgid}"/"${sendmac}"
else
error "${cmd}.ok (without ack.ok) not found"
fi
;;

peer)
# <peer> [comm loop]
if [ -e ${rqueue}/"${msgid}"/${cmd}.ok -a ! -e ${rqueue}/"${msgid}"/recv.ok ]; then
atomicnccopy ${rqueue}/"${msgid}"/rpeer.sig "${pubrqueue}"/"${msgid}".key
else
error "${cmd}.ok not found"
fi
;;

recv)
# NOTE: dir can be renamed at any moment by <fin>[service]
# <recv> [comm loop]
if [ -e ${rqueue}/"${msgid}"/${cmd}.ok ]; then
prefix=`urlprefix ${rqueue}`

recvmac=`getmac ${rqueue}/"${msgid}"/recv.mac`
curl -sSfg "${prefix}"/rcp/"${msgid}"/"${recvmac}"
else
Expand All @@ -116,19 +82,14 @@ ack)
# <ack> [comm loop]
if [ -e ${queue}/"${msgid}"/${cmd}.ok ]; then
prefix=`urlprefix ${queue}`

rm -f "${pubqueue}"/"${msgid}" "${pubqueue}"/"${msgid}".key

ackmac=`getmac ${queue}/"${msgid}"/ack.mac`
curl -sSfg "${prefix}"/ack/"${msgid}"/"${ackmac}"

mv -T ${queue}/"${msgid}" ${queue}/"${msgid}".del

# try to run 2nd ack variant immediately
# rm -f "${pubqueue}"/"${msgid}"
# rm -r --one-file-system ${queue}/"${msgid}".del
elif [ -e ${queue}/"${msgid}".del ]; then
rm -f "${pubqueue}"/"${msgid}" "${pubqueue}"/"${msgid}".key
rm -r --one-file-system ${queue}/"${msgid}".del
else
error "${cmd}.ok or .del directory not found"
Expand All @@ -138,7 +99,6 @@ ack)
fin)
# <fin> [comm loop]
if [ -e ${rqueue}/"${msgid}".del ]; then
rm -f "${pubrqueue}"/"${msgid}".key
rm -r --one-file-system ${rqueue}/"${msgid}".del
else
error ".del directory not found"
Expand Down
11 changes: 3 additions & 8 deletions cable/loop
Expand Up @@ -123,14 +123,9 @@ else
"${comm}" recv "${msgid}"
fi

if [ -e "${msgdir}"/peer.req ]; then
"${crypto}" peer "${msgid}" && \
exec "${comm}" peer "${msgid}"
elif [ -e "${msgdir}"/peer.ok ]; then
if [ ! -e "${msgdir}"/recv.ok ]; then
exec "${comm}" peer "${msgid}"
fi
else
if [ -e "${msgdir}"/peer.req ]; then
exec "${crypto}" peer "${msgid}"
elif [ ! -e "${msgdir}"/peer.ok ]; then
error "peer.req/ok not found"
fi

Expand Down
97 changes: 0 additions & 97 deletions conf/nginx.conf

This file was deleted.

18 changes: 10 additions & 8 deletions conf/profile
Expand Up @@ -21,24 +21,26 @@ export CABLE_TOR=${CABLE_MOUNT}/security/tor
export CABLE_I2P=${CABLE_MOUNT}/security/i2p

# CABLE_QUEUES/(r)queue directories, must be writable by uid 'cable'
# Value must be mirrored in /etc/conf.d/spawn-fcgi.cable
export CABLE_QUEUES=${CABLE_MOUNT}/cables

# Mail delivery directory, must be writable by uid 'cable'
export CABLE_INBOX=${CABLE_MOUNT}/mail/inbox

# CABLE_PUB/<username>/(r)queue directories, must be writable by uid 'cable'
# CABLE_PUB/<username>/{certs,(r)queue} must be readable by nginx
# Value must be mirrored in /etc/nginx/nginx.conf: "root" directive
export CABLE_PUB=/srv/www

# Supported email-like IDs
export CABLE_REGEX='[a-z2-7]{32}@([a-z2-7]{16}\.onion|[a-z2-7]{52}\.b32\.i2p)'

# Message or receipt timeout in seconds (e.g., 7 days)
export CABLE_TMOUT=$((7 * 24 * 60 * 60))


# Host and port on which cables daemon listens to HTTP connections
# (symbolic names can be used; leave host empty for wildcard bind)
export CABLE_HOST=127.0.0.1
export CABLE_PORT=9080


# Supported email-like IDs (do not modify!)
export CABLE_REGEX='[a-z2-7]{32}@([a-z2-7]{16}\.onion|[a-z2-7]{52}\.b32\.i2p)'


# OpenSSL random seed
export RANDFILE=${TMPDIR}/openssl.rnd

Expand Down
25 changes: 0 additions & 25 deletions conf/spawn-fcgi.cable

This file was deleted.

0 comments on commit 1fdd170

Please sign in to comment.