2 * Copyright (c) 1991 - 1994, Julianne Frances Haugh
3 * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4 * Copyright (c) 2000 - 2006, Tomasz Kłoczko
5 * Copyright (c) 2007 - 2009, 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.
35 #ident "$Id: groupmod.c 3015 2009-06-05 22:16:56Z nekral-guest $"
42 #include <sys/types.h>
43 #ifdef ACCT_TOOLS_SETUID
48 #endif /* ACCT_TOOLS_SETUID */
54 #include "prototypes.h"
62 #define E_SUCCESS 0 /* success */
63 #define E_USAGE 2 /* invalid command syntax */
64 #define E_BAD_ARG 3 /* invalid argument to option */
65 #define E_GID_IN_USE 4 /* gid already in use (and no -o) */
66 #define E_NOTFOUND 6 /* specified group doesn't exist */
67 #define E_NAME_IN_USE 9 /* group name already in use */
68 #define E_GRP_UPDATE 10 /* can't update group file */
75 static bool is_shadow_grp;
76 #endif /* SHADOWGRP */
77 static char *group_name;
78 static char *group_newname;
79 static char *group_passwd;
80 static gid_t group_id;
81 static gid_t group_newid;
83 struct cleanup_info_mod info_passwd;
84 struct cleanup_info_mod info_group;
86 struct cleanup_info_mod info_gshadow;
90 oflg = false, /* permit non-unique group ID to be specified with -g */
91 gflg = false, /* new ID value for the group */
92 nflg = false, /* a new name has been specified for the group */
93 pflg = false; /* new encrypted password */
95 /* local function prototypes */
96 static void usage (void);
97 static void new_grent (struct group *);
100 static void new_sgent (struct sgrp *);
102 static void grp_update (void);
103 static void check_new_gid (void);
104 static void check_new_name (void);
105 static void process_flags (int, char **);
106 static void lock_files (void);
107 static void prepare_failure_reports (void);
108 static void open_files (void);
109 static void close_files (void);
110 static void update_primary_groups (gid_t ogid, gid_t ngid);
113 * usage - display usage message and exit
116 static void usage (void)
118 (void) fprintf (stderr,
119 _("Usage: %s [options] GROUP\n"
123 (void) fputs (_(" -g, --gid GID change the group ID to GID\n"), stderr);
124 (void) fputs (_(" -h, --help display this help message and exit\n"), stderr);
125 (void) fputs (_(" -n, --new-name NEW_GROUP change the name to NEW_GROUP\n"), stderr);
126 (void) fputs (_(" -o, --non-unique allow to use a duplicate (non-unique) GID\n"), stderr);
127 (void) fputs (_(" -p, --password PASSWORD change the password to this (encrypted)\n"
128 " PASSWORD\n"), stderr);
129 (void) fputs ("\n", stderr);
134 * new_grent - updates the values in a group file entry
136 * new_grent() takes all of the values that have been entered and fills
137 * in a (struct group) with them.
139 static void new_grent (struct group *grent)
142 grent->gr_name = xstrdup (group_newname);
146 grent->gr_gid = group_newid;
150 grent->gr_passwd = group_passwd;
156 * new_sgent - updates the values in a shadow group file entry
158 * new_sgent() takes all of the values that have been entered and fills
159 * in a (struct sgrp) with them.
161 static void new_sgent (struct sgrp *sgent)
164 sgent->sg_name = xstrdup (group_newname);
168 sgent->sg_passwd = group_passwd;
171 #endif /* SHADOWGRP */
174 * grp_update - update group file entries
176 * grp_update() updates the new records in the memory databases.
178 static void grp_update (void)
181 const struct group *ogrp;
185 const struct sgrp *osgrp = NULL;
186 #endif /* SHADOWGRP */
189 * Get the current settings for this group.
191 ogrp = gr_locate (group_name);
194 _("%s: group '%s' does not exist in %s\n"),
195 Prog, group_name, gr_dbname ());
203 osgrp = sgr_locate (group_name);
208 grp.gr_passwd = SHADOW_PASSWD_STRING;
212 #endif /* SHADOWGRP */
215 update_primary_groups (ogrp->gr_gid, group_newid);
219 * Write out the new group file entry.
221 if (gr_update (&grp) == 0) {
223 _("%s: failed to prepare the new %s entry '%s'\n"),
224 Prog, gr_dbname (), grp.gr_name);
227 if (nflg && (gr_remove (group_name) == 0)) {
229 _("%s: cannot remove entry '%s' from %s\n"),
230 Prog, grp.gr_name, gr_dbname ());
236 * Make sure there was a shadow entry to begin with.
241 * Write out the new shadow group entries as well.
243 if (sgr_update (&sgrp) == 0) {
245 _("%s: failed to prepare the new %s entry '%s'\n"),
246 Prog, sgr_dbname (), sgrp.sg_name);
249 if (nflg && (sgr_remove (group_name) == 0)) {
251 _("%s: cannot remove entry '%s' from %s\n"),
252 Prog, group_name, sgr_dbname ());
256 #endif /* SHADOWGRP */
260 * check_new_gid - check the new GID value for uniqueness
262 * check_new_gid() insures that the new GID value is unique.
264 static void check_new_gid (void)
267 * First, the easy stuff. If the ID can be duplicated, or if the ID
268 * didn't really change, just return. If the ID didn't change, turn
269 * off those flags. No sense doing needless work.
271 if (group_id == group_newid) {
277 (getgrgid (group_newid) == NULL) /* local, no need for xgetgrgid */
283 * Tell the user what they did wrong.
286 _("%s: GID '%lu' already exists\n"),
287 Prog, (unsigned long int) group_newid);
292 * check_new_name - check the new name for uniqueness
294 * check_new_name() insures that the new name does not exist already.
295 * You can't have the same name twice, period.
297 static void check_new_name (void)
300 * Make sure they are actually changing the name.
302 if (strcmp (group_name, group_newname) == 0) {
307 if (is_valid_group_name (group_newname)) {
310 * If the entry is found, too bad.
312 /* local, no need for xgetgrnam */
313 if (getgrnam (group_newname) != NULL) {
315 _("%s: group '%s' already exists\n"),
316 Prog, group_newname);
317 exit (E_NAME_IN_USE);
323 * All invalid group names land here.
327 _("%s: invalid group name '%s'\n"),
328 Prog, group_newname);
333 * process_flags - perform command line argument setting
335 * process_flags() interprets the command line arguments and sets the
336 * values that the user will be created with accordingly. The values
337 * are checked for sanity.
339 static void process_flags (int argc, char **argv)
341 int option_index = 0;
343 static struct option long_options[] = {
344 {"gid", required_argument, NULL, 'g'},
345 {"help", no_argument, NULL, 'h'},
346 {"new-name", required_argument, NULL, 'n'},
347 {"non-unique", no_argument, NULL, 'o'},
348 {"password", required_argument, NULL, 'p'},
349 {NULL, 0, NULL, '\0'}
352 getopt_long (argc, argv, "g:hn:op:",
353 long_options, &option_index)) != -1) {
357 if ( (get_gid (optarg, &group_newid) == 0)
358 || (group_newid == (gid_t)-1)) {
360 _("%s: invalid group ID '%s'\n"),
367 group_newname = optarg;
373 group_passwd = optarg;
385 if (optind != (argc - 1)) {
389 group_name = argv[argc - 1];
393 * close_files - close all of the files that were opened
395 * close_files() closes all of the files that were opened for this new
396 * group. This causes any modified entries to be written out.
398 static void close_files (void)
400 if (gr_close () == 0) {
402 _("%s: failure while writing changes to %s\n"),
407 audit_logger (AUDIT_USER_ACCT, Prog,
408 info_group.audit_msg,
409 group_name, AUDIT_NO_ID,
410 SHADOW_AUDIT_SUCCESS);
413 "group changed in %s (%s)",
414 gr_dbname (), info_group.action));
415 del_cleanup (cleanup_report_mod_group);
417 cleanup_unlock_group (NULL);
418 del_cleanup (cleanup_unlock_group);
423 if (sgr_close () == 0) {
425 _("%s: failure while writing changes to %s\n"),
426 Prog, sgr_dbname ());
430 audit_logger (AUDIT_USER_ACCT, Prog,
431 info_gshadow.audit_msg,
432 group_name, AUDIT_NO_ID,
433 SHADOW_AUDIT_SUCCESS);
436 "group changed in %s (%s)",
437 sgr_dbname (), info_gshadow.action));
438 del_cleanup (cleanup_report_mod_gshadow);
440 cleanup_unlock_gshadow (NULL);
441 del_cleanup (cleanup_unlock_gshadow);
443 #endif /* SHADOWGRP */
446 if (pw_close () == 0) {
448 _("%s: failure while writing changes to %s\n"),
453 audit_logger (AUDIT_USER_ACCT, Prog,
454 info_passwd.audit_msg,
455 group_name, AUDIT_NO_ID,
456 SHADOW_AUDIT_SUCCESS);
459 "group changed in %s (%s)",
460 pw_dbname (), info_passwd.action));
461 del_cleanup (cleanup_report_mod_passwd);
463 cleanup_unlock_passwd (NULL);
464 del_cleanup (cleanup_unlock_passwd);
468 audit_logger (AUDIT_USER_ACCT, Prog,
470 group_name, AUDIT_NO_ID,
471 SHADOW_AUDIT_SUCCESS);
476 * prepare_failure_reports - Prepare the cleanup_info structure for logging
477 * of success and failure to syslog or audit.
479 static void prepare_failure_reports (void)
481 info_group.name = group_name;
483 info_gshadow.name = group_name;
485 info_passwd.name = group_name;
487 info_group.audit_msg = xmalloc (512);
489 info_gshadow.audit_msg = xmalloc (512);
491 info_passwd.audit_msg = xmalloc (512);
493 snprintf (info_group.audit_msg, 511,
494 "changing %s; ", gr_dbname ());
496 snprintf (info_gshadow.audit_msg, 511,
497 "changing %s; ", sgr_dbname ());
499 snprintf (info_passwd.audit_msg, 511,
500 "changing %s; ", pw_dbname ());
502 info_group.action = info_group.audit_msg
503 + strlen (info_group.audit_msg);
505 info_gshadow.action = info_gshadow.audit_msg
506 + strlen (info_gshadow.audit_msg);
508 info_passwd.action = info_passwd.audit_msg
509 + strlen (info_passwd.audit_msg);
511 snprintf (info_group.action, 511 - strlen (info_group.audit_msg),
512 "group %s/%lu", group_name, (unsigned long int) group_id);
514 snprintf (info_gshadow.action, 511 - strlen (info_group.audit_msg),
515 "group %s", group_name);
517 snprintf (info_passwd.action, 511 - strlen (info_group.audit_msg),
518 "group %s/%lu", group_name, (unsigned long int) group_id);
521 strncat (info_group.action, ", new name: ",
522 511 - strlen (info_group.audit_msg));
523 strncat (info_group.action, group_newname,
524 511 - strlen (info_group.audit_msg));
527 strncat (info_gshadow.action, ", new name: ",
528 511 - strlen (info_gshadow.audit_msg));
529 strncat (info_gshadow.action, group_newname,
530 511 - strlen (info_gshadow.audit_msg));
533 strncat (info_passwd.action, ", new name: ",
534 511 - strlen (info_passwd.audit_msg));
535 strncat (info_passwd.action, group_newname,
536 511 - strlen (info_passwd.audit_msg));
539 strncat (info_group.action, ", new password",
540 511 - strlen (info_group.audit_msg));
543 strncat (info_gshadow.action, ", new password",
544 511 - strlen (info_gshadow.audit_msg));
548 strncat (info_group.action, ", new gid: ",
549 511 - strlen (info_group.audit_msg));
550 snprintf (info_group.action+strlen (info_group.action),
551 511 - strlen (info_group.audit_msg),
552 "%lu", (unsigned long int) group_newid);
554 strncat (info_passwd.action, ", new gid: ",
555 511 - strlen (info_passwd.audit_msg));
556 snprintf (info_passwd.action+strlen (info_passwd.action),
557 511 - strlen (info_passwd.audit_msg),
558 "%lu", (unsigned long int) group_newid);
560 info_group.audit_msg[511] = '\0';
562 info_gshadow.audit_msg[511] = '\0';
564 info_passwd.audit_msg[511] = '\0';
566 // FIXME: add a system cleanup
567 add_cleanup (cleanup_report_mod_group, &info_group);
571 add_cleanup (cleanup_report_mod_gshadow, &info_gshadow);
575 add_cleanup (cleanup_report_mod_passwd, &info_passwd);
581 * lock_files - lock the accounts databases
583 * lock_files() locks the group, gshadow, and passwd databases.
585 static void lock_files (void)
587 if (gr_lock () == 0) {
589 _("%s: cannot lock %s; try again later.\n"),
593 add_cleanup (cleanup_unlock_group, NULL);
598 if (sgr_lock () == 0) {
600 _("%s: cannot lock %s; try again later.\n"),
601 Prog, sgr_dbname ());
604 add_cleanup (cleanup_unlock_gshadow, NULL);
609 if (pw_lock () == 0) {
611 _("%s: cannot lock %s; try again later.\n"),
615 add_cleanup (cleanup_unlock_passwd, NULL);
621 * open_files - open the accounts databases
623 * open_files() opens the group, gshadow, and passwd databases.
625 static void open_files (void)
627 if (gr_open (O_RDWR) == 0) {
628 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
629 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
636 if (sgr_open (O_RDWR) == 0) {
638 _("%s: cannot open %s\n"),
639 Prog, sgr_dbname ());
640 SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
644 #endif /* SHADOWGRP */
647 if (pw_open (O_RDWR) == 0) {
649 _("%s: cannot open %s\n"),
651 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
657 void update_primary_groups (gid_t ogid, gid_t ngid)
662 while ((pwd = getpwent ()) != NULL) {
663 if (pwd->pw_gid == ogid) {
664 const struct passwd *lpwd;
666 lpwd = pw_locate (pwd->pw_name);
669 _("%s: user '%s' does not exist in %s\n"),
670 Prog, pwd->pw_name, pw_dbname ());
675 if (pw_update (&npwd) == 0) {
677 _("%s: failed to prepare the new %s entry '%s'\n"),
678 Prog, pw_dbname (), npwd.pw_name);
688 * main - groupmod command
691 int main (int argc, char **argv)
693 #ifdef ACCT_TOOLS_SETUID
695 pam_handle_t *pamh = NULL;
698 #endif /* ACCT_TOOLS_SETUID */
703 atexit (do_cleanups);
706 * Get my name so that I can use it to report errors.
708 Prog = Basename (argv[0]);
710 (void) setlocale (LC_ALL, "");
711 (void) bindtextdomain (PACKAGE, LOCALEDIR);
712 (void) textdomain (PACKAGE);
714 process_flags (argc, argv);
716 OPENLOG ("groupmod");
718 #ifdef ACCT_TOOLS_SETUID
721 struct passwd *pampw;
722 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
725 _("%s: Cannot determine your user name.\n"),
730 retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh);
733 if (PAM_SUCCESS == retval) {
734 retval = pam_authenticate (pamh, 0);
737 if (PAM_SUCCESS == retval) {
738 retval = pam_acct_mgmt (pamh, 0);
742 (void) pam_end (pamh, retval);
744 if (PAM_SUCCESS != retval) {
745 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
749 #endif /* ACCT_TOOLS_SETUID */
752 is_shadow_grp = sgr_file_present ();
757 * Start with a quick check to see if the group exists.
759 grp = getgrnam (group_name); /* local, no need for xgetgrnam */
762 _("%s: group '%s' does not exist\n"),
766 group_id = grp->gr_gid;
772 * Now make sure it isn't an NIS group.
779 _("%s: group %s is a NIS group\n"),
782 if (!yp_get_default_domain (&nis_domain) &&
783 !yp_master (nis_domain, "group.byname", &nis_master)) {
785 _("%s: %s is the NIS master\n"),
803 * Now if the group is not changed, it's our fault.
804 * Make sure failures will be reported.
806 prepare_failure_reports ();
809 * Do the hard stuff - open the files, create the group entries,
810 * then close and update the files.
818 nscd_flush_cache ("group");