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