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 ());
90 && (0 == strcmp (usr->pw_name, grp->gr_name))) {
91 return xstrdup (usr->pw_name);
97 static void members (char **members)
101 for (i = 0; NULL != members[i]; i++) {
102 printf ("%s ", members[i]);
104 if (NULL == members[i + 1]) {
112 static void usage (void)
114 fputs (_("Usage: groupmems -a username | -d username | -D | -l [-g groupname]\n"), stderr);
115 fail_exit (EXIT_USAGE);
119 * process_flags - perform command line argument setting
121 static void process_flags (int argc, char **argv)
124 int option_index = 0;
125 static struct option long_options[] = {
126 {"add", required_argument, NULL, 'a'},
127 {"delete", required_argument, NULL, 'd'},
128 {"group", required_argument, NULL, 'g'},
129 {"list", no_argument, NULL, 'l'},
130 {"purge", no_argument, NULL, 'p'},
131 {NULL, 0, NULL, '\0'}
134 while ((arg = getopt_long (argc, argv, "a:d:g:lp", long_options,
135 &option_index)) != EOF) {
138 adduser = xstrdup (optarg);
142 deluser = xstrdup (optarg);
150 thisgroup = xstrdup (optarg);
161 if ((exclusive > 1) || (optind < argc)) {
165 /* local, no need for xgetpwnam */
166 if (getpwnam (adduser) == NULL) {
167 fprintf (stderr, _("%s: user `%s' does not exist\n"),
169 fail_exit (EXIT_INVALID_USER);
174 static void check_perms (void)
177 pam_handle_t *pamh = NULL;
178 int retval = PAM_SUCCESS;
179 struct passwd *pampw;
181 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
183 retval = PAM_USER_UNKNOWN;
186 if (PAM_SUCCESS == retval) {
187 retval = pam_start ("groupmod", pampw->pw_name,
191 if (PAM_SUCCESS == retval) {
192 retval = pam_authenticate (pamh, 0);
195 if (PAM_SUCCESS == retval) {
196 retval = pam_acct_mgmt (pamh, 0);
199 (void) pam_end (pamh, retval);
200 if (PAM_SUCCESS != retval) {
201 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
207 static void fail_exit (int code)
210 if (gr_unlock () == 0) {
212 _("%s: unable to unlock group file\n"),
219 void main (int argc, char **argv)
225 * Get my name so that I can use it to report errors.
227 Prog = Basename (argv[0]);
229 (void) setlocale (LC_ALL, "");
230 (void) bindtextdomain (PACKAGE, LOCALEDIR);
231 (void) textdomain (PACKAGE);
233 process_flags (argc, argv);
235 if (NULL == thisgroup) {
237 if (!list && (NULL == name)) {
238 fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog);
239 fail_exit (EXIT_NOT_PRIMARY);
243 if (!list && !isroot ()) {
244 fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog);
245 fail_exit (EXIT_NOT_ROOT);
252 if (gr_lock () == 0) {
254 _("%s: unable to lock group file\n"), Prog);
255 fail_exit (EXIT_GROUP_FILE);
260 if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
261 fprintf (stderr, _("%s: unable to open group file\n"), Prog);
262 fail_exit (EXIT_GROUP_FILE);
265 grp = (struct group *) gr_locate (name);
268 fprintf (stderr, _("%s: `%s' not found in /etc/group\n"),
270 fail_exit (EXIT_INVALID_GROUP);
274 members (grp->gr_mem);
275 } else if (NULL != adduser) {
276 if (is_on_list (grp->gr_mem, adduser)) {
278 _("%s: user `%s' is already a member of `%s'\n"),
279 Prog, adduser, grp->gr_name);
280 fail_exit (EXIT_MEMBER_EXISTS);
282 grp->gr_mem = add_list (grp->gr_mem, adduser);
284 } else if (NULL != deluser) {
285 if (!is_on_list (grp->gr_mem, adduser)) {
287 _("%s: user `%s' is not a member of `%s'\n"),
288 Prog, deluser, grp->gr_name);
289 fail_exit (EXIT_NOT_MEMBER);
291 grp->gr_mem = del_list (grp->gr_mem, deluser);
294 grp->gr_mem[0] = NULL;
298 if (gr_close () == 0) {
299 fprintf (stderr, _("%s: unable to close group file\n"), Prog);
300 fail_exit (EXIT_GROUP_FILE);
303 fail_exit (EXIT_SUCCESS);