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