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
This commit is contained in:
lars 2008-09-18 11:56:16 +00:00
parent 1497aeecb5
commit e2e370747f
6 changed files with 166 additions and 44 deletions

View file

@ -82,6 +82,14 @@ $DEFAULT_OPTIONS = "aBDFGHiJkLMNOpQRSTUWx";
# available values are: easy, normal and expert # available values are: easy, normal and expert
#$DEFAULT_INTERFACE_TYPE = "normal"; #$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? # What is the title of this document?
$HTML_TITLE = "ezmlm-web - a mailinglist administration interface"; $HTML_TITLE = "ezmlm-web - a mailinglist administration interface";
@ -112,7 +120,15 @@ $HTML_CSS_COLOR = "/ezmlm-web/color-red-blue.css";
# setting individually # setting individually
$HTML_LANGUAGE = "en"; $HTML_LANGUAGE = "en";
# turn support for encrypted mailing lists on or off - defaults to 0 (off) # enabled support for encrypted mailing lists - defaults to 0 (off)
# see https://systemausfall.org/toolforge/gpgpy-ezmlm for details # This include keyring management and mailing list handling in general.
#$GPG_SUPPORT = 0; #$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";

View file

@ -32,7 +32,8 @@ use MIME::QuotedPrint;
# optional modules - they will be loaded later if they are available # optional modules - they will be loaded later if they are available
#Encode #Encode
#Mail::Ezmlm::Gpg #Mail::Ezmlm::Ezmlm-GPG
#Mail::Ezmlm::GpgKeyRing
#Mail::Address OR Email::Address #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[$TEMPLATE_DIR $LANGUAGE_DIR $HTML_LANGUAGE];
use vars qw[$HTML_CSS_COMMON $HTML_CSS_COLOR]; use vars qw[$HTML_CSS_COMMON $HTML_CSS_COLOR];
use vars qw[$MAIL_ADDRESS_PREFIX @HTML_LINKS]; use vars qw[$MAIL_ADDRESS_PREFIX @HTML_LINKS];
use vars qw[@INTERFACE_OPTIONS_BLACKLIST];
# default interface template (basic/normal/expert) # default interface template (basic/normal/expert)
use vars qw[$DEFAULT_INTERFACE_TYPE]; use vars qw[$DEFAULT_INTERFACE_TYPE];
# some settings for encrypted mailing lists # 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 # settings for multi-domain setups
use vars qw[%DOMAINS $CURRENT_DOMAIN]; use vars qw[%DOMAINS $CURRENT_DOMAIN];
# cached data # cached data
@ -141,15 +143,19 @@ unless (my $return = do $config_file) {
####### validate configuration and apply some default settings ########## ####### validate configuration and apply some default settings ##########
# do we support encrypted mailing lists? # do we support encrypted mailing lists an keyring management?
# see https://systemausfall.org/toolforge/crypto-ezmlm
$GPG_SUPPORT = 0 unless defined($GPG_SUPPORT); $GPG_SUPPORT = 0 unless defined($GPG_SUPPORT);
if ($GPG_SUPPORT) { if ($GPG_SUPPORT) {
if (&safely_import_module("Mail::Ezmlm::Gpg")) { my @crypto_modules = (
$GPG_SUPPORT = 1; "Mail::Ezmlm::GpgKeyRing",
} else { "Mail::Ezmlm::Ezmlm-GPG",
$GPG_SUPPORT = 0; );
warn "WARNING: Support for encrypted mailinglists is disabled, as the module Mail::Ezmlm::Gpg failed to load!"; 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 # check DEFAULT_INTERFACE_TYPE
$DEFAULT_INTERFACE_TYPE = 'normal' unless defined($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 # determine MAIL_DOMAIN
unless (defined($MAIL_DOMAIN) && ($MAIL_DOMAIN ne '')) { unless (defined($MAIL_DOMAIN) && ($MAIL_DOMAIN ne '')) {
if ((-e "$QMAIL_BASE/virtualdomains") && open(VD, "<$QMAIL_BASE/virtualdomains")) { if ((-e "$QMAIL_BASE/virtualdomains") && open(VD, "<$QMAIL_BASE/virtualdomains")) {
@ -875,10 +888,13 @@ sub set_pagedata4list {
$list = new Mail::Ezmlm("$LIST_DIR/$listname"); $list = new Mail::Ezmlm("$LIST_DIR/$listname");
$pagedata->setValue("Data.List.Name", "$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 global or module-specific blacklist of list options
&set_pagedata_crypto($listname) if ($GPG_SUPPORT); &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? # is this a moderation/administration list?
&set_pagedata4part_list($part_type) if ($part_type ne ''); &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 { sub set_pagedata_crypto {
# extract hdf-data for encrypted lists # extract hdf-data for encrypted lists
my ($listname) = @_; 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"); $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}); $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 { sub set_pagedata_subscribers {
my ($list, $listname, $part_type) = @_; my ($list, $part_type) = @_;
my ($i, $address, $addr_name, %pretty); my ($i, $address, $addr_name, %pretty);
$i = 0; $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)) { foreach $address (sort $list->subscribers($part_type)) {
if ($address ne '') { if ($address ne '') {
$pagedata->setValue("Data.List.Subscribers." . $i . '.address', "$address"); $pagedata->setValue("Data.List.Subscribers." . $i . '.address', "$address");
@ -1636,7 +1703,7 @@ sub set_pagedata4part_list {
# Work out the address of this list ... # Work out the address of this list ...
$list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('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"); $pagedata->setValue("Data.List.PartType", "$part");
@ -1987,7 +2054,7 @@ sub gnupg_generate_key {
} }
if ($list->generate_private_key($key_name, $key_comment, 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'; $pagename = 'gnupg_secret';
return (0==0); return (0==0);
} else { } else {
@ -2053,8 +2120,8 @@ sub update_config_crypto {
} }
# Any changes? Otherwise just return. # Any changes? Otherwise just return.
# beware: the length function returns "1" for empty hashes # "scalar keys %..." calculates the length the length of a hash
return (0==0) if (length(%switches) <= 1); return (0==0) if (scalar keys %switches == 0);
# update the configuration file # update the configuration file
if ($list->update(%switches)) { 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 ... # Work out the address of this list ... Used often so put in its own subroutine ...
my ($list, $listaddress); my $listname = shift;
$list = new Mail::Ezmlm("$LIST_DIR/" . $q->param('list')); my ($listaddress, $list);
$list = new Mail::Ezmlm("$LIST_DIR/" . $listname);
chomp($listaddress = $list->getpart('outlocal')); chomp($listaddress = $list->getpart('outlocal'));
$listaddress .= '@'; $listaddress .= '@';
chomp($listaddress .= $list->getpart('outhost')); chomp($listaddress .= $list->getpart('outhost'));

View file

@ -108,9 +108,22 @@ selects a different language. The default value is \fIen\fR.
.IP \fB$DEFAULT_INTERFACE_TYPE\fR .IP \fB$DEFAULT_INTERFACE_TYPE\fR
Set the default interface template. Available values are \fIeasy\fR, Set the default interface template. Available values are \fIeasy\fR,
\fInormal\fR and \fIexpert\fR. The default value is \fInormal\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 .IP \fB$GPG_SUPPORT\fR
Enable support for encrypted mailing lists. Currently this feature is still Enable support for encrypted mailing lists. Currently this feature is still
considered as beta quality. User reports are warmly welcome! 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 .IP \fB%DOMAINS\fR
This hash of hashes (\fIname\fR associated with a hash of domain specific 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 information) can be used to define a multi-domain setup. See the example
@ -123,6 +136,18 @@ more details.
$LIST_DIR = "$HOME_DIR/lists"; $LIST_DIR = "$HOME_DIR/lists";
$LANGUAGE_DIR = "/usr/local/share/ezmlm-web/lang"; $LANGUAGE_DIR = "/usr/local/share/ezmlm-web/lang";
$TEMPLATE_DIR = "/usr/local/share/ezmlm-web/template"; $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 .SH AUTHOR
Written by Lars Kruse Written by Lars Kruse
.SH "REPORTING BUGS" .SH "REPORTING BUGS"

View file

@ -17,6 +17,7 @@ Data.Action
Data.areDefaultTextsAvailable Data.areDefaultTextsAvailable
Data.ErrorMessage Data.ErrorMessage
Data.List.Address Data.List.Address
Data.List.OptionsBlackList
Data.List.CharSet Data.List.CharSet
Data.List.CustomizedFiles.* Data.List.CustomizedFiles.*
Data.List.DefaultFiles.* Data.List.DefaultFiles.*

View file

@ -53,11 +53,22 @@ def:limit_string_len(text,limit)
else ?><?cs var:text ?><?cs /if ?><?cs else ?><?cs var:text ?><?cs /if ?><?cs
/def ?><?cs /def ?><?cs
def:show_one_option(optname)
?><?cs set:blacklist_found = 0 ?><?cs
each:black_opt = Data.List.OptionsBlackList
?><?cs if:black_opt == optname ?><?cs set:blacklist_found = 1 ?><?cs
/if ?><?cs
/each ?><?cs
if:blacklist_found == 0 ?><?cs
linclude:TemplateDir + '/config_options/' + optname + '.cs' ?><?cs
/if ?><?cs
/def ?><?cs
def:show_options(element) def:show_options(element)
?><?cs if:subcount(element) == 0 ?><li><?cs ?><?cs if:subcount(element) == 0 ?><li><?cs
linclude:TemplateDir + '/config_options/' + element + '.cs' ?></li><?cs call:show_one_option(element) ?></li><?cs
else ?><?cs if:element["Self"] ?><li><?cs else ?><?cs if:element["Self"] ?><li><?cs
linclude:TemplateDir + '/config_options/' + element["Self"] + '.cs' ?><?cs call:show_one_option(element["Self"]) ?><?cs
/if ?><ul><?cs each:opts = element ?><?cs if:name(opts) != "Self" ?><?cs /if ?><ul><?cs each:opts = element ?><?cs if:name(opts) != "Self" ?><?cs
call:show_options(opts) ?><?cs call:show_options(opts) ?><?cs
/if ?><?cs /each /if ?><?cs /each
@ -65,6 +76,7 @@ def:show_options(element)
/if ?><?cs /if ?><?cs
/def ?><?cs /def ?><?cs
def:is_substring(text_in, search_in) def:is_substring(text_in, search_in)
?><?cs set:text = text_in ?><?cs set:text = text_in
?><?cs set:search = search_in ?><?cs set:search = search_in

View file

@ -132,7 +132,7 @@
var:html_escape(Lang.Menue.ConfigAll) ?></a></li><?cs /if ?> var:html_escape(Lang.Menue.ConfigAll) ?></a></li><?cs /if ?>
</ul></li> </ul></li>
<?cs if:(subcount(UI.Navigation.Gnupg) > 0) && (Data.List.Features.Crypto) <?cs if:(subcount(UI.Navigation.Gnupg) > 0) && (Data.List.Features.GpgKeyring)
?><li><font class="no_link"><?cs var:html_escape(Lang.Menue.Gnupg) ?><li><font class="no_link"><?cs var:html_escape(Lang.Menue.Gnupg)
?></font> ?></font>
<ul> <ul>