11
13
* 2. Redistributions in binary form must reproduce the above copyright
12
14
* notice, this list of conditions and the following disclaimer in the
13
15
* documentation and/or other materials provided with the distribution.
14
* 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15
* may be used to endorse or promote products derived from this software
16
* without specific prior written permission.
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.
18
* THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
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.
31
33
#include <config.h>
52
58
#define EXIT_NOT_PRIMARY 5 /* not primary owner of group */
53
59
#define EXIT_NOT_MEMBER 6 /* member of group does not exist */
54
60
#define EXIT_MEMBER_EXISTS 7 /* member of group already exists */
61
#define EXIT_INVALID_USER 8 /* specified user does not exist */
62
#define EXIT_INVALID_GROUP 9 /* specified group does not exist */
62
69
static char *adduser = NULL;
63
70
static char *deluser = NULL;
64
71
static char *thisgroup = NULL;
65
static int purge = FALSE;
66
static int list = FALSE;
72
static bool purge = false;
73
static bool list = false;
67
74
static int exclusive = 0;
69
static int isroot (void)
71
return getuid ()? FALSE : TRUE;
74
static int isgroup (void)
77
struct group *grp = getgrgid (g); /* local, no need for xgetgrgid */
75
static bool gr_locked = false;
77
/* Indicate if shadow groups are enabled on the system
78
* (/etc/gshadow present) */
79
static bool is_shadowgrp;
80
static bool sgr_locked = false;
83
/* local function prototypes */
84
static char *whoami (void);
85
static void add_user (const char *user,
86
const struct group *grp);
87
static void remove_user (const char *user,
88
const struct group *grp);
89
static void purge_members (const struct group *grp);
90
static void display_members (const char *const *members);
91
static void usage (void);
92
static void process_flags (int argc, char **argv);
93
static void check_perms (void);
94
static void fail_exit (int code);
95
#define isroot() (getuid () == 0)
82
97
static char *whoami (void)
86
101
/* local, no need for xgetpwuid */
87
102
struct passwd *usr = getpwuid (getuid ());
89
if (0 == strcmp (usr->pw_name, grp->gr_name)) {
90
return (char *) strdup (usr->pw_name);
106
&& (0 == strcmp (usr->pw_name, grp->gr_name))) {
107
return xstrdup (usr->pw_name);
96
static void addtogroup (char *user, char **members)
100
for (i = 0; NULL != members[i]; i++) {
101
if (0 == strcmp (user, members[i])) {
102
fputs (_("Member already exists\n"), stderr);
103
exit (EXIT_MEMBER_EXISTS);
107
members = (char **) realloc (members, sizeof (char *) * (i+2));
109
members[i + 1] = NULL;
112
static void rmfromgroup (char *user, char **members)
118
while (!found && NULL != members[i]) {
119
if (0 == strcmp (user, members[i])) {
126
while (found && NULL != members[i]) {
127
members[i] = members[i+1];
132
fputs (_("Member to remove could not be found\n"), stderr);
133
exit (EXIT_NOT_MEMBER);
137
static void nomembers (char **members)
141
for (i = 0; NULL != members[i]; i++) {
146
static void members (char **members)
114
* add_user - Add an user to the specified group
116
static void add_user (const char *user,
117
const struct group *grp)
119
struct group *newgrp;
121
/* Make sure the user is not already part of the group */
122
if (is_on_list (grp->gr_mem, user)) {
124
_("%s: user '%s' is already a member of '%s'\n"),
125
Prog, user, grp->gr_name);
126
fail_exit (EXIT_MEMBER_EXISTS);
129
newgrp = __gr_dup(grp);
130
if (NULL == newgrp) {
132
_("%s: Out of memory. Cannot update %s.\n"),
137
/* Add the user to the /etc/group group */
138
newgrp->gr_mem = add_list (newgrp->gr_mem, user);
142
const struct sgrp *sg = sgr_locate (newgrp->gr_name);
146
/* Create a shadow group based on this group */
147
static struct sgrp sgrent;
148
sgrent.sg_name = xstrdup (newgrp->gr_name);
149
sgrent.sg_mem = dup_list (newgrp->gr_mem);
150
sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
151
#ifdef FIRST_MEMBER_IS_ADMIN
152
if (sgrent.sg_mem[0]) {
153
sgrent.sg_adm[0] = xstrdup (sgrent.sg_mem[0]);
154
sgrent.sg_adm[1] = NULL;
158
sgrent.sg_adm[0] = NULL;
161
/* Move any password to gshadow */
162
sgrent.sg_passwd = newgrp->gr_passwd;
163
newgrp->gr_passwd = SHADOW_PASSWD_STRING;
167
newsg = __sgr_dup (sg);
170
_("%s: Out of memory. Cannot update %s.\n"),
171
Prog, sgr_dbname ());
174
/* Add the user to the members */
175
newsg->sg_mem = add_list (newsg->sg_mem, user);
176
/* Do not touch the administrators */
179
if (sgr_update (newsg) == 0) {
181
_("%s: failed to prepare the new %s entry '%s'\n"),
182
Prog, sgr_dbname (), newsg->sg_name);
188
if (gr_update (newgrp) == 0) {
190
_("%s: failed to prepare the new %s entry '%s'\n"),
191
Prog, gr_dbname (), newgrp->gr_name);
197
* remove_user - Remove an user from a given group
199
static void remove_user (const char *user,
200
const struct group *grp)
202
struct group *newgrp;
204
/* Check if the user is a member of the specified group */
205
if (!is_on_list (grp->gr_mem, user)) {
207
_("%s: user '%s' is not a member of '%s'\n"),
208
Prog, user, grp->gr_name);
209
fail_exit (EXIT_NOT_MEMBER);
212
newgrp = __gr_dup (grp);
213
if (NULL == newgrp) {
215
_("%s: Out of memory. Cannot update %s.\n"),
220
/* Remove the user from the /etc/group group */
221
newgrp->gr_mem = del_list (newgrp->gr_mem, user);
225
const struct sgrp *sg = sgr_locate (newgrp->gr_name);
229
/* Create a shadow group based on this group */
230
static struct sgrp sgrent;
231
sgrent.sg_name = xstrdup (newgrp->gr_name);
232
sgrent.sg_mem = dup_list (newgrp->gr_mem);
233
sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
234
#ifdef FIRST_MEMBER_IS_ADMIN
235
if (sgrent.sg_mem[0]) {
236
sgrent.sg_adm[0] = xstrdup (sgrent.sg_mem[0]);
237
sgrent.sg_adm[1] = NULL;
241
sgrent.sg_adm[0] = NULL;
244
/* Move any password to gshadow */
245
sgrent.sg_passwd = newgrp->gr_passwd;
246
newgrp->gr_passwd = SHADOW_PASSWD_STRING;
250
newsg = __sgr_dup (sg);
253
_("%s: Out of memory. Cannot update %s.\n"),
254
Prog, sgr_dbname ());
257
/* Remove the user from the members */
258
newsg->sg_mem = del_list (newsg->sg_mem, user);
259
/* Remove the user from the administrators */
260
newsg->sg_adm = del_list (newsg->sg_adm, user);
263
if (sgr_update (newsg) == 0) {
265
_("%s: failed to prepare the new %s entry '%s'\n"),
266
Prog, sgr_dbname (), newsg->sg_name);
272
if (gr_update (newgrp) == 0) {
274
_("%s: failed to prepare the new %s entry '%s'\n"),
275
Prog, gr_dbname (), newgrp->gr_name);
281
* purge_members - Rmeove every members of the specified group
283
static void purge_members (const struct group *grp)
285
struct group *newgrp = __gr_dup (grp);
287
if (NULL == newgrp) {
289
_("%s: Out of memory. Cannot update %s.\n"),
294
/* Remove all the members of the /etc/group group */
295
newgrp->gr_mem[0] = NULL;
299
const struct sgrp *sg = sgr_locate (newgrp->gr_name);
303
/* Create a shadow group based on this group */
304
static struct sgrp sgrent;
305
sgrent.sg_name = xstrdup (newgrp->gr_name);
306
sgrent.sg_mem = (char **) xmalloc (sizeof (char *));
307
sgrent.sg_mem[0] = NULL;
308
sgrent.sg_adm = (char **) xmalloc (sizeof (char *));
309
sgrent.sg_adm[0] = NULL;
311
/* Move any password to gshadow */
312
sgrent.sg_passwd = newgrp->gr_passwd;
313
newgrp->gr_passwd = xstrdup(SHADOW_PASSWD_STRING);
317
newsg = __sgr_dup (sg);
320
_("%s: Out of memory. Cannot update %s.\n"),
321
Prog, sgr_dbname ());
324
/* Remove all the members of the /etc/gshadow
326
newsg->sg_mem[0] = NULL;
327
/* Remove all the administrators of the
328
* /etc/gshadow group */
329
newsg->sg_adm[0] = NULL;
332
if (sgr_update (newsg) == 0) {
334
_("%s: failed to prepare the new %s entry '%s'\n"),
335
Prog, sgr_dbname (), newsg->sg_name);
341
if (gr_update (newgrp) == 0) {
343
_("%s: failed to prepare the new %s entry '%s'\n"),
344
Prog, gr_dbname (), newgrp->gr_name);
349
static void display_members (const char *const *members)
220
if (exclusive > 1 || optind < argc) {
424
if ((exclusive > 1) || (optind < argc)) {
224
if (!isroot () && NULL != thisgroup) {
225
fputs (_("Only root can add members to different groups\n"),
227
exit (EXIT_NOT_ROOT);
228
} else if (isroot () && NULL != thisgroup) {
230
} else if (!isgroup ()) {
231
fputs (_("Group access is required\n"), stderr);
232
exit (EXIT_NOT_EROOT);
233
} else if (NULL == (name = whoami ())) {
234
fputs (_("Not primary owner of current group\n"), stderr);
235
exit (EXIT_NOT_PRIMARY);
428
/* local, no need for xgetpwnam */
429
if ( (NULL != adduser)
430
&& (getpwnam (adduser) == NULL)) {
431
fprintf (stderr, _("%s: user '%s' does not exist\n"),
433
fail_exit (EXIT_INVALID_USER);
438
static void check_perms (void)
238
retval = PAM_SUCCESS;
442
pam_handle_t *pamh = NULL;
241
444
struct passwd *pampw;
242
446
pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
244
retval = PAM_USER_UNKNOWN;
247
if (retval == PAM_SUCCESS) {
248
retval = pam_start ("groupmod", pampw->pw_name,
253
if (retval == PAM_SUCCESS) {
254
retval = pam_authenticate (pamh, 0);
255
if (retval != PAM_SUCCESS) {
256
pam_end (pamh, retval);
260
if (retval == PAM_SUCCESS) {
261
retval = pam_acct_mgmt (pamh, 0);
262
if (retval != PAM_SUCCESS) {
263
pam_end (pamh, retval);
267
if (retval != PAM_SUCCESS) {
268
fputs (_("PAM authentication failed for\n"), stderr);
274
fputs (_("Unable to lock group file\n"), stderr);
275
exit (EXIT_GROUP_FILE);
278
if (!gr_open (O_RDWR)) {
279
fputs (_("Unable to open group file\n"), stderr);
280
exit (EXIT_GROUP_FILE);
283
grp = (struct group *) gr_locate (name);
285
if (NULL != adduser) {
286
addtogroup (adduser, grp->gr_mem);
449
_("%s: Cannot determine your user name.\n"),
454
retval = pam_start ("groupmems", pampw->pw_name, &conv, &pamh);
456
if (PAM_SUCCESS == retval) {
457
retval = pam_authenticate (pamh, 0);
460
if (PAM_SUCCESS == retval) {
461
retval = pam_acct_mgmt (pamh, 0);
465
(void) pam_end (pamh, retval);
467
if (PAM_SUCCESS != retval) {
468
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
475
static void fail_exit (int code)
478
if (gr_unlock () == 0) {
480
_("%s: failed to unlock %s\n"),
482
SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
489
if (sgr_unlock () == 0) {
491
_("%s: failed to unlock %s\n"),
492
Prog, sgr_dbname ());
493
SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
502
static void open_files (void)
505
if (gr_lock () == 0) {
507
_("%s: cannot lock %s; try again later.\n"),
509
fail_exit (EXIT_GROUP_FILE);
515
if (sgr_lock () == 0) {
517
_("%s: cannot lock %s; try again later.\n"),
518
Prog, sgr_dbname ());
519
fail_exit (EXIT_GROUP_FILE);
526
if (gr_open (list ? O_RDONLY : O_RDWR) == 0) {
527
fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
528
fail_exit (EXIT_GROUP_FILE);
533
if (sgr_open (list ? O_RDONLY : O_RDWR) == 0) {
534
fprintf (stderr, _("%s: cannot open %s\n"), Prog, sgr_dbname ());
535
fail_exit (EXIT_GROUP_FILE);
541
static void close_files (void)
543
if ((gr_close () == 0) && !list) {
544
fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ());
545
SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ()));
546
fail_exit (EXIT_GROUP_FILE);
549
if (gr_unlock () == 0) {
550
fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ());
551
SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ()));
559
if ((sgr_close () == 0) && !list) {
560
fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ());
561
SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ()));
562
fail_exit (EXIT_GROUP_FILE);
565
if (sgr_unlock () == 0) {
566
fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ());
567
SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ()));
576
int main (int argc, char **argv)
579
const struct group *grp;
582
* Get my name so that I can use it to report errors.
584
Prog = Basename (argv[0]);
586
OPENLOG ("groupmems");
588
(void) setlocale (LC_ALL, "");
589
(void) bindtextdomain (PACKAGE, LOCALEDIR);
590
(void) textdomain (PACKAGE);
593
is_shadowgrp = sgr_file_present ();
596
process_flags (argc, argv);
598
if (NULL == thisgroup) {
600
if (!list && (NULL == name)) {
601
fprintf (stderr, _("%s: your groupname does not match your username\n"), Prog);
602
fail_exit (EXIT_NOT_PRIMARY);
606
if (!list && !isroot ()) {
607
fprintf (stderr, _("%s: only root can use the -g/--group option\n"), Prog);
608
fail_exit (EXIT_NOT_ROOT);
616
grp = gr_locate (name);
618
fprintf (stderr, _("%s: group '%s' does not exist in %s\n"),
619
Prog, name, gr_dbname ());
620
fail_exit (EXIT_INVALID_GROUP);
624
display_members ((const char *const *)grp->gr_mem);
625
} else if (NULL != adduser) {
626
add_user (adduser, grp);
288
627
} else if (NULL != deluser) {
289
rmfromgroup (deluser, grp->gr_mem);
628
remove_user (deluser, grp);
291
629
} else if (purge) {
292
nomembers (grp->gr_mem);
295
members (grp->gr_mem);
299
fputs (_("Cannot close group file\n"), stderr);
300
exit (EXIT_GROUP_FILE);
306
if (retval == PAM_SUCCESS) {
307
pam_end (pamh, PAM_SUCCESS);
310
635
exit (EXIT_SUCCESS);