From e2e370747f93ec254930c088806522b7c3cf3682 Mon Sep 17 00:00:00 2001 From: lars Date: Thu, 18 Sep 2008 11:56:16 +0000 Subject: [PATCH] encryption support in ezmlm-web: * add separate keyring support * implement interface option blacklisting * improved some code style manual: * document INTERFACE_OPTIONS_BLACKLIST and GPG_KEYRING_DEFAULT_LOCATION --- examples/ezmlmwebrc.dist | 20 +++++- ezmlm-web.cgi | 146 ++++++++++++++++++++++++++++----------- man/ezmlmwebrc.5 | 25 +++++++ spec/hdf-spec.txt | 1 + template/macros.cs | 16 ++++- template/nav.cs | 2 +- 6 files changed, 166 insertions(+), 44 deletions(-) diff --git a/examples/ezmlmwebrc.dist b/examples/ezmlmwebrc.dist index 4ba3d26..c784322 100644 --- a/examples/ezmlmwebrc.dist +++ b/examples/ezmlmwebrc.dist @@ -82,6 +82,14 @@ $DEFAULT_OPTIONS = "aBDFGHiJkLMNOpQRSTUWx"; # available values are: easy, normal and expert #$DEFAULT_INTERFACE_TYPE = "normal"; +# exclude some interface options from being displayed +# BEWARE: this does not protect the specific option from being changed. +# It just hides the visible interface items. Anyone can still craft a manual +# http request, that could change the specified options. +# See the list of filenames below $TEMPLATE_DIR/config_options/. The +# blacklist may contain any of these filenames (without '.cs' extension). +#$INTERFACE_OPTIONS_BLACKLIST = ('lang_select', 'mime_reject'); + # What is the title of this document? $HTML_TITLE = "ezmlm-web - a mailinglist administration interface"; @@ -112,7 +120,15 @@ $HTML_CSS_COLOR = "/ezmlm-web/color-red-blue.css"; # setting individually $HTML_LANGUAGE = "en"; -# turn support for encrypted mailing lists on or off - defaults to 0 (off) -# see https://systemausfall.org/toolforge/gpgpy-ezmlm for details +# enabled support for encrypted mailing lists - defaults to 0 (off) +# This include keyring management and mailing list handling in general. #$GPG_SUPPORT = 0; +# Define the default location of gnupg keyrings used for mailing list +# encryption. If the location starts with a slash ('/'), then it is considered +# to be an absolute path. Otherwise it is relative to the current list +# directory. For the ezmlm-gpg mailing list encryption system, the default +# (".gnupg") is usable. +#$GPG_KEYRING_DEFAULT_LOCATION = ".gnupg"; + + diff --git a/ezmlm-web.cgi b/ezmlm-web.cgi index e3f46a2..07d45a0 100755 --- a/ezmlm-web.cgi +++ b/ezmlm-web.cgi @@ -32,7 +32,8 @@ use MIME::QuotedPrint; # optional modules - they will be loaded later if they are available #Encode -#Mail::Ezmlm::Gpg +#Mail::Ezmlm::Ezmlm-GPG +#Mail::Ezmlm::GpgKeyRing #Mail::Address OR Email::Address @@ -94,10 +95,11 @@ use vars qw[$FILE_UPLOAD $WEBUSERS_FILE $MAIL_DOMAIN $HTML_TITLE]; use vars qw[$TEMPLATE_DIR $LANGUAGE_DIR $HTML_LANGUAGE]; use vars qw[$HTML_CSS_COMMON $HTML_CSS_COLOR]; use vars qw[$MAIL_ADDRESS_PREFIX @HTML_LINKS]; +use vars qw[@INTERFACE_OPTIONS_BLACKLIST]; # default interface template (basic/normal/expert) use vars qw[$DEFAULT_INTERFACE_TYPE]; # some settings for encrypted mailing lists -use vars qw[$GPG_SUPPORT]; +use vars qw[$GPG_SUPPORT $GPG_KEYRING_DEFAULT_LOCATION]; # settings for multi-domain setups use vars qw[%DOMAINS $CURRENT_DOMAIN]; # cached data @@ -141,15 +143,19 @@ unless (my $return = do $config_file) { ####### validate configuration and apply some default settings ########## -# do we support encrypted mailing lists? -# see https://systemausfall.org/toolforge/crypto-ezmlm +# do we support encrypted mailing lists an keyring management? $GPG_SUPPORT = 0 unless defined($GPG_SUPPORT); if ($GPG_SUPPORT) { - if (&safely_import_module("Mail::Ezmlm::Gpg")) { - $GPG_SUPPORT = 1; - } else { - $GPG_SUPPORT = 0; - warn "WARNING: Support for encrypted mailinglists is disabled, as the module Mail::Ezmlm::Gpg failed to load!"; + my @crypto_modules = ( + "Mail::Ezmlm::GpgKeyRing", + "Mail::Ezmlm::Ezmlm-GPG", + ); + for my $module_name (@crypto_modules) { + unless (&safely_import_module($module_name)) { + $GPG_SUPPORT = 0; + warn "WARNING: Support for encryption features is disabled, " + . "because the module '$module_name' failed to load!"; + } } } @@ -223,6 +229,13 @@ $HTML_TITLE = '' unless defined($HTML_TITLE); # check DEFAULT_INTERFACE_TYPE $DEFAULT_INTERFACE_TYPE = 'normal' unless defined($DEFAULT_INTERFACE_TYPE); +# check possible blacklist of interface options (since v3.3) +@INTERFACE_OPTIONS_BLACKLIST = () unless defined(@INTERFACE_OPTIONS_BLACKLIST); + +# check the configured detault location of gnupg keyrings +$GPG_KEYRING_DEFAULT_LOCATION = ".gnupg" + unless defined($GPG_KEYRING_DEFAULT_LOCATION); + # determine MAIL_DOMAIN unless (defined($MAIL_DOMAIN) && ($MAIL_DOMAIN ne '')) { if ((-e "$QMAIL_BASE/virtualdomains") && open(VD, "<$QMAIL_BASE/virtualdomains")) { @@ -875,10 +888,13 @@ sub set_pagedata4list { $list = new Mail::Ezmlm("$LIST_DIR/$listname"); $pagedata->setValue("Data.List.Name", "$listname"); - $pagedata->setValue("Data.List.Address", &this_listaddress); + $pagedata->setValue("Data.List.Address", &get_listaddress($listname)); - # do we support encryption? Set some data if the list is encrypted ... - &set_pagedata_crypto($listname) if ($GPG_SUPPORT); + # set global or module-specific blacklist of list options + &set_pagedata_options_blacklist($list); + + # do we support encryption? Show a possible keyring ... + &set_pagedata_keyring($list) if ($GPG_SUPPORT); # is this a moderation/administration list? &set_pagedata4part_list($part_type) if ($part_type ne ''); @@ -896,11 +912,79 @@ sub set_pagedata4list { # --------------------------------------------------------------------------- +sub set_pagedata_options_blacklist { + my $list = shift; + my ($item, @list_blacklist); + + # check if the function "get_options_blacklist" exists for the list object + eval {@list_blacklist = $list->get_options_blacklist();}; + # use an empty blacklist, if the member function was not defined + @list_blacklist = () if ($@); + + foreach $item (@list_blacklist, @INTERFACE_OPTIONS_BLACKLIST) { + $pagedata->setValue("Data.List.OptionsBlackList." . $item, $item); + } +} + +# --------------------------------------------------------------------------- + +sub get_keyring_location { + my $list = shift; + my $keyring_location; + + if ($list->can("get_keyring_location")) { + $keyring_location = $list->get_keyring_location(); + } elsif ($GPG_KEYRING_DEFAULT_LOCATION =~ m#^/#) { + $keyring_location = $GPG_KEYRING_DEFAULT_LOCATION; + } else { + $keyring_location = $list->thislist() . '/' . $GPG_KEYRING_DEFAULT_LOCATION; + } + return $keyring_location; +} + +# --------------------------------------------------------------------------- + +sub set_pagedata_keyring { + my $list = shift; + my ($keyring_location, $keyring, @gpg_keys); + + $keyring_location = &get_keyring_location($list); + # continue only, if keyring_location is defined and accessible + if (defined($keyring_location) && (-r $keyring_location)) { + $keyring = Mail::Ezmlm::GpgKeyRing->new($keyring_location); + } else { + # no keyring available -> we are finished + return; + } + + # retrieve the currently available public keys + @gpg_keys = $keyring->get_public_keys(); + for (my $i = 0; $i < @gpg_keys; $i++) { + $pagedata->setValue("Data.List.gnupg_keys.public.$i.id" , $gpg_keys[$i]{id}); + $pagedata->setValue("Data.List.gnupg_keys.public.$i.email" , $gpg_keys[$i]{email}); + $pagedata->setValue("Data.List.gnupg_keys.public.$i.name" , $gpg_keys[$i]{name}); + $pagedata->setValue("Data.List.gnupg_keys.public.$i.expires" , $gpg_keys[$i]{expires}); + } + + # retrieve the currently available secret keys + @gpg_keys = $keyring->get_secret_keys(); + for (my $i = 0; $i < @gpg_keys; $i++) { + $pagedata->setValue("Data.List.gnupg_keys.secret.$i.id" , $gpg_keys[$i]{id}); + $pagedata->setValue("Data.List.gnupg_keys.secret.$i.email" , $gpg_keys[$i]{email}); + $pagedata->setValue("Data.List.gnupg_keys.secret.$i.name" , $gpg_keys[$i]{name}); + $pagedata->setValue("Data.List.gnupg_keys.secret.$i.expires" , $gpg_keys[$i]{expires}); + } + # enable "keyring" feature in the interface + $pagedata->setValue("Data.List.Features.GpgKeyring", 1); +} + +# --------------------------------------------------------------------------- + sub set_pagedata_crypto { # extract hdf-data for encrypted lists my ($listname) = @_; - my ($gpg_list, %config, $item, @gpg_keys, $gpg_key); + my ($gpg_list, %config, $item); $gpg_list = new Mail::Ezmlm::Gpg("$LIST_DIR/$listname"); @@ -914,23 +998,6 @@ sub set_pagedata_crypto { $pagedata->setValue("Data.List.Options.gnupg_$item", $config{$item}); } - # retrieve the currently available public keys - @gpg_keys = $gpg_list->get_public_keys(); - for (my $i = 0; $i < @gpg_keys; $i++) { - $pagedata->setValue("Data.List.gnupg_keys.public.$i.id" , $gpg_keys[$i]{id}); - $pagedata->setValue("Data.List.gnupg_keys.public.$i.email" , $gpg_keys[$i]{email}); - $pagedata->setValue("Data.List.gnupg_keys.public.$i.name" , $gpg_keys[$i]{name}); - $pagedata->setValue("Data.List.gnupg_keys.public.$i.expires" , $gpg_keys[$i]{expires}); - } - - # retrieve the currently available secret keys - @gpg_keys = $gpg_list->get_secret_keys(); - for (my $i = 0; $i < @gpg_keys; $i++) { - $pagedata->setValue("Data.List.gnupg_keys.secret.$i.id" , $gpg_keys[$i]{id}); - $pagedata->setValue("Data.List.gnupg_keys.secret.$i.email" , $gpg_keys[$i]{email}); - $pagedata->setValue("Data.List.gnupg_keys.secret.$i.name" , $gpg_keys[$i]{name}); - $pagedata->setValue("Data.List.gnupg_keys.secret.$i.expires" , $gpg_keys[$i]{expires}); - } } # --------------------------------------------------------------------------- @@ -988,11 +1055,11 @@ sub set_pagedata_misc_configfiles { sub set_pagedata_subscribers { - my ($list, $listname, $part_type) = @_; + my ($list, $part_type) = @_; my ($i, $address, $addr_name, %pretty); $i = 0; - tie %pretty, "DB_File", "$LIST_DIR/$listname/webnames" if ($PRETTY_NAMES); + tie %pretty, "DB_File", $list->thislist() . "/webnames" if ($PRETTY_NAMES); foreach $address (sort $list->subscribers($part_type)) { if ($address ne '') { $pagedata->setValue("Data.List.Subscribers." . $i . '.address', "$address"); @@ -1636,7 +1703,7 @@ sub set_pagedata4part_list { # Work out the address of this list ... $list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list')); - $listaddress = &this_listaddress(); + $listaddress = &get_listaddress($q->param('list')); $pagedata->setValue("Data.List.PartType", "$part"); @@ -1987,7 +2054,7 @@ sub gnupg_generate_key { } if ($list->generate_private_key($key_name, $key_comment, - &this_listaddress(), $key_size, $key_expires)) { + &get_listaddress($listname), $key_size, $key_expires)) { $pagename = 'gnupg_secret'; return (0==0); } else { @@ -2053,8 +2120,8 @@ sub update_config_crypto { } # Any changes? Otherwise just return. - # beware: the length function returns "1" for empty hashes - return (0==0) if (length(%switches) <= 1); + # "scalar keys %..." calculates the length the length of a hash + return (0==0) if (scalar keys %switches == 0); # update the configuration file if ($list->update(%switches)) { @@ -2338,11 +2405,12 @@ sub output_mime_examples { # ------------------------------------------------------------------------ -sub this_listaddress { +sub get_listaddress { # Work out the address of this list ... Used often so put in its own subroutine ... - my ($list, $listaddress); - $list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list')); + my $listname = shift; + my ($listaddress, $list); + $list = new Mail::Ezmlm("$LIST_DIR/" . $listname); chomp($listaddress = $list->getpart('outlocal')); $listaddress .= '@'; chomp($listaddress .= $list->getpart('outhost')); diff --git a/man/ezmlmwebrc.5 b/man/ezmlmwebrc.5 index baa9696..e82f1f2 100644 --- a/man/ezmlmwebrc.5 +++ b/man/ezmlmwebrc.5 @@ -108,9 +108,22 @@ selects a different language. The default value is \fIen\fR. .IP \fB$DEFAULT_INTERFACE_TYPE\fR Set the default interface template. Available values are \fIeasy\fR, \fInormal\fR and \fIexpert\fR. The default value is \fInormal\fR. +.IP \fB$INTERFACE_OPTIONS_BLACKLIST\fR +Exclude some list options from being displayed via the web interface. +BEWARE: this does not protect the specific option from being changed. +It just hides the visible interface items. Anyone can still craft a manual +http request, that could change the specified options. +See the list of filenames below \fI$TEMPLATE_DIR/config_options/\fR. The +blacklist may contain any of these filenames (without '.cs' extension). .IP \fB$GPG_SUPPORT\fR Enable support for encrypted mailing lists. Currently this feature is still considered as beta quality. User reports are warmly welcome! +.IP \fB$GPG_KEYRING_DEFAULT_LOCATION\fR +This setting defines the default location of gnupg keyrings used for mailing list +encryption. If the location starts with a slash ('/'), then it is considered +to be an absolute path. Otherwise it is relative to the directory of the current +list. For the ezmlm-gpg mailing list encryption system, the default (".gnupg") is +usable. .IP \fB%DOMAINS\fR This hash of hashes (\fIname\fR associated with a hash of domain specific information) can be used to define a multi-domain setup. See the example @@ -123,6 +136,18 @@ more details. $LIST_DIR = "$HOME_DIR/lists"; $LANGUAGE_DIR = "/usr/local/share/ezmlm-web/lang"; $TEMPLATE_DIR = "/usr/local/share/ezmlm-web/template"; +.IP "Some more examples of settings:" +.sp +.nf +$QMAIL_BASE = $Mail::Ezmlm::QMAIL_BASE . '/control'; +$MAIL_ADDRESS_PREFIX = "lists-"; +$DEFAULT_OPTIONS = "aBDFGHiJkLMNOpQRSTUWx"; +$INTERFACE_OPTIONS_BLACKLIST = ('lang_select', 'mime_reject'); +$HTML_TITLE = "ezmlm-web - a mailinglist administration interface"; +$HTML_CSS_COMMON = "/ezmlm-web/default.css"; +$HTML_CSS_COLOR = "/ezmlm-web/color-red-blue.css"; +$HTML_LANGUAGE = "en"; +$GPG_KEYRING_DEFAULT_LOCATION = ".gnupg"; .SH AUTHOR Written by Lars Kruse .SH "REPORTING BUGS" diff --git a/spec/hdf-spec.txt b/spec/hdf-spec.txt index 8dad364..abf6ee2 100644 --- a/spec/hdf-spec.txt +++ b/spec/hdf-spec.txt @@ -17,6 +17,7 @@ Data.Action Data.areDefaultTextsAvailable Data.ErrorMessage Data.List.Address +Data.List.OptionsBlackList Data.List.CharSet Data.List.CustomizedFiles.* Data.List.DefaultFiles.* diff --git a/template/macros.cs b/template/macros.cs index 9352b3a..d570fce 100644 --- a/template/macros.cs +++ b/template/macros.cs @@ -53,11 +53,22 @@ def:limit_string_len(text,limit) else ?>
  • - 0) && (Data.List.Features.Crypto) + 0) && (Data.List.Features.GpgKeyring) ?>