> How about this: start with the user's one-line request. Append the user's
> email address, if they haven't already specified it on the request.
> Normalize it by stripping comments out of email address, stripping multiple
> whitespace, converting to all lower-case, etc. Append or prepend a
> site-specific "secret" from the /etc/majordomo.cf file. Run this whole
> line through MD5 or SNEFRU or some other crypto-checksum function; use the
> result of that as the verification cookie the user has to return, as
> Michele describes above.
>
> As long as your site's secret is still the same when the user sends back
> the verification, it's easy to verify the cookie. However, there's no good
> way for an attacker to figure out what you're site's secret is except by
> brute force; if you choose a good secret (like choosing a good password),
> that should be very difficult.
>
> So, anybody got a version of MD5 or SNEFRU implemented in Perl? :-)
>
this has already been implemented by me (but with just a simple checksum,
not md5) and submitted to you guys. i was told it would be in the next
release, but i haven't seen any sign of that.
for the brave at heart, here are some diffs that should work for people.
just add $cookie_seed to majordomo.cf with an 8 digit hex number (the
"secret").
----
diff -c ./config_parse.pl /home/majordomo/config_parse.pl
*** ./config_parse.pl Sat Jan 7 09:30:56 1995
--- /home/majordomo/config_parse.pl Tue Jan 9 13:46:19 1996
***************
*** 86,92 ****
'noadvertise', '', # if regexp matches address
# don't show list
'description', '', # description of list, one line 55 char
! 'subscribe_policy', "open\001closed\001auto\001open",
# open, closed, or auto.
'mungedomain', 'no', # is user@foo.com == user@host.foo.com
'admin_passwd', '#!$list.".admin"', # administration password
--- 86,92 ----
'noadvertise', '', # if regexp matches address
# don't show list
'description', '', # description of list, one line 55 char
! 'subscribe_policy', "open\001closed\001auto\001confirm\001open",
# open, closed, or auto.
'mungedomain', 'no', # is user@foo.com == user@host.foo.com
'admin_passwd', '#!$list.".admin"', # administration password
***************
*** 181,194 ****
characters.",
'subscribe_policy',
! "One of 3 possible values: open, closed, auto. Open allows people to
! subscribe themselves to the list. Auto allows anybody to subscribe
! anybody to the list without maintainer approval. The existence of the
! file <listname>.auto is the same as specifying the value auto. Closed
! requires maintainer approval for all subscribe requests to the
! list. In addition to the keyword, if the file <listname>.closed
! exists, it is the same as specifying the value closed. The value of
! this keyword overrides the value supplied by any existent files.",
'mungedomain',
"If set to yes, a different method is used to determine a matching
--- 181,197 ----
characters.",
'subscribe_policy',
! "One of 4 possible values: open, closed, auto, confirm. Open allows
! people to subscribe themselves to the list. Auto allows anybody to
! subscribe anybody to the list without maintainer approval. The
! existence of the file <listname>.auto is the same as specifying the
! value auto. Closed requires maintainer approval for all subscribe
! requests to the list. In addition to the keyword, if the file
! <listname>.closed exists, it is the same as specifying the value
! closed. Confirm causes majordomo to send a reply back to the
! subscriber which includes a authentication number which must
! be sent back in with another subscribe commad. The value of this
! keyword overrides the value supplied by any existent files.",
'mungedomain',
"If set to yes, a different method is used to determine a matching
diff -c ./majordomo /home/majordomo/majordomo
*** ./majordomo Wed May 10 05:11:24 1995
--- /home/majordomo/majordomo Mon Jan 8 17:32:31 1996
***************
*** 168,173 ****
--- 168,174 ----
elsif ($cmd eq "help") { &do_help(@parts); }
elsif ($cmd eq "get") { &do_get(@parts); }
elsif ($cmd eq "index") { &do_index(@parts); }
+ elsif ($cmd eq "auth") { &do_auth(@parts); }
else {
&squawk("Command '$cmd' not recognized.");
$count--; # if we get to here, it wasn't really a command
***************
*** 215,221 ****
# subscriber is the person making the request
if ($approved
|| ($config_opts{$clean_list,"subscribe_policy"} eq "auto" )
! || (($config_opts{$clean_list,"subscribe_policy"} ne "closed" )
&& &addr_match($reply_to, $subscriber,
(&cf_ck_bool($clean_list,"mungedomain") ? 2 : undef)))) {
# Either the request is approved, or the list is open and the
--- 216,224 ----
# subscriber is the person making the request
if ($approved
|| ($config_opts{$clean_list,"subscribe_policy"} eq "auto" )
! || (($config_opts{$clean_list,"subscribe_policy"} eq "confirm")
! && (&gen_cookie($sm, $clean_list, $subscriber) eq $auth_info))
! || (($config_opts{$clean_list,"subscribe_policy"} eq "open" )
&& &addr_match($reply_to, $subscriber,
(&cf_ck_bool($clean_list,"mungedomain") ? 2 : undef)))) {
# Either the request is approved, or the list is open and the
***************
*** 290,295 ****
--- 293,300 ----
# unsubscribe themselves without the owner's approval).
if ($approved
|| ($config_opts{$clean_list,"subscribe_policy"} eq "auto" )
+ || (($config_opts{$clean_list,"subscribe_policy"} eq "confirm")
+ && (&gen_cookie($sm, $clean_list, $subscriber) eq $auth_info))
|| &addr_match($reply_to, $subscriber,
(&cf_ck_bool($clean_list,"mungedomain") ? 2 : undef))) {
# Either the request is approved, or the subscriber is the
***************
*** 345,350 ****
--- 351,361 ----
}
}
+ sub do_auth {
+ # Check to see we've got all the arguments
+ ($auth_info = shift) || &squawk("auth: needs key");
+ }
+
sub do_approve {
# Check to see we've got all the arguments
(local($passwd) = shift) || &squawk("approve: needs passwd");
***************
*** 1132,1137 ****
--- 1143,1199 ----
&log("help");
}
+ sub send_confirm {
+ local($cmd) = shift;
+ local($list) = &valid_list($listdir, shift);
+ local($subscriber) = @_;
+ local($cookie) = &gen_cookie($cmd, $list, $subscriber);
+ local(*AUTH);
+
+ &sendmail(AUTH, $subscriber, "Confirmation for $cmd $list");
+
+ print AUTH <<"EOM";
+ Someone (possibly you) has requested that your email address be added
+ to or deleted from the mailing list "$list\@$whereami".
+
+ If you really want this action to be taken, please send the following
+ commands (exactly as shown) back to "$whoami":
+
+ auth $cookie
+ $cmd $list $subscriber
+
+ If you do not want to this action taken, just ignore this message and
+ no action will be taken.
+
+ If you have any questions about the policy of the list owner, please
+ contact "$list-approval@$whereami".
+
+ Thanks!
+
+ $whoami
+ EOM
+ close(AUTH);
+
+ print REPLY <<"EOM";
+ Your request to $whoami:
+
+ $cmd $list $subscriber
+
+ must be authenticated. To accomplish this, another request must be
+ sent in with an authorization key, which has been sent to:
+ $subscriber
+
+ If you have any questions about the policy of the list owner, please
+ contact "$list-approval@$whereami".
+
+ Thanks!
+
+ $whoami
+ EOM
+ &log("send_confirm $cmd $list $subscriber");
+ }
+
+
# Send a request for subscribe or unsubscribe approval to a list owner
# Usage: &request_approval($cmd, $list, @subscriber)
sub request_approval {
***************
*** 1318,1324 ****
--- 1381,1403 ----
request, the part after the list name is optional, but if it's there, it
should be an email address, NOT a person's real name.
EOM
+ } elsif ($config_opts{$clean_list,"subscribe_policy"} eq "confirm") {
+ &send_confirm($request, $clean_list, $subscriber);
} else {
&request_approval($request, $clean_list, $subscriber);
}
+ }
+
+ sub gen_cookie {
+ local($combined) = join('/', @_);
+ local($cookie) = $cookie_seed;
+ local($i, $carry);
+
+ for ($i = 0; $i < length($combined); $i++) {
+ $cookie ^= ord(substr($combined, $i));
+ $carry = ($cookie >> 28) & 0xf;
+ $cookie <<= 4;
+ $cookie |= $carry;
+ }
+ return (sprintf("%08x", $cookie));
}
|
|