]> gitweb.fluxo.info Git - firma.git/commitdiff
Implemented the ability to use GnuPG's --hidden-recipient option and to send messages...
authorluis <luis>
Sat, 3 Sep 2005 01:20:32 +0000 (01:20 +0000)
committerluis <luis>
Sat, 3 Sep 2005 01:20:32 +0000 (01:20 +0000)
firma

diff --git a/firma b/firma
index 5c8eb943419d6514d4549902053a83cce10cd0be..2ad30a7b5fe71a7a0c533af35a0dc7f8c0ab697a 100755 (executable)
--- a/firma
+++ b/firma
@@ -2,47 +2,56 @@
 #
 # firma: GnuPG-based encrypted mailing list manager
 # Feedback: firma@sarava.org
-# 
-#  Firma is free software; you can redistribute it and/or modify
-#  it under the terms of the GNU General Public License as published by
-#  the Free Software Foundation; either version 2 of the License, or
-#  (at your option) any later version.
-# 
-#  Firma is distributed in the hope that it will be useful,
-#  but WITHOUT ANY WARRANTY; without even the implied warranty of
-#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#  GNU General Public License for more details.
-# 
-#  You should have received a copy of the GNU General Public License
-#  along with this program; if not, write to the Free Software
-#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+#
+#  Firma is free software; you can redistribute it and/or modify it under the
+#  terms of the GNU General Public License as published by the Free Software
+#  Foundation; either version 2 of the License, or (at your option) any later
+#  version.
+#
+#  Firma is distributed in the hope that it will be useful, but WITHOUT ANY
+#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License along with
+#  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+#  Place - Suite 330, Boston, MA 02111-1307, USA
 #
 # Usage:
 #
-# All firma parameters are passed through two different
-# configuration files: firma.conf, containing general parameters
-# necessary to run the script, and a list specific file,
-# containing its address, administrator(s), etc. In both
-# files you should enter PARAMETER='value' (whithout spaces
-# before or after the equal sign).
+# All firma parameters are passed through two different configuration files:
+# firma.conf, containing general parameters needed to run the script, and a list
+# specific file, containing its address, administrator(s), etc. In both files
+# you should enter PARAMETER='value' (without spaces before or after the equal
+# sign).
 #
 # firma.conf should contain the following parameters:
 #
-# GPG_BINARY= path to the gnupg binary
+# GPG_BINARY= path to the GnuPG binary
 # MAIL_AGENT= path to the mail transport agent to be used (e.g., sendmail)
 # MAIL_AGENT_ARGS= command-line arguments to be passed to the command above
 # LISTS_DIR= path to the mailing lists directory
 #
+# And it may contain the following optional parameters:
+#
+# USE_GPG_HIDDEN_RECIPIENT_OPTION= set to '1' to use GnuPG's --hidden-recipient
+#                                   option, available from version 1.4.0 onwards
+#                                   (try 'man gpg' for more information)
+# SEND_MESSAGES_USING_BCC= set to '1' to send list messages to all subscribers
+#                           at once using BCC (NOTE: this option can only be
+#                           used in conjunction with the option above.)
+#
 # And the list configuration file should contain:
 #
 # LIST_ADDRESS= list's email address
 # LIST_ADMIN= list's administrators email addresses (space separated)
-# LIST_HOMEDIR= list's gnupg homedir, where the list's keyrings are located
+# LIST_HOMEDIR= list's GnuPG homedir, where the list's keyrings are located
 # PASSPHRASE= passphrase for the list's private keyring
 #
-# NOTE: The PASSPHRASE value _has_ to be enclosed in single
-# quotes and _cannot_ contain any additional single quote as part
-# of itself.
+# NOTE: The passphrase _has_ to be enclosed in single quotes and _cannot_
+# contain any additional single quote as part of itself. It has to be at least
+# 25 characters long, combining numbers, upper and lower case letters and at
+# least 5 special characters. Also, no character can be sequentially repeated
+# more than 3 times.
 #
 
 FIRMA_CONFIG_FILE="/usr/local/etc/firma.conf"
@@ -60,7 +69,7 @@ function DeclareGpgVars {
   GPG="$GPG_BINARY $GPG_FLAGS"
   GPG_LIST_KEYS="$GPG --list-keys --with-colons"
   GPG_DECRYPT="$GPG --passphrase-fd 0 --decrypt"
-  GPG_ENCRYPT="$GPG --armor --trust-model always --local-user $LIST_ADDRESS --no-emit-version --passphrase-fd 0 --sign --encrypt --recipient"
+  GPG_ENCRYPT="$GPG --armor --trust-model always --local-user $LIST_ADDRESS --no-emit-version --passphrase-fd 0 --sign --encrypt"
 }
 
 
@@ -128,13 +137,16 @@ function CheckFirmaConfigFile {
   elif [[ ! -d "$LISTS_DIR" ]]; then
     echo "$(basename $0): Lists directory ("$LISTS_DIR") could not be found."
     exit 1
+  elif [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" && "$($GPG_BINARY --version | head -n1 | tr -dc '[[:digit:]]')" -lt "140" ]]; then
+    echo "$(basename $0): GPG's \"--hidden-recipient\" option is only available from version 1.4.0 onwards."
+    exit 1
   fi
 }
 
 
 function CheckListConfigFile {
   #-------------------------------------------------------------
-  # check configuration file parameters
+  # check list configuration file parameters
   #
   # parameter(s): none
   # depends on function(s): DeclareGpgVars
@@ -228,17 +240,17 @@ function GetMessageHeaders {
     fi
   done
 
-  # list ORIG_MESSAGE_HEADERS and get some specific headers for later use
-  FROM=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ From:' | cut -d : -f 2- | sed -e 's/^ //')
-  SENDER_ADDRESS=$(if [[ -z "$(echo $FROM | grep '>$')" ]]; then echo $FROM; else echo $FROM | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g'; fi)
-  DATE=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Date:' | cut -d : -f 2- | sed -e 's/^ //')
-  SUBJECT=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Subject:' | cut -d : -f 2- | sed -e 's/^ //')
-
   # check if message headers were successfully stored in ORIG_MESSAGE_HEADERS
   if [[ "${#ORIG_MESSAGE_HEADERS[@]}" -eq "0" ]]; then
     echo "$(basename $0): Message headers could not be located within this message."
     exit 1
   fi
+
+  # list ORIG_MESSAGE_HEADERS and get some specific headers for later use
+  FROM=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ From:' | cut -d : -f 2- | sed -e 's/^ //')
+  SENDER_ADDRESS=$(if [[ -z "$(echo $FROM | grep '>$')" ]]; then echo $FROM; else echo $FROM | grep -o '<[^<>]*>$' | sed -e 's/[<>]//g'; fi)
+  DATE=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Date:' | cut -d : -f 2- | sed -e 's/^ //')
+  SUBJECT=$(echo -e "${ORIG_MESSAGE_HEADERS[@]}" | grep -m 1 '^ Subject:' | cut -d : -f 2- | sed -e 's/^ //')
 }
 
 
@@ -277,8 +289,8 @@ function GetGpgMessage {
 
       # last, assign the line matched above to the last element of ORIG_GPG_MESSAGE
       ORIG_GPG_MESSAGE[$j]="${ORIG_MESSAGE[$i]}"
-      # no need to process lines beyond this point
-      # exit for loop
+      # no need to process lines beyond this point,
+      #+exit for loop
       break 1
     fi
   done
@@ -309,7 +321,7 @@ function GetGpgDecryptStderr {
 
 function GetSubscribersList {
   #-------------------------------------------------------------
-  # get list susbscriber addresses for message encryption and delivery
+  # get list subscriber addresses for message encryption and delivery
   #
   # parameter(s): none
   # depends on function(s): DeclareGpgVars
@@ -329,13 +341,54 @@ function SendListMessage {
   #-------------------------------------------------------------
   # compose and send a message to list members
   #
-  # parameter(s): subscriber address
-  # depends on function(s): DeclareGpgVars, GetGpgMessage, GetMessageHeaders, GetGpgDecryptStderr
+  # parameter(s): none
+  # depends on function(s): DeclareGpgVars, GetGpgMessage, GetMessageHeaders,
+  #                         GetGpgDecryptStderr, GetSubscribersList
   # returns: 0 on success
   #-------------------------------------------------------------
 
-  # this is the body of the message to be sent, so no identation here
-MESSAGE_BODY=( $(echo "$PASSPHRASE
+  local subscriber
+  local recipients
+
+  # check if gpg's --hidden-recipient option should be used
+  if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" ]]; then
+
+    # get the subscribers' addresses to be used by the GPG_ENCRYPT command bellow and
+    #+possibly by the MAIL_AGENT, in case the message is going to be sent using BCC
+    for subscriber in $(GetSubscribersList); do
+      recipients="$recipients$subscriber "
+    done
+
+    # this is the body of the message to be sent, so no indentation here
+
+MESSAGE_BODY="$(echo "$PASSPHRASE
+Message from: $FROM
+Subject: $SUBJECT
+Date: $DATE
+
+$(GetGpgDecryptStderr | grep '^gpg: Signature made')
+$(GetGpgDecryptStderr | grep '^gpg: Good signature from')
+
+$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT --group subscribers="$recipients" --hidden-recipient subscribers 2> /dev/null)"
+
+    # now send the message, either using BCC to sent it to all subscribers at
+    #+once or sending it separately to each one of them
+    if [[ "$SEND_MESSAGES_USING_BCC" == "1" ]]; then
+      echo -e "From: $LIST_ADDRESS\nBCC: $recipients\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+    else
+      for subscriber in $recipients; do
+        echo -e "From: $LIST_ADDRESS\nTo: $subscriber\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+      done
+    fi
+
+  # else, if gpg's --hidden-recipient option should not be used,
+  #+encrypt and send message separately to each list subscriber
+  else
+    for subscriber in $(GetSubscribersList); do
+
+      # this is the body of the message to be sent, so no indentation here
+
+MESSAGE_BODY="$(echo "$PASSPHRASE
 Message from: $FROM
 Subject: $SUBJECT
 Date: $DATE
@@ -343,25 +396,68 @@ Date: $DATE
 $(GetGpgDecryptStderr | grep '^gpg: Signature made')
 $(GetGpgDecryptStderr | grep '^gpg: Good signature from')
 
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT $1 2> /dev/null | sed -e 's/$/\\n/') )
+$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT --recipient $subscriber 2> /dev/null)"
 
-  # now send the message
-  echo -e "From: $LIST_ADDRESS\nTo: $1\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+      # now send the message
+      echo -e "From: $LIST_ADDRESS\nTo: $subscriber\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+    done
+  fi
 }
 
 
 function SendWarningMessage {
   #-------------------------------------------------------------
-  # compose and send a "BAD signature" warning to the
-  # list administrator(s) and to sender
+  # compose and send a "BAD signature" warning to the list administrator(s) and to sender
   #
-  # parameter(s): list administrator/sender address
-  # depends on function(s): DeclareGpgVars, GetGpgMessage, GetMessageHeaders, GetGpgDecryptStderr
+  # parameter(s): none
+  # depends on function(s): DeclareGpgVars, GetGpgMessage, GetMessageHeaders,
+  #                         GetGpgDecryptStderr, GetSubscribersList
   # returns: 0 on success
   #-------------------------------------------------------------
 
-  # this is the body of the message to be sent, so no identation here
-MESSAGE_BODY=( $(echo "$PASSPHRASE
+  local email_address
+  local recipients
+
+  # check if gpg's --hidden-recipient option should be used
+  if [[ "$USE_GPG_HIDDEN_RECIPIENT_OPTION" == "1" ]]; then
+
+    # get the administrator(s) and sender addresses to be used by the GPG_ENCRYPT
+    #+command bellow and possibly by the MAIL_AGENT, in case the message is going
+    #+to be sent using BCC
+    for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
+      recipients="$recipients$email_address "
+    done
+
+    # this is the body of the message to be sent, so no indentation here
+
+MESSAGE_BODY="$(echo "$PASSPHRASE
+Message from: $FROM
+Subject: [BAD SIGNATURE] $SUBJECT
+Date: $DATE
+
+$(GetGpgDecryptStderr | grep '^gpg: Signature made')
+$(GetGpgDecryptStderr | grep '^gpg: BAD signature from')
+
+$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT --group addresses="$recipients" --hidden-recipient addresses 2> /dev/null)"
+
+    # now send the message, either using BCC to sent it to all addresses at
+    #+once or sending it separately to each one of them
+    if [[ "$SEND_MESSAGES_USING_BCC" == "1" ]]; then
+      echo -e "From: $LIST_ADDRESS\nBCC: $recipients\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+    else
+      for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
+        echo -e "From: $LIST_ADDRESS\nTo: $email_address\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+      done
+    fi
+
+  # else, if gpg's --hidden-recipient option should not be used,
+  #+encrypt and send message separately to each address
+  else
+    for email_address in $LIST_ADMIN $SENDER_ADDRESS; do
+
+      # this is the body of the message to be sent, so no indentation here
+
+MESSAGE_BODY="$(echo "$PASSPHRASE
 Message from: $FROM
 Subject: [BAD SIGNATURE] $SUBJECT
 Date: $DATE
@@ -369,10 +465,12 @@ Date: $DATE
 $(GetGpgDecryptStderr | grep '^gpg: Signature made')
 $(GetGpgDecryptStderr | grep '^gpg: BAD signature from')
 
-$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT $1 2> /dev/null | sed -e 's/$/\\n/') )
+$(echo -e "$PASSPHRASE\n${ORIG_GPG_MESSAGE[@]}" | $GPG_DECRYPT 2> /dev/null)" | sed -e 's/=20$//' | $GPG_ENCRYPT --recipient $email_address 2> /dev/null)"
 
-  # now send the message
-  echo -e "From: $LIST_ADDRESS\nTo: $1\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+      # now send the message
+      echo -e "From: $LIST_ADDRESS\nTo: $email_address\nSubject: none\n\n${MESSAGE_BODY[@]}" | sed -e 's/^ //' | $MAIL_AGENT $MAIL_AGENT_ARGS
+    done
+  fi
 }
 
 
@@ -380,14 +478,14 @@ function SendBounceMessage {
   #-------------------------------------------------------------
   # send a bounce message back to sender
   #
-  # parameter(s): sender address
+  # parameter(s): none
   # depends on function(s): GetMessageHeaders
   # returns: 0 on success
   #-------------------------------------------------------------
 
-  # this is the body of the message to be sent, so no identation here
+  # this is the body of the message to be sent, so no indentation here
 echo "From: $LIST_ADDRESS
-To: $1
+To: $SENDER_ADDRESS
 Subject: none
 
 Message from: $FROM
@@ -410,33 +508,26 @@ function ProcessMessage {
   #
   # parameter(s): none
   # depends on function(s): GetMessage, GetMessageHeaders, GetGpgMessage, GetGpgDecryptStderr,
-  #                         GetSubscribersList, SendListMessage, SendWarningMessage, SendBounceMessage
+  #                         SendListMessage, SendWarningMessage, SendBounceMessage
   # returns: 0 on success
   #-------------------------------------------------------------
 
-  local email
-
   GetMessage
   GetMessageHeaders
   GetGpgMessage
 
-  # if signature in message is valid, encrypt and send it for each list subscriber
+  # if signature in message is valid, encrypt and send it to list subscribers
   if GetGpgDecryptStderr | grep -q '^\[GNUPG:] GOODSIG'; then
-    for email in $(GetSubscribersList); do
-      SendListMessage $email
-    done
+    SendListMessage
 
-  # else, if signature is invalid, email it back to the list administrator(s) and to sender
+  # else, if signature is invalid, send it back to the list administrator(s) and to sender
   elif GetGpgDecryptStderr | grep -q '^\[GNUPG:] BADSIG'; then
-    for email in $LIST_ADMIN $SENDER_ADDRESS; do
-      SendWarningMessage $email
-    done
+    SendWarningMessage
 
   # else, probably either the message was not encrypted/signed or the sender is not subscribed to the list
   # send a bounce 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
-    SendBounceMessage $SENDER_ADDRESS
+    SendBounceMessage
   fi
 
 }
@@ -456,17 +547,18 @@ function NewList {
     mkdir "$LIST_PATH" # || (echo "$(basename $0): error creating $LIST_PATH: installation aborted"; exit 1)
     echo "creating list config file and will ask some questions."
 
-  # comented:
-#    read -rep "path to smtp command (eg, /usr/sbin/sendmail): " MAIL_AGENT
-#    read -rep "command-line arguments passed to the smtp wrapper (eg, -oem -oi -t): " MAIL_AGENT_ARGS
-#    read -rep "path to gpg binary (eg, /usr/bin/gpg): " GPG_BINARY
+  # commented:
+#    read -rep "path to smtp command (e.g., /usr/sbin/sendmail): " MAIL_AGENT
+#    read -rep "command-line arguments passed to the smtp wrapper (e.g., -oem -oi -t): " MAIL_AGENT_ARGS
+#    read -rep "path to gpg binary (e.g., /usr/bin/gpg): " GPG_BINARY
 
     # if [ ! -x "$GPG_BINARY" ]; then
 
-    read -rep "list keyring folder: " LIST_HOMEDIR # removed: (defaults to $LIST_HOMEDIR)
+    # removed: (defaults to $LIST_HOMEDIR)
+    read -rep "list keyring folder: " LIST_HOMEDIR
 
     # todo: please no utf-8 (see DETAILS)
-    read -rep "list email (eg, firma@domain.tld): " LIST_ADDRESS
+    read -rep "list email (e.g., firma@domain.tld): " LIST_ADDRESS
     read -rep "list admins emails (space delimited): " LIST_ADMIN
     read -rep "list description (fake?): " DESCRIPTION
     read -resp "password for list keyring (use a huge one): " PASSPHRASE
@@ -479,7 +571,8 @@ function NewList {
     chmod 600 $LIST_CONFIG_FILE
     if [ -f "$LIST_CONFIG_FILE" ]; then
       DeclareGpgVars
-      echo -e "LIST_HOMEDIR=$LIST_HOMEDIR\nLIST_ADDRESS=$LIST_ADDRESS\nLIST_ADMIN=$LIST_ADMIN\nPASSPHRASE=$PASSPHRASE" > $LIST_CONFIG_FILE # removed: MAIL_AGENT=$MAIL_AGENT\nGPG_BINARY=$GPG_BINARY\n
+      # removed: MAIL_AGENT=$MAIL_AGENT\nGPG_BINARY=$GPG_BINARY\n
+      echo -e "LIST_HOMEDIR=$LIST_HOMEDIR\nLIST_ADDRESS=$LIST_ADDRESS\nLIST_ADMIN=$LIST_ADMIN\nPASSPHRASE=$PASSPHRASE" > $LIST_CONFIG_FILE
       echo "now generating your keyring..."
 
       $GPG --gen-key <<EOF
@@ -648,7 +741,7 @@ EOF
 # main()
 #-------------------------------------------------------------
 
-# set enviromental variables and options
+# set environmental variables and options
 export LANG=en_US
 umask 0077