[Pkg-shadow-devel] Bug#333706: useradd lacks -r option

Matt Taggart taggart at debian.org
Thu Oct 13 10:35:51 UTC 2005


Package: passwd
Version: 1:4.0.12-6
Severity: wishlist
Tags: patch

The LSB specifies the existence of a useradd '-r' option,

http://www.linuxbase.org/spec//booksets/LSB-Core-generic/LSB-Core-generic/useradd.html

I have commented more on this issue at,

http://lists.debian.org/debian-lsb/2005/10/msg00010.html

Other distros have fixed this but none have pushed the fix upstream. I
have attached Fedora/RedHat's patch against 4.0.7 from the source rpm
at,

http://download.fedora.redhat.com/pub/fedora/linux/core/4/i386/os/SRPMS/shadow-utils-4.0.7-9.src.rpm

The patch does more than just add '-r', it adds '-M' (not in the LSB) and maybe other stuff.

I don't think Debian should make this change until upstream has accepted it. So consider this a wishlist bug to help get the change upstream so Debian can use it too.

-- System Information:
Debian Release: testing/unstable
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.12-1-k7
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages passwd depends on:
ii  libc6                         2.3.5-6    GNU C Library: Shared libraries an
ii  libpam-modules                0.76-23    Pluggable Authentication Modules f
ii  libpam0g                      0.76-23    Pluggable Authentication Modules l
ii  login                         1:4.0.3-39 system login tools

passwd recommends no packages.

-- debconf information excluded


Thanks,

-- 
Matt Taggart
taggart at debian.org
-------------- next part --------------
--- shadow-4.0.7/man/groupadd.8.redhat	2004-11-04 04:06:31.000000000 +0100
+++ shadow-4.0.7/man/groupadd.8	2005-03-01 17:06:16.000000000 +0100
@@ -30,7 +30,7 @@
 groupadd \- Create a new group
 .SH SYNOPSIS
 .TP 6
-\fBgroupadd\fR [\fB-g\fI gid \fR[\fB-o\fR]] [\fB-f\fR] \fIgroup\fR
+\fBgroupadd\fR [\fB-g\fI gid \fR[\fB-o\fR]] [\fB-r\fR] [\fB-f\fR] \fIgroup\fR
 .SH DESCRIPTION
 The \fBgroupadd\fR command creates a new group account using the values
 specified on the command line and the default values from the system. The
@@ -41,11 +41,18 @@
 This option causes to just exit with success status if the specified
 group already exists. With \fB-g\fR, if specified gid already exists,
 other (unique) gid is chosen (i.e. \fB-g\fR is turned off).
+.IP \fB-r\fR
+This flag instructs \fBgroupadd\fR to add a system
+account.  The first available \fIgid\fR lower than 499 will be
+automatically selected unless the \fB-g\fR option is also given on the
+command line.
+.br
+This is an option added by Red Hat.
 .IP "\fB-g\fR \fIgid\fR"
 The numerical value of the group's ID. This value must be unique, unless the
 \fB-o\fR option is used. The value must be non-negative. The default is to
-use the smallest ID value greater than 999 and greater than every other
-group. Values between 0 and 999 are typically reserved for system accounts.
+use the smallest ID value greater than 500 and greater than every other
+group. Values between 0 and 499 are typically reserved for system accounts.
 .SH FILES
 \fI/etc/group\fR	\- group account information
 .br
--- shadow-4.0.7/man/useradd.8.redhat	2004-12-17 00:57:46.000000000 +0100
+++ shadow-4.0.7/man/useradd.8	2005-03-01 17:06:16.000000000 +0100
@@ -36,7 +36,7 @@
 .br
 [\fB-g\fR \fIinitial_group\fR] [\fB-G\fR \fIgroup\fR[,...]]
 .br
-[[\fB-M\fR] \fB-m\fR [\fB-k\fR \fIskeleton_dir\fR]] [\fB-o\fR] [\fB-p\fR \fIpasswd\fR]
+[\fB-m\fR [\fB-k\fR \fIskeleton_dir\fR] | \fB-M\fR] [\fB-n\fR] [\fB-o\fR] [\fB-p\fR \fIpasswd\fR] [\fB-r\fP]
 .br
 [\fB-s\fR \fIshell\fR] [\fB-u\fR \fIuid\fR] \fIlogin\fR
 .TP 8
@@ -51,7 +51,9 @@
 a new user account using the values specified on the command line and the
 default values from the system. Depending on command line options, the
 useradd command will update system files and may also create the new user's
-home directory and copy initial files.
+home directory and copy initial files. The version provided with Red Hat Linux 
+will create a group for each user added to the system, unless the \fB-n\fR 
+option is given.
 .SH OPTIONS
 The options which apply to the
 \fBuseradd\fR command are:
@@ -80,11 +82,8 @@
 \fB-g\fR option. The default is for the user to belong only to the initial
 group.
 .IP \fB-M\fR
-RedHat added the CREATE_HOME option in login.defs in their version of
-shadow-utils (which makes -m the default, with new -M option to turn it
-off). Unfortunately, this changes the way useradd works (it can be run by
-scripts expecting some standard behaviour), compared to other Unices and
-other Linux distributions, and also adds a lot of confusion.
+The user's home directory will not be created, even if the system
+wide settings from \fI/etc/login.defs\fR is to create home dirs.
 .IP \fB-m\fR
 The user's home directory will be created if it does not exist. The files
 contained in \fIskeleton_dir\fR will be copied to the home directory if the
@@ -93,11 +92,24 @@
 \fI/etc/skel\fR will be created in the user's home directory as well. The
 \fB-k\fR option is only valid in conjunction with the \fB-m\fR option. The
 default is to not create the directory and to not copy any files.
+.IP \fB-n\fR
+A group having the same name as the user being added to the system
+will be created by default. This option will turn off this Red Hat
+Linux specific behavior.
 .IP "\fB-o\fR"
 Allow create user with duplicate (non-unique) UID.
 .IP "\fB-p \fIpasswd\fR"
 The encrypted password, as returned by \fBcrypt\fR(3). The default is to
 disable the account.
+.IP \fB-r\fR
+This flag is used to create a system account. That is, a user with a
+UID lower than the value of UID_MIN defined in \fI/etc/login.defs\fR and
+whose password does not expire. Note
+that \fBuseradd\fR will not create a home directory for such an user,
+regardless of the default setting in \fI/etc/login.defs\fR.
+You have to specify \fB-m\fR option if you want a home directory
+for a system account to be created.
+This is an option added by Red Hat.
 .IP "\fB-s\fR \fIshell\fR"
 The name of the user's login shell. The default is to leave this field
 blank, which causes the system to select the default login shell.
@@ -131,6 +143,9 @@
 .SH NOTES
 The system administrator is responsible for placing the default user files
 in the \fI/etc/skel/\fR directory.
+.br
+This version of useradd was modified by Red Hat to suit Red Hat
+user/group conventions.
 .SH CAVEATS
 You may not add a user to an NIS group. This must be performed on the NIS
 server.
@@ -141,8 +156,12 @@
 .br
 \fI/etc/group\fR			\- group information
 .br
+\fI/etc/gshadow\fR			\- secure group information
+.br
 \fI/etc/default/useradd\fR	\- default information
 .br
+\fI/etc/login.defs\fR			\- system-wide settings
+.br
 \fI/etc/skel/\fR			\- directory containing default files
 .SH EXIT VALUES
 .TP 2
--- shadow-4.0.7/src/useradd.c.redhat	2005-01-18 00:02:41.000000000 +0100
+++ shadow-4.0.7/src/useradd.c	2005-03-02 10:56:37.379986857 +0100
@@ -75,7 +75,7 @@
 static gid_t def_group = 100;
 static const char *def_gname = "other";
 static const char *def_home = "/home";
-static const char *def_shell = "";
+static const char *def_shell = "/sbin/nologin";
 static const char *def_template = SKEL_DIR;
 static const char *def_create_mail_spool = "no";
 
@@ -89,7 +89,7 @@
 #define	VALID(s)	(strcspn (s, ":\n") == strlen (s))
 
 static const char *user_name = "";
-static const char *user_pass = "!";
+static const char *user_pass = "!!";
 static uid_t user_id;
 static gid_t user_gid;
 static const char *user_comment = "";
@@ -121,8 +121,10 @@
  Gflg = 0,			/* secondary group set for new account */
  kflg = 0,			/* specify a directory to fill new user directory */
  mflg = 0,			/* create user's home directory if it doesn't exist */
- nflg = 0,			/* create a group having the same name as the user */
+ Mflg = 0,			/* do NOT create user's home directory no matter what */
+ nflg = 0,			/* do NOT create a group having the same name as the user */
  oflg = 0,			/* permit non-unique user ID to be specified with -u */
+ rflg = 0,			/* create a system account */
  sflg = 0,			/* shell program for new account */
  uflg = 0;			/* specify user ID for new account */
 
@@ -725,7 +727,7 @@
 #ifdef SHADOWPWD
 	fprintf (stderr, _("               [-f inactive] [-e expire]\n"));
 #endif
-	fprintf (stderr, _("               [-p passwd] name\n"));
+	fprintf (stderr, _("               [-p passwd] [-M] [-n] [-r] name\n"));
 	fprintf (stderr,
 		 _("       useradd -D [-g group] [-b base] [-s shell]\n"));
 #ifdef SHADOWPWD
@@ -782,11 +784,19 @@
 	spent->sp_namp = (char *) user_name;
 	spent->sp_pwdp = (char *) user_pass;
 	spent->sp_lstchg = time ((time_t *) 0) / SCALE;
-	spent->sp_min = scale_age (getdef_num ("PASS_MIN_DAYS", -1));
-	spent->sp_max = scale_age (getdef_num ("PASS_MAX_DAYS", -1));
-	spent->sp_warn = scale_age (getdef_num ("PASS_WARN_AGE", -1));
-	spent->sp_inact = scale_age (def_inactive);
-	spent->sp_expire = scale_age (user_expire);
+        if (!rflg) {
+                spent->sp_min = scale_age (getdef_num ("PASS_MIN_DAYS", -1));
+                spent->sp_max = scale_age (getdef_num ("PASS_MAX_DAYS", -1));
+                spent->sp_warn = scale_age (getdef_num ("PASS_WARN_AGE", -1));
+                spent->sp_inact = scale_age (def_inactive);
+                spent->sp_expire = scale_age (user_expire);
+        } else {
+                spent->sp_min = scale_age(-1);
+                spent->sp_max = scale_age(-1);
+                spent->sp_warn = scale_age(-1);
+                spent->sp_inact = scale_age(-1);
+                spent->sp_expire = scale_age(-1);
+        }
 	spent->sp_flag = -1;
 }
 #endif
@@ -808,33 +818,7 @@
 	struct sgrp *nsgrp;
 #endif
 
-	/*
-	 * Lock and open the group file. This will load all of the group
-	 * entries.
-	 */
-
-	if (!gr_lock ()) {
-		fprintf (stderr, _("%s: error locking group file\n"),
-			 Prog);
-		fail_exit (E_GRP_UPDATE);
-	}
-	if (!gr_open (O_RDWR)) {
-		fprintf (stderr, _("%s: error opening group file\n"),
-			 Prog);
-		fail_exit (E_GRP_UPDATE);
-	}
-#ifdef	SHADOWGRP
-	if (is_shadow_grp && !sgr_lock ()) {
-		fprintf (stderr,
-			 _("%s: error locking shadow group file\n"), Prog);
-		fail_exit (E_GRP_UPDATE);
-	}
-	if (is_shadow_grp && !sgr_open (O_RDWR)) {
-		fprintf (stderr,
-			 _("%s: error opening shadow group file\n"), Prog);
-		fail_exit (E_GRP_UPDATE);
-	}
-#endif
+        /* Locking and opening of the group files moved to open_files() --gafton */
 
 	/*
 	 * Scan through the entire group file looking for the groups that
@@ -973,8 +957,13 @@
 	const struct passwd *pwd;
 	uid_t uid_min, uid_max;
 
-	uid_min = getdef_unum ("UID_MIN", 100);
-	uid_max = getdef_unum ("UID_MAX", 60000);
+        if (!rflg) {
+                uid_min = getdef_unum ("UID_MIN", 500);
+                uid_max = getdef_unum ("UID_MAX", 60000);
+        } else {
+                uid_min = 1;
+                uid_max = getdef_unum ("UID_MIN", 500) - 1;
+        }
 
 	/*
 	 * Start with some UID value if the user didn't provide us with
@@ -1002,7 +991,7 @@
 				 Prog, user_name);
 			exit (E_NAME_IN_USE);
 		}
-		if (uflg && user_id == pwd->pw_uid) {
+		if (!oflg && uflg && user_id == pwd->pw_uid) {
 			fprintf (stderr, _("%s: uid %u is not unique\n"),
 				 Prog, (unsigned int) user_id);
 			exit (E_UID_IN_USE);
@@ -1041,6 +1030,86 @@
 }
 
 /*
+ * find_new_gid - find the next available GID
+ *
+ *     find_new_gid() locates the next highest unused GID in the group
+ *     file, or checks the given group ID against the existing ones for
+ *     uniqueness.
+ */
+
+static void
+find_new_gid()
+{
+       const struct group *grp;
+       gid_t gid_min, gid_max;
+
+       if (!rflg) {
+           gid_min = getdef_num("GID_MIN", 500);
+           gid_max = getdef_num("GID_MAX", 60000);
+       } else {
+           gid_min = 1;
+           gid_max = getdef_num("GID_MIN", 500) - 1;
+       }
+
+       /*
+        * Start with some GID value if the user didn't provide us with
+        * one already.
+        */
+       user_gid = gid_min;
+
+       /*
+        * Search the entire group file, either looking for this
+        * GID (if the user specified one with -g) or looking for the
+        * largest unused value.
+        */
+#ifdef NO_GETGRENT
+       gr_rewind();
+       while ((grp = gr_next()))
+#else
+       setgrent();
+       while ((grp = getgrent()))
+#endif
+           {
+               if (strcmp(user_name, grp->gr_name) == 0) {
+                   user_gid = grp->gr_gid;
+                   return;
+               }
+               if (grp->gr_gid >= user_gid) {
+                   if (grp->gr_gid > gid_max)
+                       continue;
+                   user_gid = grp->gr_gid + 1;
+               }
+       }
+#ifndef NO_GETGRENT /* glibc does have this, so ... */
+       /* A quick test gets here: if the UID is available
+        * as a GID, go ahead and use it */
+       if (!getgrgid(user_id)) {
+           user_gid = user_id;
+           return;
+       }
+#endif
+       if (user_gid == gid_max + 1) {
+               for (user_gid = gid_min; user_gid < gid_max; user_gid++) {
+#ifdef NO_GETGRENT
+                       gr_rewind();
+                       while ((grp = gr_next()) && grp->gr_gid != user_gid)
+                               ;
+                       if (!grp)
+                               break;
+#else
+                       if (!getgrgid(user_gid))
+                               break;
+#endif
+               }
+               if (user_gid == gid_max) {
+                       fprintf(stderr, "%s: can't get unique gid (run out of GIDs)\n",
+                               Prog);
+                       fail_exit(4);
+               }
+       }
+}
+
+/*
  * process_flags - perform command line argument setting
  *
  *	process_flags() interprets the command line arguments and sets
@@ -1056,9 +1125,9 @@
 	char *cp;
 
 #ifdef SHADOWPWD
-#define FLAGS "A:Du:og:G:d:s:c:mk:p:f:e:b:O:M"
+#define FLAGS "A:Du:og:G:d:s:c:mk:p:f:e:b:O:Mnr"
 #else
-#define FLAGS "A:Du:og:G:d:s:c:mk:p:b:O:M"
+#define FLAGS "A:Du:og:G:d:s:c:mk:p:b:O:Mnr"
 #endif
 	while ((arg = getopt (argc, argv, FLAGS)) != EOF) {
 #undef FLAGS
@@ -1176,12 +1245,6 @@
 		case 'm':
 			mflg++;
 			break;
-		case 'M':
-			/*
-			 * don't create home dir - this is the default,
-			 * ignored for RedHat/PLD adduser compatibility.
-			 */
-			break;
 		case 'o':
 			oflg++;
 			break;
@@ -1229,12 +1292,24 @@
 			user_id = get_uid (optarg);
 			uflg++;
 			break;
+                case 'n':
+                    nflg++;
+                    break;
+                case 'r':
+                    rflg++;
+                    break;
+                case 'M':
+                    Mflg++;
+                    break;
 		default:
 			usage ();
 		}
 		anyflag++;
 	}
 
+	if (mflg && Mflg) /* the admin is not decided .. create or not ? */
+		usage();
+
 	/*
 	 * Certain options are only valid in combination with others.
 	 * Check it here so that they can be specified in any order.
@@ -1313,7 +1388,6 @@
 				 Prog);
 			fail_exit (E_GRP_UPDATE);
 		}
-		gr_unlock ();
 #ifdef	SHADOWGRP
 		if (is_shadow_grp && !sgr_close ()) {
 			fprintf (stderr,
@@ -1322,15 +1396,8 @@
 				 Prog);
 			fail_exit (E_GRP_UPDATE);
 		}
-		if (is_shadow_grp)
-			sgr_unlock ();
 #endif
 	}
-#ifdef	SHADOWPWD
-	if (is_shadow_pwd)
-		spw_unlock ();
-#endif
-	pw_unlock ();
 }
 
 /*
@@ -1369,6 +1436,31 @@
 		exit (E_PW_UPDATE);
 	}
 #endif
+       /*
+        * Lock and open the group file.  This will load all of the group
+        * entries.
+        */
+
+       if (! gr_lock ()) {
+               fprintf(stderr, _("%s: error locking group file\n"), Prog);
+               fail_exit(E_GRP_UPDATE);
+       }
+       if (! gr_open (O_RDWR)) {
+               fprintf(stderr, _("%s: error opening group file\n"), Prog);
+               fail_exit(E_GRP_UPDATE);
+       }
+#ifdef SHADOWGRP
+       if (is_shadow_grp && ! sgr_lock ()) {
+               fprintf(stderr, _("%s: error locking shadow group file\n"),
+                       Prog);
+               fail_exit(E_GRP_UPDATE);
+       }
+       if (is_shadow_grp && ! sgr_open (O_RDWR)) {
+               fprintf(stderr, _("%s: error opening shadow group file\n"),
+                       Prog);
+               fail_exit(E_GRP_UPDATE);
+       }
+#endif        /* SHADOWGRP*/
 }
 
 
@@ -1520,6 +1612,118 @@
 		grp_update ();
 }
 
+/* a fake something */
+static char *empty_list = NULL;
+
+/*
+ * new_grent - initialize the values in a group file entry
+ *
+ *     new_grent() takes all of the values that have been entered and
+ *     fills in a (struct group) with them.
+ */
+
+static void
+new_grent(grent)
+       struct group *grent;
+{
+       bzero ((char *) grent, sizeof *grent);
+       grent->gr_name = (char *) user_name;
+       grent->gr_passwd = "x";
+       grent->gr_gid = user_gid;
+       grent->gr_mem = &empty_list;
+}
+
+#ifdef SHADOWGRP
+/*
+ * new_sgent - initialize the values in a shadow group file entry
+ *
+ *     new_sgent() takes all of the values that have been entered and
+ *     fills in a (struct sgrp) with them.
+ */
+
+static void
+new_sgent(sgent)
+       struct sgrp *sgent;
+{
+       bzero ((char *) sgent, sizeof *sgent);
+       sgent->sg_name = (char *) user_name;
+       sgent->sg_passwd = "!";
+       sgent->sg_adm = &empty_list;
+       sgent->sg_mem = &empty_list;
+}
+#endif /* SHADOWGRP */
+
+/*
+ * grp_update - add new group file entries
+ *
+ *     grp_update() writes the new records to the group files.
+ */
+
+static void grp_add()
+{
+       struct  group   grp;
+#ifdef SHADOWGRP
+       struct  sgrp    sgrp;
+#endif /* SHADOWGRP */
+
+       /*
+        * Create the initial entries for this new group.
+        */
+
+       new_grent (&grp);
+#ifdef SHADOWGRP
+       new_sgent (&sgrp);
+#endif /* SHADOWGRP */
+
+       /*
+        * Write out the new group file entry.
+        */
+       if (! gr_update (&grp)) {
+               fprintf (stderr, "%s: error adding new group entry\n", Prog);
+               fail_exit (10);
+       }
+#ifdef NDBM
+
+       /*
+        * Update the DBM group file with the new entry as well.
+        */
+
+       if (gr_dbm_present() && ! gr_dbm_update (&grp)) {
+               fprintf (stderr, "%s: cannot add new dbm group entry\n", Prog);
+               fail_exit (10);
+       }
+       endgrent ();
+#endif /* NDBM */
+
+#ifdef SHADOWGRP
+
+       /*
+        * Write out the new shadow group entries as well.
+        */
+
+       if (is_shadow_grp && ! sgr_update (&sgrp)) {
+               fprintf (stderr, "%s: error adding new group entry\n", Prog);
+               fail_exit (10);
+       }
+#ifdef NDBM
+
+       /*
+        * Update the DBM group file with the new entry as well.
+        */
+
+       if (is_shadow_grp && sg_dbm_present() && ! sg_dbm_update (&sgrp)) {
+               fprintf (stderr, "%s: cannot add new dbm group entry\n", Prog);
+               fail_exit (10);
+       }
+       endsgent ();
+#endif /* NDBM */
+#endif /* SHADOWGRP */
+       SYSLOG((LOG_INFO, "new group: name=%s, gid=%d\n",
+               user_name, user_gid));
+       /* we need to remeber we have to close the group file... */
+       do_grp_update++;
+}
+
 /*
  * create_home - create the user's home directory
  *
@@ -1693,6 +1897,14 @@
 #endif
 #endif
 
+	if (!rflg) /* for system accounts defaults are ignored and we
+                    * do not create a home dir -- gafton */
+	    if (getdef_bool("CREATE_HOME"))
+		mflg = 1;
+
+	if (Mflg) /* absolutely sure that we do not create home dirs */
+	    mflg = 0;
+
 	/*
 	 * See if we are messing with the defaults file, or creating
 	 * a new user.
@@ -1722,7 +1934,7 @@
 	 * to that group, use useradd -g username username.
 	 * --bero
 	 */
-	if (!gflg) {
+	if ( !(nflg || gflg) ) {
 		if (getgrnam (user_name)) {
 			fprintf (stderr,
 				 _
@@ -1744,29 +1956,36 @@
 
 	open_files ();
 
+	/* first, seek for a valid uid to use for this user.
+	 * We do this because later we can use the uid we found as
+	 * gid too ... --gafton */
+	find_new_uid ();
+
+	/* do we have to add a group for that user? This is why we need to
+	 * open the group files in the open_files() function  --gafton */
+	if (! (nflg || gflg)) {
+		find_new_gid();
+		grp_add();
+	}
+
 	usr_update ();
 
 	if (mflg) {
 		create_home ();
 		copy_tree (def_template, user_home, user_id, user_gid);
-	} else if (getdef_str ("CREATE_HOME")) {
-		/*
-		 * RedHat added the CREATE_HOME option in login.defs in their
-		 * version of shadow-utils (which makes -m the default, with
-		 * new -M option to turn it off). Unfortunately, this
-		 * changes the way useradd works (it can be run by scripts
-		 * expecting some standard behaviour), compared to other
-		 * Unices and other Linux distributions, and also adds a lot
-		 * of confusion :-(.
-		 * So we now recognize CREATE_HOME and give a warning here
-		 * (better than "configuration error ... notify administrator"
-		 * errors in every program that reads /etc/login.defs). -MM
-		 */
-		fprintf (stderr,
-			 _
-			 ("%s: warning: CREATE_HOME not supported, please use -m instead.\n"),
-			 Prog);
 	}
+	/* Warning removed to protect the innocent. */
+	/*
+	 * The whole idea about breaking some stupid scripts by creating a new
+	 * variable is crap - I could care less about the scripts. Historically
+	 * adduser type programs have always created the home directories and
+	 * I don't like the idea of providing a script when we can fix the
+	 * binary itself. And if the scripts are using the right options to the
+	 * useradd then they will not break. If not, they depend on unspecified
+	 * behavior and they will break, but they were broken anyway to begin
+         * with --gafton
+         */
+
 
 	create_mail ();
 
--- shadow-4.0.7/src/userdel.c.redhat	2005-01-18 00:12:05.000000000 +0100
+++ shadow-4.0.7/src/userdel.c	2005-03-01 17:06:16.000000000 +0100
@@ -60,7 +60,9 @@
 #define E_HOMEDIR	12	/* can't remove home directory */
 static char *user_name;
 static uid_t user_id;
+static gid_t user_gid;
 static char *user_home;
+static char *user_group;
 
 static char *Prog;
 static int fflg = 0, rflg = 0;
@@ -280,6 +282,100 @@
 #endif				/* SHADOWGRP */
 }
 
+/* remove_group()
+ * remove the user's group unless it is not really a user-private group
+ */
+static void
+remove_group ()
+{
+       char    *glist_name;
+       struct  group   *gr;
+       struct  passwd  *pwd;
+#ifdef NDBM
+       struct  group   *ogrp;
+#endif
+
+       if (user_group == NULL || user_name == NULL)
+           return;
+
+       if (strcmp(user_name, user_group)) {
+           return;
+       }
+
+       glist_name = NULL;
+       gr = getgrnam(user_group);
+       if (gr)
+           glist_name = *(gr->gr_mem);
+       while (glist_name) {
+           while (glist_name && *glist_name) {
+               if (strncmp(glist_name, user_name, 16)) {
+                   return;
+               }
+               glist_name++;
+           }
+       }
+
+       setpwent();
+       while ((pwd = getpwent())) {
+           if (strcmp(pwd->pw_name, user_name) == 0)
+               continue;
+
+           if (pwd->pw_gid == user_gid) {
+               return;
+           }
+       }
+
+       /* now actually do the removal if we haven't already returned */
+
+       if (! gr_remove (user_group)) {
+           fprintf (stderr, "%s: error removing group entry\n", Prog);
+       }
+#ifdef NDBM
+
+       /*
+        * Update the DBM group file
+        */
+
+       if (gr_dbm_present()) {
+               if ((ogrp = getgrnam (user_group)) &&
+                               ! gr_dbm_remove (ogrp)) {
+                       fprintf (stderr, "%s: error removing group dbm entry\n",
+                               Prog);
+               }
+       }
+       endgrent ();
+#endif /* NDBM */
+
+#ifdef SHADOWGRP
+
+       /*
+        * Delete the shadow group entries as well.
+        */
+
+       if (is_shadow_grp && ! sgr_remove (user_group)) {
+               fprintf (stderr, "%s: error removing shadow group entry\n",
+                       Prog);
+       }
+#ifdef NDBM
+
+       /*
+        * Update the DBM shadow group file
+        */
+
+       if (is_shadow_grp && sg_dbm_present()) {
+               if (! sg_dbm_remove (user_group)) {
+                       fprintf (stderr,
+                               "%s: error removing shadow group dbm entry\n",
+                               Prog);
+               }
+       }
+       endsgent ();
+#endif /* NDBM */
+#endif /* SHADOWGRP */
+       SYSLOG((LOG_INFO, "remove group `%s'\n", user_group));
+       return;
+}
+
 /*
  * close_files - close all of the files that were opened
  *
@@ -483,7 +579,8 @@
 			 _
 			 ("%s: user %s is currently logged in\n"),
 			 Prog, name);
-		exit (E_USER_BUSY);
+		if (!fflg)
+			exit (E_USER_BUSY);
 	}
 }
 
@@ -614,6 +711,7 @@
 int main (int argc, char **argv)
 {
 	struct passwd *pwd;
+	struct group *grp;
 	int arg;
 	int errors = 0;
 
@@ -732,6 +830,10 @@
 #endif
 	user_id = pwd->pw_uid;
 	user_home = xstrdup (pwd->pw_dir);
+	user_gid = pwd->pw_gid;
+	grp = getgrgid(user_gid);
+	if (grp)
+		user_group = xstrdup(grp->gr_name);
 	/*
 	 * Check to make certain the user isn't logged in.
 	 */
@@ -789,6 +891,9 @@
 	}
 #endif
 
+	/* Remove the user's group if appropriate. */
+	remove_group();
+
 	if (rflg) {
 		if (remove_tree (user_home)
 		    || rmdir (user_home)) {
--- shadow-4.0.7/src/groupadd.c.redhat	2005-01-18 00:02:41.000000000 +0100
+++ shadow-4.0.7/src/groupadd.c	2005-03-01 17:06:16.000000000 +0100
@@ -72,6 +72,8 @@
 static int oflg = 0;		/* permit non-unique group ID to be specified with -g */
 static int gflg = 0;		/* ID value for the new group */
 static int fflg = 0;		/* if group already exists, do nothing and exit(0) */
+static int rflg = 0;            /* for adding system accounts (Red Hat) */
+
 
 #ifdef	NDBM
 extern int gr_dbm_mode;
@@ -99,7 +101,7 @@
 
 static void usage (void)
 {
-	fprintf (stderr, _("Usage: groupadd [-g gid [-o]] [-f] group\n"));
+	fprintf (stderr, _("usage: groupadd [-g gid [-o]] [-r] [-f] group\n"));
 	exit (E_USAGE);
 }
 
@@ -225,8 +227,13 @@
 	const struct group *grp;
 	gid_t gid_min, gid_max;
 
-	gid_min = getdef_unum ("GID_MIN", 100);
-	gid_max = getdef_unum ("GID_MAX", 60000);
+	if (!rflg) {
+		gid_min = getdef_unum ("GID_MIN", 500);
+		gid_max = getdef_unum ("GID_MAX", 60000);
+	} else {
+		gid_min = 1;
+		gid_max = getdef_unum ("GID_MIN", 500) - 1;
+	}
 
 	/*
 	 * Start with some GID value if the user didn't provide us with
@@ -334,7 +341,7 @@
 	char *cp;
 	int arg;
 
-	while ((arg = getopt (argc, argv, "og:O:f")) != EOF) {
+	while ((arg = getopt (argc, argv, "og:O:fr")) != EOF) {
 		switch (arg) {
 		case 'g':
 			gflg++;
@@ -375,11 +382,17 @@
 			 * "force" - do nothing, just exit(0), if the
 			 * specified group already exists. With -g, if
 			 * specified gid already exists, choose another
-			 * (unique) gid (turn off -g). Based on the RedHat's
+			 * (unique) gid (turn off -g). Based on the Red Hat's
 			 * patch from shadow-utils-970616-9.
 			 */
 			fflg++;
 			break;
+		case 'r':
+			/*
+                         * create a system group
+                         */
+                        rflg++;
+                        break;
 		default:
 			usage ();
 		}


More information about the Pkg-shadow-devel mailing list