f369cd6adeba867605b40ec09154f1e6057a98b2
[ikiwiki.git] / doc / plugins / contrib / unixauth.mdwn
1 [[!template id=plugin name=unixauth core=0 author="[[schmonz]]"]]
2 [[!tag type/auth]]
3
4 This plugin authenticates users against the Unix user database. It presents a similar UI to [[plugins/passwordauth]], but simpler, as there's no need to be able to register or change one's password.
5
6 To authenticate, either [checkpassword](http://cr.yp.to/checkpwd.html) or [pwauth](http://www.unixpapa.com/pwauth/) must be installed and configured. `checkpassword` is strongly preferred. If your web server runs as an unprivileged user -- as it darn well should! -- then `checkpassword` needs to be setuid root. Other checkpassword implementations are available, notably [checkpassword-pam](http://checkpasswd-pam.sourceforge.net/).
7
8 Config variables that affect the behavior of `unixauth`:
9
10 * `unixauth_type`: defaults to unset, can be "checkpassword" or "pwauth"
11 * `unixauth_command`: defaults to unset, should contain the full path and any arguments
12 * `unixauth_sslrequire`: defaults to 1, can be 0
13 * `sslcookie`: needs to be 1 if `unixauth_sslrequire` is 1 (perhaps this should be done automatically?)
14
15 __Security__: [As with passwordauth](/security/#index14h2), be wary of sending usernames and passwords in cleartext. Unlike passwordauth, sniffing `unixauth` credentials can get an attacker much further than mere wiki access. Therefore, this plugin defaults to not even _displaying_ the login form fields unless we're running under SSL. Nobody should be able to do anything remotely dumb until the admin has done at least a little thinking. After that, dumb things are always possible. ;-)
16
17 _XXX hang on, looks like we don't have the huge CGI environment so testing for ${HTTPS} always fails; need another way to be sure_
18
19 [[!toggle id="code" text="unixauth.pm"]]
20
21 [[!toggleable id="code" text="""
22
23     #!/usr/bin/perl
24     # Ikiwiki unixauth authentication.
25     package IkiWiki::Plugin::unixauth;
26     
27     use warnings;
28     use strict;
29     use IkiWiki 2.00;
30     
31     sub import { #{{{
32             hook(type => "formbuilder_setup", id => "unixauth",
33                 call => \&formbuilder_setup);
34             hook(type => "formbuilder", id => "unixauth",
35                 call => \&formbuilder);
36         hook(type => "sessioncgi", id => "unixauth", call => \&sessioncgi);
37     } # }}}
38     
39     # Checks if a string matches a user's password, and returns true or false.
40     sub checkpassword ($$;$) { #{{{
41         my $user=shift;
42         my $password=shift;
43         my $field=shift || "password";
44     
45         # It's very important that the user not be allowed to log in with
46         # an empty password!
47         if (! length $password) {
48                 return 0;
49         }
50     
51         my $ret=0;
52         if (! exists $config{unixauth_type}) {
53                 # admin needs to carefully think over his configuration
54                 return 0;
55         }
56         elsif ($config{unixauth_type} eq "checkpassword") {
57                 open UNIXAUTH, "|$config{unixauth_command} true 3<&0" or die("Could not run $config{unixauth_type}");
58                 print UNIXAUTH "$user\0$password\0Y123456\0";
59                 close UNIXAUTH;
60                 $ret=!($?>>8);
61         }
62         elsif ($config{unixauth_type} eq "pwauth") {
63                 open UNIXAUTH, "|$config{unixauth_command}" or die("Could not run $config{unixauth_type}");
64                 print UNIXAUTH "$user\n$password\n";
65                 close UNIXAUTH;
66                 $ret=!($?>>8);
67         }
68         else {
69                 # no such authentication type
70                 return 0;
71         }
72     
73         if ($ret) {
74             my $userinfo=IkiWiki::userinfo_retrieve();
75             if (! length $user || ! defined $userinfo ||
76                 ! exists $userinfo->{$user} || ! ref $userinfo->{$user}) {
77                     IkiWiki::userinfo_setall($user, {
78                         'email' => '',
79                         'regdate' => time,
80                     });
81             }
82         }
83     
84         return $ret;
85     } #}}}
86     
87     sub formbuilder_setup (@) { #{{{
88         my %params=@_;
89     
90         my $form=$params{form};
91         my $session=$params{session};
92         my $cgi=$params{cgi};
93     
94         # if not under SSL, die before even showing a login form,
95         # unless the admin explicitly says it's fine
96         if (! exists $config{unixauth_requiressl}) {
97                 $config{unixauth_requiressl} = 1;
98         }
99         if ($config{unixauth_requiressl} && \
100             (! $config{sslcookie} || ! exists $ENV{'HTTPS'})) {
101                 die("SSL required to login. Contact your administrator.");
102         }
103     
104         if ($form->title eq "signin") {
105                 $form->field(name => "name", required => 0);
106                 $form->field(name => "password", type => "password", required => 0);
107                 
108                 if ($form->submitted) {
109                         my $submittype=$form->submitted;
110                         # Set required fields based on how form was submitted.
111                         my %required=(
112                                 "Login" => [qw(name password)],
113                         );
114                         foreach my $opt (@{$required{$submittype}}) {
115                                 $form->field(name => $opt, required => 1);
116                         }
117         
118                         # Validate password against name for Login.
119                         if ($submittype eq "Login") {
120                                 $form->field(
121                                         name => "password",
122                                         validate => sub {
123                                                 checkpassword($form->field("name"), shift);
124                                         },
125                                 );
126                         }
127                         
128                         # XXX is this reachable? looks like no
129                         elsif ($submittype eq "Login") {
130                                 $form->field( 
131                                         name => "name",
132                                         validate => sub {
133                                                 my $name=shift;
134                                                 length $name &&
135                                                 IkiWiki::userinfo_get($name, "regdate");
136                                         },
137                                 );
138                         }
139                 }
140                 else {
141                         # First time settings.
142                         $form->field(name => "name");
143                         if ($session->param("name")) {
144                                 $form->field(name => "name", value => $session->param("name"));
145                         }
146                 }
147         }
148         elsif ($form->title eq "preferences") {
149                 $form->field(name => "name", disabled => 1, 
150                         value => $session->param("name"), force => 1,
151                         fieldset => "login");
152                 $form->field(name => "password", disabled => 1, type => "password",
153                         fieldset => "login"),
154         }
155     }
156     
157     sub formbuilder (@) { #{{{
158         my %params=@_;
159     
160         my $form=$params{form};
161         my $session=$params{session};
162         my $cgi=$params{cgi};
163         my $buttons=$params{buttons};
164     
165         if ($form->title eq "signin") {
166                 if ($form->submitted && $form->validate) {
167                         if ($form->submitted eq 'Login') {
168                                 $session->param("name", $form->field("name"));
169                                 IkiWiki::cgi_postsignin($cgi, $session);
170                         }
171                 }
172         }
173         elsif ($form->title eq "preferences") {
174                 if ($form->submitted eq "Save Preferences" && $form->validate) {
175                         my $user_name=$form->field('name');
176                 }
177         }
178     } #}}}
179     
180     sub sessioncgi ($$) { #{{{
181         my $q=shift;
182         my $session=shift;
183     } #}}}
184     
185     1
186
187 """]]