home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / sbin / adduser < prev    next >
Encoding:
Text File  |  2006-07-10  |  30.5 KB  |  973 lines

  1. #!/usr/bin/perl
  2.  
  3. # adduser: a utility to add users to the system
  4. # addgroup: a utility to add groups to the system
  5. my $version = "3.92";
  6.  
  7. # Copyright (C) 1997, 1998, 1999 Guy Maor <maor@debian.org>
  8. # Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu>
  9. #                     Ian A. Murdock <imurdock@gnu.ai.mit.edu>
  10. # Bugfixes and other improvements Roland Bauerschmidt <rb@debian.org>
  11. # General scheme of the program adapted by the original debian 'adduser'
  12. #  program by Ian A. Murdock <imurdock@gnu.ai.mit.edu>.
  13. #
  14. #    This program is free software; you can redistribute it and/or modify
  15. #    it under the terms of the GNU General Public License as published by
  16. #    the Free Software Foundation; either version 2 of the License, or
  17. #    (at your option) any later version.
  18. #
  19. #    This program is distributed in the hope that it will be useful,
  20. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22. #    GNU General Public License for more details.
  23. #
  24. #    You should have received a copy of the GNU General Public License
  25. #    along with this program; if not, write to the Free Software
  26. #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  27. #
  28. #
  29. ####################
  30. # See the usage subroutine for explanation about how the program can be called
  31. ####################
  32.  
  33. use warnings;
  34. use strict;
  35. use Debian::AdduserCommon;
  36. use Getopt::Long;
  37.  
  38. BEGIN {
  39.     eval 'use Locale::gettext';
  40.     if ($@) {
  41.     *gettext = sub { shift };
  42.     *textdomain = sub { "" };
  43.     *LC_MESSAGES = sub { 5 };
  44.     }
  45.     eval {
  46.     require POSIX;
  47.     import POSIX qw(setlocale);
  48.     };
  49.     if ($@) {
  50.     *setlocale = sub { return 1 };
  51.     }
  52.     eval {
  53.     require I18N::Langinfo;
  54.     import I18N::Langinfo qw(langinfo YESEXPR NOEXPR);
  55.     };
  56.     if ($@) {
  57.     *langinfo = sub { "^[yY]" };
  58.     *YESEXPR = sub { 1 };
  59.     }
  60. }
  61.  
  62. setlocale(LC_MESSAGES, "");
  63. textdomain("adduser");
  64. my $yesexpr = langinfo(YESEXPR());
  65.  
  66. my %config;            # configuration hash
  67.  
  68. my @defaults = ("/etc/adduser.conf");
  69. my $nogroup_id = getgrnam("nogroup") || 65534;
  70. $0 =~ s+.*/++; 
  71.  
  72. our $verbose = 1;        # should we be verbose?
  73. my $allow_badname = 0;        # should we allow bad names?
  74. my $ask_passwd = 1;        # ask for a passwd? 
  75. my $disabled_login = 0;        # leave the new account disabled?
  76.  
  77. our $configfile = undef;
  78. our $found_group_opt = undef;
  79. our $found_sys_opt = undef;
  80. our $ingroup_name = undef;
  81. our $new_firstuid = undef;
  82. our $new_gecos = undef;
  83. our $new_gid = undef;
  84. our $new_lastuid = undef;
  85. our $new_uid = undef;
  86. our $no_create_home = undef;
  87. our $special_home = undef;
  88. our $special_shell = undef;
  89. our $add_extra_groups = 0;
  90.  
  91. # Global variables we need later
  92. my $existing_user = undef;
  93. my $existing_group = undef;
  94. my $new_name = undef;
  95. my $make_group_also = 0;
  96. my $home_dir = undef;
  97. my $undohome = undef;
  98. my $undouser = undef;
  99. my $undogroup = undef;
  100. my $shell = undef;
  101. my $first_uid = undef;
  102. my $last_uid = undef;
  103. my $dir_mode = undef;
  104. my $perm = undef;
  105.  
  106. our @names;
  107.  
  108. # Parse options, sanity checks
  109. unless ( GetOptions ("quiet" => sub { $verbose = 0 },
  110.             "force-badname" => \$allow_badname,
  111.         "help|h" => sub { &usage(); exit 0 },
  112.         "version" => sub { &version(); exit 0 },
  113.         "system" => \$found_sys_opt,
  114.         "group" => \$found_group_opt,
  115.         "ingroup=s" => \$ingroup_name,
  116.         "home=s" => \$special_home,
  117.         "gecos=s" => \$new_gecos,
  118.         "shell=s" => \$special_shell,
  119.         "disabled-password" => sub { $ask_passwd = 0 },
  120.         "disabled-login" => sub { $disabled_login = 1; $ask_passwd = 0 },
  121.         "uid=i" => \$new_uid,
  122.         "firstuid=i" => \$new_firstuid,
  123.         "lastuid=i" => \$new_lastuid,
  124.         "gid=i" => \$new_gid,
  125.         "conf=s" => \$configfile,
  126.         "no-create-home" => \$no_create_home,
  127.             "add_extra_groups" => \$add_extra_groups,
  128.         "debug" => sub { $verbose = 2 } ) ) {
  129.     &usage;
  130.     exit 1;
  131. }
  132.  
  133. # everyone can issue "--help" and "--version", but only root can go on
  134. dief (gtx("Only root may add a user or group to the system.\n")) if ($> != 0);
  135.  
  136. if( defined($configfile) ) { @defaults = ($configfile); }
  137.  
  138. # detect the right mode
  139. my $action = $0 eq "addgroup" ? "addgroup" : "adduser";
  140. if (defined($found_sys_opt)) {
  141.   $action = "addsysuser" if ($action eq "adduser");
  142.   $action = "addsysgroup" if ($action eq "addgroup");
  143. }
  144.  
  145.  
  146.  
  147. ############################
  148. # checks related to @names #
  149. ############################
  150.  
  151.  
  152. while (defined(my $arg = shift(@ARGV))) {
  153.   push (@names, $arg);
  154. }
  155.  
  156. if ( (! defined $names[0]) || length($names[0]) == 0 || @names > 2) {
  157.     dief (gtx("Only one or two names allowed.\n"));
  158. }
  159.         
  160.  
  161. if (@names == 2) {    # must be addusertogroup
  162.     dief (gtx("Specify only one name in this mode.\n"))
  163.     if ($action eq "addsysuser" || $found_group_opt);
  164.     $action = "addusertogroup";
  165.     $existing_user = shift (@names);
  166.     $existing_group = shift (@names);
  167. }
  168. else {
  169.     $new_name = shift (@names);
  170. }
  171.  
  172. ###################################
  173. # check for consistent parameters #
  174. ###################################
  175.  
  176. if ($action ne "addgroup" &&
  177.     defined($found_group_opt) +defined($ingroup_name) +defined($new_gid) > 1 ) {
  178.     dief (gtx("The --group, --ingroup, and --gid options are mutually exclusive.\n"));
  179. }
  180.  
  181.  
  182. if ((defined($special_home)) && ($special_home !~ m+^/+ )) {
  183.   dief (gtx("The home dir must be an absolute path.\n"));
  184. }
  185.        
  186. if (defined($special_home) && $verbose) {
  187.     print gtx("Warning: The home dir you specified already exists.\n")
  188.       if (!defined($no_create_home) && -d $special_home);
  189.     print gtx("Warning: The home dir you specified does not exist.\n")
  190.       if (defined($no_create_home) && ! -d $special_home);
  191. }
  192.  
  193.  
  194. if ($found_group_opt) {
  195.     if ($action eq "addsysuser") {
  196.     $make_group_also = 1;
  197.     }
  198.     elsif ($found_sys_opt) {
  199.     $action = "addsysgroup";
  200.     }
  201.     else {
  202.     $action = "addgroup";
  203.     }
  204. }
  205.  
  206.  
  207. $ENV{"VERBOSE"} = $verbose;
  208. $ENV{"DEBUG"}   = $verbose;
  209.  
  210.  
  211. # preseed configuration data and then read the config file
  212. preseed_config(\@defaults,\%config);
  213.  
  214. &checkname($new_name) if defined $new_name;
  215. $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'handler';
  216.  
  217. #####
  218. # OK, we've processed the arguments.  $action equals one of the following,
  219. # and the appropriate variables have been set:
  220. #
  221. # $action = "adduser"
  222. #    $new_name                - the name of the new user.
  223. #    $ingroup_name | $new_gid - the group to add the user to
  224. #    $special_home, $new_uid, $new_gecos - optional overrides
  225. # $action = "addgroup"
  226. #    $new_name                - the name of the new group
  227. #    $new_gid                 - optional override
  228. # $action = "addsysgroup"
  229. #    $new_name                - the name of the new group
  230. #    $new_gid                 - optional override
  231. # $action = "addsysuser"
  232. #    $new_name                - the name of the new user
  233. #    $make_group_also | $ingroup_name | $new_gid | 0  - which group
  234. #    $special_home, $new_uid, $new_gecos - optional overrides
  235. # $action = "addusertogroup"
  236. #    $existing_user           - the user to be added
  237. #    $existing_group          - the group to add her to
  238. #####
  239.  
  240.  
  241. #################
  242. ## addsysgroup ##
  243. #################
  244. if ($action eq "addsysgroup") {
  245.     # Check if requested group already exists and we can exit safely
  246.     if (existing_group_ok($new_name, $new_gid) == 1) {
  247.     printf (gtx("The group `%s' already exists as a system group. Exiting...\n"), $new_name) if $verbose;
  248.     exit 0;
  249.     }
  250.     if (existing_group_ok($new_name, $new_gid) == 2) {
  251.     printf (gtx("The group `%s' already exists, but has a different gid, aborting...\n"), $new_name) if $verbose;
  252.     exit 1;
  253.     }
  254.  
  255.     dief (gtx("The group `%s' already exists and is not a system group.\n"),$new_name)
  256.     if (defined getgrnam($new_name));
  257.     dief (gtx("The GID `%s' is already in use.\n"),$new_gid)
  258.     if (defined($new_gid) && defined(getgrgid($new_gid)));
  259.     if (!defined($new_gid)) {
  260.         $new_gid = &first_avail_gid($config{"first_system_gid"},
  261.                    $config{"last_system_gid"});
  262.  
  263.         if ($new_gid == -1) {
  264.         print STDERR "$0: ";
  265.         printf STDERR gtx("No GID is available in the range %d-%d (FIRST_SYS_GID - LAST_SYS_GID).\n"),$config{"first_system_gid"},$config{"last_system_gid"};
  266.             dief (gtx("The group `%s' was not created.\n"),$new_name);
  267.         }
  268.     }
  269.  
  270.     printf (gtx("Adding group `%s' (%s)...\n"),$new_name,$new_gid) if $verbose;
  271.     &invalidate_nscd("group");
  272.     my $groupadd = &which('groupadd');
  273.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  274.     &invalidate_nscd("group");
  275.     print (gtx("Done.\n")) if $verbose;
  276.     exit 0;
  277. }
  278.  
  279.  
  280. ##############
  281. ## addgroup ##
  282. ##############
  283. if ($action eq "addgroup") {
  284.     dief (gtx("The group `%s' already exists.\n"),$new_name)
  285.     if (defined getgrnam($new_name));
  286.     dief (gtx("The GID `%s' is already in use.\n"),$new_gid)
  287.     if (defined($new_gid) && defined(getgrgid($new_gid)));
  288.     if (!defined($new_gid)) {
  289.         $new_gid = &first_avail_gid($config{"first_gid"},
  290.                    $config{"last_gid"});
  291.  
  292.         if ($new_gid == -1) {
  293.         print STDERR "$0: ";
  294.         printf STDERR gtx("No GID is available in the range %d-%d (FIRST_GID - LAST_GID).\n"),$config{"first_gid"},$config{"last_gid"};
  295.             dief (gtx("The group `%s' was not created.\n"),$new_name);
  296.         }
  297.     }
  298.  
  299.     printf (gtx("Adding group `%s' (%s)...\n"),$new_name,$new_gid) if $verbose;
  300.     &invalidate_nscd("group");
  301.     my $groupadd = &which('groupadd');
  302.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  303.     &invalidate_nscd("group");
  304.     print (gtx("Done.\n")) if $verbose;
  305.     exit 0;
  306. }
  307.  
  308.  
  309. ####################
  310. ## addusertogroup ##
  311. ####################
  312. if ($action eq "addusertogroup") {
  313.     dief (gtx("The user `%s' does not exist.\n"),$existing_user)
  314.     if (!defined getpwnam($existing_user));
  315.     dief (gtx("The group `%s' does not exist.\n"),$existing_group)
  316.     if (!defined getgrnam($existing_group));
  317.     if (&user_is_member($existing_user, $existing_group)) {
  318.     printf gtx("The user `%s' is already a member of `%s'.\n"),
  319.                 $existing_user,$existing_group if $verbose;
  320.     exit 0;            # not really an error
  321.     }
  322.  
  323.     printf gtx("Adding user `%s' to group `%s'...\n"),$existing_user,$existing_group
  324.     if $verbose;
  325.     &invalidate_nscd();
  326.     # FIXME - the next line has a race condition.
  327.     #&systemcall('usermod', '-G',
  328.         #join(",", get_users_groups($existing_user), $existing_group), 
  329.         #$existing_user);
  330.     my $gpasswd = &which('gpasswd');
  331.     &systemcall($gpasswd, '-M',
  332.         join(',', get_group_members($existing_group), $existing_user),
  333.         $existing_group);
  334.     #&systemcall('gpasswd', '-a',$existing_user,$existing_group);
  335.     &invalidate_nscd();
  336.     print (gtx("Done.\n")) if $verbose;
  337.     exit 0;
  338. }
  339.  
  340.  
  341. ################
  342. ## addsysuser ##
  343. ################
  344. if ($action eq "addsysuser") {
  345.     if (existing_user_ok($new_name, $new_uid) == 1) {
  346.     printf (gtx("The user `%s' already exists as a system user. Exiting...\n"), $new_name) if $verbose;
  347.     exit 0;
  348.     }
  349.     if (existing_user_ok($new_name, $new_uid) == 2) {
  350.     printf (gtx("The user `%s' already exists with a different uid. Aborting\n"), $new_name) if $verbose;
  351.     exit 1;
  352.     }
  353.  
  354.     if (!$ingroup_name && !defined($new_gid) && !$make_group_also) {
  355.       $new_gid = $nogroup_id;
  356.     }
  357.     check_user_group(1);
  358.  
  359.     if (!defined($new_uid) && $make_group_also) {
  360.     $new_uid = &first_avail_uid($config{"first_system_uid"},
  361.                    $config{"last_system_uid"});
  362.         if ($new_uid == -1) {
  363.         print STDERR "$0: ";
  364.         printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"};
  365.             dief (gtx("The user `%s' was not created.\n"),$new_name);
  366.         }
  367.         $new_gid = &first_avail_gid($config{"first_system_gid"},
  368.                                 $config{"last_system_gid"});
  369.     $ingroup_name = $new_name;
  370.     }
  371.     elsif (!defined($new_uid) && !$make_group_also) {
  372.     $new_uid = &first_avail_uid($config{"first_system_uid"},
  373.                    $config{"last_system_uid"});
  374.         if ($new_uid == -1) {
  375.         print STDERR "$0: ";
  376.         printf STDERR gtx("No UID is available in the range %d-%d (FIRST_SYS_UID - LAST_SYS_UID).\n"),$config{"first_system_uid"},$config{"last_system_uid"};
  377.         dief (gtx("The user `%s' was not created.\n"),$new_name);
  378.         }
  379.         if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  380.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  381.     else { dief (gtx("Internal error")); }
  382.     }
  383.     else {
  384.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  385.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  386.     elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
  387.     else { dief (gtx("Internal error")); }
  388.     }
  389.     printf (gtx("Adding system user `%s' with uid %s...\n"),$new_name,$new_uid) if $verbose;
  390.  
  391.     &invalidate_nscd();
  392.     # if we reach this point, and the group does already exist, we can use it.
  393.     if ($make_group_also && !getgrnam($new_name)) {
  394.     printf (gtx("Adding new group `%s' (%s).\n"),$new_name,$new_gid) if $verbose;
  395.     $undogroup = $new_name;
  396.        my $groupadd = &which('groupadd');
  397.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  398.     &invalidate_nscd("group");
  399.     }
  400.  
  401.     printf gtx("Adding new user `%s' (%s) with group `%s'.\n"),$new_name,$new_uid,$ingroup_name
  402.     if $verbose;
  403.     $home_dir = $special_home || &homedir($new_name, $ingroup_name);
  404.     $shell = $special_shell || '/bin/false';
  405.     $undouser = $new_name;
  406.     my $useradd = &which('useradd');
  407.     &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
  408.         $shell, '-u', $new_uid, $new_name);
  409.     my $chage = &which('chage');
  410.     print "$chage -M 99999 $new_name\n" if ($verbose > 1);
  411.     # do _not_ use systemcall() here, since systemcall() dies on
  412.     # non-zero exit code and we need to do special handling here!
  413.     if (system($chage, '-M', '99999', $new_name)) {
  414.     if( ($?>>8) ne 15 ) {
  415.         &cleanup("$0: `$chage -M 99999 $new_name' returned error code " . ($?>>8) . ".  Aborting.\n")
  416.           if ($?>>8);
  417.         &cleanup("$0: `$chage -M 99999 $new_name' exited from signal " . ($?&255) . ".  Aborting.\n");
  418.     } else {
  419.         printf (gtx("%s failed with return code 15, shadow not enabled, password aging cannot be set. Continuing.\n"), $chage);
  420.     }
  421.     }
  422.     &invalidate_nscd();
  423.  
  424.     if(defined($new_gecos)) {
  425.     &ch_gecos($new_gecos);
  426.     }
  427.  
  428.     create_homedir (0);
  429.  
  430.     exit 0;
  431. }
  432.  
  433.  
  434. #############
  435. ## adduser ##
  436. #############
  437. if ($action eq "adduser") {
  438.     if (!$ingroup_name && !defined($new_gid)) {
  439.     if ($config{"usergroups"} =~  /yes/i) { $make_group_also = 1; }
  440.     else { $new_gid = $config{"users_gid"}; }
  441.     }
  442.     check_user_group(0);
  443.     $first_uid = $new_firstuid || $config{"first_uid"};
  444.     $last_uid = $new_lastuid || $config{"last_uid"};
  445.     printf (gtx("Adding user `%s'...\n"),$new_name) if $verbose;
  446.  
  447.     if (!defined($new_uid) && $make_group_also) {
  448.     $new_uid = &first_avail_uid($first_uid,
  449.                    $last_uid);
  450.                 
  451.         if ($new_uid == -1) {
  452.         print STDERR "$0: ";
  453.             printf STDERR gtx("No UID/GID pair is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$first_uid,$last_uid;
  454.         dief (gtx("The user `%s' was not created.\n"),$new_name);
  455.         }
  456.     $new_gid = &first_avail_gid($config{"first_gid"}, 
  457.                                 $config{"last_gid"});
  458.     $ingroup_name = $new_name;
  459.     }
  460.     elsif (!defined($new_uid) && !$make_group_also) {
  461.     $new_uid = &first_avail_uid($first_uid,
  462.                    $last_uid);
  463.     if ($new_uid == -1) {
  464.         print STDERR "$0: ";
  465.         printf STDERR gtx("No UID is available in the range %d-%d (FIRST_UID - LAST_UID).\n"),$config{"first_uid"},$config{"last_uid"};
  466.             dief (gtx("The user `%s' was not created.\n"),$new_name);
  467.         }
  468.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  469.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  470.     else { dief (gtx("Internal error")); }
  471.     }
  472.     else {
  473.     if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
  474.     elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
  475.     elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
  476.     else { dien (gtx("Internal error")); }
  477.     }
  478.  
  479.     &invalidate_nscd();
  480.     if ($make_group_also) {
  481.     printf (gtx("Adding new group `%s' (%s).\n"),$new_name,$new_gid) if $verbose;
  482.     $undogroup = $new_name;
  483.        my $groupadd = &which('groupadd');
  484.     &systemcall($groupadd, '-g', $new_gid, $new_name);
  485.     &invalidate_nscd();
  486.     }
  487.  
  488.     printf gtx("Adding new user `%s' (%s) with group `%s'.\n"),$new_name,$new_uid,$ingroup_name
  489.     if $verbose;
  490.     $home_dir = $special_home || &homedir($new_name, $ingroup_name);
  491.     $shell = $special_shell || $config{"dshell"};
  492.     $undouser = $new_name;
  493.     my $useradd = &which('useradd');
  494.     &systemcall($useradd, '-d', $home_dir, '-g', $ingroup_name, '-s',
  495.         $shell, '-u', $new_uid, $new_name);
  496.     &invalidate_nscd();
  497.  
  498.     create_homedir (1); # copy skeleton data
  499.  
  500.     # useradd without -p has left the account disabled (password string is '!')
  501.     if ($ask_passwd) {
  502.     for (;;) {
  503.           my $passwd = &which('passwd');
  504.       # do _not_ use systemcall() here, since systemcall() dies on
  505.       # non-zero exit code and we need to do special handling here!
  506.           system($passwd, $new_name);
  507.       my $ok = $?>>8;
  508.       if ($ok != 0) {
  509.         my $noexpr = langinfo(NOEXPR());
  510.             my $answer;
  511.             # hm, error, should we break now?
  512.         print (gtx("Permission denied\n")) if ($ok == 1);
  513.         print (gtx("invalid combination of options\n")) if ($ok == 2);
  514.         print (gtx("unexpected failure, nothing done\n")) if ($ok == 3);
  515.         print (gtx("unexpected failure, passwd file missing\n")) if ($ok == 3);
  516.         print (gtx("passwd file busy, try again\n")) if ($ok == 4);
  517.         print (gtx("invalid argument to option\n")) if ($ok == 5);
  518.         
  519.         # Translators: [y/N] has to be replaced by values defined in your
  520.         # locale.  You can see by running "locale noexpr" which regular
  521.         # expression will be checked to find positive answer.
  522.         print (gtx("Try again(Y/n)?"));
  523.         chop ($answer=<STDIN>);
  524.         last if ($answer =~ m/$noexpr/o);
  525.       }
  526.       else {
  527.         last; ## passwd ok
  528.       }
  529.     }
  530.     } else {
  531.     if(!$disabled_login) {
  532.            my $usermod = &which('usermod');
  533.         &systemcall($usermod, '-p', '*', $new_name);
  534.     }
  535.     }
  536.  
  537.     if (defined($new_gecos)) {
  538.     &ch_gecos($new_gecos);
  539.     }
  540.     else {
  541.     my $yesexpr = langinfo(YESEXPR());
  542.     for (;;) {
  543.            my $chfn = &which('chfn');
  544.         &systemcall($chfn, $new_name);
  545.         # Translators: [y/N] has to be replaced by values defined in your
  546.         # locale.  You can see by running "locale yesexpr" which regular
  547.         # expression will be checked to find positive answer.
  548.         print (gtx("Is the information correct? [y/N] "));
  549.         chop (my $answer=<STDIN>);
  550.         last if ($answer =~ m/$yesexpr/o);
  551.     }
  552.     }
  553.  
  554.     if ( ( $add_extra_groups || $config{"add_extra_groups"} ) && defined($config{"extra_groups"}) ) {
  555.         printf (gtx("Adding new user `%s' to extra groups\n"), $new_name);
  556.         foreach my $newgrp ( split ' ', $config{"extra_groups"} ) {
  557.             if (!defined getgrnam($newgrp)) {
  558.                 warnf (gtx("The group `%s' does not exist.\n"),$newgrp);
  559.                 next;
  560.             }
  561.             if (&user_is_member($new_name, $newgrp)) {
  562.                 printf gtx("The user `%s' is already a member of `%s'.\n"),
  563.                         $new_name,$newgrp if $verbose;
  564.                 next;
  565.  
  566.             }
  567.  
  568.             printf gtx("Adding user `%s' to group `%s'...\n"),$new_name,$newgrp
  569.                 if $verbose;
  570.             &invalidate_nscd();
  571.             my $gpasswd = &which('gpasswd');
  572.             &systemcall($gpasswd, '-M',
  573.                         join(',', get_group_members($newgrp), $new_name),
  574.                         $newgrp);
  575.             &invalidate_nscd();
  576.         }
  577.     }
  578.  
  579.  
  580.     if ($config{"quotauser"}) {
  581.     printf (gtx("Setting quota from `%s'.\n"),$config{quotauser});
  582.        my $edquota = &which('edquota');
  583.     &systemcall($edquota, '-p', $config{quotauser}, $new_name);
  584.     }
  585.  
  586.     &systemcall('/usr/local/sbin/adduser.local', $new_name, $new_uid,
  587.         $new_gid, $home_dir) if (-x "/usr/local/sbin/adduser.local");
  588.     
  589.     exit 0;
  590. }
  591.  
  592. #
  593. # we never go here
  594. #
  595.  
  596.  
  597. # calculate home directory
  598. sub homedir {
  599.     my $dir = $config{"dhome"};
  600.     $dir .= '/' . $_[1] if ($config{"grouphomes"} =~ /yes/i);
  601.     $dir .= '/' . substr($_[0],0,1) if ($config{"letterhomes"} =~ /yes/i);
  602.     $dir .= '/' . $_[0];
  603.     return $dir;
  604. }
  605.  
  606.  
  607. # create_homedir -- create the homedirectory
  608. # parameter 1: $copy_skeleton: 
  609. #       if 0  -> don't copy the skeleton data
  610. #       if 1  -> copy the files in /etc/skel to the newly created home directory
  611. sub create_homedir {
  612.   my ($copy_skeleton) = @_;
  613.  
  614.   if ($no_create_home) {
  615.       printf gtx("Not creating home directory `%s'.\n"), $home_dir if $verbose;
  616.   }
  617.   elsif (-e $home_dir) {
  618.       printf gtx("The home directory `%s' already exists.  Not copying from `%s'\n"),
  619.       $home_dir,$config{skel} if $verbose && !$no_create_home;
  620.       my @homedir_stat = stat($home_dir);
  621.       my $home_uid = $homedir_stat[4];
  622.       my $home_gid = $homedir_stat[5];
  623.       if (($home_uid != $new_uid) || ($home_gid != $new_gid)) {
  624.           warnf gtx("Warning: that home directory does not belong to the user you are currently creating\n");
  625.       }
  626.       undef @homedir_stat; undef $home_uid; undef $home_gid;
  627.   }
  628.   else {
  629.       printf gtx("Creating home directory `%s'.\n"),$home_dir if $verbose;
  630.       $undohome = $home_dir;
  631.       &mktree($home_dir) || &cleanup("Couldn't create $home_dir: $!.\n");
  632.       chown($new_uid, $new_gid, $home_dir)
  633.       || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n");
  634.       $dir_mode = get_dir_mode($make_group_also);
  635.       chmod ($dir_mode, $home_dir) ||
  636.       &cleanup("chmod $dir_mode $home_dir: $!\n");
  637.  
  638.       if ($config{"skel"} && $copy_skeleton) {
  639.       printf gtx("Copying files from `%s'\n"),$config{skel} if $verbose;
  640.       open(FIND, "cd $config{skel}; find .  -print |")
  641.           || &cleanup("fork for find: $!\n");
  642.       while (<FIND>) {
  643.           chop;
  644.           next if ($_ eq ".");
  645.           next if ($_ =~ qr/$config{skel_ignore_regex}/ );
  646.           ©_to_dir($config{"skel"}, $_, $home_dir, $new_uid,
  647.                 $new_gid, ($config{"setgid_home"} =~ /yes/i));
  648.       }
  649.       }
  650.   }
  651. }
  652.  
  653. # create a directory and all parent directories
  654. # we don't care about the rights and so on
  655. sub mktree {
  656.     my($tree) = @_;
  657.     my($done, @path);
  658.     my $default_dir_mode = 0755;
  659.  
  660.     $tree =~ s:^/*(.*)/*$:$1:; # chop off leading & trailing slashes
  661.     @path = split(/\//, $tree);
  662.  
  663.     $done = "";
  664.     while (@path) {
  665.     $done .= '/' . shift(@path);
  666.     -d $done || mkdir($done, $default_dir_mode) || return 0;
  667.     }
  668.     return 1;
  669. }
  670.  
  671. # returns 0 if the the user doesn't exist or
  672. # returns 1 if the user already exists with the specified uid (or $new_uid wasn't specified)
  673. # returns 2 if the user already exists, but $new_uid doesn't matches its uid
  674. sub existing_user_ok {
  675.     my($new_name,$new_uid) = @_;
  676.     my ($dummy1,$dummy2,$uid);
  677.     if (($dummy1,$dummy2,$uid) = getpwnam($new_name)) {
  678.     if( defined($new_uid) && $uid == $new_uid ) {
  679.         return 1;
  680.     }
  681.     if (! defined($new_uid)) { 
  682.         return 1;
  683.     }
  684.     if( $uid >= $config{"first_system_uid"} &&
  685.         $uid <= $config{"last_system_uid" } ) {
  686.         return 2;
  687.     }
  688.     } else {
  689.     return 0;
  690.     }
  691. }
  692.  
  693. # returns 0 if the group doesn't exist or
  694. # returns 1 if the group already exists with the specified gid (or $new_gid wasn't specified)
  695. # returns 2 if the group already exists, but $new_gid doesn't match its gid 
  696. sub existing_group_ok {
  697.     my($new_name,$new_gid) = @_;
  698.     my ($dummy1,$dummy2,$gid);
  699.     if (($dummy1,$dummy2,$gid) = getgrnam($new_name)) {
  700.     if( defined($new_gid) && $gid == $new_gid ) {
  701.         return 1;
  702.     }
  703.     if (! defined($new_gid)) {
  704.         return 1;
  705.     }
  706.     if( $gid >= $config{"first_system_gid"} &&
  707.         $gid <= $config{"last_system_gid" } ) {
  708.         return 2;
  709.     }
  710.     } else {
  711.     return 0;
  712.     }
  713. }
  714.  
  715. sub check_user_group {
  716.     my ($system) = @_;
  717.     if( !$system || !existing_user_ok($new_name, $new_uid) ) {
  718.     if( defined getpwnam($new_name) ) {
  719.         if( $system ) {
  720.         dief (gtx("The user `%s' already exists, and is not a system user.\n"),$new_name);
  721.         } else {
  722.         dief (gtx("The user `%s' already exists.\n"),$new_name);
  723.         }
  724.     }
  725.     dief (gtx("The UID %s is already in use.\n"),$new_uid)
  726.       if (defined($new_uid) && getpwuid($new_uid));
  727.     }
  728.     if ($make_group_also) {
  729.     if( !$system || !existing_group_ok($new_name, $new_uid) ) {
  730.         dief (gtx("The group `%s' already exists.\n"),$new_name)
  731.           if (defined getgrnam($new_name));
  732.         dief (gtx("The GID %s is already in use.\n"),$new_uid)
  733.           if (defined($new_uid) && defined(getgrgid($new_uid)));
  734.     }
  735.     }
  736.     else {
  737.     dief (gtx("The group `%s' does not exist.\n"),$ingroup_name)
  738.         if ($ingroup_name && !defined(getgrnam($ingroup_name)));
  739.     dief (gtx("The GID %s does not exist.\n"),$new_gid)
  740.         if (defined($new_gid) && !defined(getgrgid($new_gid)));
  741.     }
  742. }
  743.  
  744.  
  745. # copy files, directories, symlinks    
  746. sub copy_to_dir {
  747.     my($fromdir, $file, $todir, $newu, $newg, $sgiddir) = @_;
  748.  
  749.     if (-l "$fromdir/$file") {
  750.     my $target=readlink("$fromdir/$file") or &cleanup("readlink: $!\n");
  751.     my $curgid="$)";
  752.     my $curuid="$>";
  753.     my $error="";
  754.     $)="$newg";
  755.     $>="$newu";
  756.     symlink("$target", "$todir/$file") or $error="$!";
  757.         $>="$curuid";
  758.         $)="$curgid";
  759.     if( "$error" ne "" ) {
  760.         &cleanup("symlink: $!\n");
  761.     }
  762.     return;
  763.     }
  764.     elsif (-f "$fromdir/$file") {
  765.     open (FILE, "$fromdir/$file") || &cleanup("open $fromdir/$file: $!");
  766.     open (NEWFILE, ">$todir/$file") || &cleanup("open >$todir/$file: $!");
  767.  
  768.     (print NEWFILE <FILE>) || &cleanup("print $todir/$file: $!");
  769.     close FILE;
  770.     close(NEWFILE)  || &cleanup("close $todir/$file ");
  771.  
  772.     }
  773.     elsif (-d "$fromdir/$file") {
  774.     mkdir("$todir/$file", 700) || &cleanup("mkdir: $!");
  775.     }
  776.     else {
  777.     &cleanup("Can't deal with $fromdir/$file.  "
  778.          ."Not a dir, file, or symlink.\n");
  779.     }
  780.     
  781.     chown($newu, $newg, "$todir/$file")
  782.     || &cleanup("chown $newu:$newg $todir/$file: $!\n");
  783.     $perm = (stat("$fromdir/$file"))[2] & 07777;
  784.     $perm |= 02000 if (-d "$fromdir/$file" && ($perm & 010) && $sgiddir);
  785.     chmod($perm, "$todir/$file") || &cleanup("chmod $todir/$file: $!\n");
  786. }
  787.        
  788.  
  789. # check if the given name matches some sanity checks
  790. sub checkname {
  791.     my ($name) = @_;
  792.     if ($name !~ /^[_.A-Za-z0-9][-_.A-Za-z0-9]*\$?$/) {
  793.     print STDERR
  794. ("$0: " . gtx("To avoid problems, the username should consist of
  795. letters, digits, underscores, periods and dashes, and not start with a
  796. dash (as defined by IEEE Std 1003.1-2001). For compatibility with Samba
  797. machine accounts \$ is also supported at the end of the username\n"));
  798.         exit 1;
  799.     }
  800.     if ($name !~ qr/$config{"name_regex"}/) {
  801.       if ($allow_badname) {
  802.     print (gtx("Allowing use of questionable username.\n")) if ($verbose);
  803.       }
  804.       else {
  805.         print STDERR
  806. ("$0: " . gtx("Please enter a username matching the regular expression configured
  807. via the name_regex configuration variable.  Use the `--force-badname'
  808. option to relax this check or reconfigure name_regex.\n"));
  809.         exit 1;
  810.       }
  811.     }
  812. }
  813.  
  814. # return the first available uid in given range
  815. # return -1 if no free uid is available
  816. sub first_avail_uid {
  817.     my ($min, $max) = @_;
  818.     printf (gtx("Selecting uid from range %s to %s.\n"),$min,$max) if ($verbose > 1);
  819.  
  820.     my $t = $min;
  821.     while ($t <= $max) {
  822.        return $t if (!defined(getpwuid($t)));
  823.        $t++;
  824.     }
  825.     return -1; # nothing available
  826. }
  827.  
  828. # return the first available gid in given range
  829. # return -1 if no free gid is available
  830. sub first_avail_gid {
  831.     my ($min, $max) = @_;
  832.     printf (gtx("Selecting gid from range %s to %s.\n"),$min,$max) if ($verbose > 1);
  833.  
  834.     my $t = $min;
  835.     while ($t <= $max) {
  836.        return $t if (!defined(getgrgid($t)));
  837.        $t++;
  838.     }
  839.     return -1; # nothing available
  840. }
  841.  
  842. sub ch_gecos {
  843.     my $chfn = &which('chfn');
  844.     my $gecos = shift;
  845.     if($gecos =~ /,/)
  846.       {
  847.       my($gecos_name,$gecos_room,$gecos_work,$gecos_home,$gecos_other)
  848.         = split(/,/,$gecos);
  849.  
  850.       &systemcall($chfn, '-f', $gecos_name, '-r', $gecos_room, $new_name);
  851.       &systemcall($chfn,'-w',$gecos_work,$new_name)
  852.         if(defined($gecos_work));
  853.       &systemcall($chfn,'-h',$gecos_home,$new_name)
  854.         if(defined($gecos_home));
  855.       &systemcall($chfn,'-o',$gecos_other,$new_name)
  856.         if(defined($gecos_other));
  857.       }
  858.     else
  859.       {
  860.       &systemcall($chfn, '-f', $gecos, $new_name);
  861.       }
  862. }
  863.  
  864. # user is member of group?
  865. sub user_is_member {
  866.     my($user, $group) = @_;
  867.     for (split(/ /, (getgrnam($group))[3])) {
  868.     return 1 if ($user eq $_);
  869.     }
  870.     return 0;
  871. }
  872.  
  873.  
  874. sub cleanup {
  875.     my ($msg) = @_;
  876.     printf (gtx("Stopped: %s\n"),$msg);
  877.     if ($undohome) {
  878.     printf (gtx("Removing directory `%s'\n"),$undohome);
  879.     &systemcall('rm', '-rf', $undohome);
  880.     }
  881.     if ($undouser) {
  882.     printf (gtx("Removing user `%s'.\n"),$undouser);
  883.     &systemcall('userdel', $undouser);
  884.     }
  885.     if ($undogroup) {
  886.     printf (gtx("Removing group `%s'.\n"),$undogroup);
  887.     &systemcall('groupdel', $undogroup);
  888.     }
  889.     # do we need to invalidate the nscd cache here, too?
  890.     exit 1;
  891. }
  892.  
  893. sub handler {
  894.     my($sig) = @_;
  895.     &cleanup("Caught a SIG$sig.\n");
  896. }
  897.     
  898.  
  899. sub version {
  900.     print "$0: add a user or group to the system.  Version 3.92
  901. Copyright (C) 1997, 1998, 1999 Guy Maor <maor\@debian.org>
  902. Copyright (C) 1995 Ian Murdock <imurdock\@gnu.ai.mit.edu>,
  903.                    Ted Hajek <tedhajek\@boombox.micro.umn.edu>, 
  904.     
  905. This program is free software; you can redistribute it and/or modify
  906. it under the terms of the GNU General Public License as published by
  907. the Free Software Foundation; either version 2 of the License, or (at
  908. your option) any later version.
  909.  
  910. This program is distributed in the hope that it will be useful, but
  911. WITHOUT ANY WARRANTY; without even the implied warranty of
  912. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  913. General Public License, /usr/share/common-licenses/GPL, for more details.
  914. ";
  915. }
  916.  
  917. sub usage {
  918.     printf gtx(
  919. "adduser [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID]
  920. [--firstuid ID] [--lastuid ID] [--gecos GECOS] [--ingroup GROUP | --gid ID]
  921. [--disabled-password] [--disabled-login] user
  922.    Add a normal user
  923.  
  924. adduser --system [--home DIR] [--shell SHELL] [--no-create-home] [--uid ID]
  925. [--gecos GECOS] [--group | --ingroup GROUP | --gid ID] [--disabled-password]
  926. [--disabled-login] user
  927.    Add a system user
  928.  
  929. adduser --group [--gid ID] group
  930. addgroup [--gid ID] group
  931.    Add a user group
  932.  
  933. addgroup --system [--gid ID] group
  934.    Add a system group
  935.  
  936. adduser user group
  937.    Add an existing user to an existing group
  938.  
  939. Other options are [--quiet] [--force-badname] [--help] [--version] [--conf
  940. FILE].
  941. ");
  942. }
  943.  
  944. sub get_dir_mode
  945.   {
  946.       my $setgid = shift;
  947.       # no longer make home directories setgid per default (closes: #64806)
  948.       $setgid = 0 unless $config{"setgid_home"} =~  /yes/i;
  949.  
  950.       my $dir_mode = $config{"dir_mode"};
  951.       if(!defined($dir_mode) || ! ($dir_mode =~ /[0-7]{3}/ ||
  952.                    $dir_mode =~ /[0-7]{4}/))
  953.     {
  954.         $dir_mode = $setgid ? 2755 : 0755;
  955.     }
  956.       else
  957.     {
  958.         $dir_mode = $config{"dir_mode"};
  959.         if($setgid && (length($dir_mode) == 3 || $dir_mode =~ /^[0-1|4-5][0-7]{3}$/))
  960.           {
  961.           $dir_mode += 2000;
  962.           }
  963.     }
  964.       return oct($dir_mode);
  965.   }
  966.  
  967. # Local Variables:
  968. # mode:cperl
  969. # cperl-indent-level:4
  970. # End:
  971.  
  972. # vim:set ai et sts=4 sw=4 tw=0:
  973.