update
[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 [[!toggle id="code" text="unixauth.pm"]]
18
19 [[!toggleable id="code" text="""
20
21     #!/usr/bin/perl
22     # Ikiwiki unixauth authentication.
23     package IkiWiki::Plugin::unixauth;
24     
25     use warnings;
26     use strict;
27     use IkiWiki 2.00;
28     
29     sub import { #{{{
30             hook(type => "formbuilder_setup", id => "unixauth",
31                 call => \&formbuilder_setup);
32             hook(type => "formbuilder", id => "unixauth",
33                 call => \&formbuilder);
34         hook(type => "sessioncgi", id => "unixauth", call => \&sessioncgi);
35     } # }}}
36     
37     # Checks if a string matches a user's password, and returns true or false.
38     sub checkpassword ($$;$) { #{{{
39         my $user=shift;
40         my $password=shift;
41         my $field=shift || "password";
42     
43         # It's very important that the user not be allowed to log in with
44         # an empty password!
45         if (! length $password) {
46                 return 0;
47         }
48     
49         my $ret=0;
50         if (! exists $config{unixauth_type}) {
51                 # admin needs to carefully think over his configuration
52                 return 0;
53         }
54         elsif ($config{unixauth_type} eq "checkpassword") {
55                 open UNIXAUTH, "|$config{unixauth_command} true 3<&0" or die("Could not run $config{unixauth_type}");
56                 print UNIXAUTH "$user\0$password\0Y123456\0";
57                 close UNIXAUTH;
58                 $ret=!($?>>8);
59         }
60         elsif ($config{unixauth_type} eq "pwauth") {
61                 open UNIXAUTH, "|$config{unixauth_command}" or die("Could not run $config{unixauth_type}");
62                 print UNIXAUTH "$user\n$password\n";
63                 close UNIXAUTH;
64                 $ret=!($?>>8);
65         }
66         else {
67                 # no such authentication type
68                 return 0;
69         }
70     
71         if ($ret) {
72             my $userinfo=IkiWiki::userinfo_retrieve();
73             if (! length $user || ! defined $userinfo ||
74                 ! exists $userinfo->{$user} || ! ref $userinfo->{$user}) {
75                     IkiWiki::userinfo_setall($user, {
76                         'email' => '',
77                         'regdate' => time,
78                     });
79             }
80         }
81     
82         return $ret;
83     } #}}}
84     
85     sub formbuilder_setup (@) { #{{{
86         my %params=@_;
87     
88         my $form=$params{form};
89         my $session=$params{session};
90         my $cgi=$params{cgi};
91     
92         # if not under SSL, die before even showing a login form,
93         # unless the admin explicitly says it's fine
94         if (! exists $config{unixauth_requiressl}) {
95                 $config{unixauth_requiressl} = 1;
96         }
97         if ($config{unixauth_requiressl} && \
98             (! $config{sslcookie} || ! exists $ENV{'HTTPS'})) {
99                 die("SSL required to login. Contact your administrator.");
100         }
101     
102         if ($form->title eq "signin") {
103                 $form->field(name => "name", required => 0);
104                 $form->field(name => "password", type => "password", required => 0);
105                 
106                 if ($form->submitted) {
107                         my $submittype=$form->submitted;
108                         # Set required fields based on how form was submitted.
109                         my %required=(
110                                 "Login" => [qw(name password)],
111                         );
112                         foreach my $opt (@{$required{$submittype}}) {
113                                 $form->field(name => $opt, required => 1);
114                         }
115         
116                         # Validate password against name for Login.
117                         if ($submittype eq "Login") {
118                                 $form->field(
119                                         name => "password",
120                                         validate => sub {
121                                                 checkpassword($form->field("name"), shift);
122                                         },
123                                 );
124                         }
125                         
126                         # XXX is this reachable? looks like no
127                         elsif ($submittype eq "Login") {
128                                 $form->field( 
129                                         name => "name",
130                                         validate => sub {
131                                                 my $name=shift;
132                                                 length $name &&
133                                                 IkiWiki::userinfo_get($name, "regdate");
134                                         },
135                                 );
136                         }
137                 }
138                 else {
139                         # First time settings.
140                         $form->field(name => "name");
141                         if ($session->param("name")) {
142                                 $form->field(name => "name", value => $session->param("name"));
143                         }
144                 }
145         }
146         elsif ($form->title eq "preferences") {
147                 $form->field(name => "name", disabled => 1, 
148                         value => $session->param("name"), force => 1,
149                         fieldset => "login");
150                 $form->field(name => "password", disabled => 1, type => "password",
151                         fieldset => "login"),
152         }
153     }
154     
155     sub formbuilder (@) { #{{{
156         my %params=@_;
157     
158         my $form=$params{form};
159         my $session=$params{session};
160         my $cgi=$params{cgi};
161         my $buttons=$params{buttons};
162     
163         if ($form->title eq "signin") {
164                 if ($form->submitted && $form->validate) {
165                         if ($form->submitted eq 'Login') {
166                                 $session->param("name", $form->field("name"));
167                                 IkiWiki::cgi_postsignin($cgi, $session);
168                         }
169                 }
170         }
171         elsif ($form->title eq "preferences") {
172                 if ($form->submitted eq "Save Preferences" && $form->validate) {
173                         my $user_name=$form->field('name');
174                 }
175         }
176     } #}}}
177     
178     sub sessioncgi ($$) { #{{{
179         my $q=shift;
180         my $session=shift;
181     } #}}}
182     
183     1
184
185 """]]