Handle hostnames with upper-case letters
[webmin.git] / acl / save_user.cgi
1 #!/usr/local/bin/perl
2 # save_user.cgi
3 # Modify or create a webmin user
4
5 require './acl-lib.pl';
6 &foreign_require("webmin", "webmin-lib.pl");
7 &ReadParse();
8
9 # Check for special button clicks, and redirect
10 if ($in{'but_clone'}) {
11         &redirect("edit_user.cgi?clone=".&urlize($in{'old'}));
12         exit;
13         }
14 elsif ($in{'but_log'}) {
15         &redirect("../webminlog/search.cgi?uall=0&mall=1&tall=1&user=".
16                   &urlize($in{'old'}));
17         exit;
18         }
19 elsif ($in{'but_switch'}) {
20         &redirect("switch.cgi?user=".&urlize($in{'old'}));
21         exit;
22         }
23 elsif ($in{'but_delete'}) {
24         &redirect("delete_user.cgi?user=".&urlize($in{'old'}));
25         exit;
26         }
27
28 # Get the user object
29 if ($in{'old'}) {
30         %user = ( );
31         $in{'name'} = $in{'old'} if (!$access{'rename'});
32         &can_edit_user($in{'old'}) || &error($text{'save_euser'});
33         $old = &get_user($in{'old'});
34         $old || &error($text{'edit_egone'});
35         $user{'proto'} = $old->{'proto'};
36         $user{'id'} = $old->{'id'};
37         }
38 else {
39         $access{'create'} || &error($text{'save_ecreate'});
40         }
41 &error_setup($text{'save_err'});
42
43 # Validate username, and check for a clash
44 $in{'name'} =~ /^[A-z0-9\-\_\.\@]+$/ && $in{'name'} !~ /^\@/ ||
45         &error(&text('save_ename', $in{'name'}));
46 $in{'name'} eq 'webmin' && &error($text{'save_enamewebmin'});
47 if (!$in{'old'} || $in{'old'} ne $in{'name'}) {
48         $clash = &get_user($in{'name'});
49         $clash && &error(&text('save_edup', $in{'name'}));
50         }
51 !$access{'logouttime'} || $in{'logouttime_def'} ||
52         $in{'logouttime'} =~ /^\d+$/ || &error($text{'save_elogouttime'});
53 !$access{'minsize'} || $in{'minsize_def'} ||
54         $in{'minsize'} =~ /^\d+$/ || &error($text{'save_eminsize'});
55
56 # Validate password
57 if ($in{'pass_def'} == 0) {
58         $in{'pass'} =~ /:/ && &error($text{'save_ecolon'});
59         if (!$in{'temp'}) {
60                 # Check password quality, unless this is a temp password
61                 $perr = &check_password_restrictions($in{'name'}, $in{'pass'});
62                 $perr && &error(&text('save_epass', $perr));
63                 }
64         }
65
66 # Validate force change
67 if ($in{'temp'}) {
68         &get_miniserv_config(\%miniserv);
69         $miniserv{'passwd_mode'} == 2 ||
70                 &error(&text('save_etemp', '../webmin/edit_session.cgi'));
71         }
72
73
74 # Find logged-in webmin user
75 foreach $u (@ulist) {
76         if ($u->{'name'} eq $base_remote_user) {
77                 $me = $u;
78                 }
79         }
80
81 # Find the current group
82 if ($in{'old'}) {
83         foreach $g (&list_groups()) {
84                 if (&indexof($in{'old'}, @{$g->{'members'}}) >= 0) {
85                         $oldgroup = $g;
86                         }
87                 }
88         }
89
90 if (&supports_rbac()) {
91         # Save RBAC mode
92         $user{'rbacdeny'} = $in{'rbacdeny'};
93         }
94
95 if ($in{'risk'}) {
96         # Just store the skill and risk levels
97         $user{'skill'} = $in{'skill'};
98         $user{'risk'} = $in{'risk'};
99         delete($user{'modules'});
100         }
101 else {
102         if (defined($in{'group'})) {
103                 # Check if group is allowed
104                 if ($access{'gassign'} ne '*') {
105                         local @gcan = split(/\s+/, $access{'gassign'});
106                         $in{'group'} && &indexof($in{'group'}, @gcan) >= 0 ||
107                           !$in{'group'} && &indexof('_none', @gcan) >= 0 ||
108                           $oldgroup && $oldgroup->{'name'} eq $in{'group'} ||
109                                 &error($text{'save_egroup'});
110                         }
111
112                 # Store group membership
113                 $newgroup = &get_group($in{'group'});
114                 if ($in{'group'} ne ($oldgroup ? $oldgroup->{'name'} : '')) {
115                         # Group has changed - update the member lists
116                         if ($oldgroup) {
117                                 # Take out of old
118                                 $oldgroup->{'members'} =
119                                         [ grep { $_ ne $in{'old'} }
120                                           @{$oldgroup->{'members'}} ];
121                                 &modify_group($oldgroup->{'name'}, $oldgroup);
122                                 }
123                         if ($newgroup) {
124                                 # Put into new
125                                 push(@{$newgroup->{'members'}}, $in{'name'});
126                                 &modify_group($in{'group'}, $newgroup);
127                                 }
128                         }
129                 elsif ($in{'old'} ne $in{'name'} && $oldgroup && $newgroup) {
130                         # Name has changed - rename in group
131                         local $idx = &indexof(
132                                 $in{'old'}, @{$oldgroup->{'members'}});
133                         $oldgroup->{'members'}->[$idx] = $in{'name'};
134                         &modify_group($oldgroup->{'name'}, $oldgroup);
135                         }
136                 }
137
138         # Store manually selected modules
139         @mcan = $access{'mode'} == 1 ? @{$me->{'modules'}} :
140                 $access{'mode'} == 2 ? split(/\s+/, $access{'mods'}) :
141                                        &list_modules();
142         map { $mcan{$_}++ } @mcan;
143
144         @mods = split(/\0/, $in{'mod'});
145         foreach $m (@mods) {
146                 $mcan{$m} || &error(&text('save_emod', $m));
147                 }
148         if ($in{'old'}) {
149                 # Add modules that this user already has, but were not
150                 # allowed to be changed or are not available for this OS
151                 foreach $m (@{$old->{'modules'}}) {
152                         push(@mods, $m) if (!$mcan{$m});
153                         }
154                 }
155         if ($base_remote_user eq $in{'old'} &&
156             &indexof("acl", @mods) == -1 &&
157             (!$newgroup || &indexof("acl", @{$newgroup->{'modules'}}) == -1)) {
158                 &error($text{'save_edeny'});
159                 }
160
161         if ($oldgroup) {
162                 # Remove modules from the old group
163                 @mods = grep { &indexof($_, @{$oldgroup->{'modules'}}) < 0 }
164                              @mods;
165                 }
166
167         if (!$in{'old'} && $access{'perms'}) {
168                 # Copy .acl files from creator to new user
169                 &copy_acl_files($me->{'name'}, $in{'name'}, $me->{'modules'});
170                 }
171
172         if ($newgroup) {
173                 # Add modules from group to list
174                 local @ownmods;
175                 foreach $m (@mods) {
176                         push(@ownmods, $m)
177                                 if (&indexof($m, @{$newgroup->{'modules'}}) < 0);
178                         }
179                 @mods = &unique(@mods, @{$newgroup->{'modules'}});
180                 $user{'ownmods'} = \@ownmods;
181
182                 # Copy ACL files for group
183                 local $name = $in{'old'} ? $in{'old'} : $in{'name'};
184                 &copy_group_user_acl_files($in{'group'}, $name,
185                                       [ @{$newgroup->{'modules'}}, "" ]);
186                 }
187         $user{'modules'} = \@mods;
188         delete($user{'skill'});
189         delete($user{'risk'});
190         }
191
192 # Update user object
193 $salt = chr(int(rand(26))+65).chr(int(rand(26))+65);
194 $user{'name'} = $in{'name'};
195 $user{'lang'} = !$access{'lang'} ? $old->{'lang'} :
196                 $in{'lang_def'} ? undef : $in{'lang'};
197 if (!$access{'theme'}) {
198         $user{'theme'} = $old->{'theme'};
199         $user{'overlay'} = $old->{'overlay'};
200         }
201 else {
202         $user{'theme'} = $in{'theme_def'} ? undef : $in{'theme'};
203         $user{'overlay'} = $in{'overlay_def'} ? undef : $in{'overlay'};
204         if ($user{'overlay'} && !$user{'theme'}) {
205                 &error($text{'save_eoverlay'});
206                 }
207         }
208 $user{'cert'} = !$access{'chcert'} ? $old->{'cert'} :
209                 $in{'cert_def'} ? undef : $in{'cert'};
210 $user{'notabs'} = !$access{'cats'} ? $old->{'notabs'} : $in{'notabs'};
211 $user{'logouttime'} = !$access{'logouttime'} ? $old->{'logouttime'} :
212                         $in{'logouttime_def'} ? undef : $in{'logouttime'};
213 $user{'minsize'} = !$access{'minsize'} ? $old->{'minsize'} :
214                         $in{'minsize_def'} ? undef : $in{'minsize'};
215 $user{'nochange'} = !$access{'nochange'} || !defined($in{'nochange'}) ?
216                         $old->{'nochange'} : $in{'nochange'};
217 $user{'lastchange'} = $old->{'lastchange'};
218 $user{'olds'} = $old->{'olds'};
219 $user{'real'} = $in{'real'} =~ /\S/ ? $in{'real'} : undef;
220 $raddr = $ENV{'REMOTE_ADDR'};
221 if ($access{'ips'}) {
222         if ($in{'ipmode'}) {
223                 @hosts = split(/\s+/, $in{"ips"});
224                 if (!@hosts) { &error($text{'save_enone'}); }
225                 foreach $h (@hosts) {
226                         $err = &webmin::valid_allow($h);
227                         &error($err) if ($err);
228                         push(@ips, $h);
229                         }
230                 }
231         if ($in{'ipmode'} == 1) {
232                 $user{'allow'} = join(" ", @ips);
233                 if ($old->{'name'} eq $base_remote_user &&
234                     !&webmin::ip_match($raddr, @ips)) {
235                         &error(&text('save_eself', $raddr));
236                         }
237                 }
238         elsif ($in{'ipmode'} == 2) {
239                 $user{'deny'} = join(" ", @ips);
240                 if ($old->{'name'} eq $base_remote_user &&
241                     &webmin::ip_match($raddr, @ips)) {
242                         &error(&text('save_eself', $raddr));
243                         }
244                 }
245         }
246 else {
247         $user{'allow'} = $old->{'allow'};
248         $user{'deny'} = $old->{'deny'};
249         }
250 if ($in{'pass_def'} == 0) {
251         # New password
252         $user{'pass'} = &encrypt_password($in{'pass'});
253         $user{'sync'} = 0;
254         }
255 elsif ($in{'pass_def'} == 1) {
256         # No change in password
257         $user{'pass'} = $in{'oldpass'};
258         $user{'sync'} = 0;
259         }
260 elsif ($in{'pass_def'} == 3) {
261         # Unix authentication
262         $user{'pass'} = 'x';
263         $user{'sync'} = 0;
264         }
265 elsif ($in{'pass_def'} == 4) {
266         # Account is locked
267         $user{'pass'} = '*LK*';
268         $user{'sync'} = 0;
269         }
270 elsif ($in{'pass_def'} == 5) {
271         # External authentcation
272         $user{'pass'} = 'e';
273         $user{'sync'} = 0;
274         }
275 else {
276         # Password synchronization (deprecated)
277         &foreign_check("useradmin") || &error($text{'save_eos'});
278         &foreign_require("useradmin", "user-lib.pl");
279         foreach $uu (&useradmin::list_users()) {
280                 $user{'pass'} = $uu->{'pass'}
281                         if ($uu->{'user'} eq $in{'name'});
282                 }
283         defined($user{'pass'}) ||
284                 &error(&text('save_eunix', $in{'name'}));
285         $user{'sync'} = 1;
286         }
287
288 # Update allowed days and hours
289 if ($access{'times'}) {
290         # Save the allowed days
291         if (!$in{'days_def'}) {
292                 @days = split(/\0/, $in{'days'});
293                 @days || &error($text{'save_edays'});
294                 $user{'days'} = join(",", @days);
295                 }
296         if (!$in{'hours_def'}) {
297                 foreach $t ('from', 'to') {
298                         $h = $in{'hours_h'.$t};
299                         $m = $in{'hours_m'.$t};
300                         $h =~ /^\d+$/ && $h >= 0 &&
301                           $h < 24 || &error($text{'save_ehours'});
302                         $m =~ /^\d+$/ && $m >= 0 &&
303                           $m < 60 || &error($text{'save_ehours'});
304                         $user{'hours'.$t} = "$h.$m";
305                         $mins{$t} = $h*60+$m;
306                         }
307                 $mins{'from'} < $mins{'to'} || &error($text{'save_ehours2'});
308                 }
309         }
310 else {
311         $user{'days'} = $old->{'days'};
312         $user{'hoursfrom'} = $old->{'hoursfrom'};
313         $user{'hoursto'} = $old->{'hoursto'};
314         }
315
316 # Check for temporary password lock
317 if (!$in{'lock'} && $user{'pass'} =~ /^\!(.*)$/) {
318         $user{'pass'} = $1;
319         }
320 elsif ($in{'lock'} && $user{'pass'} !~ /^\!/ && $in{'pass_def'} <= 1) {
321         $user{'pass'} = "!".$user{'pass'};
322         }
323
324 # Check for force change
325 $user{'temppass'} = $in{'temp'};
326
327 if ($in{'old'}) {
328         # update user and all ACLs
329         &modify_user($in{'old'}, \%user);
330         if ($in{'old'} ne $user{'name'}) {
331                 # Change username in other user's ACLs
332                 foreach $u (&list_users()) {
333                         %uaccess = &get_module_acl($u->{'name'});
334                         local @au = split(/\s+/, $uaccess{'users'});
335                         local $idx = &indexof($in{'old'}, @au);
336                         if ($idx != -1) {
337                                 $au[$idx] = $in{'name'};
338                                 $uaccess{'users'} = join(" ", @au);
339                                 &save_module_acl(\%uaccess, $u->{'name'});
340                                 }
341                         }
342                 }
343         }
344 else {
345         # create and add to access list
346         &create_user(\%user, $in{'clone'});
347         if ($access{'users'} ne '*') {
348                 $access{'users'} .= " ".$in{'name'};
349                 &save_module_acl(\%access);
350                 }
351         }
352
353 if ($in{'old'} && $in{'acl_security_form'} && !$newgroup) {
354         # Update user's global ACL
355         &foreign_require("", "acl_security.pl");
356         &foreign_call("", "acl_security_save", \%uaccess, \%in);
357         $aclfile = "$config_directory/$in{'name'}.acl";
358         &lock_file($aclfile);
359         &save_module_acl(\%uaccess, $in{'name'}, "", 1);
360         chmod(0640, $aclfile) if (-r $aclfile);
361         &unlock_file($aclfile);
362         }
363
364 # Log the event
365 delete($in{'pass'});
366 delete($in{'oldpass'});
367 if ($in{'old'}) {
368         &webmin_log("modify", "user", $in{'old'}, \%in);
369         }
370 else {
371         &webmin_log("create", "user", $user{'name'}, \%in);
372         }
373 &reload_miniserv();
374 &redirect("");
375