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