194a17dd4Sdownsj#!/usr/bin/perl 294a17dd4Sdownsj# 3*2705e866Smpi# $OpenBSD: adduser.perl,v 1.63 2014/10/01 09:56:36 mpi Exp $ 494a17dd4Sdownsj# 594a17dd4Sdownsj# Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. 694a17dd4Sdownsj# All rights reserved. 794a17dd4Sdownsj# 894a17dd4Sdownsj# Redistribution and use in source and binary forms, with or without 994a17dd4Sdownsj# modification, are permitted provided that the following conditions 1094a17dd4Sdownsj# are met: 1194a17dd4Sdownsj# 1. Redistributions of source code must retain the above copyright 1294a17dd4Sdownsj# notice, this list of conditions and the following disclaimer. 1394a17dd4Sdownsj# 2. Redistributions in binary form must reproduce the above copyright 1494a17dd4Sdownsj# notice, this list of conditions and the following disclaimer in the 1594a17dd4Sdownsj# documentation and/or other materials provided with the distribution. 1694a17dd4Sdownsj# 1794a17dd4Sdownsj# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1894a17dd4Sdownsj# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1994a17dd4Sdownsj# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2094a17dd4Sdownsj# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2194a17dd4Sdownsj# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2294a17dd4Sdownsj# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2394a17dd4Sdownsj# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2494a17dd4Sdownsj# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2594a17dd4Sdownsj# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2694a17dd4Sdownsj# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2794a17dd4Sdownsj# SUCH DAMAGE. 2894a17dd4Sdownsj# 29d7ab7c04Sdownsj# $From: adduser.perl,v 1.22 1996/12/07 21:25:12 ache Exp $ 3094a17dd4Sdownsj 312bbaf33aSalexuse IPC::Open2; 3227250d79Smillertuse Fcntl qw(:DEFAULT :flock); 332bbaf33aSalex 3417cfdb33Sgene################ 3517cfdb33Sgene# main 3617cfdb33Sgene# 3717cfdb33Sgene$check_only = 0; 3894a17dd4Sdownsj 3927250d79Smillert$SIG{'INT'} = 'cleanup'; 4027250d79Smillert$SIG{'QUIT'} = 'cleanup'; 4127250d79Smillert$SIG{'HUP'} = 'cleanup'; 4227250d79Smillert$SIG{'TERM'} = 'cleanup'; 4327250d79Smillert 4417cfdb33Sgene&check_root; # you must be root to run this script! 4517cfdb33Sgene&variables; # initialize variables 4617cfdb33Sgene&config_read(@ARGV); # read variables from config-file 4717cfdb33Sgene&parse_arguments(@ARGV); # parse arguments 4817cfdb33Sgene 4917cfdb33Sgeneif (!$check_only && $#batch < 0) { 504424054eSmillert &hints; 5117cfdb33Sgene} 5217cfdb33Sgene 5317cfdb33Sgene# check 5417cfdb33Sgene$changes = 0; 554cdcf8a6Sweingart&variable_check; # check for valid variables 5617cfdb33Sgene&passwd_check; # check for valid passwdb 5717cfdb33Sgene&shells_read; # read /etc/shells 58fa4eb53aSmillert&login_conf_read; # read /etc/login.conf 5917cfdb33Sgene&passwd_read; # read /etc/master.passwd 6017cfdb33Sgene&group_read; # read /etc/group 6117cfdb33Sgene&group_check; # check for incon* 6217cfdb33Sgeneexit 0 if $check_only; # only check consistence and exit 6317cfdb33Sgene 6417cfdb33Sgeneexit(!&batch(@batch)) if $#batch >= 0; # batch mode 6517cfdb33Sgene 6617cfdb33Sgene# Interactive: 6717cfdb33Sgene# main loop for creating new users 6817cfdb33Sgene&new_users; # add new users 6917cfdb33Sgene 7017cfdb33Sgene#end 7117cfdb33Sgene 7217cfdb33Sgene 7317cfdb33Sgene# Set adduser "default" variables internally before groking config file 74ed5470abSdavid# Adduser.conf supersedes these 7594a17dd4Sdownsjsub variables { 7694a17dd4Sdownsj $verbose = 1; # verbose = [0-2] 7794a17dd4Sdownsj $defaultpasswd = "yes"; # use password for new users 780ef5f496Sjakob $dotdir = "/etc/skel"; # copy dotfiles from this dir 7994a17dd4Sdownsj $dotdir_bak = $dotdir; 8094a17dd4Sdownsj $send_message = "no"; # send message to new user 816b3add36Slum $message_file = "/etc/adduser.message"; 8294a17dd4Sdownsj $config = "/etc/adduser.conf"; # config file for adduser 8394a17dd4Sdownsj $config_read = 1; # read config file 8494a17dd4Sdownsj $logfile = "/var/log/adduser"; # logfile 8594a17dd4Sdownsj $home = "/home"; # default HOME 8694a17dd4Sdownsj $etc_shells = "/etc/shells"; 8794a17dd4Sdownsj $etc_passwd = "/etc/master.passwd"; 8827250d79Smillert $etc_ptmp = "/etc/ptmp"; 8994a17dd4Sdownsj $group = "/etc/group"; 90fa4eb53aSmillert $etc_login_conf = "/etc/login.conf"; 912cf95264Smillert @pwd_mkdb = ("pwd_mkdb", "-p"); # program for building passwd database 92d7bb54ecSmillert $encryptionmethod = "auto"; 9394a17dd4Sdownsj 9494a17dd4Sdownsj # List of directories where shells located 9594a17dd4Sdownsj @path = ('/bin', '/usr/bin', '/usr/local/bin'); 9694a17dd4Sdownsj # common shells, first element has higher priority 9794a17dd4Sdownsj @shellpref = ('csh', 'sh', 'bash', 'tcsh', 'ksh'); 9894a17dd4Sdownsj 999f3bae82Stedu @encryption_methods = ('auto', 'blowfish' ); 10017cfdb33Sgene 101be5f6eb6Skettenis $defaultshell = 'ksh'; # defaultshell if not empty 10294a17dd4Sdownsj $group_uniq = 'USER'; 10394a17dd4Sdownsj $defaultgroup = $group_uniq;# login groupname, $group_uniq means username 104fa4eb53aSmillert $defaultclass = 'default'; # default user login class 10594a17dd4Sdownsj 10694a17dd4Sdownsj $uid_start = 1000; # new users get this uid 107a56eb4e7Smpech $uid_end = 2147483647; # max. uid 10894a17dd4Sdownsj 10994a17dd4Sdownsj # global variables 11094a17dd4Sdownsj # passwd 1114424054eSmillert %username = (); # $username{username} = uid 1124424054eSmillert %uid = (); # $uid{uid} = username 1134424054eSmillert %pwgid = (); # $pwgid{pwgid} = username; gid from passwd db 11494a17dd4Sdownsj 11594a17dd4Sdownsj $password = ''; # password for new users 11694a17dd4Sdownsj 11794a17dd4Sdownsj # group 1184424054eSmillert %groupname = (); # $groupname{groupname} = gid 1194424054eSmillert %groupmembers = (); # $groupmembers{gid} = members of group/kommalist 12065aa9a9eSray %gid = (); # $gid{gid} = groupname; gid from group db 12194a17dd4Sdownsj 12294a17dd4Sdownsj # shell 123ab051decSmillert %shell = (); # $shell{`basename sh`} = sh 12494a17dd4Sdownsj 12594a17dd4Sdownsj umask 022; # don't give login group write access 12694a17dd4Sdownsj 1276b3add36Slum # regexs used in determining user supplied yes/no 1286b3add36Slum $yes = qr/^(yes|YES|y|Y)$/; 1296b3add36Slum $no = qr/^(no|NO|n|N)$/; 1306b3add36Slum 13194a17dd4Sdownsj $ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin"; 132ab051decSmillert @passwd_backup = (); 133ab051decSmillert @group_backup = (); 134ab051decSmillert @message_buffer = (); 135fa4eb53aSmillert @login_classes = (); 136ab051decSmillert @user_variable_list = (); # user variables in /etc/adduser.conf 13794a17dd4Sdownsj $do_not_delete = '## DO NOT DELETE THIS LINE!'; 13894a17dd4Sdownsj} 13994a17dd4Sdownsj 140fa4eb53aSmillertsub login_conf_read { 14116cd0059Smillert foreach (`getcap -f $etc_login_conf -a -s localcipher`) { 142fa4eb53aSmillert chomp; 14316cd0059Smillert s/:.*//; 14416cd0059Smillert push(@login_classes, $_); 145fa4eb53aSmillert } 146fa4eb53aSmillert} 147fa4eb53aSmillert 14894a17dd4Sdownsj# read shell database, see also: shells(5) 14994a17dd4Sdownsjsub shells_read { 15094a17dd4Sdownsj local($sh); 15194a17dd4Sdownsj local($err) = 0; 15294a17dd4Sdownsj 15317cfdb33Sgene print "Reading $etc_shells\n" if $verbose; 15494a17dd4Sdownsj open(S, $etc_shells) || die "$etc_shells: $!\n"; 15594a17dd4Sdownsj 15694a17dd4Sdownsj while(<S>) { 15794a17dd4Sdownsj if (/^\s*\//) { 15894a17dd4Sdownsj s/^\s*//; s/\s+.*//; # chop 15994a17dd4Sdownsj $sh = $_; 16094a17dd4Sdownsj if (-x $sh) { 16194a17dd4Sdownsj $shell{&basename($sh)} = $sh; 16294a17dd4Sdownsj } else { 16394a17dd4Sdownsj warn "Shell: $sh not executable!\n"; 16494a17dd4Sdownsj $err++; 16594a17dd4Sdownsj } 16694a17dd4Sdownsj } 16794a17dd4Sdownsj } 16805254fbeSmillert close(S); 16994a17dd4Sdownsj 1701a6ce792Sderaadt push(@list, "/sbin/nologin"); 1711a6ce792Sderaadt &shell_pref_add("nologin"); 17207625999Sweingart $shell{"nologin"} = "/sbin/nologin"; 17394a17dd4Sdownsj 17494a17dd4Sdownsj return $err; 17594a17dd4Sdownsj} 17694a17dd4Sdownsj 17794a17dd4Sdownsj# add new shells if possible 17894a17dd4Sdownsjsub shells_add { 17994a17dd4Sdownsj local($sh,$dir,@list); 18094a17dd4Sdownsj 18194a17dd4Sdownsj return 1 unless $verbose; 18294a17dd4Sdownsj 18394a17dd4Sdownsj foreach $sh (@shellpref) { 18494a17dd4Sdownsj # all known shells 18594a17dd4Sdownsj if (!$shell{$sh}) { 18694a17dd4Sdownsj # shell $sh is not defined as login shell 18794a17dd4Sdownsj foreach $dir (@path) { 18894a17dd4Sdownsj if (-x "$dir/$sh") { 18994a17dd4Sdownsj # found shell 19094a17dd4Sdownsj if (&confirm_yn("Found shell: $dir/$sh. Add to $etc_shells?", "yes")) { 19194a17dd4Sdownsj push(@list, "$dir/$sh"); 1922fb3fdceSdownsj &shell_pref_add("$sh"); 19394a17dd4Sdownsj $shell{&basename("$dir/$sh")} = "$dir/$sh"; 19494a17dd4Sdownsj $changes++; 19594a17dd4Sdownsj } 19694a17dd4Sdownsj } 19794a17dd4Sdownsj } 19894a17dd4Sdownsj } 19994a17dd4Sdownsj } 20094a17dd4Sdownsj &append_file($etc_shells, @list) if $#list >= 0; 20194a17dd4Sdownsj} 20294a17dd4Sdownsj 2032fb3fdceSdownsj# add shell to preference list without duplication 2042fb3fdceSdownsjsub shell_pref_add { 2052fb3fdceSdownsj local($new_shell) = @_; 2062fb3fdceSdownsj local($shell); 2072fb3fdceSdownsj 2082fb3fdceSdownsj foreach $shell (@shellpref) { 2092fb3fdceSdownsj return if ($shell eq $new_shell); 2102fb3fdceSdownsj } 2112fb3fdceSdownsj push(@shellpref, $new_shell); 2122fb3fdceSdownsj} 2132fb3fdceSdownsj 21494a17dd4Sdownsj# choose your favourite shell and return the shell 21594a17dd4Sdownsjsub shell_default { 21694a17dd4Sdownsj local($e,$i,$new_shell); 21794a17dd4Sdownsj local($sh); 21894a17dd4Sdownsj 21994a17dd4Sdownsj $sh = &shell_default_valid($defaultshell); 22094a17dd4Sdownsj return $sh unless $verbose; 22194a17dd4Sdownsj 22294a17dd4Sdownsj $new_shell = &confirm_list("Enter your default shell:", 0, 22394a17dd4Sdownsj $sh, sort(keys %shell)); 22494a17dd4Sdownsj print "Your default shell is: $new_shell -> $shell{$new_shell}\n"; 22594a17dd4Sdownsj $changes++ if $new_shell ne $sh; 22694a17dd4Sdownsj return $new_shell; 22794a17dd4Sdownsj} 22894a17dd4Sdownsj 22994a17dd4Sdownsjsub shell_default_valid { 23094a17dd4Sdownsj local($sh) = @_; 23194a17dd4Sdownsj local($s,$e); 23294a17dd4Sdownsj 23394a17dd4Sdownsj return $sh if $shell{$sh}; 23494a17dd4Sdownsj 23594a17dd4Sdownsj foreach $e (@shellpref) { 23694a17dd4Sdownsj $s = $e; 23794a17dd4Sdownsj last if defined($shell{$s}); 23894a17dd4Sdownsj } 23994a17dd4Sdownsj $s = "sh" unless $s; 24094a17dd4Sdownsj warn "Shell ``$sh'' is undefined, use ``$s''\n"; 24194a17dd4Sdownsj return $s; 24294a17dd4Sdownsj} 24394a17dd4Sdownsj 244150e5e64Stedu# return default home partition (e.g. "/home") 245e4d25771Stodd# create base directory if necessary 24694a17dd4Sdownsjsub home_partition { 24794a17dd4Sdownsj local($home) = @_; 24894a17dd4Sdownsj $home = &stripdir($home); 24994a17dd4Sdownsj local($h) = $home; 25094a17dd4Sdownsj 25194a17dd4Sdownsj return $h if !$verbose && $h eq &home_partition_valid($h); 25294a17dd4Sdownsj 25394a17dd4Sdownsj while(1) { 25494a17dd4Sdownsj $h = &confirm_list("Enter your default HOME partition:", 1, $home, ""); 25594a17dd4Sdownsj $h = &stripdir($h); 25694a17dd4Sdownsj last if $h eq &home_partition_valid($h); 25794a17dd4Sdownsj } 25894a17dd4Sdownsj 25994a17dd4Sdownsj $changes++ if $h ne $home; 26094a17dd4Sdownsj return $h; 26194a17dd4Sdownsj} 26294a17dd4Sdownsj 26394a17dd4Sdownsjsub home_partition_valid { 26494a17dd4Sdownsj local($h) = @_; 26594a17dd4Sdownsj 26694a17dd4Sdownsj $h = &stripdir($h); 26794a17dd4Sdownsj # all right (I hope) 2684424054eSmillert return $h if $h =~ "^/" && -e $h && -w _ && (-d _ || -l $h); 26994a17dd4Sdownsj 27094a17dd4Sdownsj # Errors or todo 27194a17dd4Sdownsj if ($h !~ "^/") { 27294a17dd4Sdownsj warn "Please use absolute path for home: ``$h''.\a\n"; 27394a17dd4Sdownsj return 0; 27494a17dd4Sdownsj } 27594a17dd4Sdownsj 27694a17dd4Sdownsj if (-e $h) { 27794a17dd4Sdownsj warn "$h exists, but is not a directory or symlink!\n" 27894a17dd4Sdownsj unless -d $h || -l $h; 27994a17dd4Sdownsj warn "$h is not writable!\n" 28094a17dd4Sdownsj unless -w $h; 28194a17dd4Sdownsj return 0; 28294a17dd4Sdownsj } else { 28394a17dd4Sdownsj # create home partition 28494a17dd4Sdownsj return $h if &mkdir_home($h); 28594a17dd4Sdownsj } 28694a17dd4Sdownsj return 0; 28794a17dd4Sdownsj} 28894a17dd4Sdownsj 28994a17dd4Sdownsj# check for valid passwddb 29094a17dd4Sdownsjsub passwd_check { 2912cf95264Smillert system(@pwd_mkdb, "-c", $etc_passwd); 29294a17dd4Sdownsj die "\nInvalid $etc_passwd - cannot add any users!\n" if $?; 29394a17dd4Sdownsj} 29494a17dd4Sdownsj 29594a17dd4Sdownsj# read /etc/passwd 29694a17dd4Sdownsjsub passwd_read { 2974424054eSmillert local($p_username, $pw, $p_uid, $p_gid, $sh); 29894a17dd4Sdownsj 29994a17dd4Sdownsj print "Check $etc_passwd\n" if $verbose; 300f5e249d4Smillert open(P, "$etc_passwd") || die "$etc_passwd: $!\n"; 30194a17dd4Sdownsj 30227250d79Smillert # we only use this to lock the password file 30327250d79Smillert sysopen(PTMP, $etc_ptmp, O_RDWR|O_CREAT|O_EXCL, 0600) || 30427250d79Smillert die "Password file busy\n"; 30527250d79Smillert 30694a17dd4Sdownsj while(<P>) { 30794a17dd4Sdownsj chop; 30894a17dd4Sdownsj push(@passwd_backup, $_); 30994a17dd4Sdownsj ($p_username, $pw, $p_uid, $p_gid, $sh) = (split(/:/, $_))[0..3,9]; 31094a17dd4Sdownsj 31194a17dd4Sdownsj print "$p_username already exists with uid: $username{$p_username}!\n" 31294a17dd4Sdownsj if $username{$p_username} && $verbose; 31394a17dd4Sdownsj $username{$p_username} = $p_uid; 31494a17dd4Sdownsj print "User $p_username: uid $p_uid exists twice: $uid{$p_uid}\n" 31594a17dd4Sdownsj if $uid{$p_uid} && $verbose && $p_uid; # don't warn for uid 0 31694a17dd4Sdownsj print "User $p_username: illegal shell: ``$sh''\n" 31794a17dd4Sdownsj if ($verbose && $sh && 31894a17dd4Sdownsj !$shell{&basename($sh)} && 319d7ab7c04Sdownsj $p_username !~ /^(news|xten|bin|nobody|uucp)$/ && 320*2705e866Smpi $sh !~ /\/pppd$/); 32194a17dd4Sdownsj $uid{$p_uid} = $p_username; 32294a17dd4Sdownsj $pwgid{$p_gid} = $p_username; 32394a17dd4Sdownsj } 32494a17dd4Sdownsj close P; 32594a17dd4Sdownsj} 32694a17dd4Sdownsj 32794a17dd4Sdownsj# read /etc/group 32894a17dd4Sdownsjsub group_read { 32994a17dd4Sdownsj local($g_groupname,$pw,$g_gid, $memb); 33094a17dd4Sdownsj 33194a17dd4Sdownsj print "Check $group\n" if $verbose; 33294a17dd4Sdownsj open(G, "$group") || die "$group: $!\n"; 33394a17dd4Sdownsj while(<G>) { 33494a17dd4Sdownsj chop; 33594a17dd4Sdownsj push(@group_backup, $_); 33694a17dd4Sdownsj ($g_groupname, $pw, $g_gid, $memb) = (split(/:/, $_))[0..3]; 33794a17dd4Sdownsj 33894a17dd4Sdownsj $groupmembers{$g_gid} = $memb; 33994a17dd4Sdownsj warn "Groupname exists twice: $g_groupname:$g_gid -> $g_groupname:$groupname{$g_groupname}\n" 34094a17dd4Sdownsj if $groupname{$g_groupname} && $verbose; 34194a17dd4Sdownsj $groupname{$g_groupname} = $g_gid; 34294a17dd4Sdownsj warn "Groupid exists twice: $g_groupname:$g_gid -> $gid{$g_gid}:$g_gid\n" 34394a17dd4Sdownsj if $gid{$g_gid} && $verbose; 34494a17dd4Sdownsj $gid{$g_gid} = $g_groupname; 34594a17dd4Sdownsj } 34694a17dd4Sdownsj close G; 34794a17dd4Sdownsj} 34894a17dd4Sdownsj 34994a17dd4Sdownsj# check gids /etc/passwd <-> /etc/group 35094a17dd4Sdownsjsub group_check { 35194a17dd4Sdownsj local($c_gid, $c_username, @list); 35294a17dd4Sdownsj 35394a17dd4Sdownsj foreach $c_gid (keys %pwgid) { 35494a17dd4Sdownsj if (!$gid{$c_gid}) { 35594a17dd4Sdownsj $c_username = $pwgid{$c_gid}; 35694a17dd4Sdownsj warn "User ``$c_username'' has gid $c_gid but a group with this " . 35794a17dd4Sdownsj "gid does not exist.\n" if $verbose; 35894a17dd4Sdownsj } 35994a17dd4Sdownsj } 36094a17dd4Sdownsj} 36194a17dd4Sdownsj 36294a17dd4Sdownsj# 36394a17dd4Sdownsj# main loop for creating new users 36494a17dd4Sdownsj# 36594a17dd4Sdownsj 36694a17dd4Sdownsj# return username 36794a17dd4Sdownsjsub new_users_name { 36894a17dd4Sdownsj local($name); 36994a17dd4Sdownsj 37094a17dd4Sdownsj while(1) { 3716b22066cSmillert $name = &confirm_list("Enter username", 1, "", ""); 372105d9608Sderaadt if (length($name) > 31) { 373105d9608Sderaadt warn "Username is longer than 31 characters\a\n"; 37494a17dd4Sdownsj next; 37594a17dd4Sdownsj } 37694a17dd4Sdownsj last if (&new_users_name_valid($name) eq $name); 37794a17dd4Sdownsj } 37894a17dd4Sdownsj return $name; 37994a17dd4Sdownsj} 38094a17dd4Sdownsj 38194a17dd4Sdownsjsub new_users_name_valid { 38294a17dd4Sdownsj local($name) = @_; 38394a17dd4Sdownsj 3846b22066cSmillert if ($name !~ /^[a-zA-Z0-9_\.][a-zA-Z0-9_\.\-]*\$?$/ || $name eq "") { 3854424054eSmillert warn "Illegal username. " . 3866b22066cSmillert "Please see the restrictions section of the man page.\a\n"; 38794a17dd4Sdownsj return 0; 38894a17dd4Sdownsj } elsif ($username{$name}) { 38994a17dd4Sdownsj warn "Username ``$name'' already exists!\a\n"; return 0; 39094a17dd4Sdownsj } 39194a17dd4Sdownsj return $name; 39294a17dd4Sdownsj} 39394a17dd4Sdownsj 39494a17dd4Sdownsj# return full name 39594a17dd4Sdownsjsub new_users_fullname { 39694a17dd4Sdownsj local($name) = @_; 39794a17dd4Sdownsj local($fullname); 39894a17dd4Sdownsj 39994a17dd4Sdownsj while(1) { 40094a17dd4Sdownsj $fullname = &confirm_list("Enter full name", 1, "", ""); 40194a17dd4Sdownsj last if $fullname eq &new_users_fullname_valid($fullname); 40294a17dd4Sdownsj } 40394a17dd4Sdownsj $fullname = $name unless $fullname; 40494a17dd4Sdownsj return $fullname; 40594a17dd4Sdownsj} 40694a17dd4Sdownsj 40794a17dd4Sdownsjsub new_users_fullname_valid { 40894a17dd4Sdownsj local($fullname) = @_; 40994a17dd4Sdownsj 41094a17dd4Sdownsj return $fullname if $fullname !~ /:/; 41194a17dd4Sdownsj 41294a17dd4Sdownsj warn "``:'' is not allowed!\a\n"; 41394a17dd4Sdownsj return 0; 41494a17dd4Sdownsj} 41594a17dd4Sdownsj 41694a17dd4Sdownsj# return shell (full path) for user 41794a17dd4Sdownsjsub new_users_shell { 41894a17dd4Sdownsj local($sh); 41994a17dd4Sdownsj 42094a17dd4Sdownsj $sh = &confirm_list("Enter shell", 0, $defaultshell, keys %shell); 42194a17dd4Sdownsj return $shell{$sh}; 42294a17dd4Sdownsj} 42394a17dd4Sdownsj 424fa4eb53aSmillertsub new_users_login_class { 425fa4eb53aSmillert local($log_cl); 426fa4eb53aSmillert 427fa4eb53aSmillert $log_cl = &confirm_list("Login class", 0, $defaultclass, @login_classes); 428fa4eb53aSmillert return($log_cl); 429fa4eb53aSmillert} 430fa4eb53aSmillert 43194a17dd4Sdownsj# return free uid and gid 43294a17dd4Sdownsjsub new_users_id { 43394a17dd4Sdownsj local($name) = @_; 43494a17dd4Sdownsj local($u_id, $g_id) = &next_id($name); 43594a17dd4Sdownsj local($u_id_tmp, $e); 43694a17dd4Sdownsj 43794a17dd4Sdownsj while(1) { 43894a17dd4Sdownsj $u_id_tmp = &confirm_list("Uid", 1, $u_id, ""); 43994a17dd4Sdownsj last if $u_id_tmp =~ /^[0-9]+$/ && $u_id_tmp <= $uid_end && 44094a17dd4Sdownsj ! $uid{$u_id_tmp}; 44194a17dd4Sdownsj if ($uid{$u_id_tmp}) { 44294a17dd4Sdownsj warn "Uid ``$u_id_tmp'' in use!\a\n"; 44394a17dd4Sdownsj } else { 44494a17dd4Sdownsj warn "Wrong uid.\a\n"; 44594a17dd4Sdownsj } 44694a17dd4Sdownsj } 44794a17dd4Sdownsj # use calculated uid 44894a17dd4Sdownsj return ($u_id_tmp, $g_id) if $u_id_tmp eq $u_id; 44994a17dd4Sdownsj # recalculate gid 45094a17dd4Sdownsj $uid_start = $u_id_tmp; 45194a17dd4Sdownsj return &next_id($name); 45294a17dd4Sdownsj} 45394a17dd4Sdownsj 45494a17dd4Sdownsj# add user to group 45594a17dd4Sdownsjsub add_group { 45694a17dd4Sdownsj local($gid, $name) = @_; 45794a17dd4Sdownsj 45894a17dd4Sdownsj return 0 if 45959d518ccSespie $groupmembers{$gid} =~ /^(.*,)?$name(,.*)?$/; 46094a17dd4Sdownsj 46194a17dd4Sdownsj $groupmembers_bak{$gid} = $groupmembers{$gid}; 46294a17dd4Sdownsj $groupmembers{$gid} .= "," if $groupmembers{$gid}; 46394a17dd4Sdownsj $groupmembers{$gid} .= "$name"; 46494a17dd4Sdownsj 46594a17dd4Sdownsj local(@l) = split(',', $groupmembers{$gid}); 46694a17dd4Sdownsj # group(5): A group cannot have more than 200 members. 46794a17dd4Sdownsj # The maximum line length of /etc/group is 1024 characters. 46861503c37Sjmc # Longer lines will be skipped. 46994a17dd4Sdownsj if ($#l >= 200 || 47094a17dd4Sdownsj length($groupmembers{$gid}) > 1024 - 50) { # 50 is for group name 4714424054eSmillert warn "WARNING, group line ``$gid{$gid}'' is either too long or has\n" . 4724424054eSmillert "too many users in the group, see group(5)\a\n"; 47394a17dd4Sdownsj } 47494a17dd4Sdownsj return $name; 47594a17dd4Sdownsj} 47694a17dd4Sdownsj 47794a17dd4Sdownsj 47894a17dd4Sdownsj# return login group 47994a17dd4Sdownsjsub new_users_grplogin { 48094a17dd4Sdownsj local($name, $defaultgroup, $new_users_ok) = @_; 48194a17dd4Sdownsj local($group_login, $group); 48294a17dd4Sdownsj 48394a17dd4Sdownsj $group = $name; 48494a17dd4Sdownsj $group = $defaultgroup if $defaultgroup ne $group_uniq; 48594a17dd4Sdownsj 48694a17dd4Sdownsj if ($new_users_ok) { 48794a17dd4Sdownsj # clean up backup 48894a17dd4Sdownsj foreach $e (keys %groupmembers_bak) { delete $groupmembers_bak{$e}; } 48994a17dd4Sdownsj } else { 49094a17dd4Sdownsj # restore old groupmembers, user was not accept 49194a17dd4Sdownsj foreach $e (keys %groupmembers_bak) { 49294a17dd4Sdownsj $groupmembers{$e} = $groupmembers_bak{$e}; 49394a17dd4Sdownsj } 49494a17dd4Sdownsj } 49594a17dd4Sdownsj 49694a17dd4Sdownsj while(1) { 49794a17dd4Sdownsj $group_login = &confirm_list("Login group", 1, $group, 49894a17dd4Sdownsj ($name, $group)); 49994a17dd4Sdownsj last if $group_login eq $group; 50094a17dd4Sdownsj last if $group_login eq $name; 50194a17dd4Sdownsj last if defined $groupname{$group_login}; 50294a17dd4Sdownsj if ($group_login eq $group_uniq) { 50394a17dd4Sdownsj $group_login = $name; last; 50494a17dd4Sdownsj } 50594a17dd4Sdownsj 50694a17dd4Sdownsj if (defined $gid{$group_login}) { 50794a17dd4Sdownsj # convert numeric groupname (gid) to groupname 50894a17dd4Sdownsj $group_login = $gid{$group_login}; 50994a17dd4Sdownsj last; 51094a17dd4Sdownsj } 51194a17dd4Sdownsj warn "Group does not exist!\a\n"; 51294a17dd4Sdownsj } 51394a17dd4Sdownsj 51494a17dd4Sdownsj #if (defined($groupname{$group_login})) { 51594a17dd4Sdownsj # &add_group($groupname{$group_login}, $name); 51694a17dd4Sdownsj #} 51794a17dd4Sdownsj 51894a17dd4Sdownsj return ($group_login, $group_uniq) if $group_login eq $name; 51994a17dd4Sdownsj return ($group_login, $group_login); 52094a17dd4Sdownsj} 52194a17dd4Sdownsj 52294a17dd4Sdownsj# return login group 52394a17dd4Sdownsjsub new_users_grplogin_batch { 52494a17dd4Sdownsj local($name, $defaultgroup) = @_; 52594a17dd4Sdownsj local($group_login, $group); 52694a17dd4Sdownsj 52794a17dd4Sdownsj $group_login = $name; 52894a17dd4Sdownsj $group_login = $defaultgroup if $defaultgroup ne $group_uniq; 52994a17dd4Sdownsj 53094a17dd4Sdownsj if (defined $gid{$group_login}) { 53194a17dd4Sdownsj # convert numeric groupname (gid) to groupname 53294a17dd4Sdownsj $group_login = $gid{$group_login}; 53394a17dd4Sdownsj } 53494a17dd4Sdownsj 53594a17dd4Sdownsj # if (defined($groupname{$group_login})) { 53694a17dd4Sdownsj # &add_group($groupname{$group_login}, $name); 53794a17dd4Sdownsj # } 53894a17dd4Sdownsj 53994a17dd4Sdownsj return $group_login 54094a17dd4Sdownsj if defined($groupname{$group_login}) || $group_login eq $name; 54194a17dd4Sdownsj warn "Group ``$group_login'' does not exist\a\n"; 54294a17dd4Sdownsj return 0; 54394a17dd4Sdownsj} 54494a17dd4Sdownsj 54594a17dd4Sdownsj# return other groups (string) 54694a17dd4Sdownsjsub new_users_groups { 54794a17dd4Sdownsj local($name, $other_groups) = @_; 54894a17dd4Sdownsj local($string) = 54994a17dd4Sdownsj "Login group is ``$group_login''. Invite $name into other groups:"; 55094a17dd4Sdownsj local($e, $flag); 55194a17dd4Sdownsj local($new_groups,$groups); 55294a17dd4Sdownsj 55394a17dd4Sdownsj $other_groups = "no" unless $other_groups; 55494a17dd4Sdownsj 55594a17dd4Sdownsj while(1) { 55694a17dd4Sdownsj $groups = &confirm_list($string, 1, $other_groups, 55794a17dd4Sdownsj ("no", $other_groups, "guest")); 55894a17dd4Sdownsj # no other groups 55994a17dd4Sdownsj return "" if $groups eq "no"; 56094a17dd4Sdownsj 56194a17dd4Sdownsj ($flag, $new_groups) = &new_users_groups_valid($groups); 56294a17dd4Sdownsj last unless $flag; 56394a17dd4Sdownsj } 56494a17dd4Sdownsj $new_groups =~ s/\s*$//; 56594a17dd4Sdownsj return $new_groups; 56694a17dd4Sdownsj} 56794a17dd4Sdownsj 56894a17dd4Sdownsjsub new_users_groups_valid { 56994a17dd4Sdownsj local($groups) = @_; 57094a17dd4Sdownsj local($e, $new_groups); 57194a17dd4Sdownsj local($flag) = 0; 57294a17dd4Sdownsj 57394a17dd4Sdownsj foreach $e (split(/[,\s]+/, $groups)) { 57494a17dd4Sdownsj # convert numbers to groupname 57594a17dd4Sdownsj if ($e =~ /^[0-9]+$/ && $gid{$e}) { 57694a17dd4Sdownsj $e = $gid{$e}; 57794a17dd4Sdownsj } 57894a17dd4Sdownsj if (defined($groupname{$e})) { 57994a17dd4Sdownsj if ($e eq $group_login) { 58094a17dd4Sdownsj # do not add user to a group if this group 58194a17dd4Sdownsj # is also the login group. 58294a17dd4Sdownsj } elsif (&add_group($groupname{$e}, $name)) { 58394a17dd4Sdownsj $new_groups .= "$e "; 58494a17dd4Sdownsj } else { 58594a17dd4Sdownsj warn "$name is already member of group ``$e''\n"; 58694a17dd4Sdownsj } 58794a17dd4Sdownsj } else { 58894a17dd4Sdownsj warn "Group ``$e'' does not exist\a\n"; $flag++; 58994a17dd4Sdownsj } 59094a17dd4Sdownsj } 59194a17dd4Sdownsj return ($flag, $new_groups); 59294a17dd4Sdownsj} 59394a17dd4Sdownsj 594150e5e64Stedu# your last chance 59594a17dd4Sdownsjsub new_users_ok { 59694a17dd4Sdownsj 59794a17dd4Sdownsj print <<EOF; 59894a17dd4Sdownsj 59994a17dd4SdownsjName: $name 60094a17dd4SdownsjPassword: **** 60194a17dd4SdownsjFullname: $fullname 60294a17dd4SdownsjUid: $u_id 60394a17dd4SdownsjGid: $g_id ($group_login) 60494a17dd4SdownsjGroups: $group_login $new_groups 605fa4eb53aSmillertLogin Class: $log_cl 60694a17dd4SdownsjHOME: $home/$name 60794a17dd4SdownsjShell: $sh 60894a17dd4SdownsjEOF 60994a17dd4Sdownsj 61094a17dd4Sdownsj return &confirm_yn("OK?", "yes"); 61194a17dd4Sdownsj} 61294a17dd4Sdownsj 61394a17dd4Sdownsj# make password database 61494a17dd4Sdownsjsub new_users_pwdmkdb { 61594a17dd4Sdownsj local($last) = @_; 6164a9d0c9dSmillert local($user); 61794a17dd4Sdownsj 6184a9d0c9dSmillert $user = (split(/:/, $last))[0]; 6192cf95264Smillert system(@pwd_mkdb, "-u", $user, $etc_passwd); 62094a17dd4Sdownsj if ($?) { 62194a17dd4Sdownsj warn "$last\n"; 6222cf95264Smillert warn "``pwd_mkdb'' failed\n"; 62394a17dd4Sdownsj exit($? >> 8); 62494a17dd4Sdownsj } 62594a17dd4Sdownsj} 62694a17dd4Sdownsj 62794a17dd4Sdownsj# update group database 62894a17dd4Sdownsjsub new_users_group_update { 62922941f2aSsimon local($e, $n, $a, @a); 63094a17dd4Sdownsj 63194a17dd4Sdownsj # Add *new* group 632fa3ef9c3Smillert if (!defined($groupname{$group_login}) && !defined($gid{$g_id})) { 63394a17dd4Sdownsj push(@group_backup, "$group_login:*:$g_id:"); 63494a17dd4Sdownsj $groupname{$group_login} = $g_id; 63594a17dd4Sdownsj $gid{$g_id} = $group_login; 63694a17dd4Sdownsj # $groupmembers{$g_id} = $group_login; 63794a17dd4Sdownsj } 63894a17dd4Sdownsj 63994a17dd4Sdownsj if ($new_groups || defined($groupname{$group_login}) || 64094a17dd4Sdownsj defined($gid{$groupname{$group_login}}) && 64194a17dd4Sdownsj $gid{$groupname{$group_login}} ne "+") { 64294a17dd4Sdownsj # new user is member of some groups 64394a17dd4Sdownsj # new login group is already in name space 64494a17dd4Sdownsj rename($group, "$group.bak"); 64594a17dd4Sdownsj #warn "$group_login $groupname{$group_login} $groupmembers{$groupname{$group_login}}\n"; 64622941f2aSsimon foreach (@group_backup) { 64722941f2aSsimon ($n, $e) = (split(/:/, $_))[0,2]; 64822941f2aSsimon # special handling of YP entries 64922941f2aSsimon if (substr($n, 0, 1) eq "+") { 65022941f2aSsimon # remember and skip the empty group 65122941f2aSsimon if (length($n) == 1) { 65222941f2aSsimon $a = $_; 65322941f2aSsimon next; 65422941f2aSsimon } 65522941f2aSsimon # pass other groups 65622941f2aSsimon push(@a, $_); 65722941f2aSsimon } 65822941f2aSsimon # group membership might have changed 65922941f2aSsimon else { 66094a17dd4Sdownsj push(@a, "$gid{$e}:*:$e:$groupmembers{$e}"); 66194a17dd4Sdownsj } 66222941f2aSsimon } 66322941f2aSsimon # append empty YP group 66422941f2aSsimon if ($a) { 66522941f2aSsimon push(@a, $a); 66622941f2aSsimon } 66794a17dd4Sdownsj &append_file($group, @a); 66894a17dd4Sdownsj } else { 66994a17dd4Sdownsj &append_file($group, "$group_login:*:$g_id:"); 67094a17dd4Sdownsj } 67194a17dd4Sdownsj 67294a17dd4Sdownsj} 67394a17dd4Sdownsj 67494a17dd4Sdownsjsub new_users_passwd_update { 67594a17dd4Sdownsj # update passwd/group variables 67694a17dd4Sdownsj push(@passwd_backup, $new_entry); 67794a17dd4Sdownsj $username{$name} = $u_id; 67894a17dd4Sdownsj $uid{$u_id} = $name; 67994a17dd4Sdownsj $pwgid{$g_id} = $name; 68094a17dd4Sdownsj} 68194a17dd4Sdownsj 68294a17dd4Sdownsj# send message to new user 68394a17dd4Sdownsjsub new_users_sendmessage { 68494a17dd4Sdownsj return 1 if $send_message eq "no"; 68594a17dd4Sdownsj 6866b3add36Slum return 1 if !&confirm_yn("Send welcome message to ``$name''", "yes"); 68794a17dd4Sdownsj 68835d66568Sbitblt @message_buffer = (); 6896b3add36Slum message_read ($message_file); 6906b3add36Slum 6916b3add36Slum local($e); 69235d66568Sbitblt 69394a17dd4Sdownsj foreach $e (@message_buffer) { 69494a17dd4Sdownsj print eval "\"$e\""; 69594a17dd4Sdownsj } 69694a17dd4Sdownsj print "\n"; 69794a17dd4Sdownsj 69894a17dd4Sdownsj local(@message_buffer_append) = (); 6996b3add36Slum if (!&confirm_yn("Add anything to the message", "no")) { 70094a17dd4Sdownsj print "Use ``.'' or ^D alone on a line to finish your message.\n"; 70194a17dd4Sdownsj push(@message_buffer_append, "\n"); 70294a17dd4Sdownsj while($read = <STDIN>) { 70394a17dd4Sdownsj last if $read eq "\.\n"; 70494a17dd4Sdownsj push(@message_buffer_append, $read); 70594a17dd4Sdownsj } 70694a17dd4Sdownsj } 7076b3add36Slum local($cc) = 7086b3add36Slum &confirm_list("Copy message to another user?:", 7096b3add36Slum 1, "no", ("root", "second_mail_address", 7106b3add36Slum "no")); 7116b3add36Slum $cc = "" if $cc eq "no"; 71294a17dd4Sdownsj 7136b3add36Slum &sendmessage("$name $cc", (@message_buffer, @message_buffer_append)); 71494a17dd4Sdownsj} 71594a17dd4Sdownsj 71694a17dd4Sdownsjsub sendmessage { 71794a17dd4Sdownsj local($to, @message) = @_; 71894a17dd4Sdownsj local($e); 71994a17dd4Sdownsj 72094a17dd4Sdownsj if (!open(M, "| mail -s Welcome $to")) { 72194a17dd4Sdownsj warn "Cannot send mail to: $to!\n"; 72294a17dd4Sdownsj return 0; 72394a17dd4Sdownsj } else { 72494a17dd4Sdownsj foreach $e (@message) { 72594a17dd4Sdownsj print M eval "\"$e\""; 72694a17dd4Sdownsj } 72794a17dd4Sdownsj close M; 7286b3add36Slum print "Mail sent!\n" if $verbose; 72994a17dd4Sdownsj } 73094a17dd4Sdownsj} 73194a17dd4Sdownsj 73294a17dd4Sdownsj 73394a17dd4Sdownsjsub new_users_password { 73494a17dd4Sdownsj 73594a17dd4Sdownsj # empty password 73694a17dd4Sdownsj return "" if $defaultpasswd ne "yes"; 73794a17dd4Sdownsj 73894a17dd4Sdownsj local($password); 73994a17dd4Sdownsj 74094a17dd4Sdownsj while(1) { 7414a9d0c9dSmillert system("stty", "-echo"); 74294a17dd4Sdownsj $password = &confirm_list("Enter password", 1, "", ""); 7434a9d0c9dSmillert system("stty", "echo"); 74494a17dd4Sdownsj print "\n"; 74594a17dd4Sdownsj if ($password ne "") { 7464a9d0c9dSmillert system("stty", "-echo"); 74794a17dd4Sdownsj $newpass = &confirm_list("Enter password again", 1, "", ""); 7484a9d0c9dSmillert system("stty", "echo"); 74994a17dd4Sdownsj print "\n"; 75094a17dd4Sdownsj last if $password eq $newpass; 75194a17dd4Sdownsj print "They didn't match, please try again\n"; 75294a17dd4Sdownsj } 75396bc4355Stedu elsif (!&confirm_yn("Disable password logins for the user?", "no")) { 75494a17dd4Sdownsj last; 75594a17dd4Sdownsj } 75694a17dd4Sdownsj } 75794a17dd4Sdownsj 75894a17dd4Sdownsj return $password; 75994a17dd4Sdownsj} 76094a17dd4Sdownsj 76194a17dd4Sdownsj 76294a17dd4Sdownsjsub new_users { 76394a17dd4Sdownsj 76494a17dd4Sdownsj print "\n" if $verbose; 76594a17dd4Sdownsj print "Ok, let's go.\n" . 766995c2a9bSderaadt "Don't worry about mistakes. There will be a chance later to " . 76794a17dd4Sdownsj "correct any input.\n" if $verbose; 76894a17dd4Sdownsj 76994a17dd4Sdownsj # name: Username 77094a17dd4Sdownsj # fullname: Full name 77194a17dd4Sdownsj # sh: shell 77294a17dd4Sdownsj # u_id: user id 77394a17dd4Sdownsj # g_id: group id 77494a17dd4Sdownsj # group_login: groupname of g_id 77594a17dd4Sdownsj # new_groups: some other groups 776fa4eb53aSmillert # log_cl: login class 777fa4eb53aSmillert local($name, $group_login, $fullname, $sh, $u_id, $g_id, $new_groups, 778fa4eb53aSmillert $log_cl); 77994a17dd4Sdownsj local($groupmembers_bak, $cryptpwd); 78094a17dd4Sdownsj local($new_users_ok) = 1; 78194a17dd4Sdownsj 78294a17dd4Sdownsj 78394a17dd4Sdownsj $new_groups = "no" unless $groupname{$new_groups}; 78494a17dd4Sdownsj 78594a17dd4Sdownsj while(1) { 78694a17dd4Sdownsj $name = &new_users_name; 78794a17dd4Sdownsj $fullname = &new_users_fullname($name); 78894a17dd4Sdownsj $sh = &new_users_shell; 78994a17dd4Sdownsj ($u_id, $g_id) = &new_users_id($name); 79094a17dd4Sdownsj ($group_login, $defaultgroup) = 79194a17dd4Sdownsj &new_users_grplogin($name, $defaultgroup, $new_users_ok); 79294a17dd4Sdownsj # do not use uniq username and login group 79394a17dd4Sdownsj $g_id = $groupname{$group_login} if (defined($groupname{$group_login})); 79494a17dd4Sdownsj 79594a17dd4Sdownsj $new_groups = &new_users_groups($name, $new_groups); 796fa4eb53aSmillert $log_cl = &new_users_login_class; 79794a17dd4Sdownsj $password = &new_users_password; 79894a17dd4Sdownsj 79994a17dd4Sdownsj 80094a17dd4Sdownsj if (&new_users_ok) { 80194a17dd4Sdownsj $new_users_ok = 1; 80294a17dd4Sdownsj 80317cfdb33Sgene $cryptpwd = "*"; # Locked by default 80417cfdb33Sgene $cryptpwd = encrypt($password, &salt) if ($password ne ""); 805d7bb54ecSmillert $log_cl = "" if ($log_cl eq "default"); 80617cfdb33Sgene 80794a17dd4Sdownsj # obscure perl bug 80894a17dd4Sdownsj $new_entry = "$name\:" . "$cryptpwd" . 809fa4eb53aSmillert "\:$u_id\:$g_id\:$log_cl:0:0:$fullname:$home/$name:$sh"; 81094a17dd4Sdownsj &append_file($etc_passwd, "$new_entry"); 81194a17dd4Sdownsj &new_users_pwdmkdb("$new_entry"); 81294a17dd4Sdownsj &new_users_group_update; 81394a17dd4Sdownsj &new_users_passwd_update; print "Added user ``$name''\n"; 81494a17dd4Sdownsj &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname"); 81594a17dd4Sdownsj &home_create($name, $group_login); 81635d66568Sbitblt &new_users_sendmessage; 81794a17dd4Sdownsj } else { 81894a17dd4Sdownsj $new_users_ok = 0; 81994a17dd4Sdownsj } 82094a17dd4Sdownsj if (!&confirm_yn("Add another user?", "yes")) { 82194a17dd4Sdownsj print "Goodbye!\n" if $verbose; 82294a17dd4Sdownsj last; 82394a17dd4Sdownsj } 82494a17dd4Sdownsj print "\n" if !$verbose; 82594a17dd4Sdownsj } 82694a17dd4Sdownsj} 82794a17dd4Sdownsj 82894a17dd4Sdownsjsub batch { 82994a17dd4Sdownsj local($name, $groups, $fullname, $password) = @_; 83094a17dd4Sdownsj local($sh); 83194a17dd4Sdownsj 83294a17dd4Sdownsj $defaultshell = &shell_default_valid($defaultshell); 83394a17dd4Sdownsj return 0 unless $home = &home_partition_valid($home); 83494a17dd4Sdownsj return 0 if $dotdir ne &dotdir_default_valid($dotdir); 8356b3add36Slum $message_file = &choosetxt_yn_default($send_message, $message_file); 83694a17dd4Sdownsj $send_message = &message_default; 83794a17dd4Sdownsj 83894a17dd4Sdownsj return 0 if $name ne &new_users_name_valid($name); 83994a17dd4Sdownsj $sh = $shell{$defaultshell}; 84094a17dd4Sdownsj ($u_id, $g_id) = &next_id($name); 84194a17dd4Sdownsj $group_login = &new_users_grplogin_batch($name, $defaultgroup); 84294a17dd4Sdownsj return 0 unless $group_login; 84394a17dd4Sdownsj $g_id = $groupname{$group_login} if (defined($groupname{$group_login})); 84494a17dd4Sdownsj ($flag, $new_groups) = &new_users_groups_valid($groups); 84594a17dd4Sdownsj return 0 if $flag; 846fa4eb53aSmillert $log_cl = ($defaultclass eq "default") ? "" : $defaultclass; 84794a17dd4Sdownsj 84817cfdb33Sgene $cryptpwd = "*"; # Locked by default 84984079b4aSderaadt if ($password ne "" && $password ne "*") { 85084079b4aSderaadt if($unencrypted) { $cryptpwd = encrypt($password, &salt) } 85184079b4aSderaadt else { $cryptpwd = $password } 85284079b4aSderaadt } 85394a17dd4Sdownsj # obscure perl bug 85494a17dd4Sdownsj $new_entry = "$name\:" . "$cryptpwd" . 855fa4eb53aSmillert "\:$u_id\:$g_id\:$log_cl:0:0:$fullname:$home/$name:$sh"; 85694a17dd4Sdownsj &append_file($etc_passwd, "$new_entry"); 85794a17dd4Sdownsj &new_users_pwdmkdb("$new_entry"); 85894a17dd4Sdownsj &new_users_group_update; 85994a17dd4Sdownsj &new_users_passwd_update; print "Added user ``$name''\n"; 86094a17dd4Sdownsj &sendmessage($name, @message_buffer) if $send_message ne "no"; 86194a17dd4Sdownsj &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname"); 86294a17dd4Sdownsj &home_create($name, $group_login); 86394a17dd4Sdownsj} 86494a17dd4Sdownsj 86594a17dd4Sdownsj# ask for password usage 86694a17dd4Sdownsjsub password_default { 86794a17dd4Sdownsj local($p) = $defaultpasswd; 86894a17dd4Sdownsj if ($verbose) { 86917cfdb33Sgene $p = &confirm_yn("Prompt for passwords by default", $defaultpasswd); 87094a17dd4Sdownsj $changes++ unless $p; 87194a17dd4Sdownsj } 87294a17dd4Sdownsj return "yes" if (($defaultpasswd eq "yes" && $p) || 87394a17dd4Sdownsj ($defaultpasswd eq "no" && !$p)); 87494a17dd4Sdownsj return "no"; # otherwise 87594a17dd4Sdownsj} 87694a17dd4Sdownsj 87717cfdb33Sgene# get default encryption method 87817cfdb33Sgenesub encryption_default { 87917cfdb33Sgene local($m) = ""; 88017cfdb33Sgene if ($verbose) { 88117cfdb33Sgene while (&encryption_check($m) == 0) { 8822384d3f1Smillert $m = &confirm_list("Default encryption method for passwords:", 1, 88317cfdb33Sgene $encryption_methods[0], @encryption_methods); 88417cfdb33Sgene } 88517cfdb33Sgene } 88617cfdb33Sgene return($m); 88717cfdb33Sgene} 88817cfdb33Sgene 889fa4eb53aSmillertsub class_default { 890fa4eb53aSmillert local($c) = $defaultclass; 891fa4eb53aSmillert 892fa4eb53aSmillert if ($verbose) { 893fa4eb53aSmillert $c = &confirm_list("Default login class:", 0, 894fa4eb53aSmillert $defaultclass, @login_classes); 895fa4eb53aSmillert $changes++ if $c ne $defaultclass; 896fa4eb53aSmillert } 897fa4eb53aSmillert return($c); 898fa4eb53aSmillert} 899fa4eb53aSmillert 90017cfdb33Sgene# Confirm that we have a valid encryption method 90117cfdb33Sgenesub encryption_check { 90217cfdb33Sgene local($m) = $_[0]; 90317cfdb33Sgene 90417cfdb33Sgene foreach $i (@encryption_methods) { 90517cfdb33Sgene if ($m eq $i) { return 1; } 90617cfdb33Sgene } 90717cfdb33Sgene 90817cfdb33Sgene if ($m =~ /^blowfish,(\d+)$/) { return 1; } 90917cfdb33Sgene return 0; 91017cfdb33Sgene} 91117cfdb33Sgene 91294a17dd4Sdownsj# misc 91394a17dd4Sdownsjsub check_root { 9146b3add36Slum die "You are not root!\n" if $<; 91594a17dd4Sdownsj} 91694a17dd4Sdownsj 91794a17dd4Sdownsjsub usage { 91894a17dd4Sdownsj warn <<USAGE; 91994a17dd4Sdownsjusage: adduser 92094a17dd4Sdownsj [-batch username [group[,group]...] [fullname] [password]] 92194a17dd4Sdownsj [-check_only] 92294a17dd4Sdownsj [-config_create] 92394a17dd4Sdownsj [-dotdir dotdir] 92417cfdb33Sgene [-e|-encryption method] 92594a17dd4Sdownsj [-group login_group] 926fa4eb53aSmillert [-class login_class] 92794a17dd4Sdownsj [-h|-help] 92894a17dd4Sdownsj [-home home] 92994a17dd4Sdownsj [-message message_file] 93094a17dd4Sdownsj [-noconfig] 93194a17dd4Sdownsj [-shell shell] 93217cfdb33Sgene [-s|-silent|-q|-quiet] 9334cdcf8a6Sweingart [-uid_start uid_start] 9344cdcf8a6Sweingart [-uid_end uid_end] 93584079b4aSderaadt [-unencrypted] 93694a17dd4Sdownsj [-v|-verbose] 93794a17dd4Sdownsj 93894a17dd4Sdownsjhome=$home shell=$defaultshell dotdir=$dotdir login_group=$defaultgroup 9396b3add36Slumlogin_class=$defaultclass uid_start=$uid_start uid_end=$uid_end 9406b3add36Slumsend_message=$send_message message_file=$message_file 94194a17dd4SdownsjUSAGE 94294a17dd4Sdownsj exit 1; 94394a17dd4Sdownsj} 94494a17dd4Sdownsj 94594a17dd4Sdownsj# uniq(1) 94694a17dd4Sdownsjsub uniq { 94794a17dd4Sdownsj local(@list) = @_; 9484424054eSmillert local($e, $last = "", @array); 94994a17dd4Sdownsj 95094a17dd4Sdownsj foreach $e (sort @list) { 95194a17dd4Sdownsj push(@array, $e) unless $e eq $last; 95294a17dd4Sdownsj $last = $e; 95394a17dd4Sdownsj } 95494a17dd4Sdownsj return @array; 95594a17dd4Sdownsj} 95694a17dd4Sdownsj 95717cfdb33Sgene# Generate an appropriate argument to encrypt() 95817cfdb33Sgene# That may be a DES salt or a blowfish rotation count 95994a17dd4Sdownsjsub salt { 96094a17dd4Sdownsj local($salt); # initialization 9619f3bae82Stedu if ($encryptionmethod eq "auto") { 96217cfdb33Sgene $salt = ""; 96317cfdb33Sgene } elsif ($encryptionmethod =~ /^blowfish/ ) { 96417cfdb33Sgene ($encryptionmethod, $salt) = split(/\,/, $encryptionmethod); 965d7bb54ecSmillert $salt = 7 unless $salt; # default rounds if unspecified 96617cfdb33Sgene } else { 96717cfdb33Sgene warn "$encryptionmethod encryption method invalid\n" if ($verbose > 0); 96817cfdb33Sgene warn "Falling back to blowfish,7...\n" if ($verbose > 0); 96917cfdb33Sgene $encryptionmethod = "blowfish"; 97017cfdb33Sgene $salt = 7; 97117cfdb33Sgene } 97217cfdb33Sgene 97394a17dd4Sdownsj warn "Salt is: $salt\n" if $verbose > 1; 97494a17dd4Sdownsj 97594a17dd4Sdownsj return $salt; 97694a17dd4Sdownsj} 97794a17dd4Sdownsj 97817cfdb33Sgene# Encrypt a password using the selected method 97917cfdb33Sgenesub encrypt { 98017cfdb33Sgene local($pass, $salt) = ($_[0], $_[1]); 981d7bb54ecSmillert local(@args, $crypt); 98217cfdb33Sgene 9839f3bae82Stedu if ($encryptionmethod eq "blowfish") { 984d7bb54ecSmillert @args = ("-b", $salt); 985d7bb54ecSmillert } elsif ($encryptionmethod eq "auto") { 986d7bb54ecSmillert @args = ("-c", $log_cl); 98717cfdb33Sgene } 98817cfdb33Sgene 989d7bb54ecSmillert open2(\*ENCRD, \*ENCWR, "/usr/bin/encrypt", @args); 9902bbaf33aSalex print ENCWR "$pass\n"; 9912bbaf33aSalex close ENCWR; 9922bbaf33aSalex $crypt = <ENCRD>; 9932bbaf33aSalex close ENCRD; 9942bbaf33aSalex chomp $crypt; 9952bbaf33aSalex die "encrypt failed" if (wait == -1 || $? != 0); 99617cfdb33Sgene return($crypt); 99717cfdb33Sgene} 99894a17dd4Sdownsj 99994a17dd4Sdownsj# hints 100094a17dd4Sdownsjsub hints { 100194a17dd4Sdownsj if ($verbose) { 10024516d2a0Saaron print "Use option ``-silent'' if you don't want to see " . 10034516d2a0Saaron "all warnings and questions.\n\n"; 100494a17dd4Sdownsj } 100594a17dd4Sdownsj} 100694a17dd4Sdownsj 100794a17dd4Sdownsj# 100894a17dd4Sdownsjsub parse_arguments { 100994a17dd4Sdownsj local(@argv) = @_; 101094a17dd4Sdownsj 101194a17dd4Sdownsj while ($_ = $argv[0], /^-/) { 101294a17dd4Sdownsj shift @argv; 101394a17dd4Sdownsj last if /^--$/; 101494a17dd4Sdownsj if (/^--?(v|verbose)$/) { $verbose = 1 } 101517cfdb33Sgene elsif (/^--?(s|silent|q|quiet)$/) { $verbose = 0 } 101694a17dd4Sdownsj elsif (/^--?(debug)$/) { $verbose = 2 } 101794a17dd4Sdownsj elsif (/^--?(h|help|\?)$/) { &usage } 101894a17dd4Sdownsj elsif (/^--?(home)$/) { $home = $argv[0]; shift @argv } 101994a17dd4Sdownsj elsif (/^--?(shell)$/) { $defaultshell = $argv[0]; shift @argv } 1020fa4eb53aSmillert elsif (/^--?(class)$/) { $defaultclass = $argv[0]; shift @argv } 102194a17dd4Sdownsj elsif (/^--?(dotdir)$/) { $dotdir = $argv[0]; shift @argv } 10224cdcf8a6Sweingart elsif (/^--?(uid_start)$/) { $uid_start = $argv[0]; shift @argv } 10234cdcf8a6Sweingart elsif (/^--?(uid_end)$/) { $uid_end = $argv[0]; shift @argv } 102494a17dd4Sdownsj elsif (/^--?(group)$/) { $defaultgroup = $argv[0]; shift @argv } 102594a17dd4Sdownsj elsif (/^--?(check_only)$/) { $check_only = 1 } 10266b3add36Slum elsif (/^--?(message)$/) { 10276b3add36Slum $send_message = $argv[0]; shift @argv; 10286b3add36Slum $message_file = &choosetxt_yn_default($send_message, $message_file); 10296b3add36Slum } 103084079b4aSderaadt elsif (/^--?(unencrypted)$/) { $unencrypted = 1 } 103194a17dd4Sdownsj elsif (/^--?(batch)$/) { 103294a17dd4Sdownsj @batch = splice(@argv, 0, 4); $verbose = 0; 103394a17dd4Sdownsj die "batch: too few arguments\n" if $#batch < 0; 103494a17dd4Sdownsj } 103594a17dd4Sdownsj # see &config_read 10364424054eSmillert elsif (/^--?(config_create)$/) { &hints; &create_conf; exit(0); } 103794a17dd4Sdownsj elsif (/^--?(noconfig)$/) { $config_read = 0; } 103817cfdb33Sgene elsif (/^--?(e|encryption)$/) { 103917cfdb33Sgene $encryptionmethod = $argv[0]; 104017cfdb33Sgene shift @argv; 104117cfdb33Sgene } 104294a17dd4Sdownsj else { &usage } 104394a17dd4Sdownsj } 104494a17dd4Sdownsj #&usage if $#argv < 0; 104594a17dd4Sdownsj} 104694a17dd4Sdownsj 104794a17dd4Sdownsjsub basename { 104894a17dd4Sdownsj local($name) = @_; 104994a17dd4Sdownsj $name =~ s|/+$||; 105094a17dd4Sdownsj $name =~ s|.*/+||; 105194a17dd4Sdownsj return $name; 105294a17dd4Sdownsj} 105394a17dd4Sdownsj 105494a17dd4Sdownsjsub dirname { 105594a17dd4Sdownsj local($name) = @_; 105694a17dd4Sdownsj $name = &stripdir($name); 105794a17dd4Sdownsj $name =~ s|/+[^/]+$||; 105894a17dd4Sdownsj $name = "/" unless $name; # dirname of / is / 105994a17dd4Sdownsj return $name; 106094a17dd4Sdownsj} 106194a17dd4Sdownsj 106294a17dd4Sdownsj# return 1 if $file is a readable file or link 106394a17dd4Sdownsjsub filetest { 10644424054eSmillert local($file, $verbose) = @_; 106594a17dd4Sdownsj 106694a17dd4Sdownsj if (-e $file) { 106794a17dd4Sdownsj if (-f $file || -l $file) { 106894a17dd4Sdownsj return 1 if -r _; 106994a17dd4Sdownsj warn "$file unreadable\n" if $verbose; 107094a17dd4Sdownsj } else { 107194a17dd4Sdownsj warn "$file is not a plain file or link\n" if $verbose; 107294a17dd4Sdownsj } 107394a17dd4Sdownsj } 107494a17dd4Sdownsj return 0; 107594a17dd4Sdownsj} 107694a17dd4Sdownsj 107717cfdb33Sgene# create or recreate configuration file prompting for values 107894a17dd4Sdownsjsub create_conf { 107994a17dd4Sdownsj $create_conf = 1; 108017cfdb33Sgene 108117cfdb33Sgene &shells_read; # Pull in /etc/shells info 108217cfdb33Sgene &shells_add; # maybe add some new shells 108317cfdb33Sgene $defaultshell = &shell_default; # enter default shell 1084fa4eb53aSmillert &login_conf_read; # read /etc/login.conf 1085fa4eb53aSmillert $defaultclass = &class_default; # default login.conf class 108617cfdb33Sgene $home = &home_partition($home); # find HOME partition 108717cfdb33Sgene $dotdir = &dotdir_default; # check $dotdir 108817cfdb33Sgene $send_message = &message_default; # send message to new user 108917cfdb33Sgene $defaultpasswd = &password_default; # maybe use password 109017cfdb33Sgene $defaultencryption = &encryption_default; # Encryption method 109117cfdb33Sgene 109294a17dd4Sdownsj &config_write(1); 109394a17dd4Sdownsj} 109494a17dd4Sdownsj 109594a17dd4Sdownsj# log for new user in /var/log/adduser 109694a17dd4Sdownsjsub adduser_log { 109794a17dd4Sdownsj local($string) = @_; 109894a17dd4Sdownsj local($e); 109994a17dd4Sdownsj 110094a17dd4Sdownsj return 1 if $logfile eq "no"; 110194a17dd4Sdownsj 110294a17dd4Sdownsj local($sec, $min, $hour, $mday, $mon, $year) = localtime; 1103ca08161eSmillert $year += 1900; 110494a17dd4Sdownsj $mon++; 110594a17dd4Sdownsj 1106ca08161eSmillert foreach $e ('sec', 'min', 'hour', 'mday', 'mon') { 110794a17dd4Sdownsj # '7' -> '07' 110894a17dd4Sdownsj eval "\$$e = 0 . \$$e" if (eval "\$$e" < 10); 110994a17dd4Sdownsj } 111094a17dd4Sdownsj 111194a17dd4Sdownsj &append_file($logfile, "$year/$mon/$mday $hour:$min:$sec $string"); 111294a17dd4Sdownsj} 111394a17dd4Sdownsj 111494a17dd4Sdownsj# create HOME directory, copy dotfiles from $dotdir to $HOME 111594a17dd4Sdownsjsub home_create { 111694a17dd4Sdownsj local($name, $group) = @_; 111794a17dd4Sdownsj local($homedir) = "$home/$name"; 111894a17dd4Sdownsj 111994a17dd4Sdownsj if (-e "$homedir") { 112049b56239Shugh warn "HOME Directory ``$homedir'' already exists\a\n"; 112194a17dd4Sdownsj return 0; 112294a17dd4Sdownsj } 112394a17dd4Sdownsj 112494a17dd4Sdownsj if ($dotdir eq 'no') { 112594a17dd4Sdownsj if (!mkdir("$homedir", 0755)) { 112694a17dd4Sdownsj warn "mkdir $homedir: $!\n"; return 0; 112794a17dd4Sdownsj } 112894a17dd4Sdownsj system 'chown', "$name:$group", $homedir; 112994a17dd4Sdownsj return !$?; 113094a17dd4Sdownsj } 113194a17dd4Sdownsj 113294a17dd4Sdownsj # copy files from $dotdir to $homedir 113394a17dd4Sdownsj # rename 'dot.foo' files to '.foo' 113494a17dd4Sdownsj print "Copy files from $dotdir to $homedir\n" if $verbose; 1135ab051decSmillert system("cp", "-R", $dotdir, $homedir); 11364a9d0c9dSmillert system("chmod", "-R", "u+wrX,go-w", $homedir); 11374a9d0c9dSmillert system("chown", "-R", "$name:$group", $homedir); 113894a17dd4Sdownsj 113994a17dd4Sdownsj # security 114094a17dd4Sdownsj opendir(D, $homedir); 114194a17dd4Sdownsj foreach $file (readdir(D)) { 114294a17dd4Sdownsj if ($file =~ /^dot\./ && -f "$homedir/$file") { 114394a17dd4Sdownsj $file =~ s/^dot\././; 114494a17dd4Sdownsj rename("$homedir/dot$file", "$homedir/$file"); 114594a17dd4Sdownsj } 114694a17dd4Sdownsj chmod(0600, "$homedir/$file") 114794a17dd4Sdownsj if ($file =~ /^\.(rhosts|Xauthority|kermrc|netrc)$/); 114894a17dd4Sdownsj chmod(0700, "$homedir/$file") 114994a17dd4Sdownsj if ($file =~ /^(Mail|prv|\.(iscreen|term))$/); 115094a17dd4Sdownsj } 115194a17dd4Sdownsj closedir D; 115294a17dd4Sdownsj return 1; 115394a17dd4Sdownsj} 115494a17dd4Sdownsj 115594a17dd4Sdownsj# makes a directory hierarchy 115694a17dd4Sdownsjsub mkdir_home { 115794a17dd4Sdownsj local($dir) = @_; 115894a17dd4Sdownsj $dir = &stripdir($dir); 115994a17dd4Sdownsj local($user_partition) = "/usr"; 116094a17dd4Sdownsj local($dirname) = &dirname($dir); 116194a17dd4Sdownsj 116294a17dd4Sdownsj 116394a17dd4Sdownsj -e $dirname || &mkdirhier($dirname); 116494a17dd4Sdownsj 116594a17dd4Sdownsj if (((stat($dirname))[0]) == ((stat("/"))[0])){ 116694a17dd4Sdownsj # home partition is on root partition 116794a17dd4Sdownsj # create home partition on $user_partition and make 116894a17dd4Sdownsj # a symlink from $dir to $user_partition/`basename $dir` 116994a17dd4Sdownsj # For instance: /home -> /usr/home 117094a17dd4Sdownsj 117194a17dd4Sdownsj local($basename) = &basename($dir); 117294a17dd4Sdownsj local($d) = "$user_partition/$basename"; 117394a17dd4Sdownsj 117494a17dd4Sdownsj 117594a17dd4Sdownsj if (-d $d) { 117649b56239Shugh warn "Oops, $d already exists\n" if $verbose; 117794a17dd4Sdownsj } else { 117894a17dd4Sdownsj print "Create $d\n" if $verbose; 117994a17dd4Sdownsj if (!mkdir("$d", 0755)) { 118094a17dd4Sdownsj warn "$d: $!\a\n"; return 0; 118194a17dd4Sdownsj } 118294a17dd4Sdownsj } 118394a17dd4Sdownsj 118494a17dd4Sdownsj unlink($dir); # symlink to nonexist file 118594a17dd4Sdownsj print "Create symlink: $dir -> $d\n" if $verbose; 118694a17dd4Sdownsj if (!symlink("$d", $dir)) { 118794a17dd4Sdownsj warn "Symlink $d: $!\a\n"; return 0; 118894a17dd4Sdownsj } 118994a17dd4Sdownsj } else { 119094a17dd4Sdownsj print "Create $dir\n" if $verbose; 119194a17dd4Sdownsj if (!mkdir("$dir", 0755)) { 119294a17dd4Sdownsj warn "Directory ``$dir'': $!\a\n"; return 0; 119394a17dd4Sdownsj } 119494a17dd4Sdownsj } 119594a17dd4Sdownsj return 1; 119694a17dd4Sdownsj} 119794a17dd4Sdownsj 119894a17dd4Sdownsjsub mkdirhier { 119994a17dd4Sdownsj local($dir) = @_; 120094a17dd4Sdownsj local($d,$p); 120194a17dd4Sdownsj 120294a17dd4Sdownsj $dir = &stripdir($dir); 120394a17dd4Sdownsj 120494a17dd4Sdownsj foreach $d (split('/', $dir)) { 120594a17dd4Sdownsj $dir = "$p/$d"; 120694a17dd4Sdownsj $dir =~ s|^//|/|; 120794a17dd4Sdownsj if (! -e "$dir") { 120894a17dd4Sdownsj print "Create $dir\n" if $verbose; 120994a17dd4Sdownsj if (!mkdir("$dir", 0755)) { 121094a17dd4Sdownsj warn "$dir: $!\n"; return 0; 121194a17dd4Sdownsj } 121294a17dd4Sdownsj } 121394a17dd4Sdownsj $p .= "/$d"; 121494a17dd4Sdownsj } 121594a17dd4Sdownsj return 1; 121694a17dd4Sdownsj} 121794a17dd4Sdownsj 121894a17dd4Sdownsj# stript unused '/' 1219150e5e64Stedu# e.g.: //usr///home// -> /usr/home 122094a17dd4Sdownsjsub stripdir { 122194a17dd4Sdownsj local($dir) = @_; 122294a17dd4Sdownsj 122394a17dd4Sdownsj $dir =~ s|/+|/|g; # delete double '/' 122494a17dd4Sdownsj $dir =~ s|/$||; # delete '/' at end 122594a17dd4Sdownsj return $dir if $dir ne ""; 122694a17dd4Sdownsj return '/'; 122794a17dd4Sdownsj} 122894a17dd4Sdownsj 1229150e5e64Stedu# Read one of the elements from @list. $confirm is the default. 1230150e5e64Stedu# If !$allow then accept only elements from @list. 123194a17dd4Sdownsjsub confirm_list { 123294a17dd4Sdownsj local($message, $allow, $confirm, @list) = @_; 123394a17dd4Sdownsj local($read, $c, $print); 123494a17dd4Sdownsj 123594a17dd4Sdownsj $print = "$message" if $message; 123694a17dd4Sdownsj $print .= " " unless $message =~ /\n$/ || $#list == 0; 123794a17dd4Sdownsj 123894a17dd4Sdownsj $print .= join($", &uniq(@list)); #" 123994a17dd4Sdownsj $print .= " " unless $message =~ /\n$/ && $#list == 0; 124094a17dd4Sdownsj print "$print"; 124194a17dd4Sdownsj print "\n" if (length($print) + length($confirm)) > 60; 124294a17dd4Sdownsj print "[$confirm]: "; 124394a17dd4Sdownsj 124494a17dd4Sdownsj chop($read = <STDIN>); 124594a17dd4Sdownsj $read =~ s/^\s*//; 124694a17dd4Sdownsj $read =~ s/\s*$//; 124794a17dd4Sdownsj return $confirm if $read eq ""; 124894a17dd4Sdownsj return "$read" if $allow; 124994a17dd4Sdownsj 125094a17dd4Sdownsj foreach $c (@list) { 125194a17dd4Sdownsj return $read if $c eq $read; 125294a17dd4Sdownsj } 125394a17dd4Sdownsj warn "$read: is not allowed!\a\n"; 125494a17dd4Sdownsj return &confirm_list($message, $allow, $confirm, @list); 125594a17dd4Sdownsj} 125694a17dd4Sdownsj 12576b3add36Slum# YES, NO, DEFAULT or userstring 12586b3add36Slum# 1. return "" if "no" or no string is provided by the user. 12596b3add36Slum# 2. return the $default parameter if "yes" or "default" provided. 12606b3add36Slum# otherwise return user provided string. 12616b3add36Slumsub confirm_yn_default { 12626b3add36Slum local($message, $confirm, $default) = @_; 12636b3add36Slum 12646b3add36Slum print "$message [$confirm]: "; 12656b3add36Slum chop($read = <STDIN>); 12666b3add36Slum $read =~ s/^\s*//; 12676b3add36Slum $read =~ s/\s*$//; 12686b3add36Slum return "" unless $read; 12696b3add36Slum 12706b3add36Slum return choosetxt_yn_default($read, $default); 12716b3add36Slum} 12726b3add36Slum 12736b3add36Slumsub choosetxt_yn_default { 12746b3add36Slum local($read, $default) = @_; 12756b3add36Slum 12766b3add36Slum if ($read =~ "$no") { 12776b3add36Slum return ""; 12786b3add36Slum } 12796b3add36Slum if ($read eq "default") { 12806b3add36Slum return $default; 12816b3add36Slum } 12826b3add36Slum if ($read =~ "$yes") { 12836b3add36Slum if ($verbose == 1) { 12846b3add36Slum return $read; 12856b3add36Slum } 12866b3add36Slum return $default; 12876b3add36Slum } 12886b3add36Slum return $read; 12896b3add36Slum} 12906b3add36Slum 129194a17dd4Sdownsj# YES or NO question 129294a17dd4Sdownsj# return 1 if &confirm("message", "yes") and answer is yes 1293c8564365Stedu# or if &confirm("message", "no") and answer is no 1294150e5e64Stedu# otherwise return 0 129594a17dd4Sdownsjsub confirm_yn { 129694a17dd4Sdownsj local($message, $confirm) = @_; 129794a17dd4Sdownsj local($read, $c); 129894a17dd4Sdownsj 129994a17dd4Sdownsj if ($confirm && ($confirm =~ "$yes" || $confirm == 1)) { 130094a17dd4Sdownsj $confirm = "y"; 130194a17dd4Sdownsj } else { 130294a17dd4Sdownsj $confirm = "n"; 130394a17dd4Sdownsj } 130494a17dd4Sdownsj print "$message (y/n) [$confirm]: "; 130594a17dd4Sdownsj chop($read = <STDIN>); 130694a17dd4Sdownsj $read =~ s/^\s*//; 130794a17dd4Sdownsj $read =~ s/\s*$//; 130894a17dd4Sdownsj return 1 unless $read; 130994a17dd4Sdownsj 131094a17dd4Sdownsj if (($confirm eq "y" && $read =~ "$yes") || 131194a17dd4Sdownsj ($confirm eq "n" && $read =~ "$no")) { 131294a17dd4Sdownsj return 1; 131394a17dd4Sdownsj } 131494a17dd4Sdownsj 131594a17dd4Sdownsj if ($read !~ "$yes" && $read !~ "$no") { 131694a17dd4Sdownsj warn "Wrong value. Enter again!\a\n"; 131794a17dd4Sdownsj return &confirm_yn($message, $confirm); 131894a17dd4Sdownsj } 131994a17dd4Sdownsj return 0; 132094a17dd4Sdownsj} 132194a17dd4Sdownsj 132294a17dd4Sdownsj# test if $dotdir exist 132394a17dd4Sdownsj# return "no" if $dotdir not exist or dotfiles should not copied 132494a17dd4Sdownsjsub dotdir_default { 132594a17dd4Sdownsj local($dir) = $dotdir; 132694a17dd4Sdownsj 132794a17dd4Sdownsj return &dotdir_default_valid($dir) unless $verbose; 132894a17dd4Sdownsj while($verbose) { 132994a17dd4Sdownsj $dir = &confirm_list("Copy dotfiles from:", 1, 133094a17dd4Sdownsj $dir, ("no", $dotdir_bak, $dir)); 133194a17dd4Sdownsj last if $dir eq &dotdir_default_valid($dir); 133294a17dd4Sdownsj } 133394a17dd4Sdownsj warn "Do not copy dotfiles.\n" if $verbose && $dir eq "no"; 133494a17dd4Sdownsj 133594a17dd4Sdownsj $changes++ if $dir ne $dotdir; 133694a17dd4Sdownsj return $dir; 133794a17dd4Sdownsj} 133894a17dd4Sdownsj 133994a17dd4Sdownsjsub dotdir_default_valid { 134094a17dd4Sdownsj local($dir) = @_; 134194a17dd4Sdownsj 134294a17dd4Sdownsj return $dir if (-e $dir && -r _ && (-d _ || -l $dir) && $dir =~ "^/"); 134394a17dd4Sdownsj return $dir if $dir eq "no"; 134494a17dd4Sdownsj warn "Dotdir ``$dir'' is not a directory\a\n"; 134594a17dd4Sdownsj return "no"; 134694a17dd4Sdownsj} 134794a17dd4Sdownsj 134894a17dd4Sdownsj# ask for messages to new users 134994a17dd4Sdownsjsub message_default { 13506b3add36Slum local($tmp_message_file) = $message_file; 135194a17dd4Sdownsj 135294a17dd4Sdownsj while($verbose) { 13536b3add36Slum $send_message = "no"; 135494a17dd4Sdownsj 13556b3add36Slum $message_file = &confirm_yn_default( 13566b3add36Slum "Send welcome message?: /path/file default no", 13576b3add36Slum "no", $tmp_message_file); 13586b3add36Slum if ($message_file eq "") { 13596b3add36Slum $message_file = $tmp_message_file; 13606b3add36Slum last; 13616b3add36Slum } 13626b3add36Slum if ($message_file =~ $yes) { 13636b3add36Slum $message_file = &confirm_yn_default( 13646b3add36Slum "Really? Type the filepath, 'default' or 'no'", 13656b3add36Slum "no", $tmp_message_file); 13666b3add36Slum if ($message_file eq "") { 13676b3add36Slum $message_file = $tmp_message_file; 13686b3add36Slum last; 13696b3add36Slum } 13706b3add36Slum } 13716b3add36Slum 13726b3add36Slum # try and create the message file 13736b3add36Slum if (&filetest($message_file, 0)) { 13746b3add36Slum if (&confirm_yn("File ``$message_file'' exists. Overwrite?:", 13756b3add36Slum "no")) { 13766b3add36Slum print "Retry: choose a different location\n"; 13776b3add36Slum next; 13786b3add36Slum } 13796b3add36Slum if (&message_create($message_file)) { 13806b3add36Slum print "Message file ``$message_file'' overwritten\n" 13814ad95c62Slum if $verbose; 13826b3add36Slum } 13836b3add36Slum } else { 13846b3add36Slum if (&message_create($message_file)) { 13854ad95c62Slum print "Message file ``$message_file'' created\n" if $verbose; 13866b3add36Slum } 13876b3add36Slum } 13886b3add36Slum 13896b3add36Slum if (&filetest($message_file, 0)) { 13906b3add36Slum $send_message = "yes"; 13916b3add36Slum last; 13926b3add36Slum } 13936b3add36Slum last if !&confirm_yn("Unable to create ``$message_file'', try again?", 139494a17dd4Sdownsj "yes"); 139594a17dd4Sdownsj } 139694a17dd4Sdownsj 13976b3add36Slum if ($send_message eq "no" || !&filetest($message_file, 0)) { 13986b3add36Slum warn "Do not send message(s)\n" if $verbose; 13996b3add36Slum $send_message = "no"; 140094a17dd4Sdownsj } else { 14016b3add36Slum &message_read($message_file); 140294a17dd4Sdownsj } 140394a17dd4Sdownsj 14046b3add36Slum $changes++ if $tmp_message_file ne $message_file && $verbose; 14056b3add36Slum return $send_message; 140694a17dd4Sdownsj} 140794a17dd4Sdownsj 140894a17dd4Sdownsj# create message file 140994a17dd4Sdownsjsub message_create { 141094a17dd4Sdownsj local($file) = @_; 141194a17dd4Sdownsj 141294a17dd4Sdownsj rename($file, "$file.bak"); 141394a17dd4Sdownsj if (!open(M, "> $file")) { 141494a17dd4Sdownsj warn "Messagefile ``$file'': $!\n"; return 0; 141594a17dd4Sdownsj } 141694a17dd4Sdownsj print M <<EOF; 141794a17dd4Sdownsj# 141894a17dd4Sdownsj# Message file for adduser(8) 141994a17dd4Sdownsj# comment: ``#'' 142094a17dd4Sdownsj# default variables: \$name, \$fullname, \$password 142194a17dd4Sdownsj# other variables: see /etc/adduser.conf after 142294a17dd4Sdownsj# line ``$do_not_delete'' 142394a17dd4Sdownsj# 142494a17dd4Sdownsj 142594a17dd4Sdownsj\$fullname, 142694a17dd4Sdownsj 1427d7ab7c04Sdownsjyour account ``\$name'' was created. 1428d7ab7c04SdownsjHave fun! 142994a17dd4Sdownsj 143094a17dd4SdownsjSee also chpass(1), finger(1), passwd(1) 143194a17dd4SdownsjEOF 143294a17dd4Sdownsj close M; 143394a17dd4Sdownsj return 1; 143494a17dd4Sdownsj} 143594a17dd4Sdownsj 143694a17dd4Sdownsj# read message file into buffer 143794a17dd4Sdownsjsub message_read { 143894a17dd4Sdownsj local($file) = @_; 143935d66568Sbitblt @message_buffer = (); 144094a17dd4Sdownsj 144194a17dd4Sdownsj if (!open(R, "$file")) { 144294a17dd4Sdownsj warn "File ``$file'':$!\n"; return 0; 144394a17dd4Sdownsj } 144494a17dd4Sdownsj while(<R>) { 144594a17dd4Sdownsj push(@message_buffer, $_) unless /^\s*#/; 144694a17dd4Sdownsj } 144794a17dd4Sdownsj close R; 144894a17dd4Sdownsj} 144994a17dd4Sdownsj 145094a17dd4Sdownsj# write @list to $file with file-locking 145194a17dd4Sdownsjsub append_file { 145294a17dd4Sdownsj local($file,@list) = @_; 145394a17dd4Sdownsj local($e); 145494a17dd4Sdownsj 145594a17dd4Sdownsj open(F, ">> $file") || die "$file: $!\n"; 145694a17dd4Sdownsj print "Lock $file.\n" if $verbose > 1; 145727250d79Smillert while(!flock(F, LOCK_EX | LOCK_NB)) { 145894a17dd4Sdownsj warn "Cannot lock file: $file\a\n"; 14594424054eSmillert die "Sorry, gave up\n" 146094a17dd4Sdownsj unless &confirm_yn("Try again?", "yes"); 146194a17dd4Sdownsj } 146294a17dd4Sdownsj print F join("\n", @list) . "\n"; 146394a17dd4Sdownsj print "Unlock $file.\n" if $verbose > 1; 146427250d79Smillert flock(F, LOCK_UN); 14654424054eSmillert close F; 146694a17dd4Sdownsj} 146794a17dd4Sdownsj 146894a17dd4Sdownsj# return free uid+gid 146994a17dd4Sdownsj# uid == gid if possible 147094a17dd4Sdownsjsub next_id { 147194a17dd4Sdownsj local($group) = @_; 147294a17dd4Sdownsj 147394a17dd4Sdownsj $uid_start = 1000 if ($uid_start <= 0 || $uid_start >= $uid_end); 147494a17dd4Sdownsj # looking for next free uid 147594a17dd4Sdownsj while($uid{$uid_start}) { 147694a17dd4Sdownsj $uid_start++; 147794a17dd4Sdownsj $uid_start = 1000 if $uid_start >= $uid_end; 147894a17dd4Sdownsj print "$uid_start\n" if $verbose > 1; 147994a17dd4Sdownsj } 148094a17dd4Sdownsj 148194a17dd4Sdownsj local($gid_start) = $uid_start; 148294a17dd4Sdownsj # group for user (username==groupname) already exist 148394a17dd4Sdownsj if ($groupname{$group}) { 148494a17dd4Sdownsj $gid_start = $groupname{$group}; 148594a17dd4Sdownsj } 148694a17dd4Sdownsj # gid is in use, looking for another gid. 14874424054eSmillert # Note: uid and gid are not equal 148894a17dd4Sdownsj elsif ($gid{$uid_start}) { 148994a17dd4Sdownsj while($gid{$gid_start} || $uid{$gid_start}) { 149094a17dd4Sdownsj $gid_start--; 149194a17dd4Sdownsj $gid_start = $uid_end if $gid_start < 100; 149294a17dd4Sdownsj } 149394a17dd4Sdownsj } 149494a17dd4Sdownsj return ($uid_start, $gid_start); 149594a17dd4Sdownsj} 149694a17dd4Sdownsj 149717cfdb33Sgene# read config file - typically /etc/adduser.conf 149894a17dd4Sdownsjsub config_read { 14994424054eSmillert local($opt) = join " ", @_; 150094a17dd4Sdownsj local($user_flag) = 0; 150194a17dd4Sdownsj 150294a17dd4Sdownsj # don't read config file 150394a17dd4Sdownsj return 1 if $opt =~ /-(noconfig|config_create)/ || !$config_read; 150494a17dd4Sdownsj 150517cfdb33Sgene if (!-f $config) { 150617cfdb33Sgene warn("Couldn't find $config: creating a new adduser configuration file\n"); 150717cfdb33Sgene &create_conf; 150817cfdb33Sgene } 150917cfdb33Sgene 151094a17dd4Sdownsj if (!open(C, "$config")) { 151194a17dd4Sdownsj warn "$config: $!\n"; return 0; 151294a17dd4Sdownsj } 151394a17dd4Sdownsj 151494a17dd4Sdownsj while(<C>) { 151594a17dd4Sdownsj # user defined variables 151694a17dd4Sdownsj /^$do_not_delete/ && $user_flag++; 151794a17dd4Sdownsj # found @array or $variable 151894a17dd4Sdownsj if (s/^(\w+\s*=\s*\()/\@$1/ || s/^(\w+\s*=)/\$$1/) { 151994a17dd4Sdownsj eval $_; 152094a17dd4Sdownsj #warn "$_"; 152194a17dd4Sdownsj } 1522f2b78e83Sdownsj next if /^$/; 152394a17dd4Sdownsj # lines with '^##' are not saved 152494a17dd4Sdownsj push(@user_variable_list, $_) 152594a17dd4Sdownsj if $user_flag && !/^##/ && (s/^[\$\@]// || /^[#\s]/); 152694a17dd4Sdownsj } 152794a17dd4Sdownsj #warn "X @user_variable_list X\n"; 152894a17dd4Sdownsj close C; 152994a17dd4Sdownsj} 153094a17dd4Sdownsj 153194a17dd4Sdownsj 153294a17dd4Sdownsj# write config file 153394a17dd4Sdownsjsub config_write { 153494a17dd4Sdownsj local($silent) = @_; 153594a17dd4Sdownsj 153694a17dd4Sdownsj # nothing to do 153794a17dd4Sdownsj return 1 unless ($changes || ! -e $config || !$config_read || $silent); 153894a17dd4Sdownsj 153994a17dd4Sdownsj if (!$silent) { 154094a17dd4Sdownsj if (-e $config) { 154194a17dd4Sdownsj return 1 if &confirm_yn("\nWrite your changes to $config?", "no"); 154294a17dd4Sdownsj } else { 154394a17dd4Sdownsj return 1 unless 154494a17dd4Sdownsj &confirm_yn("\nWrite your configuration to $config?", "yes"); 154594a17dd4Sdownsj } 154694a17dd4Sdownsj } 154794a17dd4Sdownsj 154894a17dd4Sdownsj rename($config, "$config.bak"); 154994a17dd4Sdownsj open(C, "> $config") || die "$config: $!\n"; 155094a17dd4Sdownsj 155194a17dd4Sdownsj # prepare some variables 155294a17dd4Sdownsj $send_message = "no" unless $send_message; 155394a17dd4Sdownsj $defaultpasswd = "no" unless $defaultpasswd; 155494a17dd4Sdownsj local($shpref) = "'" . join("', '", @shellpref) . "'"; 155594a17dd4Sdownsj local($shpath) = "'" . join("', '", @path) . "'"; 155694a17dd4Sdownsj local($user_var) = join('', @user_variable_list); 1557fa4eb53aSmillert local($def_lc) = "'" . join("', '", @login_classes) . "'"; 155894a17dd4Sdownsj 155994a17dd4Sdownsj print C <<EOF; 156094a17dd4Sdownsj# 156194a17dd4Sdownsj# $config - automatic generated by adduser(8) 156294a17dd4Sdownsj# 156327250d79Smillert# Note: adduser reads *and* writes this file. 15643adbd14eSespie# You may change values, but don't add new things before the 156594a17dd4Sdownsj# line ``$do_not_delete'' 1566ad025105Slum# Also, unquoted strings may cause warnings 156794a17dd4Sdownsj# 156894a17dd4Sdownsj 156994a17dd4Sdownsj# verbose = [0-2] 157094a17dd4Sdownsjverbose = $verbose 157194a17dd4Sdownsj 157217cfdb33Sgene# Get new password for new users 157394a17dd4Sdownsj# defaultpasswd = yes | no 1574ad025105Slumdefaultpasswd = "$defaultpasswd" 157594a17dd4Sdownsj 157617cfdb33Sgene# Default encryption method for user passwords 1577d7bb54ecSmillert# Methods are all those listed in login.conf(5) 157817cfdb33Sgeneencryptionmethod = "$defaultencryption" 157917cfdb33Sgene 15800ef5f496Sjakob# copy dotfiles from this dir ("/etc/skel" or "no") 158194a17dd4Sdownsjdotdir = "$dotdir" 158294a17dd4Sdownsj 15836b3add36Slum# send message to user? ("yes" or "no") 158494a17dd4Sdownsjsend_message = "$send_message" 158594a17dd4Sdownsj 15866b3add36Slum# send this file to new user ("/etc/adduser.message") 1587572a3c94Slummessage_file = "$message_file" 15886b3add36Slum 158994a17dd4Sdownsj# config file for adduser ("/etc/adduser.conf") 159094a17dd4Sdownsjconfig = "$config" 159194a17dd4Sdownsj 159294a17dd4Sdownsj# logfile ("/var/log/adduser" or "no") 159394a17dd4Sdownsjlogfile = "$logfile" 159494a17dd4Sdownsj 159594a17dd4Sdownsj# default HOME directory ("/home") 159694a17dd4Sdownsjhome = "$home" 159794a17dd4Sdownsj 159894a17dd4Sdownsj# List of directories where shells located 159994a17dd4Sdownsj# path = ('/bin', '/usr/bin', '/usr/local/bin') 160094a17dd4Sdownsjpath = ($shpath) 160194a17dd4Sdownsj 160294a17dd4Sdownsj# common shell list, first element has higher priority 160394a17dd4Sdownsj# shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh') 160494a17dd4Sdownsjshellpref = ($shpref) 160594a17dd4Sdownsj 160694a17dd4Sdownsj# defaultshell if not empty ("bash") 160794a17dd4Sdownsjdefaultshell = "$defaultshell" 160894a17dd4Sdownsj 1609f2b78e83Sdownsj# defaultgroup ('USER' for same as username or any other valid group) 1610ad025105Slumdefaultgroup = "$defaultgroup" 161194a17dd4Sdownsj 1612f2b78e83Sdownsj# new users get this uid 1613f2b78e83Sdownsjuid_start = $uid_start 16144cdcf8a6Sweingartuid_end = $uid_end 161594a17dd4Sdownsj 1616fa4eb53aSmillert# default login.conf(5) login class 1617ad025105Slumdefaultclass = "$defaultclass" 1618fa4eb53aSmillert 1619fa4eb53aSmillert# login classes available from login.conf(5) 1620fa4eb53aSmillert# login_classes = ('default', 'daemon', 'staff') 1621fa4eb53aSmillertlogin_classes = ($def_lc) 1622fa4eb53aSmillert 162394a17dd4Sdownsj$do_not_delete 162494a17dd4Sdownsj## your own variables, see /etc/adduser.message 162594a17dd4SdownsjEOF 1626f2b78e83Sdownsj print C "$user_var\n" if ($user_var ne ''); 1627f2b78e83Sdownsj print C "\n## end\n"; 162894a17dd4Sdownsj close C; 162994a17dd4Sdownsj} 163094a17dd4Sdownsj 16314cdcf8a6Sweingart# check for sane variables 16324cdcf8a6Sweingartsub variable_check { 16334cdcf8a6Sweingart # Check uid_start & uid_end 16344cdcf8a6Sweingart warn "WARNING: uid_start < 1000!\n" if($uid_start < 1000); 16354cdcf8a6Sweingart die "ERROR: uid_start >= uid_end!\n" if($uid_start >= $uid_end); 163684079b4aSderaadt # unencrypted really only usable in batch mode 163784079b4aSderaadt warn "WARNING: unencrypted only effective in batch mode\n" 163884079b4aSderaadt if($#batch < 0 && $unencrypted); 16394cdcf8a6Sweingart} 16404cdcf8a6Sweingart 164127250d79Smillertsub cleanup { 164227250d79Smillert local($sig) = @_; 164327250d79Smillert 164427250d79Smillert print STDERR "Caught signal SIG$sig -- cleaning up.\n"; 164504a4b599Sericj system("stty", "echo"); 164627250d79Smillert exit(0); 164727250d79Smillert} 164827250d79Smillert 164927250d79SmillertEND { 1650518d82b2Smillert if (-e $etc_ptmp && defined(fileno(PTMP))) { 16514424054eSmillert close PTMP; 165227250d79Smillert unlink($etc_ptmp) || warn "Error: unable to remove $etc_ptmp: $!\nPlease verify that $etc_ptmp no longer exists!\n"; 165327250d79Smillert } 165427250d79Smillert} 1655