home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/perl -w
- #
- # adduser 3.8
- #
- # adduser: a utility to add users to the system
- # addgroup: a utility to add groups to the system
-
- # Copyright (C) 1997 Guy Maor <maor@debian.org>
- # Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu>
- # Ian A. Murdock <imurdock@gnu.ai.mit.edu>
- # General scheme of the program adapted by the original debian 'adduser'
- # program by Ian A. Murdock <imurdock@gnu.ai.mit.edu>.
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- #
- #
- ####################
- # the program can be called as:
- #
- # adduser [--home DIR] [--uid ID] [--ingroup GROUP | --gid ID]
- # [--disabled-password] [--gecos GECOS] user
- # add a normal user to the system
- # example: adduser fred
- # $action = "adduser"
- #
- # adduser --group [--gid ID] group
- # addgroup [--gid ID] group
- # add a system group
- # example: addgroup --quiet www-data
- # $action = "addgroup"
- #
- # adduser --system [--home DIR] [--uid ID] [--group | --ingroup GROUP
- # | --gid ID] [--disabled-password] [--gecos GECOS] user
- # add a system user. Create a like-named, like-id'd group with
- # --group, add to an existing group with --ingroup or --gid. Add
- # to "nogroup" with neither.
- # example: adduser --system --home /home/gopher-data --group gopher
- # $action = "addsysuser"
- #
- # adduser user group
- # add the existing user to an existing group.
- # $action = "addusertogroup"
- #
- # all commands take the following options:
- # --quiet don't give progress information on STDOUT
- # --force-badname disable checking of names for funny characters
- # --help usage message
- # --version version number and copyright
- # --conf FILE use FILE instead of /etc/adduser.conf
- ############
-
- $verbose = 1; # should we be verbose?
- $allow_badname = 0; # should we allow bad names?
- $ask_passwd = 1; # ask for a passwd?
-
- $defaults = "/etc/adduser.conf";
- $nogroup_id = getgrnam("nogroup") || 65534;
- $0 =~ s+.*/++;
-
- $config{"dshell"} = "/bin/bash";
- $config{"first_system_uid"} = 100;
- $config{"last_system_uid"} = 999;
- $config{"first_uid"} = 1000;
- $config{"last_uid"} = 29999;
- $config{"dhome"} = "/home";
- $config{"skel"} = "/etc/skel";
- $config{"usergroups"} = "yes";
- $config{"users_gid"} = "100";
- $config{"grouphomes"} = "no";
- $config{"letterhomes"} = "no";
- $config{"quotauser"} = "";
-
- $action = $0 eq "addgroup" ? "addgroup" : "adduser";
-
- while ($arg = shift(@ARGV)) {
- die "$0: No options allowed after names.\n"
- if ($names[0] && $arg =~ /^--/);
- if ($arg eq "--quiet") {
- $verbose = 0;
- } elsif ($arg eq "--force-badname") {
- $allow_badname = 1;
- } elsif ($arg eq "--help") {
- &usage();
- exit 0;
- } elsif ($arg eq "--version") {
- &version();
- exit 0;
- } elsif ($arg eq "--system") {
- $action = "addsysuser";
- } elsif ($arg eq "--group") {
- $found_group_opt = 1;
- } elsif ($arg eq "--ingroup") {
- die "$0: --ingroup requires an argument.\n"
- if (!($ingroup_name = shift(@ARGV)));
- } elsif ($arg eq "--home") {
- die "$0: --home requires an argument.\n"
- if (!($special_home = shift(@ARGV)));
- print "$0: Warning: The home dir you specified already exists.\n"
- if (-d $special_home && $verbose);
- die "$0: The home dir must be an absolute path.\n"
- if ($special_home !~ m+^/+ );
- } elsif ($arg eq "--gecos") {
- $new_gecos = shift(@ARGV);
- } elsif ($arg eq "--disabled-password") {
- $ask_passwd = 0;
- } elsif ($arg eq "--uid") {
- die "$0: --uid requires a numeric argument.\n"
- if (!($new_uid = shift(@ARGV)) || $new_uid !~ /^\d+$/);
- } elsif ($arg eq "--gid") {
- die "$0: --gid requires a numeric argument.\n"
- if (!($new_gid = shift(@ARGV)) || $new_gid !~ /^\d+$/);
- } elsif ($arg eq "--conf") {
- die "$0: --conf requires an argument.\n"
- if (!($defaults = shift(@ARGV)));
- die "$0: `$defaults' doesn't exist.\n"
- if (! -f $defaults);
- } elsif ($arg eq "--debug") {
- $debugging = 1;
- } elsif ($arg =~ /^--/) { # bad argument!
- die "$0: Unknown argument `$arg'.\n";
- } else { # it's a username
- push (@names, $arg);
- }
- }
-
- die "$0: Only root may add a user or group to the system.\n" if ($> != 0);
-
- die "$0: I need a name to add.\n" if (@names == 0);
- die "$0: No more than two names.\n" if (@names > 2);
- if (@names == 2) { # must be addusertogroup
- die "$0: Specify only one name in this mode.\n"
- if ($action eq "addsysuser" || $found_group_opt);
- $action = "addusertogroup";
- $existing_user = shift (@names);
- $existing_group = shift (@names);
- }
- else {
- $new_name = shift (@names);
- }
-
- if ($found_group_opt) {
- if ($action eq "addsysuser") {
- $make_group_also = 1;
- }
- else {
- $action = "addgroup";
- }
- }
- die "$0: --group, --ingroup, and --gid options are mutually exclusive.\n" if
- ($action ne "addgroup" &&
- defined($found_group_opt) +defined($ingroup_name) +defined($new_gid) > 1);
-
-
- #####
- # OK, we've processed the arguments. $action equals one of the following,
- # and the appropriate variables have been set:
- #
- # $action = "adduser"
- # $new_name - the name of the new user.
- # $ingroup_name | $new_gid - the group to add the user to
- # $special_home, $new_uid, $new_gecos - optional overrides
- # $action = "addgroup"
- # $new_name - the name of the new group
- # $new_gid - optional override
- # $action = "addsysuser"
- # $new_name - the name of the new user
- # $make_group_also | $ingroup_name | $new_gid | 0 - which group
- # $special_home, $new_uid, $new_gecos - optional overrides
- # $action = "addusertogroup"
- # $existing_user - the user to be added
- # $existing_group - the group to add her to
- #####
-
- &read_config($defaults);
- &checkname($new_name) if $new_name;
- $SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'handler';
-
- ##############
- ## addgroup ##
- ##############
- if ($action eq "addgroup") {
- die "$0: The group `$new_name' already exists.\n"
- if (getgrnam($new_name));
- die "$0: The GID `$new_gid' is already in use.\n"
- if (defined($new_gid) && getgrgid($new_gid));
- if (!defined($new_gid)) {
- $new_gid = &first_avail_id($config{"first_system_uid"},
- $config{"last_system_uid"},
- &get_current_gids);
- die "$0: No GID is available in the range ",
- "$config{\"first_system_uid\"} - $config{\"last_system_uid\"}\n",
- "(FIRST_SYS_UID - LAST_SYS_UID). ",
- "Group `$new_name' not created.\n" if ($new_gid == -1);
- }
-
- print "Adding group $new_name ($new_gid)...\n" if $verbose;
- &systemcall('groupadd', '-g', $new_gid, $new_name);
- print "Done.\n" if $verbose;
- exit 0;
- }
-
-
- ####################
- ## addusertogroup ##
- ####################
- elsif ($action eq "addusertogroup") {
- die "$0: The user `$existing_user' doesn't exist.\n"
- if (!getpwnam($existing_user));
- die "$0: The group `$existing_group' doesn't exist.\n"
- if (!getgrnam($existing_group));
- if (&user_is_member($existing_user, $existing_group)) {
- print "The user `$existing_user' is already a member of ",
- "`$existing_group'.\n" if $verbose;
- exit 0; # not really an error
- }
-
- print "Adding user $existing_user to group $existing_group...\n"
- if $verbose;
- &systemcall('usermod', '-G',
- join(",", get_users_groups($existing_user), $existing_group),
- $existing_user);
- print "Done.\n" if $verbose;
- exit 0;
- }
-
-
- ################
- ## addsysuser ##
- ################
- elsif ($action eq "addsysuser") {
- $new_gid = $nogroup_id
- if (!$ingroup_name && !defined($new_gid) && !$make_group_also);
- &check_user_group();
- print "Adding system user $new_name...\n" if $verbose;
-
- if (!defined($new_uid) && $make_group_also) {
- $new_uid = &first_avail_id($config{"first_system_uid"},
- $config{"last_system_uid"},
- &get_current_uids, &get_current_gids);
- die "$0: No UID/GID pair is available in the range ",
- "$config{\"first_system_uid\"} - $config{\"last_system_uid\"}\n",
- "(FIRST_SYS_UID - LAST_SYS_UID). ",
- "User `$new_name' not created.\n" if ($new_uid == -1);
- $new_gid = $new_uid;
- $ingroup_name = $new_name;
- }
- elsif (!defined($new_uid) && !$make_group_also) {
- $new_uid = &first_avail_id($config{"first_system_uid"},
- $config{"last_system_uid"},
- &get_current_uids);
- die "$0: No UID is available in the range ",
- "$config{\"first_system_uid\"} - $config{\"last_system_uid\"}\n",
- "(FIRST_SYS_UID - LAST_SYS_UID). ",
- "User `$new_name' not created.\n" if ($new_uid == -1);
- if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
- elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
- else { die "Internal error"; }
- }
- else {
- if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
- elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
- elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
- else { die "Internal error"; }
- }
-
- if ($make_group_also) {
- print "Adding new group $new_name ($new_gid).\n" if $verbose;
- $undogroup = $new_name;
- &systemcall('groupadd', '-g', $new_gid, $new_name);
- }
-
- print "Adding new user $new_name ($new_uid) with group $ingroup_name.\n"
- if $verbose;
- $home_dir = $special_home || &homedir($new_name, $ingroup_name);
- $undouser = $new_name;
- &systemcall('useradd', '-d', $home_dir, '-g', $ingroup_name, '-s',
- '/bin/false', '-u', $new_uid, $new_name);
- &systemcall('chfn', '-f', $new_gecos, $new_name) if ($new_gecos);
-
- if (-d $home_dir) {
- print "Home directory $home_dir already exists.\n" if $verbose;
- } else {
- print "Creating home directory $home_dir.\n" if $verbose;
- $undohome = $home_dir;
- &mktree($home_dir) || &cleanup("Couldn't create $home_dir: $!.\n");
- chown($new_uid, $new_gid, $home_dir)
- || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n");
- $dir_mode = $make_group_also ? 02755 : 0755;
- chmod ($dir_mode, $home_dir) ||
- &cleanup("chmod $dir_mode $home_dir: $!\n");
- }
-
- exit 0;
- }
-
-
- #############
- ## adduser ##
- #############
- elsif ($action eq "adduser") {
- if (!$ingroup_name && !defined($new_gid)) {
- if ($config{"usergroups"} eq "yes") { $make_group_also = 1; }
- else { $new_gid = $config{"users_gid"}; }
- }
- &check_user_group();
- print "Adding user $new_name...\n" if $verbose;
-
- if (!defined($new_uid) && $make_group_also) {
- $new_uid = &first_avail_id($config{"first_uid"},
- $config{"last_uid"},
- &get_current_uids, &get_current_gids);
- die "$0: No UID/GID pair is available in the range ",
- "$config{\"first_uid\"} - $config{\"last_uid\"}\n",
- "(FIRST_UID - LAST_UID). ",
- "User `$new_name' not created.\n" if ($new_uid == -1);
- $new_gid = $new_uid;
- $ingroup_name = $new_name;
- }
- elsif (!defined($new_uid) && !$make_group_also) {
- $new_uid = &first_avail_id($config{"first_uid"},
- $config{"last_uid"},
- &get_current_uids);
- die "$0: No UID is available in the range ",
- "$config{\"first_uid\"} - $config{\"last_uid\"}\n",
- "(FIRST_UID - LAST_UID). ",
- "User `$new_name' not created.\n" if ($new_uid == -1);
- if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
- elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
- else { die "Internal error"; }
- }
- else {
- if (defined($new_gid)) { $ingroup_name = getgrgid($new_gid); }
- elsif ($ingroup_name) { $new_gid = getgrnam($ingroup_name); }
- elsif ($make_group_also){ $new_gid=$new_uid; $ingroup_name=$new_name; }
- else { die "Internal error"; }
- }
-
- if ($make_group_also) {
- print "Adding new group $new_name ($new_gid).\n" if $verbose;
- $undogroup = $new_name;
- &systemcall('groupadd', '-g', $new_gid, $new_name);
- }
-
- print "Adding new user $new_name ($new_uid) with group $ingroup_name.\n"
- if $verbose;
- $home_dir = $special_home || &homedir($new_name, $ingroup_name);
- $undouser = $new_name;
- &systemcall('useradd', '-d', $home_dir, '-g', $ingroup_name, '-s',
- $config{dshell}, '-u', $new_uid, $new_name);
-
- if (-d $home_dir) {
- print "Home directory $home_dir already exists. Not copying from",
- " $config{skel}\n" if $verbose;
- }
- else {
- print "Creating home directory $home_dir.\n" if $verbose;
- $undohome = $home_dir;
- &mktree($home_dir) || &cleanup("Couldn't create $home_dir: $!.\n");
- chown($new_uid, $new_gid, $home_dir)
- || &cleanup("chown $new_uid:$new_gid $home_dir: $!\n");
- $dir_mode = $make_group_also ? 02755 : 0755;
- chmod ($dir_mode, $home_dir) ||
- &cleanup("chmod $dir_mode $home_dir: $!\n");
-
- if ($config{"skel"}) {
- print "Copying files from $config{skel}\n" if $verbose;
- open(FIND, "cd $config{skel}; find . -print |")
- || &cleanup("fork for find: $!\n");
- while (<FIND>) {
- chop;
- next if ($_ eq ".");
- ©_to_dir($config{"skel"}, $_, $home_dir, $new_uid,
- $new_gid, $make_group_also);
- }
- }
- }
-
- if ($ask_passwd) {
- &systemcall('passwd', $new_name);
- }
-
- if ($new_gecos) {
- &systemcall('chfn', '-f', $new_gecos, $new_name);
- }
- else {
- for (;;) {
- &systemcall('chfn', $new_name);
- print "Is the information correct? [y/n] ";
- chop ($answer=<STDIN>);
- last if ($answer =~ /^y/i);
- }
- }
-
- if ($config{"quotauser"}) {
- print "Setting quota from $config{quotauser}.\n";
- &systemcall('edquota', '-p', $config{quotauser}, $new_name);
- }
-
- &systemcall('/usr/local/sbin/adduser.local', $new_name, $new_uid,
- $new_gid, $home_dir) if (-x "/usr/local/sbin/adduser.local");
-
- exit 0;
- }
-
-
- # calculate home directory
- sub homedir {
- my $dir = $config{"dhome"};
- $dir .= '/' . $_[1] if ($config{"grouphomes"} =~ /yes/i);
- $dir .= '/' . substr($_[0],0,1) if ($config{"letterhomes"} =~ /yes/i);
- $dir .= '/' . $_[0];
- }
-
-
- # create a directory and all leading directories
- sub mktree {
- my($tree) = @_;
- my($done, @path);
- my $default_dir_mode = 0755;
-
- $tree =~ s:^/*(.*)/*$:$1:; # chop off leading & trailing slashes
- @path = split(/\//, $tree);
-
- $done = "";
- while (@path) {
- $done .= '/' . shift(@path);
- -d $done || mkdir($done, $default_dir_mode) || return 0;
- }
- 1;
- }
-
-
- sub check_user_group() {
- die "$0: The user `$new_name' already exists.\n" if (getpwnam($new_name));
- die "$0: The UID `$new_uid' already exists.\n"
- if (defined($new_uid) && getpwuid($new_uid));
- if ($make_group_also) {
- die "$0: The group `$new_name' already exists.\n"
- if (getgrnam($new_name));
- die "$0: The GID `$new_uid' already exists.\n"
- if (defined($new_uid) && getgrgid($new_uid));
- }
- else {
- die "$0: The group `$ingroup_name' doesn't exist.\n"
- if ($ingroup_name && !getgrnam($ingroup_name));
- die "$0: The GID `$new_gid' doesn't exist.\n"
- if (defined($new_gid) && !getgrgid($new_gid));
- }
- }
-
-
- # copy files, directories, symlinks
- sub copy_to_dir {
- my($fromdir, $file, $todir, $newu, $newg, $sgiddir) = @_;
-
- if (-l "$fromdir/$file") {
- symlink(readlink("$fromdir/$file"), "$todir/$file")
- || &cleanup("symlink: $!\n");
- }
- elsif (-f "$fromdir/$file") {
- open (FILE, "$fromdir/$file") || &cleanup("open $fromdir/$file: $!");
- open (NEWFILE, ">$todir/$file") || &cleanup("open >$todir/$file: $!");
-
- (print NEWFILE <FILE>) || &cleanup("print $todir/$file: $!");
- close FILE;
- close(NEWFILE) || &cleanup("close $todir/$file ");
-
- }
- elsif (-d "$fromdir/$file") {
- mkdir("$todir/$file", 700) || &cleanup("mkdir: $!");
- }
- else {
- &cleanup("Can't deal with $fromdir/$file. "
- ."Not a dir, file, or symlink.\n");
- }
-
- chown($newu, $newg, "$todir/$file")
- || &cleanup("chown $newu:$newg $todir/$file: $!\n");
- $perm = (stat("$fromdir/$file"))[2] & 07777;
- $perm |= 02000 if (-d "$fromdir/$file" && ($perm & 010) && $sgiddir);
- chmod($perm, "$todir/$file") || &cleanup("chmod $todir/$file: $!\n");
- }
-
-
- # is name ok?
- sub checkname {
- my ($name) = @_;
- if ($allow_badname && $name !~ /^[A-Za-z_][-_A-Za-z0-9]*$/) {
- print STDERR
- "$0: To avoid problems, the username should consist of a letter or
- underscore followed by letters, digits, underscores, and dashes.\n";
- exit 1;
- }
- elsif ($name !~ /^[a-z][a-z0-9]*$/) {
- if (!$allow_badname) {
- print STDERR
- "$0: Please enter a username consisting of a lower case letter
- followed by lower case letters and numbers. Use the `--force-badname'
- option to allow underscores, dashes, and uppercase.\n";
- exit 1;
- }
- print "Allowing use of questionable username.\n" if ($verbose);
- }
- }
-
-
- # return the smallest X such that
- # $min <= X <= $max, and X is not an element of @ids
- # or -1 if no such X
- sub first_avail_id {
- my ($min, $max, @ids) = @_;
- @ids = sort {$a <=> $b} @ids;
- print "Selecting from $min $max (" . join(",",@ids) . ").\n" if $debugging;
-
- while ($min <= $max) {
- return $min if ($min < $ids[0] || @ids==0);
- shift @ids if ($min > $ids[0]);
- $min++ if ($min == $ids[0]);
- }
-
- -1; # nothing available
- }
-
-
- # return an array containing all the GIDs
- sub get_current_gids {
- my(@gids, $gid);
- setgrent;
- push @gids, $gid while defined($gid = (getgrent)[2]);
- endgrent;
- @gids;
- }
-
-
- # return an array containing all the UIDs
- sub get_current_uids {
- my(@uids, $uid);
- setpwent;
- push @uids, $uid while defined($uid = (getpwent)[2]);
- endpwent;
- @uids;
- }
-
-
- # return a user's groups
- sub get_users_groups {
- my($user) = @_;
- my($name,$members,@groups);
- setgrent;
- while (($name,$members) = (getgrent)[0,3]) {
- for (split(/ /, $members)) {
- if ($user eq $_) {
- push @groups, $name;
- last;
- }
- }
- }
- endgrent;
- @groups;
- }
-
-
- # user is member of group?
- sub user_is_member {
- my($user, $group) = @_;
- for (split(/ /, (getgrnam($group))[3])) {
- return 1 if ($user eq $_);
- }
- 0;
- }
-
-
- # parse the configuration file
- sub read_config {
- my ($conf_file) = @_;
- my ($var, $lcvar, $val);
-
- if (! -f $conf_file) {
- print "$0: $conf_file doesn't exist. Using defaults.\n" if $verbose;
- return;
- }
-
- open (CONF, $conf_file) || die "$0: $conf_file: $!\n";
- while (<CONF>) {
- chomp;
- next if /^#/ || /^\s*$/;
-
- if ((($var, $val) = /^\s*(\S+)\s*=\s*(.*)/) != 2) {
- warn "$0: Couldn't parse $conf_file:$..\n";
- next;
- }
- $lcvar = lc $var;
- if (!defined($config{$lcvar})) {
- warn "$0: Unknown variable `$var' at $conf_file:$..\n";
- next;
- }
-
- $val =~ s/^"(.*)"$/$1/;
- $val =~ s/^'(.*)'$/$1/;
-
- $config{$lcvar} = $val;
- }
-
- close CONF || die "$!";
- }
-
-
- sub systemcall {
- my $c = join(' ', @_);
- print "$c\n" if $debugging;
- system(@_) && &cleanup("$0: `$c' returned error code $?. Aborting.\n");
- }
-
-
- sub cleanup {
- print "@{_}Cleaning up.\n";
- if ($undohome) {
- print "Removing directory `$undohome'\n";
- system('rm', '-rf', $undohome);
- }
- if ($undouser) {
- print "Removing user `$undouser'.\n";
- system('userdel', $undouser);
- }
- if ($undogroup) {
- print "Removing group `$undogroup'.\n";
- system('groupdel', $undogroup);
- }
- exit 1;
- }
-
-
- sub handler {
- my($sig) = @_;
- &cleanup("Caught a SIG$sig.\n");
- }
-
-
- sub version {
- print "$0: add a user or group to the system. Version 3.8
- Copyright (C) 1997 Guy Maor <maor\@debian.org>
- Copyright (C) 1995 Ian Murdock <imurdock\@gnu.ai.mit.edu>,
- Ted Hajek <tedhajek\@boombox.micro.umn.edu>,
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at
- your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License, /usr/doc/copyright/GPL, for more details.
- ";
- }
-
-
- sub usage {
- print "adduser [--home DIR] [--uid ID] [--ingroup GROUP | --gid ID]
- [--disabled-password] [--gecos GECOS] user
- Add a normal user
-
- adduser --system [--home DIR] [--uid ID] [--group | --ingroup GROUP |
- --gid ID] [--disabled-password] [--gecos GECOS] user
- Add a system user
-
- adduser --group [--gid ID] group
- addgroup [--gid ID] group
- Add a system group
-
- adduser user group
- Add an existing user to an existing group
-
- Global configuration is in the file $defaults.
- Other options are [--quiet] [--force-badname] [--help] [--version] [--conf
- FILE].
- ";
- }
-