#!/bin/sh

# Version 2.31

# This script may be called with an argument, in which case it is requested to
# check a detached signature.

# For debugging purposes: Enter an initial -d <nr> for debugging as first
# argument, then call remailer as 
#           su pseudouser -c "/usr/local/bin/remailer process-nr" < mail-file
# files in the pseudouser's tmp directory will start with the provided 
# process-id

# if the argument following -d <nr> equals -H a local head file in the current
# working directory is expected, and no change of directory is performed

# Set to 0 to keep files in the tmp subdir, set to 1 to remove on
# on successful completion
REMOVE_TMP_FILES=1

HEAD=/usr/local/lib/remailer/head

if [ "$1" != "-d" ]
then
    ID=$$
else
    DEBUG=yes
    ID=$2
    shift 2
fi

if [ "$1" == "-H" ]
then
    H_OPTION=$1
    HEAD=head
    shift
fi


#   0. Preliminaries: 
#       - Create a group remailer, and store this script in 
#           /usr/local/bin, root.remailer, chmod 0750 
#       - Make sure `sudo' (or comparable software) is available 
#
#   1. Define an entry for pseudo (the pseudo user) in the mail aliases
#           /etc/mail/aliases:    
#               pseudo:        "|sudo -u pseudo /usr/local/bin/remailer"
#           and rerun `newaliases'
#
#       Add an entry allowing `mail' to call the remailer to /etc/sudoers: 
#               mail host.domain.xx=(pseudo) NOPASSWD: /usr/local/bin/remailer
#           (host.domain.xx is the name of the host on which the remailer is
#           installed. `ALL' instead of `host.domain.xx' may also be
#           specified) 
#
#   Having thus set-up the `pseudo' user, /usr/local/bin/bin/remailer (this
#   script) will be activated for `pseudo' when called by `mail', using the id
#   `pseudo', group `remailer'.  Now we're ready to send mail to `pseudo',
#   which is then handled by this script.
#
#   2. Create a pseudo account for the `pseudo' user. E.g.,
#       adduser --home /var/lib/pseudo --shell /usr/bin/tcsh 
#               --ingroup remailer --disabled-password  pseudo
#       Chmod the account to 0700
#       Make sure the account does not expire:
#       /etc/shadow:
#           pseudo:!:12797:0:99999:7:::
#                            ^^^^^
#       Change identity to the pseudo user.
#       Define gpg keys for the pseudo user:
#           gpg --gen-key
#       - decrypting key without password
#           - define default  DSA key, size 1024, never to expire
#           - real name: `Pseudo' remailer
#           - email address: pseudo@host.name (this is the address
#                           from where mail is sent by the remailer)
#           - no passphrase required: press 2 x enter.
#       - edit this gpg key, and add a subkey, EIGamal for encryption only
#           (addkey)
#       - Use --search-keys or --recv-keys or --import to provide the pseudo
#           user's public key ring with the group member's public keys, and
#           add the names or key-ids to the `members' definition in
#           ~/gnupg/gpg.conf 
#       - Add an element like the following to gpg.conf:
#           force-mdc
#               to prevent the `WARNING: message was not integrity protected'
#               warning.
#       - Add an element like the following to gpg.conf:
#           keyserver wwwkeys.eu.pgp.net
#           (maybe not: there may be no need for the remailer to access
#                       a keyserver. A maintainer should be the agent
#                       monitoring the keys of the pseudo-user
#       - create a tmp/ directory under this account
#       - Create a directory etc/ under this account and store in here the
#           following two files:
#               remailer.rc
#                   This file may contain standard shell-comment and
#                   empty lines, but must contain the following lines:
#                       remailTo="address(es)" to where the reencrypted mail
#                                  will be sent. This may be the address of
#                                  (e.g.) a mailing list or the plain word
#                                  `members' in which case all members listed
#                                  in the file members (see below) will
#                                  receive the mail. The double quotes are 
#                                  required when multiple addresses are used.
#                                  Use a blank to separate multiple addresses.
#                                  Also, a form like the following can be used
#                                  (without the #-chars at the begin of the
#                                  lines, of course):
#                                  remailTo="\
#                                      first@address1\
#                                      second@address2\
#                                  "
#                       replyTo="name <address>"
#                               The name and address to appear in the
#                               Reply-To header
#               acceptFrom=patternFile
#                  This entry is optional. If specified it contains regular
#                   expressions (1 per line) of all from-addresses who are
#                   allowed to send signed and encrypted mail to the
#                   remailer. The members are by default allowed. To allow
#                   everybody to send e-mail (encrypted and signed) specify .*
#                   Note that keyserver must probably be specified when public
#                   keys of senders should be retrievable from a pgp
#                   keyserver. 
#               members
#                  This file may contain standard shell-comment and empty
#                   lines but must contain the addresses of the members for
#                   whom the received mail will be encrypted. Addresses must
#                   be separated by white space (blanks, tabs, newlines). The
#                   e-mail addresses must be the addresses mentioned in the
#                   public keyring of the pseudo user.
#   3. Optionally give ultimate trust to a member who has signed all other
#       member's keys, as this will prevent warnings the use of `--trust-model
#       always' 
#   
#   4. Export the public key, members should import the pseudo user's public
#   key and sign it.
#       (gpg --armor --export pseudo > pseudo.pub)
#       (gpg --import pseudo.pub)
#
#  Adding new members involves:
#       1. obtain their public key
#       2. insert it to the pseudo user's public keyring
#       3. add their name or id to the `members' definition in
#           ~/etc/members
#       4. provide the new user with the new key 
#
#  Removing members involves:
#       1. remove their name or id from the `members' definition in
#           ~/etc/members
#       2. optionally remove their public key
#       3. optionally, distribute a new subkey, and invalidate the current
#           subkey 
#
#   Questions, remarks to Frank.

#   If provided: non-option $1 is the name of the file containing the 
#   detached sig, and the script should read $1 from stdin as well.


pseudouser=`/usr/bin/id -nu`
    # common arguments for all gpg calls. Call gpg as $gpg
gpg="gpg --homedir /var/lib/$pseudouser/.gnupg"
[ "$DEBUG" != "yes" ] && gpg="$gpg --quiet --batch"


GOODSIGNATURE="gpg: Good signature from"

checkSig()
{
    while :                         # skip until the first boundary
    do
        read  LINE || break
        echo "$LINE" | grep "$BOUND" > /dev/null && break
    done

    while :                         # save all lines, up to, but excluding
    do                              # the line just before the next boundary
        read LAST || break
        echo "$LAST" | grep "$BOUND" > /dev/null && break

        ALL="${ALL}${PREV}"
        PREV="$LAST
"
    done

    echo -n "$ALL" > /tmp/message

                                    # check for the signature
    echo -n "$ALL" | $gpg --verify $1  -  2>&1 |
                                    grep "$GOODSIGNATURE" >/dev/null
    return $?
}
        
detachedSignature()
{
    while :
    do
        read LINE || break
                                        # find the boundary
        echo $LINE | grep 'boundary="[^"]\+"' > /dev/null
        if [ "$?" == "0" ] 
        then                            # keep it in BOUND
            BOUND=`echo $LINE | 
                    sed 's/[[:space:]]*boundary="\([^"]\+\)".*/\1/'`
            checkSig $1                 # check the signature
            return $?
        fi
    done
    return 1
}

if [ $# != 0 ]
then
    detachedSignature $1
    exit $?
fi

[ "$DEBUG" == "yes" ] && echo "Pseudouser: $pseudouser" >> /tmp/remailer

# this script is  /usr/local/bin/remailer. 
# .gnupg/ contains the configuration and keyrings for GPG
# tmp/ is used for temporary file processing.
# etc/ contains configuration files
# the account /var/lib/pseudo was chmod-ed 700.

failed()
{
    echo "Failed `date`: $*" >> tmp/${ID}.log
    exit 0
}

    # We're at the root directory, not at the home dir.  home/HOME is not
    # available 

    # go to the psuedo user's home directory
[ "$H_OPTION" != "" ] ||    cd /var/lib/$pseudouser         || 
                            failed cd /var/lib/$pseudouser

[ "$DEBUG" == "yes" ] && ls >> /tmp/remailer

    # check the existence of the relevant files
[ -e etc/members -a -e etc/remailer.rc ] || 
                        failed missing etc/members and/or /etc/remailer.rc

    # source the configuration file
. etc/remailer.rc

    # determine all members, store them as a single line.
members=`grep -v '^[[:space:]]*\(#.*\)*$' etc/members | tr '\n' ' '`

echo "
    Remail To: $remailTo
    Reply-To: $replyTo
    Members list: \"$members\"
" > tmp/${ID}.members

# save the received mail on a file
if [ "$DEBUG" == "yes" ] 
then
    cp tmp/${ID}.members tmp/${ID}.debug
fi

/usr/local/bin/remailerunhex > tmp/${ID}.org || 
                failed no original message

    # determine the subject of the mail, removing double quotes
    # to prevent nested double quotes in the eventually sent subject.
SUBJECT=`grep Subject: tmp/${ID}.org | sed 's/Subject: //' | tr '"' "'"`

    # decrypt the mail
if [ "$H_OPTION" != "" ]
then
    gpg --decrypt <tmp/${ID}.org >tmp/${ID}.decrypted 2>tmp/${ID}.signature ||
                    failed decrypting to .decrypted and .signature
else
    $gpg --decrypt <tmp/${ID}.org >tmp/${ID}.decrypted 2>tmp/${ID}.signature ||
                    failed decrypting to .decrypted and .signature
fi


#     # verify the signature
# 
# grep "$GOODSIGNATURE" tmp/${ID}.signature > tmp/${ID}.debug
# if [ "$?" != "0" ]                  # signature not yet verified
# then
#     $0 $H_OPTION tmp/${ID}.decrypted < tmp/${ID}.decrypted
# fi
# [ "$?" == "0" ] || failed signature $? not verifiable

    # re-encrypt the mail
if [ "$H_OPTION" != "" ]
then
    (echo ""; echo ""; cat tmp/${ID}.signature tmp/${ID}.decrypted)     |
    gpg --trust-model always --armor --encrypt --sign   \
        --group ads="$members" -r ads                   \
        > tmp/${ID}.encrypted 2>tmp/${ID}.reencrypt ||
                                            failed reencrypting to members
else
    (echo ""; echo ""; cat tmp/${ID}.signature tmp/${ID}.decrypted)     |
    $gpg --trust-model always --armor --encrypt --sign      \
        --group ads="$members" -r ads                       \
        > tmp/${ID}.encrypted 2>tmp/${ID}.reencrypt ||
                                            failed reencrypting to members
fi

    # next make sure the proper content type headers etc. are added
boundary=`date | md5sum | cut -f1 -d' '`

sed 's/@/'$boundary'/' < $HEAD \
                                        > tmp/${ID}.mail    ||
                        failed generating the boundary

cat tmp/${ID}.encrypted >> tmp/${ID}.mail                   ||
                        failed constructing the e-mail

if [ "$DEBUG" == "yes" ] ; then
    echo "Recipients: $remailTo" >> tmp/${ID}.remailer
    echo "Mail in tmp/${ID}.mail" >> tmp/${ID}.remailer

    echo Debug completed see tmp/${ID}.remailer 
    exit 0
fi

    # finally send the mail
    for sendTo in `echo $remailTo`
    do
mail -s "$SUBJECT" -a \
  "Reply-To: $replyTo"\
  -a 'Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="'$boundary'"' \
    $sendTo < tmp/${ID}.mail ||
            failed mailing the re-encrypted e-mail
    done

echo "Completed `date`: $*" >> tmp/${ID}.log
[ "$REMOVE_TMP_FILES" == "1" -a "$DEBUG" != "yes" ]  && rm -f tmp/${ID}.*

exit 0
