xref: /openbsd-src/usr.sbin/adduser/adduser.perl (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1#!/usr/bin/perl
2#
3#	$OpenBSD: adduser.perl,v 1.15 1999/02/04 19:38:39 provos Exp $
4#
5# Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
6# All rights reserved.
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28#
29# $From: adduser.perl,v 1.22 1996/12/07 21:25:12 ache Exp $
30
31################
32# main
33#
34$test = 0;			# test mode, only for development
35$check_only = 0;
36
37&check_root;			# you must be root to run this script!
38&variables;			# initialize variables
39&config_read(@ARGV);		# read variables from config-file
40&parse_arguments(@ARGV);	# parse arguments
41
42if (!$check_only &&  $#batch < 0) {
43    &copyright; &hints;
44}
45
46# check
47$changes = 0;
48&variable_check;		# check for valid variables
49&passwd_check;			# check for valid passwdb
50&shells_read;			# read /etc/shells
51&passwd_read;			# read /etc/master.passwd
52&group_read;			# read /etc/group
53&group_check;			# check for incon*
54exit 0 if $check_only;		# only check consistence and exit
55
56exit(!&batch(@batch)) if $#batch >= 0; # batch mode
57
58# Interactive:
59# main loop for creating new users
60&new_users;	     # add new users
61
62#end
63
64
65# Set adduser "default" variables internally before groking config file
66# Adduser.conf supercedes these
67sub variables {
68    $verbose = 1;		# verbose = [0-2]
69    $defaultpasswd = "yes";	# use password for new users
70    $dotdir = "/usr/share/skel"; # copy dotfiles from this dir
71    $dotdir_bak = $dotdir;
72    $send_message = "no"; # send message to new user
73    $send_message_bak = '/etc/adduser.message';
74    $config = "/etc/adduser.conf"; # config file for adduser
75    $config_read = 1;		# read config file
76    $logfile = "/var/log/adduser"; # logfile
77    $home = "/home";		# default HOME
78    $etc_shells = "/etc/shells";
79    $etc_passwd = "/etc/master.passwd";
80    $group = "/etc/group";
81    $pwd_mkdb = "pwd_mkdb -p";	# program for building passwd database
82    $encryptionmethod = "blowfish";
83
84    # List of directories where shells located
85    @path = ('/bin', '/usr/bin', '/usr/local/bin');
86    # common shells, first element has higher priority
87    @shellpref = ('csh', 'sh', 'bash', 'tcsh', 'ksh');
88
89    @encryption_methods = ('blowfish', 'md5', 'des', 'old');
90
91    $defaultshell = 'sh';	# defaultshell if not empty
92    $group_uniq = 'USER';
93    $defaultgroup = $group_uniq;# login groupname, $group_uniq means username
94
95    $uid_start = 1000;		# new users get this uid
96    $uid_end   = 2147483648;	# max. uid
97
98    # global variables
99    # passwd
100    $username = '';		# $username{username} = uid
101    $uid = '';			# $uid{uid} = username
102    $pwgid = '';		# $pwgid{pwgid} = username; gid from passwd db
103
104    $password = '';		# password for new users
105
106    # group
107    $groupname ='';		# $groupname{groupname} = gid
108    $groupmembers = '';		# $groupmembers{gid} = members of group/kommalist
109    $gid = '';			# $gid{gid} = groupname;    gid form group db
110
111    # shell
112    $shell = '';		# $shell{`basename sh`} = sh
113
114    # only for me (=Wolfram)
115    if ($test) {
116	$home = "/home/w/tmp/adduser/home";
117	$etc_shells = "./shells";
118	$etc_passwd = "./master.passwd";
119	$group = "./group";
120	$pwd_mkdb = "pwd_mkdb -p -d .";
121	$config = "adduser.conf";
122	$send_message = "./adduser.message";
123	$logfile = "./log.adduser";
124    }
125
126    umask 022;			# don't give login group write access
127
128    $ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin";
129    @passwd_backup = '';
130    @group_backup = '';
131    @message_buffer = '';
132    @user_variable_list = '';	# user variables in /etc/adduser.conf
133    $do_not_delete = '## DO NOT DELETE THIS LINE!';
134}
135
136# read shell database, see also: shells(5)
137sub shells_read {
138    local($sh);
139    local($err) = 0;
140
141    print "Reading $etc_shells\n" if $verbose;
142    open(S, $etc_shells) || die "$etc_shells:$!\n";
143
144    while(<S>) {
145	if (/^\s*\//) {
146	    s/^\s*//; s/\s+.*//; # chop
147	    $sh = $_;
148	    if (-x  $sh) {
149		$shell{&basename($sh)} = $sh;
150	    } else {
151		warn "Shell: $sh not executable!\n";
152		$err++;
153	    }
154	}
155    }
156
157    push(@list, "/sbin/nologin");
158    &shell_pref_add("nologin");
159    $shell{"nologin"} = "/sbin/nologin";
160
161    return $err;
162}
163
164# add new shells if possible
165sub shells_add {
166    local($sh,$dir,@list);
167
168    return 1 unless $verbose;
169
170    foreach $sh (@shellpref) {
171	# all known shells
172	if (!$shell{$sh}) {
173	    # shell $sh is not defined as login shell
174	    foreach $dir (@path) {
175		if (-x "$dir/$sh") {
176		    # found shell
177		    if (&confirm_yn("Found shell: $dir/$sh. Add to $etc_shells?", "yes")) {
178			push(@list, "$dir/$sh");
179			&shell_pref_add("$sh");
180			$shell{&basename("$dir/$sh")} = "$dir/$sh";
181			$changes++;
182		    }
183		}
184	    }
185	}
186    }
187    &append_file($etc_shells, @list) if $#list >= 0;
188}
189
190# add shell to preference list without duplication
191sub shell_pref_add {
192    local($new_shell) = @_;
193    local($shell);
194
195    foreach $shell (@shellpref) {
196	return if ($shell eq $new_shell);
197    }
198    push(@shellpref, $new_shell);
199}
200
201# choose your favourite shell and return the shell
202sub shell_default {
203    local($e,$i,$new_shell);
204    local($sh);
205
206    $sh = &shell_default_valid($defaultshell);
207    return $sh unless $verbose;
208
209    $new_shell = &confirm_list("Enter your default shell:", 0,
210		       $sh, sort(keys %shell));
211    print "Your default shell is: $new_shell -> $shell{$new_shell}\n";
212    $changes++ if $new_shell ne $sh;
213    return $new_shell;
214}
215
216sub shell_default_valid {
217    local($sh) = @_;
218    local($s,$e);
219
220    return $sh if $shell{$sh};
221
222    foreach $e (@shellpref) {
223	$s = $e;
224	last if defined($shell{$s});
225    }
226    $s = "sh" unless $s;
227    warn "Shell ``$sh'' is undefined, use ``$s''\n";
228    return $s;
229}
230
231# return default home partition (f.e. "/home")
232# create base directory if nesseccary
233sub home_partition {
234    local($home) = @_;
235    $home = &stripdir($home);
236    local($h) = $home;
237
238    return $h if !$verbose && $h eq &home_partition_valid($h);
239
240    while(1) {
241	$h = &confirm_list("Enter your default HOME partition:", 1, $home, "");
242	$h = &stripdir($h);
243	last if $h eq &home_partition_valid($h);
244    }
245
246    $changes++ if $h ne $home;
247    return $h;
248}
249
250sub home_partition_valid {
251    local($h) = @_;
252
253    $h = &stripdir($h);
254    # all right (I hope)
255    return $h if $h =~ "^/" && -e $h && -w $h && (-d $h || -l $h);
256
257    # Errors or todo
258    if ($h !~ "^/") {
259	warn "Please use absolute path for home: ``$h''.\a\n";
260	return 0;
261    }
262
263    if (-e $h) {
264	warn "$h exists, but is not a directory or symlink!\n"
265	    unless -d $h || -l $h;
266	warn "$h is not writable!\n"
267	    unless -w $h;
268	return 0;
269    } else {
270	# create home partition
271	return $h if &mkdir_home($h);
272    }
273    return 0;
274}
275
276# check for valid passwddb
277sub passwd_check {
278    system("$pwd_mkdb -c $etc_passwd");
279    die "\nInvalid $etc_passwd - cannot add any users!\n" if $?;
280}
281
282# read /etc/passwd
283sub passwd_read {
284    local($p_username, $pw, $p_uid, $p_gid, $sh, %shlist);
285
286    print "Check $etc_passwd\n" if $verbose;
287    open(P, "$etc_passwd") || die "$passwd: $!\n";
288
289    while(<P>) {
290	chop;
291	push(@passwd_backup, $_);
292	($p_username, $pw, $p_uid, $p_gid, $sh) = (split(/:/, $_))[0..3,9];
293
294	print "$p_username already exists with uid: $username{$p_username}!\n"
295	    if $username{$p_username} && $verbose;
296	$username{$p_username} = $p_uid;
297	print "User $p_username: uid $p_uid exists twice: $uid{$p_uid}\n"
298	    if $uid{$p_uid} && $verbose && $p_uid;    # don't warn for uid 0
299	print "User $p_username: illegal shell: ``$sh''\n"
300	    if ($verbose && $sh &&
301		!$shell{&basename($sh)} &&
302		$p_username !~ /^(news|xten|bin|nobody|uucp)$/ &&
303		$sh !~ /\/(pppd|sliplogin)$/);
304	$uid{$p_uid} = $p_username;
305	$pwgid{$p_gid} = $p_username;
306    }
307    close P;
308}
309
310# read /etc/group
311sub group_read {
312    local($g_groupname,$pw,$g_gid, $memb);
313
314    print "Check $group\n" if $verbose;
315    open(G, "$group") || die "$group: $!\n";
316    while(<G>) {
317	chop;
318	push(@group_backup, $_);
319	($g_groupname, $pw, $g_gid, $memb) = (split(/:/, $_))[0..3];
320
321	$groupmembers{$g_gid} = $memb;
322	warn "Groupname exists twice: $g_groupname:$g_gid ->  $g_groupname:$groupname{$g_groupname}\n"
323	    if $groupname{$g_groupname} && $verbose;
324	$groupname{$g_groupname} = $g_gid;
325	warn "Groupid exists twice:   $g_groupname:$g_gid -> $gid{$g_gid}:$g_gid\n"
326	    if $gid{$g_gid} && $verbose;
327	$gid{$g_gid} = $g_groupname;
328    }
329    close G;
330}
331
332# check gids /etc/passwd <-> /etc/group
333sub group_check {
334    local($c_gid, $c_username, @list);
335
336    foreach $c_gid (keys %pwgid) {
337	if (!$gid{$c_gid}) {
338	    $c_username = $pwgid{$c_gid};
339	    warn "User ``$c_username'' has gid $c_gid but a group with this " .
340		"gid does not exist.\n" if $verbose;
341	}
342    }
343}
344
345#
346# main loop for creating new users
347#
348
349# return username
350sub new_users_name {
351    local($name);
352
353    while(1) {
354	$name = &confirm_list("Enter username", 1, "a-z0-9_", "");
355	if (length($name) > 8) {
356	    warn "Username is longer than 8 chars\a\n";
357	    next;
358	}
359	last if (&new_users_name_valid($name) eq $name);
360    }
361    return $name;
362}
363
364sub new_users_name_valid {
365    local($name) = @_;
366
367    if ($name !~ /^[a-z0-9_][a-z0-9_\-]*$/ || $name eq "a-z0-9_-") {
368	warn "Wrong username. " .
369	    "Please use only lowercase characters or digits\a\n";
370	return 0;
371    } elsif ($username{$name}) {
372	warn "Username ``$name'' already exists!\a\n"; return 0;
373    }
374    return $name;
375}
376
377# return full name
378sub new_users_fullname {
379    local($name) = @_;
380    local($fullname);
381
382    while(1) {
383	$fullname = &confirm_list("Enter full name", 1, "", "");
384	last if $fullname eq &new_users_fullname_valid($fullname);
385    }
386    $fullname = $name unless $fullname;
387    return $fullname;
388}
389
390sub new_users_fullname_valid {
391    local($fullname) = @_;
392
393    return $fullname if $fullname !~ /:/;
394
395    warn "``:'' is not allowed!\a\n";
396    return 0;
397}
398
399# return shell (full path) for user
400sub new_users_shell {
401    local($sh);
402
403    $sh = &confirm_list("Enter shell", 0, $defaultshell, keys %shell);
404    return $shell{$sh};
405}
406
407# return free uid and gid
408sub new_users_id {
409    local($name) = @_;
410    local($u_id, $g_id) = &next_id($name);
411    local($u_id_tmp, $e);
412
413    while(1) {
414	$u_id_tmp = &confirm_list("Uid", 1, $u_id, "");
415	last if $u_id_tmp =~ /^[0-9]+$/ && $u_id_tmp <= $uid_end &&
416		! $uid{$u_id_tmp};
417	if ($uid{$u_id_tmp}) {
418	    warn "Uid ``$u_id_tmp'' in use!\a\n";
419	} else {
420	    warn "Wrong uid.\a\n";
421	}
422    }
423    # use calculated uid
424    return ($u_id_tmp, $g_id) if $u_id_tmp eq $u_id;
425    # recalculate gid
426    $uid_start = $u_id_tmp;
427    return &next_id($name);
428}
429
430# add user to group
431sub add_group {
432    local($gid, $name) = @_;
433
434    return 0 if
435	$groupmembers{$gid} =~ /^(.+,)?$name(,.+)?$/;
436
437    $groupmembers_bak{$gid} = $groupmembers{$gid};
438    $groupmembers{$gid} .= "," if $groupmembers{$gid};
439    $groupmembers{$gid} .= "$name";
440
441    local(@l) = split(',', $groupmembers{$gid});
442    # group(5): A group cannot have more than 200 members.
443    # The maximum line length of /etc/group is 1024 characters.
444    # Longer lines will be skiped.
445    if ($#l >= 200 ||
446	length($groupmembers{$gid}) > 1024 - 50) { # 50 is for group name
447	warn "WARNING, maybe group line ``$gid{$gid}'' is to long or to\n" .
448	    "much users in group, see group(5)\a\n";
449    }
450    return $name;
451}
452
453
454# return login group
455sub new_users_grplogin {
456    local($name, $defaultgroup, $new_users_ok) = @_;
457    local($group_login, $group);
458
459    $group = $name;
460    $group = $defaultgroup if $defaultgroup ne $group_uniq;
461
462    if ($new_users_ok) {
463	# clean up backup
464	foreach $e (keys %groupmembers_bak) { delete $groupmembers_bak{$e}; }
465    } else {
466	# restore old groupmembers, user was not accept
467	foreach $e (keys %groupmembers_bak) {
468	    $groupmembers{$e} = $groupmembers_bak{$e};
469	}
470    }
471
472    while(1) {
473	$group_login = &confirm_list("Login group", 1, $group,
474				     ($name, $group));
475	last if $group_login eq $group;
476	last if $group_login eq $name;
477	last if defined $groupname{$group_login};
478	if ($group_login eq $group_uniq) {
479	    $group_login = $name; last;
480	}
481
482	if (defined $gid{$group_login}) {
483	    # convert numeric groupname (gid) to groupname
484	    $group_login = $gid{$group_login};
485	    last;
486	}
487	warn "Group does not exist!\a\n";
488    }
489
490    #if (defined($groupname{$group_login})) {
491    #	&add_group($groupname{$group_login}, $name);
492    #}
493
494    return ($group_login, $group_uniq) if $group_login eq $name;
495    return ($group_login, $group_login);
496}
497
498# return login group
499sub new_users_grplogin_batch {
500    local($name, $defaultgroup) = @_;
501    local($group_login, $group);
502
503    $group_login = $name;
504    $group_login = $defaultgroup if $defaultgroup ne $group_uniq;
505
506    if (defined $gid{$group_login}) {
507	# convert numeric groupname (gid) to groupname
508	$group_login = $gid{$group_login};
509    }
510
511    # if (defined($groupname{$group_login})) {
512    #	&add_group($groupname{$group_login}, $name);
513    # }
514
515    return $group_login
516	if defined($groupname{$group_login}) || $group_login eq $name;
517    warn "Group ``$group_login'' does not exist\a\n";
518    return 0;
519}
520
521# return other groups (string)
522sub new_users_groups {
523    local($name, $other_groups) = @_;
524    local($string) =
525	"Login group is ``$group_login''. Invite $name into other groups:";
526    local($e, $flag);
527    local($new_groups,$groups);
528
529    $other_groups = "no" unless $other_groups;
530
531    while(1) {
532	$groups = &confirm_list($string, 1, $other_groups,
533				("no", $other_groups, "guest"));
534	# no other groups
535	return "" if $groups eq "no";
536
537	($flag, $new_groups) = &new_users_groups_valid($groups);
538	last unless $flag;
539    }
540    $new_groups =~ s/\s*$//;
541    return $new_groups;
542}
543
544sub new_users_groups_valid {
545    local($groups) = @_;
546    local($e, $new_groups);
547    local($flag) = 0;
548
549    foreach $e (split(/[,\s]+/, $groups)) {
550	# convert numbers to groupname
551	if ($e =~ /^[0-9]+$/ && $gid{$e}) {
552	    $e = $gid{$e};
553	}
554	if (defined($groupname{$e})) {
555	    if ($e eq $group_login) {
556		# do not add user to a group if this group
557		# is also the login group.
558	    } elsif (&add_group($groupname{$e}, $name)) {
559		$new_groups .= "$e ";
560	    } else {
561		warn "$name is already member of group ``$e''\n";
562	    }
563	} else {
564	    warn "Group ``$e'' does not exist\a\n"; $flag++;
565	}
566    }
567    return ($flag, $new_groups);
568}
569
570# your last change
571sub new_users_ok {
572
573    print <<EOF;
574
575Name:	  $name
576Password: ****
577Fullname: $fullname
578Uid:	  $u_id
579Gid:	  $g_id ($group_login)
580Groups:	  $group_login $new_groups
581HOME:	  $home/$name
582Shell:	  $sh
583EOF
584
585    return &confirm_yn("OK?", "yes");
586}
587
588# make password database
589sub new_users_pwdmkdb {
590    local($last) = @_;
591
592    system("$pwd_mkdb $etc_passwd");
593    if ($?) {
594	warn "$last\n";
595	warn "``$pwd_mkdb'' failed\n";
596	exit($? >> 8);
597    }
598}
599
600# update group database
601sub new_users_group_update {
602    local($e, @a);
603
604    # Add *new* group
605    if (!defined($groupname{$group_login}) &&
606	!defined($gid{$groupname{$group_login}})) {
607	push(@group_backup, "$group_login:*:$g_id:");
608	$groupname{$group_login} = $g_id;
609	$gid{$g_id} = $group_login;
610	# $groupmembers{$g_id} = $group_login;
611    }
612
613    if ($new_groups || defined($groupname{$group_login}) ||
614	defined($gid{$groupname{$group_login}}) &&
615		$gid{$groupname{$group_login}} ne "+") {
616	# new user is member of some groups
617	# new login group is already in name space
618	rename($group, "$group.bak");
619	#warn "$group_login $groupname{$group_login} $groupmembers{$groupname{$group_login}}\n";
620	foreach $e (sort {$a <=> $b} (keys %gid)) {
621	    push(@a, "$gid{$e}:*:$e:$groupmembers{$e}");
622	}
623	&append_file($group, @a);
624    } else {
625	&append_file($group, "$group_login:*:$g_id:");
626    }
627
628}
629
630sub new_users_passwd_update {
631    # update passwd/group variables
632    push(@passwd_backup, $new_entry);
633    $username{$name} = $u_id;
634    $uid{$u_id} = $name;
635    $pwgid{$g_id} = $name;
636}
637
638# send message to new user
639sub new_users_sendmessage {
640    return 1 if $send_message eq "no";
641
642    local($cc) =
643	&confirm_list("Send message to ``$name'' and:",
644		      1, "no", ("root", "second_mail_address",
645		      "no carbon copy"));
646    local($e);
647    $cc = "" if $cc eq "no";
648
649    @message_buffer = ();
650    message_read ($send_message);
651
652    foreach $e (@message_buffer) {
653	print eval "\"$e\"";
654    }
655    print "\n";
656
657    local(@message_buffer_append) = ();
658    if (!&confirm_yn("Add anything to default message", "no")) {
659	print "Use ``.'' or ^D alone on a line to finish your message.\n";
660	push(@message_buffer_append, "\n");
661	while($read = <STDIN>) {
662	    last if $read eq "\.\n";
663	    push(@message_buffer_append, $read);
664	}
665    }
666
667    &sendmessage("$name $cc", (@message_buffer, @message_buffer_append))
668	if (&confirm_yn("Send message", "yes"));
669}
670
671sub sendmessage {
672    local($to, @message) = @_;
673    local($e);
674
675    if (!open(M, "| mail -s Welcome $to")) {
676	warn "Cannot send mail to: $to!\n";
677	return 0;
678    } else {
679	foreach $e (@message) {
680	    print M eval "\"$e\"";
681	}
682	close M;
683    }
684}
685
686
687sub new_users_password {
688
689    # empty password
690    return "" if $defaultpasswd ne "yes";
691
692    local($password);
693
694    while(1) {
695	system("stty -echo");
696	$password = &confirm_list("Enter password", 1, "", "");
697	system("stty echo");
698	print "\n";
699	if ($password ne "") {
700	    system("stty -echo");
701	    $newpass = &confirm_list("Enter password again", 1, "", "");
702	    system("stty echo");
703	    print "\n";
704	    last if $password eq $newpass;
705	    print "They didn't match, please try again\n";
706	}
707	elsif (&confirm_yn("Use an empty password?", "yes")) {
708	    last;
709	}
710    }
711
712    return $password;
713}
714
715
716sub new_users {
717
718    print "\n" if $verbose;
719    print "Ok, let's go.\n" .
720	  "Don't worry about mistakes. I will give you the chance later to " .
721	  "correct any input.\n" if $verbose;
722
723    # name: Username
724    # fullname: Full name
725    # sh: shell
726    # u_id: user id
727    # g_id: group id
728    # group_login: groupname of g_id
729    # new_groups: some other groups
730    local($name, $group_login, $fullname, $sh, $u_id, $g_id, $new_groups);
731    local($groupmembers_bak, $cryptpwd);
732    local($new_users_ok) = 1;
733
734
735    $new_groups = "no";
736    $new_groups = "no" unless $groupname{$new_groups};
737
738    while(1) {
739	$name = &new_users_name;
740	$fullname = &new_users_fullname($name);
741	$sh = &new_users_shell;
742	($u_id, $g_id) = &new_users_id($name);
743	($group_login, $defaultgroup) =
744	    &new_users_grplogin($name, $defaultgroup, $new_users_ok);
745	# do not use uniq username and login group
746	$g_id = $groupname{$group_login} if (defined($groupname{$group_login}));
747
748	$new_groups = &new_users_groups($name, $new_groups);
749	$password = &new_users_password;
750
751
752	if (&new_users_ok) {
753	    $new_users_ok = 1;
754
755	    $cryptpwd = "*";	# Locked by default
756	    $cryptpwd = encrypt($password, &salt) if ($password ne "");
757
758	    # obscure perl bug
759	    $new_entry = "$name\:" . "$cryptpwd" .
760		"\:$u_id\:$g_id\::0:0:$fullname:$home/$name:$sh";
761	    &append_file($etc_passwd, "$new_entry");
762	    &new_users_pwdmkdb("$new_entry");
763	    &new_users_group_update;
764	    &new_users_passwd_update;  print "Added user ``$name''\n";
765	    &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname");
766	    &home_create($name, $group_login);
767	    &new_users_sendmessage;
768	} else {
769	    $new_users_ok = 0;
770	}
771	if (!&confirm_yn("Add another user?", "yes")) {
772	    print "Goodbye!\n" if $verbose;
773	    last;
774	}
775	print "\n" if !$verbose;
776    }
777}
778
779sub batch {
780    local($name, $groups, $fullname, $password) = @_;
781    local($sh);
782
783    $defaultshell = &shell_default_valid($defaultshell);
784    return 0 unless $home = &home_partition_valid($home);
785    return 0 if $dotdir ne &dotdir_default_valid($dotdir);
786    $send_message = &message_default;
787
788    return 0 if $name ne &new_users_name_valid($name);
789    $sh = $shell{$defaultshell};
790    ($u_id, $g_id) = &next_id($name);
791    $group_login = &new_users_grplogin_batch($name, $defaultgroup);
792    return 0 unless $group_login;
793    $g_id = $groupname{$group_login} if (defined($groupname{$group_login}));
794    ($flag, $new_groups) = &new_users_groups_valid($groups);
795    return 0 if $flag;
796
797    $cryptpwd = "*";	# Locked by default
798    if ($password ne "" && $password ne "*") {
799	if($unencrypted)	{ $cryptpwd = encrypt($password, &salt) }
800	else			{ $cryptpwd = $password }
801    }
802    # obscure perl bug
803    $new_entry = "$name\:" . "$cryptpwd" .
804	"\:$u_id\:$g_id\::0:0:$fullname:$home/$name:$sh";
805    &append_file($etc_passwd, "$new_entry");
806    &new_users_pwdmkdb("$new_entry");
807    &new_users_group_update;
808    &new_users_passwd_update;  print "Added user ``$name''\n";
809    &sendmessage($name, @message_buffer) if $send_message ne "no";
810    &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname");
811    &home_create($name, $group_login);
812}
813
814# ask for password usage
815sub password_default {
816    local($p) = $defaultpasswd;
817    if ($verbose) {
818	$p = &confirm_yn("Prompt for passwords by default", $defaultpasswd);
819	$changes++ unless $p;
820    }
821    return "yes" if (($defaultpasswd eq "yes" && $p) ||
822		     ($defaultpasswd eq "no" && !$p));
823    return "no";    # otherwise
824}
825
826# get default encryption method
827sub encryption_default {
828    local($m) = "";
829    if ($verbose) {
830	while (&encryption_check($m) == 0) {
831            $m = &confirm_list("Default encryption method for passwords", 1,
832                              $encryption_methods[0], @encryption_methods);
833	}
834    }
835    return($m);
836}
837
838# Confirm that we have a valid encryption method
839sub encryption_check {
840    local($m) = $_[0];
841
842    foreach $i (@encryption_methods) {
843        if ($m eq $i) { return 1; }
844    }
845
846    if ($m =~ /^blowfish,(\d+)$/) { return 1; }
847    return 0;
848}
849
850# misc
851sub check_root {
852    die "You are not root!\n" if $< && !$test;
853}
854
855sub usage {
856    warn <<USAGE;
857usage: adduser
858    [-batch username [group[,group]...] [fullname] [password]]
859    [-check_only]
860    [-config_create]
861    [-dotdir dotdir]
862    [-e|-encryption method]
863    [-group login_group]
864    [-h|-help]
865    [-home home]
866    [-message message_file]
867    [-noconfig]
868    [-shell shell]
869    [-s|-silent|-q|-quiet]
870    [-uid_start uid_start]
871    [-uid_end uid_end]
872    [-unencrypted]
873    [-v|-verbose]
874
875home=$home shell=$defaultshell dotdir=$dotdir login_group=$defaultgroup
876message_file=$send_message uid_start=$uid_start uid_end=$uid_end
877USAGE
878    exit 1;
879}
880
881# uniq(1)
882sub uniq {
883    local(@list) = @_;
884    local($e, $last, @array);
885
886    foreach $e (sort @list) {
887	push(@array, $e) unless $e eq $last;
888	$last = $e;
889    }
890    return @array;
891}
892
893# Generate an appropriate argument to encrypt()
894# That may be a DES salt or a blowfish rotation count
895sub salt {
896    local($salt);		# initialization
897    if ($encryptionmethod eq "des" || $encryptionmethod eq "old") {
898        local($i, $rand);
899        local(@itoa64) = ( 0 .. 9, a .. z, A .. Z ); # 0 .. 63
900
901        warn "calculate salt\n" if $verbose > 1;
902
903        for ($i = 0; $i < 8; $i++) {
904	    srand(time + $rand + $$);
905	    $rand = rand(25*29*17 + $rand);
906	    $salt .=  $itoa64[$rand & $#itoa64];
907        }
908    } elsif ($encryptionmethod eq "md5") {
909        $salt = "";
910    } elsif ($encryptionmethod =~ /^blowfish/ ) {
911        ($encryptionmethod, $salt) = split(/\,/, $encryptionmethod);
912	if ($salt eq "") { $salt = 7; }	# default rounds inf unspecified
913    } else {
914        warn "$encryptionmethod encryption method invalid\n" if ($verbose > 0);
915	warn "Falling back to blowfish,7...\n" if ($verbose > 0);
916	$encryptionmethod = "blowfish";
917	$salt = 7;
918    }
919
920    warn "Salt is: $salt\n" if $verbose > 1;
921
922    return $salt;
923}
924
925# Encrypt a password using the selected method
926sub encrypt {
927    local($pass, $salt) = ($_[0], $_[1]);
928    local($args, $crypt);
929    local($goodpass);
930
931    if ($encryptionmethod eq "des" || $encryptionmethod eq "old") {
932        $args = "-s $salt";
933    } elsif ($encryptionmethod eq "md5") {
934        $args = "-m";
935    } elsif ($encryptionmethod eq "blowfish") {
936        $args = "-b $salt";
937    }
938
939    $pass =~ s/(.)/\\$1/g;
940
941    $crypt = `/usr/bin/encrypt $args -- $pass`;
942    chop $crypt;
943    return($crypt);
944}
945
946# print banner
947sub copyright {
948    return;
949}
950
951# hints
952sub hints {
953    if ($verbose) {
954	print "Use option ``-silent'' if you don't want see " .
955	      "all warnings & questions.\n\n";
956    }
957}
958
959#
960sub parse_arguments {
961    local(@argv) = @_;
962
963    while ($_ = $argv[0], /^-/) {
964	shift @argv;
965	last if /^--$/;
966	if    (/^--?(v|verbose)$/)	{ $verbose = 1 }
967	elsif (/^--?(s|silent|q|quiet)$/)  { $verbose = 0 }
968	elsif (/^--?(debug)$/)	    { $verbose = 2 }
969	elsif (/^--?(h|help|\?)$/)	{ &usage }
970	elsif (/^--?(home)$/)	 { $home = $argv[0]; shift @argv }
971	elsif (/^--?(shell)$/)	 { $defaultshell = $argv[0]; shift @argv }
972	elsif (/^--?(dotdir)$/)	 { $dotdir = $argv[0]; shift @argv }
973	elsif (/^--?(uid_start)$/)	 { $uid_start = $argv[0]; shift @argv }
974	elsif (/^--?(uid_end)$/)	 { $uid_end = $argv[0]; shift @argv }
975	elsif (/^--?(group)$/)	 { $defaultgroup = $argv[0]; shift @argv }
976	elsif (/^--?(check_only)$/) { $check_only = 1 }
977	elsif (/^--?(message)$/) { $send_message = $argv[0]; shift @argv;
978				   $sendmessage = 1; }
979	elsif (/^--?(unencrypted)$/)	{ $unencrypted = 1 }
980	elsif (/^--?(batch)$/)	 {
981	    @batch = splice(@argv, 0, 4); $verbose = 0;
982	    die "batch: too few arguments\n" if $#batch < 0;
983	}
984	# see &config_read
985	elsif (/^--?(config_create)$/)	{ &copyright; &hints; &create_conf; exit(0); }
986	elsif (/^--?(noconfig)$/)	{ $config_read = 0; }
987	elsif (/^--?(e|encryption)$/) {
988	    $encryptionmethod = $argv[0];
989	    shift @argv;
990	}
991	else			    { &usage }
992    }
993    #&usage if $#argv < 0;
994}
995
996sub basename {
997    local($name) = @_;
998    $name =~ s|/+$||;
999    $name =~ s|.*/+||;
1000    return $name;
1001}
1002
1003sub dirname {
1004    local($name) = @_;
1005    $name = &stripdir($name);
1006    $name =~ s|/+[^/]+$||;
1007    $name = "/" unless $name;	# dirname of / is /
1008    return $name;
1009}
1010
1011# return 1 if $file is a readable file or link
1012sub filetest {
1013    local($file, $verb) = @_;
1014
1015    if (-e $file) {
1016	if (-f $file || -l $file) {
1017	    return 1 if -r _;
1018	    warn "$file unreadable\n" if $verbose;
1019	} else {
1020	    warn "$file is not a plain file or link\n" if $verbose;
1021	}
1022    }
1023    return 0;
1024}
1025
1026# create or recreate configuration file prompting for values
1027sub create_conf {
1028    $create_conf = 1;
1029
1030    &shells_read;			# Pull in /etc/shells info
1031    &shells_add;			# maybe add some new shells
1032    $defaultshell = &shell_default;	# enter default shell
1033    $home = &home_partition($home);	# find HOME partition
1034    $dotdir = &dotdir_default;		# check $dotdir
1035    $send_message = &message_default;   # send message to new user
1036    $defaultpasswd = &password_default; # maybe use password
1037    $defaultencryption = &encryption_default;	# Encryption method
1038
1039    if ($send_message ne 'no') {
1040	&message_create($send_message);
1041    } else {
1042	&message_create($send_message_bak);
1043    }
1044    &config_write(1);
1045}
1046
1047# log for new user in /var/log/adduser
1048sub adduser_log {
1049    local($string) = @_;
1050    local($e);
1051
1052    return 1 if $logfile eq "no";
1053
1054    local($sec, $min, $hour, $mday, $mon, $year) = localtime;
1055    $mon++;
1056
1057    foreach $e ('sec', 'min', 'hour', 'mday', 'mon', 'year') {
1058	# '7' -> '07'
1059	eval "\$$e = 0 . \$$e" if (eval "\$$e" < 10);
1060    }
1061
1062    &append_file($logfile, "$year/$mon/$mday $hour:$min:$sec $string");
1063}
1064
1065# create HOME directory, copy dotfiles from $dotdir to $HOME
1066sub home_create {
1067    local($name, $group) = @_;
1068    local($homedir) = "$home/$name";
1069
1070    if (-e "$homedir") {
1071	warn "HOME Directory ``$homedir'' already exist\a\n";
1072	return 0;
1073    }
1074
1075    if ($dotdir eq 'no') {
1076	if (!mkdir("$homedir",0755)) {
1077	    warn "mkdir $homedir: $!\n"; return 0;
1078	}
1079	system 'chown', "$name:$group", $homedir;
1080	return !$?;
1081    }
1082
1083    # copy files from  $dotdir to $homedir
1084    # rename 'dot.foo' files to '.foo'
1085    print "Copy files from $dotdir to $homedir\n" if $verbose;
1086    system("cp -r $dotdir $homedir");
1087    system("chmod -R u+wrX,go-w $homedir");
1088    system("chown -R $name:$group $homedir");
1089
1090    # security
1091    opendir(D, $homedir);
1092    foreach $file (readdir(D)) {
1093	if ($file =~ /^dot\./ && -f "$homedir/$file") {
1094	    $file =~ s/^dot\././;
1095	    rename("$homedir/dot$file", "$homedir/$file");
1096	}
1097	chmod(0600, "$homedir/$file")
1098	    if ($file =~ /^\.(rhosts|Xauthority|kermrc|netrc)$/);
1099	chmod(0700, "$homedir/$file")
1100	    if ($file =~ /^(Mail|prv|\.(iscreen|term))$/);
1101    }
1102    closedir D;
1103    return 1;
1104}
1105
1106# makes a directory hierarchy
1107sub mkdir_home {
1108    local($dir) = @_;
1109    $dir = &stripdir($dir);
1110    local($user_partition) = "/usr";
1111    local($dirname) = &dirname($dir);
1112
1113
1114    -e $dirname || &mkdirhier($dirname);
1115
1116    if (((stat($dirname))[0]) == ((stat("/"))[0])){
1117	# home partition is on root partition
1118	# create home partition on $user_partition and make
1119	# a symlink from $dir to $user_partition/`basename $dir`
1120	# For instance: /home -> /usr/home
1121
1122	local($basename) = &basename($dir);
1123	local($d) = "$user_partition/$basename";
1124
1125
1126	if (-d $d) {
1127	    warn "Oops, $d already exist\n" if $verbose;
1128	} else {
1129	    print "Create $d\n" if $verbose;
1130	    if (!mkdir("$d", 0755)) {
1131		warn "$d: $!\a\n"; return 0;
1132	    }
1133	}
1134
1135	unlink($dir);		# symlink to nonexist file
1136	print "Create symlink: $dir -> $d\n" if $verbose;
1137	if (!symlink("$d", $dir)) {
1138	    warn "Symlink $d: $!\a\n"; return 0;
1139	}
1140    } else {
1141	print "Create $dir\n" if $verbose;
1142	if (!mkdir("$dir", 0755)) {
1143	    warn "Directory ``$dir'': $!\a\n"; return 0;
1144	}
1145    }
1146    return 1;
1147}
1148
1149sub mkdirhier {
1150    local($dir) = @_;
1151    local($d,$p);
1152
1153    $dir = &stripdir($dir);
1154
1155    foreach $d (split('/', $dir)) {
1156	$dir = "$p/$d";
1157	$dir =~ s|^//|/|;
1158	if (! -e "$dir") {
1159	    print "Create $dir\n" if $verbose;
1160	    if (!mkdir("$dir", 0755)) {
1161		warn "$dir: $!\n"; return 0;
1162	    }
1163	}
1164	$p .= "/$d";
1165    }
1166    return 1;
1167}
1168
1169# stript unused '/'
1170# F.i.: //usr///home// -> /usr/home
1171sub stripdir {
1172    local($dir) = @_;
1173
1174    $dir =~ s|/+|/|g;		# delete double '/'
1175    $dir =~ s|/$||;		# delete '/' at end
1176    return $dir if $dir ne "";
1177    return '/';
1178}
1179
1180# Read one of the elements from @list. $confirm is default.
1181# If !$allow accept only elements from @list.
1182sub confirm_list {
1183    local($message, $allow, $confirm, @list) = @_;
1184    local($read, $c, $print);
1185
1186    $print = "$message" if $message;
1187    $print .= " " unless $message =~ /\n$/ || $#list == 0;
1188
1189    $print .= join($", &uniq(@list)); #"
1190    $print .= " " unless $message =~ /\n$/ && $#list == 0;
1191    print "$print";
1192    print "\n" if (length($print) + length($confirm)) > 60;
1193    print "[$confirm]: ";
1194
1195    chop($read = <STDIN>);
1196    $read =~ s/^\s*//;
1197    $read =~ s/\s*$//;
1198    return $confirm if $read eq "";
1199    return "$read" if $allow;
1200
1201    foreach $c (@list) {
1202	return $read if $c eq $read;
1203    }
1204    warn "$read: is not allowed!\a\n";
1205    return &confirm_list($message, $allow, $confirm, @list);
1206}
1207
1208# YES or NO question
1209# return 1 if &confirm("message", "yes") and answer is yes
1210#	or if &confirm("message", "no") an answer is no
1211# otherwise 0
1212sub confirm_yn {
1213    local($message, $confirm) = @_;
1214    local($yes) = '^(yes|YES|y|Y)$';
1215    local($no) = '^(no|NO|n|N)$';
1216    local($read, $c);
1217
1218    if ($confirm && ($confirm =~ "$yes" || $confirm == 1)) {
1219	$confirm = "y";
1220    } else {
1221	$confirm = "n";
1222    }
1223    print "$message (y/n) [$confirm]: ";
1224    chop($read = <STDIN>);
1225    $read =~ s/^\s*//;
1226    $read =~ s/\s*$//;
1227    return 1 unless $read;
1228
1229    if (($confirm eq "y" && $read =~ "$yes") ||
1230	($confirm eq "n" && $read =~ "$no")) {
1231	return 1;
1232    }
1233
1234    if ($read !~ "$yes" && $read !~ "$no") {
1235	warn "Wrong value. Enter again!\a\n";
1236	return &confirm_yn($message, $confirm);
1237    }
1238    return 0;
1239}
1240
1241# test if $dotdir exist
1242# return "no" if $dotdir not exist or dotfiles should not copied
1243sub dotdir_default {
1244    local($dir) = $dotdir;
1245
1246    return &dotdir_default_valid($dir) unless $verbose;
1247    while($verbose) {
1248	$dir = &confirm_list("Copy dotfiles from:", 1,
1249	    $dir, ("no", $dotdir_bak, $dir));
1250	last if $dir eq &dotdir_default_valid($dir);
1251    }
1252    warn "Do not copy dotfiles.\n" if $verbose && $dir eq "no";
1253
1254    $changes++ if $dir ne $dotdir;
1255    return $dir;
1256}
1257
1258sub dotdir_default_valid {
1259    local($dir) = @_;
1260
1261    return $dir if (-e $dir && -r _ && (-d _ || -l $dir) && $dir =~ "^/");
1262    return $dir if $dir eq "no";
1263    warn "Dotdir ``$dir'' is not a directory\a\n";
1264    return "no";
1265}
1266
1267# ask for messages to new users
1268sub message_default {
1269    local($file) = $send_message;
1270    local(@d) = ($file, $send_message_bak, "no");
1271
1272    while($verbose) {
1273	$file = &confirm_list("Send message from file:", 1, $file, @d);
1274	last if $file eq "no";
1275	last if &filetest($file, 1);
1276
1277	# maybe create message file
1278	&message_create($file) if &confirm_yn("Create ``$file''?", "yes");
1279	last if &filetest($file, 0);
1280	last if !&confirm_yn("File ``$file'' does not exist, try again?",
1281			     "yes");
1282    }
1283
1284    if ($file eq "no" || !&filetest($file, 0)) {
1285	warn "Do not send message\n" if $verbose;
1286	$file = "no";
1287    } else {
1288	&message_read($file);
1289    }
1290
1291    $changes++ if $file ne $send_message && $verbose;
1292    return $file;
1293}
1294
1295# create message file
1296sub message_create {
1297    local($file) = @_;
1298
1299    rename($file, "$file.bak");
1300    if (!open(M, "> $file")) {
1301	warn "Messagefile ``$file'': $!\n"; return 0;
1302    }
1303    print M <<EOF;
1304#
1305# Message file for adduser(8)
1306#   comment: ``#''
1307#   default variables: \$name, \$fullname, \$password
1308#   other variables:  see /etc/adduser.conf after
1309#		     line  ``$do_not_delete''
1310#
1311
1312\$fullname,
1313
1314your account ``\$name'' was created.
1315Have fun!
1316
1317See also chpass(1), finger(1), passwd(1)
1318EOF
1319    close M;
1320    return 1;
1321}
1322
1323# read message file into buffer
1324sub message_read {
1325    local($file) = @_;
1326    @message_buffer = ();
1327
1328    if (!open(R, "$file")) {
1329	warn "File ``$file'':$!\n"; return 0;
1330    }
1331    while(<R>) {
1332	push(@message_buffer, $_) unless /^\s*#/;
1333    }
1334    close R;
1335}
1336
1337# write @list to $file with file-locking
1338sub append_file {
1339    local($file,@list) = @_;
1340    local($e);
1341    local($LOCK_EX) = 2;
1342    local($LOCK_NB) = 4;
1343    local($LOCK_UN) = 8;
1344
1345    open(F, ">> $file") || die "$file: $!\n";
1346    print "Lock $file.\n" if $verbose > 1;
1347    while(!flock(F, $LOCK_EX | $LOCK_NB)) {
1348	warn "Cannot lock file: $file\a\n";
1349	die "Sorry, give up\n"
1350	    unless &confirm_yn("Try again?", "yes");
1351    }
1352    print F join("\n", @list) . "\n";
1353    close F;
1354    print "Unlock $file.\n" if $verbose > 1;
1355    flock(F, $LOCK_UN);
1356}
1357
1358# return free uid+gid
1359# uid == gid if possible
1360sub next_id {
1361    local($group) = @_;
1362
1363    $uid_start = 1000 if ($uid_start <= 0 || $uid_start >= $uid_end);
1364    # looking for next free uid
1365    while($uid{$uid_start}) {
1366	$uid_start++;
1367	$uid_start = 1000 if $uid_start >= $uid_end;
1368	print "$uid_start\n" if $verbose > 1;
1369    }
1370
1371    local($gid_start) = $uid_start;
1372    # group for user (username==groupname) already exist
1373    if ($groupname{$group}) {
1374	$gid_start = $groupname{$group};
1375    }
1376    # gid is in use, looking for another gid.
1377    # Note: uid an gid are not equal
1378    elsif ($gid{$uid_start}) {
1379	while($gid{$gid_start} || $uid{$gid_start}) {
1380	    $gid_start--;
1381	    $gid_start = $uid_end if $gid_start < 100;
1382	}
1383    }
1384    return ($uid_start, $gid_start);
1385}
1386
1387# read config file - typically /etc/adduser.conf
1388sub config_read {
1389    local($opt) = @_;
1390    local($user_flag) = 0;
1391
1392    # don't read config file
1393    return 1 if $opt =~ /-(noconfig|config_create)/ || !$config_read;
1394
1395    if (!-f $config) {
1396        warn("Couldn't find $config: creating a new adduser configuration file\n");
1397        &create_conf;
1398    }
1399
1400    if (!open(C, "$config")) {
1401	warn "$config: $!\n"; return 0;
1402    }
1403
1404    while(<C>) {
1405	# user defined variables
1406	/^$do_not_delete/ && $user_flag++;
1407	# found @array or $variable
1408	if (s/^(\w+\s*=\s*\()/\@$1/ || s/^(\w+\s*=)/\$$1/) {
1409	    eval $_;
1410	    #warn "$_";
1411	}
1412	next if /^$/;
1413	# lines with '^##' are not saved
1414	push(@user_variable_list, $_)
1415	    if $user_flag && !/^##/ && (s/^[\$\@]// || /^[#\s]/);
1416    }
1417    #warn "X @user_variable_list X\n";
1418    close C;
1419}
1420
1421
1422# write config file
1423sub config_write {
1424    local($silent) = @_;
1425
1426    # nothing to do
1427    return 1 unless ($changes || ! -e $config || !$config_read || $silent);
1428
1429    if (!$silent) {
1430	if (-e $config) {
1431	    return 1 if &confirm_yn("\nWrite your changes to $config?", "no");
1432	} else {
1433	    return 1 unless
1434		&confirm_yn("\nWrite your configuration to $config?", "yes");
1435	}
1436    }
1437
1438    rename($config, "$config.bak");
1439    open(C, "> $config") || die "$config: $!\n";
1440
1441    # prepare some variables
1442    $send_message = "no" unless $send_message;
1443    $defaultpasswd = "no" unless $defaultpasswd;
1444    local($shpref) = "'" . join("', '", @shellpref) . "'";
1445    local($shpath) = "'" . join("', '", @path) . "'";
1446    local($user_var) = join('', @user_variable_list);
1447
1448    print C <<EOF;
1449#
1450# $OpenBSD: adduser.perl,v 1.15 1999/02/04 19:38:39 provos Exp $
1451# $config - automatic generated by adduser(8)
1452#
1453# Note: adduser read *and* write this file.
1454#	You may change values, but don't add new things befor the
1455#	line ``$do_not_delete''
1456#
1457
1458# verbose = [0-2]
1459verbose = $verbose
1460
1461# Get new password for new users
1462# defaultpasswd =  yes | no
1463defaultpasswd = $defaultpasswd
1464
1465# Default encryption method for user passwords
1466# Methods are all those listed in passwd.conf(5)
1467encryptionmethod = "$defaultencryption"
1468
1469# copy dotfiles from this dir ("/usr/share/skel" or "no")
1470dotdir = "$dotdir"
1471
1472# send this file to new user ("/etc/adduser.message" or "no")
1473send_message = "$send_message"
1474
1475# config file for adduser ("/etc/adduser.conf")
1476config = "$config"
1477
1478# logfile ("/var/log/adduser" or "no")
1479logfile = "$logfile"
1480
1481# default HOME directory ("/home")
1482home = "$home"
1483
1484# List of directories where shells located
1485# path = ('/bin', '/usr/bin', '/usr/local/bin')
1486path = ($shpath)
1487
1488# common shell list, first element has higher priority
1489# shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh')
1490shellpref = ($shpref)
1491
1492# defaultshell if not empty ("bash")
1493defaultshell = "$defaultshell"
1494
1495# defaultgroup ('USER' for same as username or any other valid group)
1496defaultgroup = $defaultgroup
1497
1498# new users get this uid
1499uid_start = $uid_start
1500uid_end = $uid_end
1501
1502$do_not_delete
1503## your own variables, see /etc/adduser.message
1504EOF
1505    print C "$user_var\n" if ($user_var ne '');
1506    print C "\n## end\n";
1507    close C;
1508}
1509
1510# check for sane variables
1511sub variable_check {
1512	local($abort) = 0;
1513
1514	# Check uid_start & uid_end
1515	warn "WARNING: uid_start < 1000!\n" if($uid_start < 1000);
1516	die "ERROR: uid_start >= uid_end!\n" if($uid_start >= $uid_end);
1517	# unencrypted really only usable in batch mode
1518	warn "WARNING: unencrypted only effective in batch mode\n"
1519	    if($#batch < 0 && $unencrypted);
1520}
1521
1522