2006-03-23 07:51:15 +01:00
|
|
|
#!/usr/bin/perl
|
|
|
|
|
2008-07-11 12:07:48 +02:00
|
|
|
package IkiWiki;
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
use warnings;
|
|
|
|
use strict;
|
2006-05-02 08:53:33 +02:00
|
|
|
use IkiWiki;
|
2006-04-25 01:09:26 +02:00
|
|
|
use IkiWiki::UserInfo;
|
2006-07-04 00:14:52 +02:00
|
|
|
use open qw{:utf8 :std};
|
2006-07-03 22:18:16 +02:00
|
|
|
use Encode;
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-08-27 22:25:05 +02:00
|
|
|
sub printheader ($) { #{{{
|
|
|
|
my $session=shift;
|
|
|
|
|
|
|
|
if ($config{sslcookie}) {
|
|
|
|
print $session->header(-charset => 'utf-8',
|
2008-08-28 22:09:58 +02:00
|
|
|
-cookie => $session->cookie(-httponly => 1, -secure => 1));
|
2006-08-27 22:25:05 +02:00
|
|
|
} else {
|
2008-08-28 22:09:58 +02:00
|
|
|
print $session->header(-charset => 'utf-8',
|
|
|
|
-cookie => $session->cookie(-httponly => 1));
|
2006-08-27 22:25:05 +02:00
|
|
|
}
|
|
|
|
} #}}}
|
2008-01-07 22:34:13 +01:00
|
|
|
|
2008-03-12 19:21:48 +01:00
|
|
|
sub showform ($$$$;@) { #{{{
|
2007-08-22 23:06:13 +02:00
|
|
|
my $form=shift;
|
|
|
|
my $buttons=shift;
|
|
|
|
my $session=shift;
|
|
|
|
my $cgi=shift;
|
|
|
|
|
|
|
|
if (exists $hooks{formbuilder}) {
|
|
|
|
run_hooks(formbuilder => sub {
|
|
|
|
shift->(form => $form, cgi => $cgi, session => $session,
|
|
|
|
buttons => $buttons);
|
|
|
|
});
|
|
|
|
}
|
2007-12-12 09:01:15 +01:00
|
|
|
|
|
|
|
printheader($session);
|
2008-03-12 19:21:48 +01:00
|
|
|
print misctemplate($form->title, $form->render(submit => $buttons), @_);
|
2007-08-22 23:06:13 +02:00
|
|
|
}
|
2006-08-27 22:25:05 +02:00
|
|
|
|
2006-07-07 23:00:48 +02:00
|
|
|
sub redirect ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $url=shift;
|
|
|
|
if (! $config{w3mmode}) {
|
|
|
|
print $q->redirect($url);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
print "Content-type: text/plain\n";
|
|
|
|
print "W3m-control: GOTO $url\n\n";
|
|
|
|
}
|
|
|
|
} #}}}
|
|
|
|
|
2007-02-02 03:33:03 +01:00
|
|
|
sub check_canedit ($$$;$) { #{{{
|
2006-03-23 07:51:15 +01:00
|
|
|
my $page=shift;
|
2007-02-02 03:33:03 +01:00
|
|
|
my $q=shift;
|
2006-03-23 07:51:15 +01:00
|
|
|
my $session=shift;
|
|
|
|
my $nonfatal=shift;
|
|
|
|
|
2007-02-02 03:33:03 +01:00
|
|
|
my $canedit;
|
|
|
|
run_hooks(canedit => sub {
|
|
|
|
return if defined $canedit;
|
|
|
|
my $ret=shift->($page, $q, $session);
|
2008-01-07 22:34:13 +01:00
|
|
|
if (defined $ret) {
|
|
|
|
if ($ret eq "") {
|
|
|
|
$canedit=1;
|
|
|
|
}
|
|
|
|
elsif (ref $ret eq 'CODE') {
|
2008-01-07 22:35:16 +01:00
|
|
|
$ret->() unless $nonfatal;
|
2008-01-07 22:34:13 +01:00
|
|
|
$canedit=0;
|
|
|
|
}
|
|
|
|
elsif (defined $ret) {
|
|
|
|
error($ret) unless $nonfatal;
|
2008-01-07 22:35:16 +01:00
|
|
|
$canedit=0;
|
2008-01-07 22:34:13 +01:00
|
|
|
}
|
2007-02-02 03:33:03 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
return $canedit;
|
2006-03-23 07:51:15 +01:00
|
|
|
} #}}}
|
|
|
|
|
2008-05-21 21:30:56 +02:00
|
|
|
sub decode_cgi_utf8 ($) { #{{{
|
|
|
|
# decode_form_utf8 method is needed for 5.10
|
|
|
|
if ($] < 5.01) {
|
|
|
|
my $cgi = shift;
|
|
|
|
foreach my $f ($cgi->param) {
|
|
|
|
$cgi->param($f, map { decode_utf8 $_ } $cgi->param($f));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} #}}}
|
|
|
|
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
sub decode_form_utf8 ($) { #{{{
|
2008-05-21 21:30:56 +02:00
|
|
|
if ($] >= 5.01) {
|
|
|
|
my $form = shift;
|
|
|
|
foreach my $f ($form->field) {
|
|
|
|
$form->field(name => $f,
|
|
|
|
value => decode_utf8($form->field($f)),
|
|
|
|
force => 1,
|
|
|
|
);
|
|
|
|
}
|
2006-07-11 23:20:14 +02:00
|
|
|
}
|
|
|
|
} #}}}
|
|
|
|
|
2007-02-02 03:33:03 +01:00
|
|
|
# Check if the user is signed in. If not, redirect to the signin form and
|
|
|
|
# save their place to return to later.
|
|
|
|
sub needsignin ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
|
|
|
if (! defined $session->param("name") ||
|
|
|
|
! userinfo_get($session->param("name"), "regdate")) {
|
2008-01-07 21:56:39 +01:00
|
|
|
$session->param(postsignin => $ENV{QUERY_STRING});
|
2007-02-02 03:33:03 +01:00
|
|
|
cgi_signin($q, $session);
|
|
|
|
cgi_savesession($session);
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
} #}}}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
sub cgi_signin ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
2008-05-21 21:30:56 +02:00
|
|
|
decode_cgi_utf8($q);
|
2006-03-23 07:51:15 +01:00
|
|
|
eval q{use CGI::FormBuilder};
|
2006-11-08 22:03:33 +01:00
|
|
|
error($@) if $@;
|
2006-03-23 07:51:15 +01:00
|
|
|
my $form = CGI::FormBuilder->new(
|
|
|
|
title => "signin",
|
2007-04-30 23:27:58 +02:00
|
|
|
name => "signin",
|
2006-06-11 20:51:49 +02:00
|
|
|
charset => "utf-8",
|
2006-03-23 07:51:15 +01:00
|
|
|
method => 'POST',
|
|
|
|
required => 'NONE',
|
|
|
|
javascript => 0,
|
|
|
|
params => $q,
|
2006-04-04 20:43:26 +02:00
|
|
|
action => $config{cgiurl},
|
2006-03-23 07:51:15 +01:00
|
|
|
header => 0,
|
2007-04-30 23:27:58 +02:00
|
|
|
template => {type => 'div'},
|
2006-08-22 00:27:02 +02:00
|
|
|
stylesheet => baseurl()."style.css",
|
2006-03-23 07:51:15 +01:00
|
|
|
);
|
2006-11-20 21:37:27 +01:00
|
|
|
my $buttons=["Login"];
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-07-31 01:08:10 +02:00
|
|
|
if ($q->param("do") ne "signin" && !$form->submitted) {
|
2006-12-29 05:38:40 +01:00
|
|
|
$form->text(gettext("You need to log in first."));
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
2007-02-02 03:33:03 +01:00
|
|
|
$form->field(name => "do", type => "hidden", value => "signin",
|
|
|
|
force => 1);
|
2006-03-23 07:51:15 +01:00
|
|
|
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
decode_form_utf8($form);
|
2006-11-20 21:37:27 +01:00
|
|
|
run_hooks(formbuilder_setup => sub {
|
2007-08-17 07:34:59 +02:00
|
|
|
shift->(form => $form, cgi => $q, session => $session,
|
|
|
|
buttons => $buttons);
|
2006-11-20 21:37:27 +01:00
|
|
|
});
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
decode_form_utf8($form);
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2007-08-22 23:06:13 +02:00
|
|
|
if ($form->submitted) {
|
|
|
|
$form->validate;
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
2007-08-22 23:06:13 +02:00
|
|
|
|
|
|
|
showform($form, $buttons, $session, $q);
|
2006-03-23 07:51:15 +01:00
|
|
|
} #}}}
|
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
sub cgi_postsignin ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
2007-02-24 01:20:36 +01:00
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
# Continue with whatever was being done before the signin process.
|
2007-02-24 01:20:36 +01:00
|
|
|
if (defined $session->param("postsignin")) {
|
|
|
|
my $postsignin=CGI->new($session->param("postsignin"));
|
|
|
|
$session->clear("postsignin");
|
|
|
|
cgi($postsignin, $session);
|
|
|
|
cgi_savesession($session);
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
else {
|
2008-01-07 21:56:39 +01:00
|
|
|
error(gettext("login failed, perhaps you need to turn on cookies?"));
|
2007-02-24 01:20:36 +01:00
|
|
|
}
|
2006-11-20 10:40:09 +01:00
|
|
|
} #}}}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
sub cgi_prefs ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
2007-02-02 03:33:03 +01:00
|
|
|
needsignin($q, $session);
|
2008-05-21 21:30:56 +02:00
|
|
|
decode_cgi_utf8($q);
|
Fix CSRF attacks against the preferences and edit forms. Closes: #475445
The fix involved embedding the session id in the forms, and not allowing the
forms to be submitted if the embedded id does not match the session id.
In the case of the preferences form, if the session id is not embedded,
then the CGI parameters are cleared. This avoids a secondary attack where the
link to the preferences form prefills password or other fields, and
the user hits "submit" without noticing these prefilled values.
In the case of the editpage form, the anonok plugin can allow anyone to edit,
and so I chose not to guard against CSRF attacks against users who are not
logged in. Otherwise, it also embeds the session id and checks it.
For page editing, I assume that the user will notice if content or commit
message is changed because of CGI parameters, and won't blndly hit save page.
So I didn't block those CGI paramters. (It's even possible to use those CGI
parameters, for good, not for evil, I guess..)
The only other CSRF attack I can think of in ikiwiki involves the poll plugin.
It's certianly possible to set up a link that causes the user to unknowingly
vote in a poll. However, the poll plugin is not intended to be used for things
that people would want to attack, since anyone can after all edit the poll page
and fill in any values they like. So this "attack" is ignorable.
2008-04-10 22:35:30 +02:00
|
|
|
|
|
|
|
# The session id is stored on the form and checked to
|
|
|
|
# guard against CSRF.
|
|
|
|
my $sid=$q->param('sid');
|
|
|
|
if (! defined $sid) {
|
|
|
|
$q->delete_all;
|
|
|
|
}
|
|
|
|
elsif ($sid ne $session->id) {
|
|
|
|
error(gettext("Your login session has expired."));
|
|
|
|
}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
eval q{use CGI::FormBuilder};
|
2006-11-08 22:03:33 +01:00
|
|
|
error($@) if $@;
|
2006-03-23 07:51:15 +01:00
|
|
|
my $form = CGI::FormBuilder->new(
|
|
|
|
title => "preferences",
|
2007-04-30 23:27:58 +02:00
|
|
|
name => "preferences",
|
2006-03-23 07:51:15 +01:00
|
|
|
header => 0,
|
2006-06-11 20:51:49 +02:00
|
|
|
charset => "utf-8",
|
2006-03-23 07:51:15 +01:00
|
|
|
method => 'POST',
|
|
|
|
validate => {
|
|
|
|
email => 'EMAIL',
|
|
|
|
},
|
|
|
|
required => 'NONE',
|
|
|
|
javascript => 0,
|
|
|
|
params => $q,
|
2006-04-04 20:43:26 +02:00
|
|
|
action => $config{cgiurl},
|
2007-04-30 23:27:58 +02:00
|
|
|
template => {type => 'div'},
|
2006-08-22 00:27:02 +02:00
|
|
|
stylesheet => baseurl()."style.css",
|
2007-04-29 23:57:25 +02:00
|
|
|
fieldsets => [
|
|
|
|
[login => gettext("Login")],
|
|
|
|
[preferences => gettext("Preferences")],
|
|
|
|
[admin => gettext("Admin")]
|
|
|
|
],
|
2006-03-23 07:51:15 +01:00
|
|
|
);
|
2006-11-20 21:37:27 +01:00
|
|
|
my $buttons=["Save Preferences", "Logout", "Cancel"];
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
|
|
|
|
decode_form_utf8($form);
|
2006-11-20 21:37:27 +01:00
|
|
|
run_hooks(formbuilder_setup => sub {
|
2007-08-17 07:34:59 +02:00
|
|
|
shift->(form => $form, cgi => $q, session => $session,
|
|
|
|
buttons => $buttons);
|
2006-11-20 21:37:27 +01:00
|
|
|
});
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
decode_form_utf8($form);
|
2006-03-23 07:51:15 +01:00
|
|
|
|
Fix CSRF attacks against the preferences and edit forms. Closes: #475445
The fix involved embedding the session id in the forms, and not allowing the
forms to be submitted if the embedded id does not match the session id.
In the case of the preferences form, if the session id is not embedded,
then the CGI parameters are cleared. This avoids a secondary attack where the
link to the preferences form prefills password or other fields, and
the user hits "submit" without noticing these prefilled values.
In the case of the editpage form, the anonok plugin can allow anyone to edit,
and so I chose not to guard against CSRF attacks against users who are not
logged in. Otherwise, it also embeds the session id and checks it.
For page editing, I assume that the user will notice if content or commit
message is changed because of CGI parameters, and won't blndly hit save page.
So I didn't block those CGI paramters. (It's even possible to use those CGI
parameters, for good, not for evil, I guess..)
The only other CSRF attack I can think of in ikiwiki involves the poll plugin.
It's certianly possible to set up a link that causes the user to unknowingly
vote in a poll. However, the poll plugin is not intended to be used for things
that people would want to attack, since anyone can after all edit the poll page
and fill in any values they like. So this "attack" is ignorable.
2008-04-10 22:35:30 +02:00
|
|
|
$form->field(name => "do", type => "hidden", value => "prefs",
|
|
|
|
force => 1);
|
|
|
|
$form->field(name => "sid", type => "hidden", value => $session->id,
|
|
|
|
force => 1);
|
2007-04-29 23:57:25 +02:00
|
|
|
$form->field(name => "email", size => 50, fieldset => "preferences");
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-11-20 21:37:27 +01:00
|
|
|
my $user_name=$session->param("name");
|
2008-08-01 23:37:24 +02:00
|
|
|
|
|
|
|
# XXX deprecated, should be removed eventually
|
2008-08-02 19:28:12 +02:00
|
|
|
$form->field(name => "banned_users", size => 50, fieldset => "admin");
|
2006-03-23 07:51:15 +01:00
|
|
|
if (! is_admin($user_name)) {
|
2006-10-28 02:35:33 +02:00
|
|
|
$form->field(name => "banned_users", type => "hidden");
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
if (! $form->submitted) {
|
|
|
|
$form->field(name => "email", force => 1,
|
|
|
|
value => userinfo_get($user_name, "email"));
|
2006-10-28 02:35:33 +02:00
|
|
|
if (is_admin($user_name)) {
|
2008-08-01 23:37:24 +02:00
|
|
|
my $value=join(" ", get_banned_users());
|
|
|
|
if (length $value) {
|
|
|
|
$form->field(name => "banned_users", force => 1,
|
|
|
|
value => join(" ", get_banned_users()),
|
|
|
|
comment => "deprecated; please move to banned_users in setup file");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$form->field(name => "banned_users", type => "hidden");
|
|
|
|
}
|
2006-10-28 02:35:33 +02:00
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($form->submitted eq 'Logout') {
|
|
|
|
$session->delete();
|
2006-07-07 23:00:48 +02:00
|
|
|
redirect($q, $config{url});
|
2006-03-23 07:51:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
elsif ($form->submitted eq 'Cancel') {
|
2006-07-07 23:00:48 +02:00
|
|
|
redirect($q, $config{url});
|
2006-03-23 07:51:15 +01:00
|
|
|
return;
|
|
|
|
}
|
2006-12-29 05:38:40 +01:00
|
|
|
elsif ($form->submitted eq 'Save Preferences' && $form->validate) {
|
2008-01-29 06:36:58 +01:00
|
|
|
if (defined $form->field('email')) {
|
|
|
|
userinfo_set($user_name, 'email', $form->field('email')) ||
|
|
|
|
error("failed to set email");
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
2008-08-01 23:37:24 +02:00
|
|
|
|
|
|
|
# XXX deprecated, should be removed eventually
|
2006-10-28 02:35:33 +02:00
|
|
|
if (is_admin($user_name)) {
|
|
|
|
set_banned_users(grep { ! is_admin($_) }
|
2007-02-15 03:22:08 +01:00
|
|
|
split(' ',
|
|
|
|
$form->field("banned_users"))) ||
|
|
|
|
error("failed saving changes");
|
2008-08-01 23:37:24 +02:00
|
|
|
if (! length $form->field("banned_users")) {
|
|
|
|
$form->field(name => "banned_users", type => "hidden");
|
|
|
|
}
|
2006-10-28 02:35:33 +02:00
|
|
|
}
|
2008-08-01 23:37:24 +02:00
|
|
|
|
2006-12-29 05:38:40 +01:00
|
|
|
$form->text(gettext("Preferences saved."));
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
|
2007-08-22 23:06:13 +02:00
|
|
|
showform($form, $buttons, $session, $q);
|
2006-03-23 07:51:15 +01:00
|
|
|
} #}}}
|
|
|
|
|
2007-03-08 07:03:59 +01:00
|
|
|
sub cgi_editpage ($$) { #{{{
|
2006-03-23 07:51:15 +01:00
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
2006-09-16 02:52:26 +02:00
|
|
|
|
2008-05-21 21:30:56 +02:00
|
|
|
decode_cgi_utf8($q);
|
|
|
|
|
Fix CSRF attacks against the preferences and edit forms. Closes: #475445
The fix involved embedding the session id in the forms, and not allowing the
forms to be submitted if the embedded id does not match the session id.
In the case of the preferences form, if the session id is not embedded,
then the CGI parameters are cleared. This avoids a secondary attack where the
link to the preferences form prefills password or other fields, and
the user hits "submit" without noticing these prefilled values.
In the case of the editpage form, the anonok plugin can allow anyone to edit,
and so I chose not to guard against CSRF attacks against users who are not
logged in. Otherwise, it also embeds the session id and checks it.
For page editing, I assume that the user will notice if content or commit
message is changed because of CGI parameters, and won't blndly hit save page.
So I didn't block those CGI paramters. (It's even possible to use those CGI
parameters, for good, not for evil, I guess..)
The only other CSRF attack I can think of in ikiwiki involves the poll plugin.
It's certianly possible to set up a link that causes the user to unknowingly
vote in a poll. However, the poll plugin is not intended to be used for things
that people would want to attack, since anyone can after all edit the poll page
and fill in any values they like. So this "attack" is ignorable.
2008-04-10 22:35:30 +02:00
|
|
|
my @fields=qw(do rcsinfo subpage from page type editcontent comments);
|
|
|
|
my @buttons=("Save Page", "Preview", "Cancel");
|
2006-12-02 01:19:55 +01:00
|
|
|
eval q{use CGI::FormBuilder};
|
2006-11-08 22:03:33 +01:00
|
|
|
error($@) if $@;
|
2006-03-23 07:51:15 +01:00
|
|
|
my $form = CGI::FormBuilder->new(
|
2006-09-16 02:52:26 +02:00
|
|
|
fields => \@fields,
|
2006-06-11 20:51:49 +02:00
|
|
|
charset => "utf-8",
|
2006-03-23 07:51:15 +01:00
|
|
|
method => 'POST',
|
2006-05-27 21:04:46 +02:00
|
|
|
required => [qw{editcontent}],
|
2006-03-23 07:51:15 +01:00
|
|
|
javascript => 0,
|
|
|
|
params => $q,
|
2006-04-04 20:43:26 +02:00
|
|
|
action => $config{cgiurl},
|
2007-07-16 07:24:31 +02:00
|
|
|
header => 0,
|
2006-03-23 07:51:15 +01:00
|
|
|
table => 0,
|
2007-01-12 21:48:19 +01:00
|
|
|
template => scalar template_params("editpage.tmpl"),
|
2007-05-11 22:09:58 +02:00
|
|
|
wikiname => $config{wikiname},
|
2006-03-23 07:51:15 +01:00
|
|
|
);
|
|
|
|
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
decode_form_utf8($form);
|
2006-11-20 21:37:27 +01:00
|
|
|
run_hooks(formbuilder_setup => sub {
|
2007-08-17 07:34:59 +02:00
|
|
|
shift->(form => $form, cgi => $q, session => $session,
|
|
|
|
buttons => \@buttons);
|
2006-11-20 21:37:27 +01:00
|
|
|
});
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
decode_form_utf8($form);
|
2006-11-20 21:37:27 +01:00
|
|
|
|
2008-07-06 21:52:04 +02:00
|
|
|
# This untaint is safe because we check file_pruned.
|
2008-07-01 19:31:03 +02:00
|
|
|
my $page=$form->field('page');
|
2008-07-06 21:52:04 +02:00
|
|
|
$page=possibly_foolish_untaint($page);
|
2008-07-10 21:36:18 +02:00
|
|
|
my $absolute=($page =~ s#^/+##);
|
2007-08-28 03:59:01 +02:00
|
|
|
if (! defined $page || ! length $page ||
|
2008-07-10 21:36:18 +02:00
|
|
|
file_pruned($page, $config{srcdir})) {
|
2006-03-23 07:51:15 +01:00
|
|
|
error("bad page name");
|
|
|
|
}
|
2008-03-12 19:21:48 +01:00
|
|
|
|
|
|
|
my $baseurl=$config{url}."/".htmlpage($page);
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-07-26 23:23:06 +02:00
|
|
|
my $from;
|
|
|
|
if (defined $form->field('from')) {
|
|
|
|
($from)=$form->field('from')=~/$config{wiki_file_regexp}/;
|
|
|
|
}
|
|
|
|
|
2006-07-04 03:29:45 +02:00
|
|
|
my $file;
|
2006-08-13 04:03:43 +02:00
|
|
|
my $type;
|
2007-03-18 00:20:27 +01:00
|
|
|
if (exists $pagesources{$page} && $form->field("do") ne "create") {
|
2006-07-26 23:23:06 +02:00
|
|
|
$file=$pagesources{$page};
|
|
|
|
$type=pagetype($file);
|
2008-01-29 05:08:48 +01:00
|
|
|
if (! defined $type || $type=~/^_/) {
|
2007-02-10 21:37:36 +01:00
|
|
|
error(sprintf(gettext("%s is not an editable page"), $page));
|
|
|
|
}
|
2007-03-18 00:20:27 +01:00
|
|
|
if (! $form->submitted) {
|
|
|
|
$form->field(name => "rcsinfo",
|
|
|
|
value => rcs_prepedit($file), force => 1);
|
|
|
|
}
|
2007-12-13 01:11:29 +01:00
|
|
|
$form->field(name => "editcontent", validate => '/.*/');
|
2006-03-29 20:35:04 +02:00
|
|
|
}
|
2006-07-04 03:29:45 +02:00
|
|
|
else {
|
2006-07-26 23:23:06 +02:00
|
|
|
$type=$form->param('type');
|
|
|
|
if (defined $type && length $type && $hooks{htmlize}{$type}) {
|
|
|
|
$type=possibly_foolish_untaint($type);
|
|
|
|
}
|
2008-01-05 07:47:04 +01:00
|
|
|
elsif (defined $from && exists $pagesources{$from}) {
|
2006-07-26 23:23:06 +02:00
|
|
|
# favor the type of linking page
|
|
|
|
$type=pagetype($pagesources{$from});
|
|
|
|
}
|
2006-08-13 07:56:01 +02:00
|
|
|
$type=$config{default_pageext} unless defined $type;
|
2006-07-26 23:23:06 +02:00
|
|
|
$file=$page.".".$type;
|
2007-03-18 00:20:27 +01:00
|
|
|
if (! $form->submitted) {
|
|
|
|
$form->field(name => "rcsinfo", value => "", force => 1);
|
|
|
|
}
|
2007-12-13 01:11:29 +01:00
|
|
|
$form->field(name => "editcontent", validate => '/.+/');
|
2006-07-04 03:29:45 +02:00
|
|
|
}
|
2006-07-26 23:23:06 +02:00
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
$form->field(name => "do", type => 'hidden');
|
Fix CSRF attacks against the preferences and edit forms. Closes: #475445
The fix involved embedding the session id in the forms, and not allowing the
forms to be submitted if the embedded id does not match the session id.
In the case of the preferences form, if the session id is not embedded,
then the CGI parameters are cleared. This avoids a secondary attack where the
link to the preferences form prefills password or other fields, and
the user hits "submit" without noticing these prefilled values.
In the case of the editpage form, the anonok plugin can allow anyone to edit,
and so I chose not to guard against CSRF attacks against users who are not
logged in. Otherwise, it also embeds the session id and checks it.
For page editing, I assume that the user will notice if content or commit
message is changed because of CGI parameters, and won't blndly hit save page.
So I didn't block those CGI paramters. (It's even possible to use those CGI
parameters, for good, not for evil, I guess..)
The only other CSRF attack I can think of in ikiwiki involves the poll plugin.
It's certianly possible to set up a link that causes the user to unknowingly
vote in a poll. However, the poll plugin is not intended to be used for things
that people would want to attack, since anyone can after all edit the poll page
and fill in any values they like. So this "attack" is ignorable.
2008-04-10 22:35:30 +02:00
|
|
|
$form->field(name => "sid", type => "hidden", value => $session->id,
|
|
|
|
force => 1);
|
2006-03-23 07:51:15 +01:00
|
|
|
$form->field(name => "from", type => 'hidden');
|
|
|
|
$form->field(name => "rcsinfo", type => 'hidden');
|
2006-03-24 06:33:23 +01:00
|
|
|
$form->field(name => "subpage", type => 'hidden');
|
2008-07-06 21:52:04 +02:00
|
|
|
$form->field(name => "page", value => $page, force => 1);
|
2006-07-26 23:23:06 +02:00
|
|
|
$form->field(name => "type", value => $type, force => 1);
|
2006-03-23 07:51:15 +01:00
|
|
|
$form->field(name => "comments", type => "text", size => 80);
|
2006-05-27 21:04:46 +02:00
|
|
|
$form->field(name => "editcontent", type => "textarea", rows => 20,
|
2006-03-23 07:51:15 +01:00
|
|
|
cols => 80);
|
|
|
|
$form->tmpl_param("can_commit", $config{rcs});
|
|
|
|
$form->tmpl_param("indexlink", indexlink());
|
|
|
|
$form->tmpl_param("helponformattinglink",
|
2008-03-12 19:21:48 +01:00
|
|
|
htmllink($page, $page, "ikiwiki/formatting",
|
2007-12-08 21:59:08 +01:00
|
|
|
noimageinline => 1,
|
|
|
|
linktext => "FormattingHelp"));
|
2006-03-23 07:51:15 +01:00
|
|
|
|
|
|
|
if ($form->submitted eq "Cancel") {
|
2007-03-18 00:20:27 +01:00
|
|
|
if ($form->field("do") eq "create" && defined $from) {
|
2006-09-16 03:23:14 +02:00
|
|
|
redirect($q, "$config{url}/".htmlpage($from));
|
|
|
|
}
|
2007-03-18 00:20:27 +01:00
|
|
|
elsif ($form->field("do") eq "create") {
|
2006-09-16 03:23:14 +02:00
|
|
|
redirect($q, $config{url});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
redirect($q, "$config{url}/".htmlpage($page));
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
elsif ($form->submitted eq "Preview") {
|
2008-03-18 02:28:31 +01:00
|
|
|
my $new=not exists $pagesources{$page};
|
|
|
|
if ($new) {
|
|
|
|
# temporarily record its type
|
|
|
|
$pagesources{$page}=$page.".".$type;
|
|
|
|
}
|
|
|
|
|
2007-08-26 23:33:25 +02:00
|
|
|
my $content=$form->field('editcontent');
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
|
2007-08-26 23:33:25 +02:00
|
|
|
run_hooks(editcontent => sub {
|
|
|
|
$content=shift->(
|
|
|
|
content => $content,
|
|
|
|
page => $page,
|
|
|
|
cgi => $q,
|
|
|
|
session => $session,
|
|
|
|
);
|
|
|
|
});
|
2008-06-29 05:08:24 +02:00
|
|
|
my $preview=htmlize($page, $page, $type,
|
2008-03-12 19:21:48 +01:00
|
|
|
linkify($page, $page,
|
|
|
|
preprocess($page, $page,
|
2008-06-29 05:08:24 +02:00
|
|
|
filter($page, $page, $content), 0, 1)));
|
|
|
|
run_hooks(format => sub {
|
|
|
|
$preview=shift->(
|
|
|
|
page => $page,
|
|
|
|
content => $preview,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
$form->tmpl_param("page_preview", $preview);
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
|
2008-03-18 02:28:31 +01:00
|
|
|
if ($new) {
|
|
|
|
delete $pagesources{$page};
|
|
|
|
}
|
2008-02-04 01:51:00 +01:00
|
|
|
# previewing may have created files on disk
|
|
|
|
saveindex();
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
2007-08-17 07:34:59 +02:00
|
|
|
elsif ($form->submitted eq "Save Page") {
|
2006-03-23 07:51:15 +01:00
|
|
|
$form->tmpl_param("page_preview", "");
|
|
|
|
}
|
|
|
|
|
2007-08-26 23:33:25 +02:00
|
|
|
if ($form->submitted ne "Save Page" || ! $form->validate) {
|
2006-03-23 07:51:15 +01:00
|
|
|
if ($form->field("do") eq "create") {
|
|
|
|
my @page_locs;
|
|
|
|
my $best_loc;
|
|
|
|
if (! defined $from || ! length $from ||
|
2006-07-11 23:20:14 +02:00
|
|
|
$from ne $form->field('from') ||
|
2006-12-21 20:36:15 +01:00
|
|
|
file_pruned($from, $config{srcdir}) ||
|
2008-07-10 21:36:18 +02:00
|
|
|
$from=~/^\// ||
|
|
|
|
$absolute ||
|
2006-03-29 00:56:56 +02:00
|
|
|
$form->submitted eq "Preview") {
|
2006-03-23 07:51:15 +01:00
|
|
|
@page_locs=$best_loc=$page;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
my $dir=$from."/";
|
2006-04-25 03:15:20 +02:00
|
|
|
$dir=~s![^/]+/+$!!;
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-07-11 23:20:14 +02:00
|
|
|
if ((defined $form->field('subpage') && length $form->field('subpage')) ||
|
2006-12-29 05:38:40 +01:00
|
|
|
$page eq gettext('discussion')) {
|
2006-03-23 07:51:15 +01:00
|
|
|
$best_loc="$from/$page";
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$best_loc=$dir.$page;
|
|
|
|
}
|
|
|
|
|
|
|
|
push @page_locs, $dir.$page;
|
|
|
|
push @page_locs, "$from/$page";
|
|
|
|
while (length $dir) {
|
2006-04-25 03:15:20 +02:00
|
|
|
$dir=~s![^/]+/+$!!;
|
2006-03-23 07:51:15 +01:00
|
|
|
push @page_locs, $dir.$page;
|
|
|
|
}
|
2008-02-14 21:42:14 +01:00
|
|
|
|
|
|
|
push @page_locs, "$config{userdir}/$page"
|
|
|
|
if length $config{userdir};
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
|
2006-05-02 04:57:45 +02:00
|
|
|
@page_locs = grep {
|
2007-02-02 03:33:03 +01:00
|
|
|
! exists $pagecase{lc $_}
|
2006-05-02 04:57:45 +02:00
|
|
|
} @page_locs;
|
|
|
|
if (! @page_locs) {
|
|
|
|
# hmm, someone else made the page in the
|
|
|
|
# meantime?
|
2008-02-14 21:42:14 +01:00
|
|
|
if ($form->submitted eq "Preview") {
|
|
|
|
# let them go ahead with the edit
|
|
|
|
# and resolve the conflict at save
|
|
|
|
# time
|
|
|
|
@page_locs=$page;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
redirect($q, "$config{url}/".htmlpage($page));
|
|
|
|
return;
|
|
|
|
}
|
2006-05-02 04:57:45 +02:00
|
|
|
}
|
2007-02-02 03:33:03 +01:00
|
|
|
|
|
|
|
my @editable_locs = grep {
|
|
|
|
check_canedit($_, $q, $session, 1)
|
|
|
|
} @page_locs;
|
|
|
|
if (! @editable_locs) {
|
|
|
|
# let it throw an error this time
|
|
|
|
map { check_canedit($_, $q, $session) } @page_locs;
|
|
|
|
}
|
2006-07-26 23:23:06 +02:00
|
|
|
|
|
|
|
my @page_types;
|
|
|
|
if (exists $hooks{htmlize}) {
|
2008-01-29 05:08:48 +01:00
|
|
|
@page_types=grep { !/^_/ }
|
|
|
|
keys %{$hooks{htmlize}};
|
2006-07-26 23:23:06 +02:00
|
|
|
}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
$form->tmpl_param("page_select", 1);
|
|
|
|
$form->field(name => "page", type => 'select',
|
2008-07-06 21:52:04 +02:00
|
|
|
options => [ map { [ $_, pagetitle($_, 1) ] } @editable_locs ],
|
|
|
|
value => $best_loc);
|
2006-07-26 23:23:06 +02:00
|
|
|
$form->field(name => "type", type => 'select',
|
|
|
|
options => \@page_types);
|
2006-12-29 05:38:40 +01:00
|
|
|
$form->title(sprintf(gettext("creating %s"), pagetitle($page)));
|
2007-07-16 07:24:31 +02:00
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
elsif ($form->field("do") eq "edit") {
|
2007-02-02 03:33:03 +01:00
|
|
|
check_canedit($page, $q, $session);
|
2006-05-27 21:04:46 +02:00
|
|
|
if (! defined $form->field('editcontent') ||
|
|
|
|
! length $form->field('editcontent')) {
|
2006-03-23 07:51:15 +01:00
|
|
|
my $content="";
|
2006-08-13 04:03:43 +02:00
|
|
|
if (exists $pagesources{$page}) {
|
|
|
|
$content=readfile(srcfile($pagesources{$page}));
|
2006-03-23 07:51:15 +01:00
|
|
|
$content=~s/\n/\r\n/g;
|
|
|
|
}
|
2006-05-27 21:04:46 +02:00
|
|
|
$form->field(name => "editcontent", value => $content,
|
2006-03-23 07:51:15 +01:00
|
|
|
force => 1);
|
|
|
|
}
|
|
|
|
$form->tmpl_param("page_select", 0);
|
|
|
|
$form->field(name => "page", type => 'hidden');
|
2006-07-26 23:23:06 +02:00
|
|
|
$form->field(name => "type", type => 'hidden');
|
2006-12-29 05:38:40 +01:00
|
|
|
$form->title(sprintf(gettext("editing %s"), pagetitle($page)));
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
|
2008-03-12 19:21:48 +01:00
|
|
|
showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
# save page
|
2007-02-02 03:33:03 +01:00
|
|
|
check_canedit($page, $q, $session);
|
Fix CSRF attacks against the preferences and edit forms. Closes: #475445
The fix involved embedding the session id in the forms, and not allowing the
forms to be submitted if the embedded id does not match the session id.
In the case of the preferences form, if the session id is not embedded,
then the CGI parameters are cleared. This avoids a secondary attack where the
link to the preferences form prefills password or other fields, and
the user hits "submit" without noticing these prefilled values.
In the case of the editpage form, the anonok plugin can allow anyone to edit,
and so I chose not to guard against CSRF attacks against users who are not
logged in. Otherwise, it also embeds the session id and checks it.
For page editing, I assume that the user will notice if content or commit
message is changed because of CGI parameters, and won't blndly hit save page.
So I didn't block those CGI paramters. (It's even possible to use those CGI
parameters, for good, not for evil, I guess..)
The only other CSRF attack I can think of in ikiwiki involves the poll plugin.
It's certianly possible to set up a link that causes the user to unknowingly
vote in a poll. However, the poll plugin is not intended to be used for things
that people would want to attack, since anyone can after all edit the poll page
and fill in any values they like. So this "attack" is ignorable.
2008-04-10 22:35:30 +02:00
|
|
|
|
|
|
|
# The session id is stored on the form and checked to
|
|
|
|
# guard against CSRF. But only if the user is logged in,
|
|
|
|
# as anonok can allow anonymous edits.
|
|
|
|
if (defined $session->param("name")) {
|
|
|
|
my $sid=$q->param('sid');
|
|
|
|
if (! defined $sid || $sid ne $session->id) {
|
|
|
|
error(gettext("Your login session has expired."));
|
|
|
|
}
|
|
|
|
}
|
2007-08-15 02:06:20 +02:00
|
|
|
|
|
|
|
my $exists=-e "$config{srcdir}/$file";
|
|
|
|
|
2007-08-28 03:59:01 +02:00
|
|
|
if ($form->field("do") ne "create" && ! $exists &&
|
2008-05-02 19:02:07 +02:00
|
|
|
! defined srcfile($file, 1)) {
|
2008-07-23 01:58:34 +02:00
|
|
|
$form->tmpl_param("message", template("editpagegone.tmpl")->output);
|
2007-03-18 00:20:27 +01:00
|
|
|
$form->field(name => "do", value => "create", force => 1);
|
2007-02-24 01:39:06 +01:00
|
|
|
$form->tmpl_param("page_select", 0);
|
|
|
|
$form->field(name => "page", type => 'hidden');
|
|
|
|
$form->field(name => "type", type => 'hidden');
|
2007-03-18 00:20:27 +01:00
|
|
|
$form->title(sprintf(gettext("editing %s"), $page));
|
2008-03-12 19:21:48 +01:00
|
|
|
showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
|
2007-02-24 01:39:06 +01:00
|
|
|
return;
|
|
|
|
}
|
2007-08-15 02:06:20 +02:00
|
|
|
elsif ($form->field("do") eq "create" && $exists) {
|
2008-07-23 01:58:34 +02:00
|
|
|
$form->tmpl_param("message", template("editcreationconflict.tmpl")->output);
|
2007-03-18 00:57:03 +01:00
|
|
|
$form->field(name => "do", value => "edit", force => 1);
|
|
|
|
$form->tmpl_param("page_select", 0);
|
|
|
|
$form->field(name => "page", type => 'hidden');
|
|
|
|
$form->field(name => "type", type => 'hidden');
|
|
|
|
$form->title(sprintf(gettext("editing %s"), $page));
|
|
|
|
$form->field("editcontent",
|
|
|
|
value => readfile("$config{srcdir}/$file").
|
|
|
|
"\n\n\n".$form->field("editcontent"),
|
|
|
|
force => 1);
|
2008-03-12 19:21:48 +01:00
|
|
|
showform($form, \@buttons, $session, $q, forcebaseurl => $baseurl);
|
2007-03-18 00:57:03 +01:00
|
|
|
return;
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-07-11 23:20:14 +02:00
|
|
|
my $content=$form->field('editcontent');
|
2007-08-26 23:33:25 +02:00
|
|
|
run_hooks(editcontent => sub {
|
|
|
|
$content=shift->(
|
|
|
|
content => $content,
|
|
|
|
page => $page,
|
|
|
|
cgi => $q,
|
|
|
|
session => $session,
|
|
|
|
);
|
|
|
|
});
|
2006-03-23 07:51:15 +01:00
|
|
|
$content=~s/\r\n/\n/g;
|
|
|
|
$content=~s/\r/\n/g;
|
2007-12-12 19:41:21 +01:00
|
|
|
$content.="\n" if $content !~ /\n$/;
|
2007-02-15 03:22:08 +01:00
|
|
|
|
|
|
|
$config{cgi}=0; # avoid cgi error message
|
|
|
|
eval { writefile($file, $config{srcdir}, $content) };
|
|
|
|
$config{cgi}=1;
|
|
|
|
if ($@) {
|
|
|
|
$form->field(name => "rcsinfo", value => rcs_prepedit($file),
|
|
|
|
force => 1);
|
2008-07-23 01:58:34 +02:00
|
|
|
my $mtemplate=template("editfailedsave.tmpl");
|
|
|
|
$mtemplate->param(error_message => $@);
|
|
|
|
$form->tmpl_param("message", $mtemplate->output);
|
2007-02-15 03:22:08 +01:00
|
|
|
$form->field("editcontent", value => $content, force => 1);
|
|
|
|
$form->tmpl_param("page_select", 0);
|
|
|
|
$form->field(name => "page", type => 'hidden');
|
|
|
|
$form->field(name => "type", type => 'hidden');
|
|
|
|
$form->title(sprintf(gettext("editing %s"), $page));
|
2008-03-12 19:21:48 +01:00
|
|
|
showform($form, \@buttons, $session, $q,
|
|
|
|
forcebaseurl => $baseurl);
|
2007-02-15 03:22:08 +01:00
|
|
|
return;
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2007-02-21 09:55:28 +01:00
|
|
|
my $conflict;
|
2006-03-23 07:51:15 +01:00
|
|
|
if ($config{rcs}) {
|
2006-11-22 15:28:38 +01:00
|
|
|
my $message="";
|
|
|
|
if (defined $form->field('comments') &&
|
|
|
|
length $form->field('comments')) {
|
|
|
|
$message=$form->field('comments');
|
|
|
|
}
|
|
|
|
|
2007-08-15 02:06:20 +02:00
|
|
|
if (! $exists) {
|
2006-03-23 07:51:15 +01:00
|
|
|
rcs_add($file);
|
|
|
|
}
|
2007-02-21 09:55:28 +01:00
|
|
|
|
|
|
|
# Prevent deadlock with post-commit hook by
|
|
|
|
# signaling to it that it should not try to
|
2008-02-04 01:47:01 +01:00
|
|
|
# do anything.
|
2007-02-21 09:55:28 +01:00
|
|
|
disable_commit_hook();
|
|
|
|
$conflict=rcs_commit($file, $message,
|
2006-11-22 15:28:38 +01:00
|
|
|
$form->field("rcsinfo"),
|
|
|
|
$session->param("name"), $ENV{REMOTE_ADDR});
|
2007-02-21 09:55:28 +01:00
|
|
|
enable_commit_hook();
|
|
|
|
rcs_update();
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2007-02-21 09:55:28 +01:00
|
|
|
# Refresh even if there was a conflict, since other changes
|
|
|
|
# may have been committed while the post-commit hook was
|
|
|
|
# disabled.
|
|
|
|
require IkiWiki::Render;
|
|
|
|
refresh();
|
|
|
|
saveindex();
|
|
|
|
|
|
|
|
if (defined $conflict) {
|
|
|
|
$form->field(name => "rcsinfo", value => rcs_prepedit($file),
|
|
|
|
force => 1);
|
2008-07-23 01:58:34 +02:00
|
|
|
$form->tmpl_param("message", template("editconflict.tmpl")->output);
|
2007-02-21 09:55:28 +01:00
|
|
|
$form->field("editcontent", value => $conflict, force => 1);
|
2007-02-24 01:39:06 +01:00
|
|
|
$form->field("do", "edit", force => 1);
|
2007-02-21 09:55:28 +01:00
|
|
|
$form->tmpl_param("page_select", 0);
|
|
|
|
$form->field(name => "page", type => 'hidden');
|
|
|
|
$form->field(name => "type", type => 'hidden');
|
|
|
|
$form->title(sprintf(gettext("editing %s"), $page));
|
2008-03-12 19:21:48 +01:00
|
|
|
showform($form, \@buttons, $session, $q,
|
|
|
|
forcebaseurl => $baseurl);
|
2007-02-21 09:55:28 +01:00
|
|
|
return;
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-02-21 09:55:28 +01:00
|
|
|
# The trailing question mark tries to avoid broken
|
|
|
|
# caches and get the most recent version of the page.
|
|
|
|
redirect($q, "$config{url}/".htmlpage($page)."?updated");
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} #}}}
|
2008-08-01 23:37:24 +02:00
|
|
|
|
|
|
|
sub check_banned ($$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
|
|
|
my $name=$session->param("name");
|
|
|
|
if (defined $name) {
|
|
|
|
# XXX banned in userinfo is deprecated, should be removed
|
|
|
|
# eventually, and only banned_users be checked.
|
|
|
|
if (userinfo_get($session->param("name"), "banned") ||
|
|
|
|
grep { $name eq $_ } @{$config{banned_users}}) {
|
|
|
|
print $q->header(-status => "403 Forbidden");
|
|
|
|
$session->delete();
|
|
|
|
print gettext("You are banned.");
|
|
|
|
cgi_savesession($session);
|
|
|
|
exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-11-26 20:43:50 +01:00
|
|
|
sub cgi_getsession ($) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
|
|
|
|
eval q{use CGI::Session};
|
2008-05-21 21:15:11 +02:00
|
|
|
error($@) if $@;
|
2006-11-26 20:43:50 +01:00
|
|
|
CGI::Session->name("ikiwiki_session_".encode_utf8($config{wikiname}));
|
|
|
|
|
|
|
|
my $oldmask=umask(077);
|
2008-07-10 19:16:03 +02:00
|
|
|
my $session = eval {
|
|
|
|
CGI::Session->new("driver:DB_File", $q,
|
|
|
|
{ FileName => "$config{wikistatedir}/sessions.db" })
|
|
|
|
};
|
|
|
|
if (! $session || $@) {
|
|
|
|
error($@." ".CGI::Session->errstr());
|
|
|
|
}
|
|
|
|
|
2006-11-26 20:43:50 +01:00
|
|
|
umask($oldmask);
|
|
|
|
|
|
|
|
return $session;
|
|
|
|
} #}}}
|
|
|
|
|
|
|
|
sub cgi_savesession ($) { #{{{
|
|
|
|
my $session=shift;
|
|
|
|
|
|
|
|
# Force session flush with safe umask.
|
|
|
|
my $oldmask=umask(077);
|
|
|
|
$session->flush;
|
|
|
|
umask($oldmask);
|
2007-02-02 03:33:03 +01:00
|
|
|
} #}}}
|
2006-11-26 20:43:50 +01:00
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
sub cgi (;$$) { #{{{
|
|
|
|
my $q=shift;
|
|
|
|
my $session=shift;
|
|
|
|
|
2008-07-01 19:43:32 +02:00
|
|
|
eval q{use CGI};
|
|
|
|
error($@) if $@;
|
|
|
|
$CGI::DISABLE_UPLOADS=$config{cgi_disable_uploads};
|
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
if (! $q) {
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
binmode(STDIN);
|
2006-11-20 10:40:09 +01:00
|
|
|
$q=CGI->new;
|
Fixes for behavior changes in perl 5.10's CGI
Something has changed in CGI.pm in perl 5.10. It used to not care
if STDIN was opened using :utf8, but now it'll mis-encode utf-8 values
when used that way by ikiwiki. Now I have to binmode(STDIN) before
instantiating the CGI object.
In 57bba4dac132a06729eeec809f5e1a5adf829806, I changed from decoding
CGI::Formbuilder fields to utf-8, to decoding cgi parameters before setting
up the form object. As of perl 5.10, that approach no longer has any effect
(reason unknown). To get correctly encoded values in FormBuilder forms,
they must once again be decoded after the form is set up.
As noted in 57bba4da, this can cause one set of problems for
formbuilder_setup hooks if decode_form_utf8 is called before the hooks, and
a different set if it's called after. To avoid both sets of problems, call
it both before and after. (Only remaining problem is the sheer ugliness and
inefficiency of that..)
I think that these changes will also work with older perl versions, but I
haven't checked.
Also, in the case of the poll plugin, the cgi parameter needs to be
explcitly decoded before it is used to handle utf-8 values. (This may have
always been broken, not sure if it's related to perl 5.10 or not.)
2008-05-13 02:40:59 +02:00
|
|
|
binmode(STDIN, ":utf8");
|
2006-05-03 23:50:39 +02:00
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
run_hooks(cgi => sub { shift->($q) });
|
|
|
|
}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
my $do=$q->param('do');
|
|
|
|
if (! defined $do || ! length $do) {
|
2006-07-10 23:13:41 +02:00
|
|
|
my $error = $q->cgi_error;
|
|
|
|
if ($error) {
|
|
|
|
error("Request not processed: $error");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
error("\"do\" parameter missing");
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
}
|
|
|
|
|
2006-11-20 13:03:35 +01:00
|
|
|
# Need to lock the wiki before getting a session.
|
|
|
|
lockwiki();
|
2008-02-03 06:23:04 +01:00
|
|
|
loadindex();
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-11-20 10:40:09 +01:00
|
|
|
if (! $session) {
|
2006-11-26 20:43:50 +01:00
|
|
|
$session=cgi_getsession($q);
|
2006-11-20 10:40:09 +01:00
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
|
2006-11-20 02:52:18 +01:00
|
|
|
# Auth hooks can sign a user in.
|
|
|
|
if ($do ne 'signin' && ! defined $session->param("name")) {
|
|
|
|
run_hooks(auth => sub {
|
|
|
|
shift->($q, $session)
|
|
|
|
});
|
|
|
|
if (defined $session->param("name")) {
|
|
|
|
# Make sure whatever user was authed is in the
|
|
|
|
# userinfo db.
|
|
|
|
if (! userinfo_get($session->param("name"), "regdate")) {
|
|
|
|
userinfo_setall($session->param("name"), {
|
|
|
|
email => "",
|
|
|
|
password => "",
|
|
|
|
regdate => time,
|
2007-02-15 03:22:08 +01:00
|
|
|
}) || error("failed adding user");
|
2006-11-20 02:52:18 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-02-02 03:33:03 +01:00
|
|
|
|
2008-08-01 23:37:24 +02:00
|
|
|
check_banned($q, $session);
|
|
|
|
|
2007-08-05 23:38:27 +02:00
|
|
|
run_hooks(sessioncgi => sub { shift->($q, $session) });
|
|
|
|
|
|
|
|
if ($do eq 'signin') {
|
2007-02-02 03:33:03 +01:00
|
|
|
cgi_signin($q, $session);
|
|
|
|
cgi_savesession($session);
|
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
elsif ($do eq 'prefs') {
|
|
|
|
cgi_prefs($q, $session);
|
|
|
|
}
|
2007-02-02 03:33:03 +01:00
|
|
|
elsif ($do eq 'create' || $do eq 'edit') {
|
|
|
|
cgi_editpage($q, $session);
|
|
|
|
}
|
2008-01-07 22:39:49 +01:00
|
|
|
elsif (defined $session->param("postsignin") || $do eq 'postsignin') {
|
2008-01-07 21:56:39 +01:00
|
|
|
cgi_postsignin($q, $session);
|
2007-01-12 21:56:54 +01:00
|
|
|
}
|
2006-03-23 07:51:15 +01:00
|
|
|
else {
|
|
|
|
error("unknown do parameter");
|
|
|
|
}
|
|
|
|
} #}}}
|
|
|
|
|
2008-08-06 01:58:33 +02:00
|
|
|
# Does not need to be called directly; all errors will go through here.
|
2008-07-13 05:23:25 +02:00
|
|
|
sub cgierror ($) { #{{{
|
|
|
|
my $message=shift;
|
|
|
|
|
|
|
|
print "Content-type: text/html\n\n";
|
|
|
|
print misctemplate(gettext("Error"),
|
|
|
|
"<p class=\"error\">".gettext("Error").": $message</p>");
|
|
|
|
die $@;
|
|
|
|
} #}}}
|
|
|
|
|
2006-03-23 07:51:15 +01:00
|
|
|
1
|