2 * Copyright (c) 2000 , International Business Machines
3 * George Kraft IV, gk4@us.ibm.com, 03/23/2000
4 * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5 * Copyright (c) 2007 - 2008, Nicolas François
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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 * 3. The name of the copyright holders or contributors may not be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 #include <sys/types.h>
45 #include "prototypes.h"
48 /* Exit Status Values */
50 #define EXIT_SUCCESS 0 /* success */
51 #define EXIT_USAGE 1 /* invalid command syntax */
52 #define EXIT_GROUP_FILE 2 /* group file access problems */
53 #define EXIT_NOT_ROOT 3 /* not superuser */
54 #define EXIT_NOT_EROOT 4 /* not effective superuser */
55 #define EXIT_NOT_PRIMARY 5 /* not primary owner of group */
56 #define EXIT_NOT_MEMBER 6 /* member of group does not exist */
57 #define EXIT_MEMBER_EXISTS 7 /* member of group already exists */
58 #define EXIT_INVALID_USER 8 /* specified user does not exist */
59 #define EXIT_INVALID_GROUP 9 /* specified group does not exist */
64 static char *adduser = NULL;
65 static char *deluser = NULL;
66 static char *thisgroup = NULL;
67 static bool purge = false;
68 static bool list = false;
69 static int exclusive = 0;
71 static bool group_locked = false;
73 static char *whoami (void);
74 static void members (char **members);
75 static void usage (void);
76 static void process_flags (int argc, char **argv);
77 static void check_perms (void);
78 static void fail_exit (int code);
79 #define isroot() (getuid () == 0)
81 static char *whoami (void)
83 /* local, no need for xgetgrgid */
84 struct group *grp = getgrgid (getgid ());
85 /* local, no need for xgetpwuid */
86 struct passwd *usr = getpwuid (getuid ());
88 if (0 == strcmp (usr->pw_name, grp->gr_name)) {
89 return xstrdup (usr->pw_name);
95 static void members (char **members)
99 for (i = 0; NULL != members[i]; i++) {
100 printf ("%s ", members[i]);
102 if (NULL == members[i + 1]) {
110 static void usage (void)
112 fputs (_("Usage: groupmems -a username | -d username | -D | -l [-g groupname]\n"), stderr);
113 fail_exit (EXIT_USAGE);
117 * process_flags - perform command line argument setting
119 static void process_flags (int argc, char **argv)
122 int option_index = 0;
123 static struct option long_options[] = {
124 {"add", required_argument, NULL, 'a'},
125 {"delete", required_argument, NULL, 'd'},
126 {"group", required_argument, NULL, 'g'},
127 {"list", no_argument, NULL, 'l'},
128 {"purge", no_argument, NULL, 'p'},
129 {NULL, 0, NULL, '\0'}
132 while ((arg = getopt_long (argc, argv, "a:d:g:lp", long_options,
133 &option_index)) != EOF) {
136 adduser = xstrdup (optarg);
140 deluser = xstrdup (optarg);
148 thisgroup = xstrdup (optarg);
159 if ((exclusive > 1) || (optind < argc)) {
163 /* local, no need for xgetpwnam */
164 if (getpwnam (adduser) == NULL) {
165 fprintf (stderr, _("%s: user `%s' does not exist\n"),
167 fail_exit (EXIT_INVALID_USER);
172 static void check_perms (void)
175 pam_handle_t *pamh = NULL;
176 int retval = PAM_SUCCESS;
177 struct passwd *pampw;
179 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
181 retval = PAM_USER_UNKNOWN;
184 if (PAM_SUCCESS == retval) {
185 retval = pam_start ("groupmod", pampw->pw_name,
189 if (PAM_SUCCESS == retval) {
190 retval = pam_authenticate (pamh, 0);
193 if (PAM_SUCCESS == retval) {
194 retval = pam_acct_mgmt (pamh, 0);
197 (void) pam_end (pamh, retval);
198 if (PAM_SUCCESS != retval) {
199 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
205 static void fail_exit (int code)
208 if (gr_unlock () == 0) {
210 _("%s: unable to unlock group file\n"),
217 void main (int argc, char **argv)
223 * Get my name so that I can use it to report errors.
225 Prog = Basename (argv[0]);
227 (void) setlocale (LC_ALL, "");
228 (void) bindtextdomain (PACKAGE, LOCALEDIR);
229 (void) textdomain (PACKAGE);
231 process_flags (argc, argv);
233 if (NULL == thisgroup) {
235 if (!list && (NULL == name)) {
236 fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog);
237 fail_exit (EXIT_NOT_PRIMARY);
241 if (!list && !isroot ()) {
242 fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog);
243 fail_exit (EXIT_NOT_ROOT);
250 if (gr_lock () == 0) {
252 _("%s: unable to lock group file\n"), Prog);
253 fail_exit (EXIT_GROUP_FILE);
258 if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
259 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
260 fail_exit (EXIT_GROUP_FILE);
263 grp = (struct group *) gr_locate (name);
266 fprintf (stderr, _("%s: `%s' not found in /etc/group\n"),
268 fail_exit (EXIT_INVALID_GROUP);
272 members (grp->gr_mem);
273 } else if (NULL != adduser) {
274 if (is_on_list (grp->gr_mem, adduser)) {
276 _("%s: user `%s' is already a member of `%s'\n"),
277 Prog, adduser, grp->gr_name);
278 fail_exit (EXIT_MEMBER_EXISTS);
280 grp->gr_mem = add_list (grp->gr_mem, adduser);
282 } else if (NULL != deluser) {
283 if (!is_on_list (grp->gr_mem, adduser)) {
285 _("%s: user `%s' is not a member of `%s'\n"),
286 Prog, deluser, grp->gr_name);
287 fail_exit (EXIT_NOT_MEMBER);
289 grp->gr_mem = del_list (grp->gr_mem, deluser);
292 grp->gr_mem[0] = NULL;
296 if (gr_close () == 0) {
297 fprintf (stderr, _("%s: unable to close group file\n"), Prog);
298 fail_exit (EXIT_GROUP_FILE);
301 fail_exit (EXIT_SUCCESS);