1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
#!/bin/bash
#
# firma v0.2.3: encrypted mailing list manager
# feedback: rhatto@riseup.net luis@riseup.net | GPL
#
# list configuration is passed thru the config file,
# where you put PARAMETER=value (whithout spaces)
#
# MAIL= path for mail program
# GPG= path for gnupg binary
# TMP= where you want the temp files
# LISTNAME= list email
# LISTADMIN= list administrator email addresses (space separated)
# GPGDIR= gpg dir for the lists' keyring
# PASSWD= passwd for the lists' keyring
#
VERSION=0.2.3
function usage {
echo usage: $0 firma \<option\> \<config-file\>
echo -c: create a new list using config-file
echo -p: process a message
echo -a: admin commands
}
function check_config {
# check configuration file parameters
# todo: check if $TMP directory/files exist
if [ ! -f $GPG -o ! -x $GPG ]; then
echo -e "\n$1: GPG binary ($GPG) could not be found.\n"
exit 1
elif [ ! -f $MAIL -o ! -x $MAIL ]; then
echo -e "\n$1: Mail program ($MAIL) could not be found.\n"
exit 1
elif [ ! -d $GPGDIR -o ! -f $GPGDIR/pubring.gpg -o ! -f $GPGDIR/secring.gpg ]; then
echo -e "\n$1: GPG home directory ($GPGDIR) or the GPG keyrings could not be found.\n"
exit 1
elif [ -z "$(cat $CONFIG | grep -o ^PASSWD=\'[^\']*\'$)" -o \
-z "$(echo -n $PASSWD)" -o \
"$(echo -n $PASSWD | wc -m)" -lt "25" -o \
-z "$(echo -n $PASSWD | grep -o [[:lower:][:upper:]])" -o \
-z "$(echo -n $PASSWD | grep -o [[:digit:]])" -o \
"$(echo -n $PASSWD | grep -o [[:punct:]] | wc -l)" -lt "5" ]; then
echo -e "\n$CONFIG: PASSWD is empty or does not meet the minimum complexity requirements."
echo "$1: Please set a new passphrase for the list's private key. Make it at least"
echo "$1: 25 characters long (using a combination of letters, numbers and at least"
echo "$1: 5 special characters) and enclose it in 'single quotes'. The passphrase"
echo -e "$CONFIG: itself, though, cannot contain any single quote.\n"
exit 1
elif [ -z "$($GPGLIST | grep ^pub | cut -d : -f 10 | grep -i \<$LISTNAME\>$)" ]; then
echo -e "\n$CONFIG: GPG key for list \"$LISTNAME\" could not be found."
echo -e "$CONFIG: Note that this parameter expects an email address.\n"
exit 1
else
for ADMIN in $LISTADMIN; do {
if [ -z "$($GPGLIST | grep ^pub | cut -d : -f 10 | grep -i \<$ADMIN\>$)" ]; then
echo -e "\n$CONFIG: GPG key for list administrator \"$ADMIN\" could not be found."
echo -e "$CONFIG: Note that this parameter expects one or more space separated email addresses.\n"
exit 1
fi; }
done
fi
}
function GPGSTDERR {
# discard $GPGDECRYPT STDOUT and get its STDERR instead, for signature checking
echo $PASSWD | ($GPGDECRYPT --status-fd 2 $TMP.gpg 1> /dev/null) 2>&1 ;
}
function SUBSCRIBERS {
# get list susbscriber's addresses
$GPGLIST | sed -ne "/$LISTNAME/Id" -e '/pub/p' | cut -d : -f 10 | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g' ;
}
function process_message {
# process a message sent to the list
# create the temporary files and restrict their permissions
rm -f $TMP $TMP.gpg
touch $TMP && chmod 600 $TMP
touch $TMP.gpg && chmod 600 $TMP.gpg
# todo: use an array
while read STDIN; do
echo $STDIN >> $TMP
done
# get the message headers and the sender's email address
FROM=$(grep -m 1 ^From: $TMP | cut -d : -f 2- | sed -e 's/^ //')
FROMADD=$(if [ -z "$(echo $FROM | grep '>$')" ] ; then echo $FROM ; else echo $FROM | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g' ; fi)
DATE=$(grep -m 1 ^Date: $TMP)
SUBJECT=$(grep -m 1 ^Subject: $TMP | cut -d : -f 2- | sed -e 's/^ //')
# get the encrypted message
sed -ne '/-----BEGIN PGP MESSAGE-----/,/-----END PGP MESSAGE-----/p' $TMP >> $TMP.gpg
# if signature is Good, encrypt and send it for each list subscriber
# todo: declare a function to decrypt, re-encrypt and send the list messages
if (GPGSTDERR | grep -Fq GOODSIG) ; then
for EMAIL in $(SUBSCRIBERS); do
echo "$PASSWD
Message from: $FROM
Subject: $SUBJECT
$DATE
$(GPGSTDERR | grep -F 'gpg: Signature made')
$(GPGSTDERR | grep -F 'gpg: Good signature from')
$(echo $PASSWD | $GPGDECRYPT $TMP.gpg 2> /dev/null)" | sed -e 's/=20$//' | $GPGENCRYPT $EMAIL | $MAIL -r $LISTNAME $EMAIL
done
# else, if signature is BAD, email it back to the list admins and to sender
elif (GPGSTDERR | grep -Fq BADSIG) ; then
for EMAIL in $(echo $LISTADMIN $FROMADD); do
echo "$PASSWD
Message from: $FROM
Subject: [BAD SIGNATURE] $SUBJECT
$DATE
$(GPGSTDERR | grep -F 'gpg: Signature made')
$(GPGSTDERR | grep -F 'gpg: BAD signature from')
$(echo $PASSWD | $GPGDECRYPT $TMP.gpg 2> /dev/null)" | sed -e 's/=20$//' | $GPGENCRYPT $EMAIL | $MAIL -r $LISTNAME $EMAIL
done
# else, probably either the message was not signed or the sender is not subscribed to the list
# email the message back to sender including a note about this
# todo: parse STDERR to find out why the signature couldn't be checked and send more specific errors back to sender
else
echo "
Message from: $FROM
Subject: [RETURNED MAIL] $SUBJECT
$DATE
[ It was not possible to process this message. Either or both
the message was not encrypted and/or signed, or you are not
subscribed to this list. Contact the list administrator if
you have any questions. ]
--
firma v$VERSION" | $MAIL -r $LISTNAME $FROMADD
fi
rm -f $TMP $TMP.gpg
}
# main -
# command line checking
if [ -z $2 ]; then
usage; exit 1
else
CONFIG=$2
fi
# if the configuration file exists, disable "sourcepath" and evaluate the parameters
if [ -f $CONFIG ] && [[ $1 != "-c" ]]; then
shopt -u sourcepath && source $CONFIG
else
echo -e "\nConfiguration file \"$CONFIG\" could not be found.\n"
exit 1
fi
# declare GPG variables
GPGCOMMAND="$GPG --quiet --homedir $GPGDIR --batch --no-tty --no-use-agent --no-permission-warning"
GPGLIST="$GPGCOMMAND --list-keys --with-colons"
GPGDECRYPT="$GPGCOMMAND --passphrase-fd 0 --decrypt"
GPGENCRYPT="$GPGCOMMAND --passphrase-fd 0 --always-trust --encrypt --sign --armor --recipient"
# then check the config
check_config
# command line parsing
if [[ $1 == "-c" ]]; then
newlist
elif [[ $1 == "-p" ]]; then
process_message
elif [[ $1 == "-a" ]]; then
admin_task
else
usage; exit 1
fi
|