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.
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 (int status);
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 (int status)
118 FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
119 (void) fprintf (usageout,
120 _("Usage: %s [options] GROUP\n"
124 (void) fputs (_(" -g, --gid GID change the group ID to GID\n"), usageout);
125 (void) fputs (_(" -h, --help display this help message and exit\n"), usageout);
126 (void) fputs (_(" -n, --new-name NEW_GROUP change the name to NEW_GROUP\n"), usageout);
127 (void) fputs (_(" -o, --non-unique allow to use a duplicate (non-unique) GID\n"), usageout);
128 (void) fputs (_(" -p, --password PASSWORD change the password to this (encrypted)\n"
129 " PASSWORD\n"), usageout);
130 (void) fputs ("\n", usageout);
135 * new_grent - updates the values in a group file entry
137 * new_grent() takes all of the values that have been entered and fills
138 * in a (struct group) with them.
140 static void new_grent (struct group *grent)
143 grent->gr_name = xstrdup (group_newname);
147 grent->gr_gid = group_newid;
151 grent->gr_passwd = group_passwd;
157 * new_sgent - updates the values in a shadow group file entry
159 * new_sgent() takes all of the values that have been entered and fills
160 * in a (struct sgrp) with them.
162 static void new_sgent (struct sgrp *sgent)
165 sgent->sg_name = xstrdup (group_newname);
169 sgent->sg_passwd = group_passwd;
172 #endif /* SHADOWGRP */
175 * grp_update - update group file entries
177 * grp_update() updates the new records in the memory databases.
179 static void grp_update (void)
182 const struct group *ogrp;
186 const struct sgrp *osgrp = NULL;
187 #endif /* SHADOWGRP */
190 * Get the current settings for this group.
192 ogrp = gr_locate (group_name);
195 _("%s: group '%s' does not exist in %s\n"),
196 Prog, group_name, gr_dbname ());
204 osgrp = sgr_locate (group_name);
209 grp.gr_passwd = SHADOW_PASSWD_STRING;
213 #endif /* SHADOWGRP */
216 update_primary_groups (ogrp->gr_gid, group_newid);
220 * Write out the new group file entry.
222 if (gr_update (&grp) == 0) {
224 _("%s: failed to prepare the new %s entry '%s'\n"),
225 Prog, gr_dbname (), grp.gr_name);
228 if (nflg && (gr_remove (group_name) == 0)) {
230 _("%s: cannot remove entry '%s' from %s\n"),
231 Prog, grp.gr_name, gr_dbname ());
237 * Make sure there was a shadow entry to begin with.
242 * Write out the new shadow group entries as well.
244 if (sgr_update (&sgrp) == 0) {
246 _("%s: failed to prepare the new %s entry '%s'\n"),
247 Prog, sgr_dbname (), sgrp.sg_name);
250 if (nflg && (sgr_remove (group_name) == 0)) {
252 _("%s: cannot remove entry '%s' from %s\n"),
253 Prog, group_name, sgr_dbname ());
257 #endif /* SHADOWGRP */
261 * check_new_gid - check the new GID value for uniqueness
263 * check_new_gid() insures that the new GID value is unique.
265 static void check_new_gid (void)
268 * First, the easy stuff. If the ID can be duplicated, or if the ID
269 * didn't really change, just return. If the ID didn't change, turn
270 * off those flags. No sense doing needless work.
272 if (group_id == group_newid) {
278 (getgrgid (group_newid) == NULL) /* local, no need for xgetgrgid */
284 * Tell the user what they did wrong.
287 _("%s: GID '%lu' already exists\n"),
288 Prog, (unsigned long int) group_newid);
293 * check_new_name - check the new name for uniqueness
295 * check_new_name() insures that the new name does not exist already.
296 * You can't have the same name twice, period.
298 static void check_new_name (void)
301 * Make sure they are actually changing the name.
303 if (strcmp (group_name, group_newname) == 0) {
308 if (is_valid_group_name (group_newname)) {
311 * If the entry is found, too bad.
313 /* local, no need for xgetgrnam */
314 if (getgrnam (group_newname) != NULL) {
316 _("%s: group '%s' already exists\n"),
317 Prog, group_newname);
318 exit (E_NAME_IN_USE);
324 * All invalid group names land here.
328 _("%s: invalid group name '%s'\n"),
329 Prog, group_newname);
334 * process_flags - perform command line argument setting
336 * process_flags() interprets the command line arguments and sets the
337 * values that the user will be created with accordingly. The values
338 * are checked for sanity.
340 static void process_flags (int argc, char **argv)
342 int option_index = 0;
344 static struct option long_options[] = {
345 {"gid", required_argument, NULL, 'g'},
346 {"help", no_argument, NULL, 'h'},
347 {"new-name", required_argument, NULL, 'n'},
348 {"non-unique", no_argument, NULL, 'o'},
349 {"password", required_argument, NULL, 'p'},
350 {NULL, 0, NULL, '\0'}
353 getopt_long (argc, argv, "g:hn:op:",
354 long_options, &option_index)) != -1) {
358 if ( (get_gid (optarg, &group_newid) == 0)
359 || (group_newid == (gid_t)-1)) {
361 _("%s: invalid group ID '%s'\n"),
371 group_newname = optarg;
377 group_passwd = optarg;
389 if (optind != (argc - 1)) {
393 group_name = argv[argc - 1];
397 * close_files - close all of the files that were opened
399 * close_files() closes all of the files that were opened for this new
400 * group. This causes any modified entries to be written out.
402 static void close_files (void)
404 if (gr_close () == 0) {
406 _("%s: failure while writing changes to %s\n"),
411 audit_logger (AUDIT_USER_ACCT, Prog,
412 info_group.audit_msg,
413 group_name, AUDIT_NO_ID,
414 SHADOW_AUDIT_SUCCESS);
417 "group changed in %s (%s)",
418 gr_dbname (), info_group.action));
419 del_cleanup (cleanup_report_mod_group);
421 cleanup_unlock_group (NULL);
422 del_cleanup (cleanup_unlock_group);
427 if (sgr_close () == 0) {
429 _("%s: failure while writing changes to %s\n"),
430 Prog, sgr_dbname ());
434 audit_logger (AUDIT_USER_ACCT, Prog,
435 info_gshadow.audit_msg,
436 group_name, AUDIT_NO_ID,
437 SHADOW_AUDIT_SUCCESS);
440 "group changed in %s (%s)",
441 sgr_dbname (), info_gshadow.action));
442 del_cleanup (cleanup_report_mod_gshadow);
444 cleanup_unlock_gshadow (NULL);
445 del_cleanup (cleanup_unlock_gshadow);
447 #endif /* SHADOWGRP */
450 if (pw_close () == 0) {
452 _("%s: failure while writing changes to %s\n"),
457 audit_logger (AUDIT_USER_ACCT, Prog,
458 info_passwd.audit_msg,
459 group_name, AUDIT_NO_ID,
460 SHADOW_AUDIT_SUCCESS);
463 "group changed in %s (%s)",
464 pw_dbname (), info_passwd.action));
465 del_cleanup (cleanup_report_mod_passwd);
467 cleanup_unlock_passwd (NULL);
468 del_cleanup (cleanup_unlock_passwd);
472 audit_logger (AUDIT_USER_ACCT, Prog,
474 group_name, AUDIT_NO_ID,
475 SHADOW_AUDIT_SUCCESS);
480 * prepare_failure_reports - Prepare the cleanup_info structure for logging
481 * of success and failure to syslog or audit.
483 static void prepare_failure_reports (void)
485 info_group.name = group_name;
487 info_gshadow.name = group_name;
489 info_passwd.name = group_name;
491 info_group.audit_msg = xmalloc (512);
493 info_gshadow.audit_msg = xmalloc (512);
495 info_passwd.audit_msg = xmalloc (512);
497 snprintf (info_group.audit_msg, 511,
498 "changing %s; ", gr_dbname ());
500 snprintf (info_gshadow.audit_msg, 511,
501 "changing %s; ", sgr_dbname ());
503 snprintf (info_passwd.audit_msg, 511,
504 "changing %s; ", pw_dbname ());
506 info_group.action = info_group.audit_msg
507 + strlen (info_group.audit_msg);
509 info_gshadow.action = info_gshadow.audit_msg
510 + strlen (info_gshadow.audit_msg);
512 info_passwd.action = info_passwd.audit_msg
513 + strlen (info_passwd.audit_msg);
515 snprintf (info_group.action, 511 - strlen (info_group.audit_msg),
516 "group %s/%lu", group_name, (unsigned long int) group_id);
518 snprintf (info_gshadow.action, 511 - strlen (info_group.audit_msg),
519 "group %s", group_name);
521 snprintf (info_passwd.action, 511 - strlen (info_group.audit_msg),
522 "group %s/%lu", group_name, (unsigned long int) group_id);
525 strncat (info_group.action, ", new name: ",
526 511 - strlen (info_group.audit_msg));
527 strncat (info_group.action, group_newname,
528 511 - strlen (info_group.audit_msg));
531 strncat (info_gshadow.action, ", new name: ",
532 511 - strlen (info_gshadow.audit_msg));
533 strncat (info_gshadow.action, group_newname,
534 511 - strlen (info_gshadow.audit_msg));
537 strncat (info_passwd.action, ", new name: ",
538 511 - strlen (info_passwd.audit_msg));
539 strncat (info_passwd.action, group_newname,
540 511 - strlen (info_passwd.audit_msg));
543 strncat (info_group.action, ", new password",
544 511 - strlen (info_group.audit_msg));
547 strncat (info_gshadow.action, ", new password",
548 511 - strlen (info_gshadow.audit_msg));
552 strncat (info_group.action, ", new gid: ",
553 511 - strlen (info_group.audit_msg));
554 snprintf (info_group.action+strlen (info_group.action),
555 511 - strlen (info_group.audit_msg),
556 "%lu", (unsigned long int) group_newid);
558 strncat (info_passwd.action, ", new gid: ",
559 511 - strlen (info_passwd.audit_msg));
560 snprintf (info_passwd.action+strlen (info_passwd.action),
561 511 - strlen (info_passwd.audit_msg),
562 "%lu", (unsigned long int) group_newid);
564 info_group.audit_msg[511] = '\0';
566 info_gshadow.audit_msg[511] = '\0';
568 info_passwd.audit_msg[511] = '\0';
570 // FIXME: add a system cleanup
571 add_cleanup (cleanup_report_mod_group, &info_group);
575 add_cleanup (cleanup_report_mod_gshadow, &info_gshadow);
579 add_cleanup (cleanup_report_mod_passwd, &info_passwd);
585 * lock_files - lock the accounts databases
587 * lock_files() locks the group, gshadow, and passwd databases.
589 static void lock_files (void)
591 if (gr_lock () == 0) {
593 _("%s: cannot lock %s; try again later.\n"),
597 add_cleanup (cleanup_unlock_group, NULL);
602 if (sgr_lock () == 0) {
604 _("%s: cannot lock %s; try again later.\n"),
605 Prog, sgr_dbname ());
608 add_cleanup (cleanup_unlock_gshadow, NULL);
613 if (pw_lock () == 0) {
615 _("%s: cannot lock %s; try again later.\n"),
619 add_cleanup (cleanup_unlock_passwd, NULL);
625 * open_files - open the accounts databases
627 * open_files() opens the group, gshadow, and passwd databases.
629 static void open_files (void)
631 if (gr_open (O_RDWR) == 0) {
632 fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
633 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
640 if (sgr_open (O_RDWR) == 0) {
642 _("%s: cannot open %s\n"),
643 Prog, sgr_dbname ());
644 SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
648 #endif /* SHADOWGRP */
651 if (pw_open (O_RDWR) == 0) {
653 _("%s: cannot open %s\n"),
655 SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
661 void update_primary_groups (gid_t ogid, gid_t ngid)
666 while ((pwd = getpwent ()) != NULL) {
667 if (pwd->pw_gid == ogid) {
668 const struct passwd *lpwd;
670 lpwd = pw_locate (pwd->pw_name);
673 _("%s: user '%s' does not exist in %s\n"),
674 Prog, pwd->pw_name, pw_dbname ());
679 if (pw_update (&npwd) == 0) {
681 _("%s: failed to prepare the new %s entry '%s'\n"),
682 Prog, pw_dbname (), npwd.pw_name);
692 * main - groupmod command
695 int main (int argc, char **argv)
697 #ifdef ACCT_TOOLS_SETUID
699 pam_handle_t *pamh = NULL;
702 #endif /* ACCT_TOOLS_SETUID */
707 atexit (do_cleanups);
710 * Get my name so that I can use it to report errors.
712 Prog = Basename (argv[0]);
714 (void) setlocale (LC_ALL, "");
715 (void) bindtextdomain (PACKAGE, LOCALEDIR);
716 (void) textdomain (PACKAGE);
718 process_flags (argc, argv);
720 OPENLOG ("groupmod");
722 #ifdef ACCT_TOOLS_SETUID
725 struct passwd *pampw;
726 pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
729 _("%s: Cannot determine your user name.\n"),
734 retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh);
737 if (PAM_SUCCESS == retval) {
738 retval = pam_authenticate (pamh, 0);
741 if (PAM_SUCCESS == retval) {
742 retval = pam_acct_mgmt (pamh, 0);
746 (void) pam_end (pamh, retval);
748 if (PAM_SUCCESS != retval) {
749 fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
753 #endif /* ACCT_TOOLS_SETUID */
756 is_shadow_grp = sgr_file_present ();
761 * Start with a quick check to see if the group exists.
763 grp = getgrnam (group_name); /* local, no need for xgetgrnam */
766 _("%s: group '%s' does not exist\n"),
770 group_id = grp->gr_gid;
776 * Now make sure it isn't an NIS group.
783 _("%s: group %s is a NIS group\n"),
786 if (!yp_get_default_domain (&nis_domain) &&
787 !yp_master (nis_domain, "group.byname", &nis_master)) {
789 _("%s: %s is the NIS master\n"),
807 * Now if the group is not changed, it's our fault.
808 * Make sure failures will be reported.
810 prepare_failure_reports ();
813 * Do the hard stuff - open the files, create the group entries,
814 * then close and update the files.
822 nscd_flush_cache ("group");