* Copyright (c) 1991 - 1993, Julianne Frances Haugh
* Copyright (c) 1996 - 2000, Marek Michałkiewicz
* Copyright (c) 2000 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2008, Nicolas François
+ * Copyright (c) 2007 - 2009, Nicolas François
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
#ident "$Id$"
-#include <assert.h>
#include <ctype.h>
#include <fcntl.h>
#include <getopt.h>
#include <grp.h>
#include <stdio.h>
#include <sys/types.h>
+#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
#include "pam_defs.h"
#include <pwd.h>
#endif /* USE_PAM */
+#endif /* ACCT_TOOLS_SETUID */
#include "chkname.h"
#include "defines.h"
#include "getdef.h"
#include "prototypes.h"
#ifdef SHADOWGRP
#include "sgroupio.h"
-static int is_shadow_grp;
#endif
/*
* exit status values
*/
+/*@-exitarg@*/
#define E_SUCCESS 0 /* success */
#define E_USAGE 2 /* invalid command syntax */
#define E_BAD_ARG 3 /* invalid argument to option */
/*
* Global variables
*/
-static char *group_name;
-static gid_t group_id;
-static char *group_passwd;
-static char *empty_list = NULL;
+const char *Prog;
-static char *Prog;
+static /*@null@*/char *group_name;
+static gid_t group_id;
+static /*@null@*/char *group_passwd;
+static /*@null@*/char *empty_list = NULL;
-static int oflg = 0; /* permit non-unique group ID to be specified with -g */
-static int gflg = 0; /* ID value for the new group */
-static int fflg = 0; /* if group already exists, do nothing and exit(0) */
-static int rflg = 0; /* create a system account */
-static int pflg = 0; /* new encrypted password */
+static bool oflg = false; /* permit non-unique group ID to be specified with -g */
+static bool gflg = false; /* ID value for the new group */
+static bool fflg = false; /* if group already exists, do nothing and exit(0) */
+static bool rflg = false; /* create a system account */
+static bool pflg = false; /* new encrypted password */
-#ifdef USE_PAM
-static pam_handle_t *pamh = NULL;
+#ifdef SHADOWGRP
+static bool is_shadow_grp;
#endif
/* local function prototypes */
-static void usage (void);
+static void usage (int status);
static void new_grent (struct group *grent);
#ifdef SHADOWGRP
static void check_new_name (void);
static void close_files (void);
static void open_files (void);
-static void fail_exit (int code);
-static gid_t get_gid (const char *gidstr);
static void process_flags (int argc, char **argv);
static void check_flags (void);
static void check_perms (void);
/*
* usage - display usage message and exit
*/
-static void usage (void)
+static void usage (int status)
{
- fputs (_("Usage: groupadd [options] GROUP\n"
- "\n"
- "Options:\n"
- " -f, --force force exit with success status if the\n"
- " specified group already exists\n"
- " -g, --gid GID use GID for the new group\n"
- " -h, --help display this help message and exit\n"
- " -K, --key KEY=VALUE overrides /etc/login.defs defaults\n"
- " -o, --non-unique allow create group with duplicate\n"
- " (non-unique) GID\n"
- " -p, --password PASSWORD use encrypted password for the new group\n"
- " -r, --system create a system account\n"
- "\n"), stderr);
- exit (E_USAGE);
+ FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+ (void) fprintf (usageout,
+ _("Usage: %s [options] GROUP\n"
+ "\n"
+ "Options:\n"),
+ Prog);
+ (void) fputs (_(" -f, --force exit successfully if the group already exists,\n"
+ " and cancel -g if the GID is already used\n"), usageout);
+ (void) fputs (_(" -g, --gid GID use GID for the new group\n"), usageout);
+ (void) fputs (_(" -h, --help display this help message and exit\n"), usageout);
+ (void) fputs (_(" -K, --key KEY=VALUE override /etc/login.defs defaults\n"), usageout);
+ (void) fputs (_(" -o, --non-unique allow to create groups with duplicate\n"
+ " (non-unique) GID\n"), usageout);
+ (void) fputs (_(" -p, --password PASSWORD use this encrypted password for the new group\n"), usageout);
+ (void) fputs (_(" -r, --system create a system account\n"), usageout);
+ (void) fputs ("\n", usageout);
+ exit (status);
}
/*
struct sgrp sgrp;
#endif /* SHADOWGRP */
+ /*
+ * To add the group, we need to update /etc/group.
+ * Make sure failures will be reported.
+ */
+ add_cleanup (cleanup_report_add_group_group, group_name);
+#ifdef SHADOWGRP
+ if (is_shadow_grp) {
+ /* We also need to update /etc/gshadow */
+ add_cleanup (cleanup_report_add_group_gshadow, group_name);
+ }
+#endif
+
/*
* Create the initial entries for this new group.
*/
/*
* Write out the new group file entry.
*/
- if (!gr_update (&grp)) {
- fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
- fail_exit (E_GRP_UPDATE);
+ if (gr_update (&grp) == 0) {
+ fprintf (stderr,
+ _("%s: failed to prepare the new %s entry '%s'\n"),
+ Prog, gr_dbname (), grp.gr_name);
+ exit (E_GRP_UPDATE);
}
#ifdef SHADOWGRP
/*
* Write out the new shadow group entries as well.
*/
- if (is_shadow_grp && !sgr_update (&sgrp)) {
- fprintf (stderr, _("%s: error adding new group entry\n"), Prog);
- fail_exit (E_GRP_UPDATE);
+ if (is_shadow_grp && (sgr_update (&sgrp) == 0)) {
+ fprintf (stderr,
+ _("%s: failed to prepare the new %s entry '%s'\n"),
+ Prog, sgr_dbname (), sgrp.sg_name);
+ exit (E_GRP_UPDATE);
}
#endif /* SHADOWGRP */
-#ifdef WITH_AUDIT
- audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group", group_name,
- group_id, 1);
-#endif
- SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
- group_name, (unsigned int) group_id));
}
/*
*/
static void check_new_name (void)
{
- if (check_group_name (group_name)) {
+ if (is_valid_group_name (group_name)) {
return;
}
* All invalid group names land here.
*/
- fprintf (stderr, _("%s: %s is not a valid group name\n"),
+ fprintf (stderr, _("%s: '%s' is not a valid group name\n"),
Prog, group_name);
exit (E_BAD_ARG);
*/
static void close_files (void)
{
- if (!gr_close ()) {
- fprintf (stderr, _("%s: cannot rewrite group file\n"), Prog);
- fail_exit (E_GRP_UPDATE);
- }
- gr_unlock ();
-#ifdef SHADOWGRP
- if (is_shadow_grp && !sgr_close ()) {
+ /* First, write the changes in the regular group database */
+ if (gr_close () == 0) {
fprintf (stderr,
- _("%s: cannot rewrite shadow group file\n"), Prog);
- fail_exit (E_GRP_UPDATE);
+ _("%s: failure while writing changes to %s\n"),
+ Prog, gr_dbname ());
+ exit (E_GRP_UPDATE);
}
+#ifdef WITH_AUDIT
+ audit_logger (AUDIT_ADD_GROUP, Prog,
+ "adding group to /etc/group",
+ group_name, (unsigned int) group_id,
+ SHADOW_AUDIT_SUCCESS);
+#endif
+ SYSLOG ((LOG_INFO, "group added to %s: name=%s, GID=%u",
+ gr_dbname (), group_name, (unsigned int) group_id));
+ del_cleanup (cleanup_report_add_group_group);
+
+ cleanup_unlock_group (NULL);
+ del_cleanup (cleanup_unlock_group);
+
+ /* Now, write the changes in the shadow database */
+#ifdef SHADOWGRP
if (is_shadow_grp) {
- sgr_unlock ();
+ if (sgr_close () == 0) {
+ fprintf (stderr,
+ _("%s: failure while writing changes to %s\n"),
+ Prog, sgr_dbname ());
+ exit (E_GRP_UPDATE);
+ }
+#ifdef WITH_AUDIT
+ audit_logger (AUDIT_ADD_GROUP, Prog,
+ "adding group to /etc/gshadow",
+ group_name, (unsigned int) group_id,
+ SHADOW_AUDIT_SUCCESS);
+#endif
+ SYSLOG ((LOG_INFO, "group added to %s: name=%s",
+ sgr_dbname (), group_name));
+ del_cleanup (cleanup_report_add_group_gshadow);
+
+ cleanup_unlock_gshadow (NULL);
+ del_cleanup (cleanup_unlock_gshadow);
}
#endif /* SHADOWGRP */
+
+ /* Report success at the system level */
+#ifdef WITH_AUDIT
+ audit_logger (AUDIT_ADD_GROUP, Prog,
+ "",
+ group_name, (unsigned int) group_id,
+ SHADOW_AUDIT_SUCCESS);
+#endif
+ SYSLOG ((LOG_INFO, "new group: name=%s, GID=%u",
+ group_name, (unsigned int) group_id));
+ del_cleanup (cleanup_report_add_group);
}
/*
*/
static void open_files (void)
{
- if (!gr_lock ()) {
- fprintf (stderr, _("%s: unable to lock group file\n"), Prog);
-#ifdef WITH_AUDIT
- audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "locking group file",
- group_name, -1, 0);
-#endif
- exit (E_GRP_UPDATE);
- }
- if (!gr_open (O_RDWR)) {
- fprintf (stderr, _("%s: unable to open group file\n"), Prog);
-#ifdef WITH_AUDIT
- audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "opening group file",
- group_name, -1, 0);
-#endif
- fail_exit (E_GRP_UPDATE);
- }
-#ifdef SHADOWGRP
- if (is_shadow_grp && !sgr_lock ()) {
- fprintf (stderr,
- _("%s: unable to lock shadow group file\n"), Prog);
- fail_exit (E_GRP_UPDATE);
- }
- if (is_shadow_grp && !sgr_open (O_RDWR)) {
+ /* First, lock the databases */
+ if (gr_lock () == 0) {
fprintf (stderr,
- _("%s: unable to open shadow group file\n"), Prog);
- fail_exit (E_GRP_UPDATE);
+ _("%s: cannot lock %s; try again later.\n"),
+ Prog, gr_dbname ());
+ exit (E_GRP_UPDATE);
}
-#endif /* SHADOWGRP */
-}
+ add_cleanup (cleanup_unlock_group, NULL);
-/*
- * fail_exit - exit with an error code after unlocking files
- */
-static void fail_exit (int code)
-{
- (void) gr_unlock ();
#ifdef SHADOWGRP
if (is_shadow_grp) {
- sgr_unlock ();
+ if (sgr_lock () == 0) {
+ fprintf (stderr,
+ _("%s: cannot lock %s; try again later.\n"),
+ Prog, sgr_dbname ());
+ exit (E_GRP_UPDATE);
+ }
+ add_cleanup (cleanup_unlock_gshadow, NULL);
}
-#endif
+#endif /* SHADOWGRP */
-#ifdef WITH_AUDIT
- if (code != E_SUCCESS) {
- audit_logger (AUDIT_USER_CHAUTHTOK, Prog, "adding group",
- group_name, -1, 0);
- }
-#endif
+ /*
+ * Now if the group is not added, it's our fault.
+ * Make sure failures will be reported.
+ */
+ add_cleanup (cleanup_report_add_group, group_name);
-#ifdef USE_PAM
- if (NULL != pamh) {
- /* If there is a PAM error, fail_exit is not called.
- * We always end the pam transaction with PAM_SUCCESS here.
- */
- pam_end (pamh, PAM_SUCCESS);
+ /* And now open the databases */
+ if (gr_open (O_RDWR) == 0) {
+ fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ());
+ SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ()));
+ exit (E_GRP_UPDATE);
}
-#endif
- exit (code);
-}
-/*
- * get_id - validate and get group ID
- */
-static gid_t get_gid (const char *gidstr)
-{
- long val;
- char *errptr;
-
- val = strtol (gidstr, &errptr, 10);
- if (('\0' != *errptr) || (errno == ERANGE) || (val < 0)) {
- fprintf (stderr, _("%s: invalid numeric argument '%s'\n"),
- Prog, gidstr);
- exit (E_BAD_ARG);
+#ifdef SHADOWGRP
+ if (is_shadow_grp) {
+ if (sgr_open (O_RDWR) == 0) {
+ fprintf (stderr,
+ _("%s: cannot open %s\n"),
+ Prog, sgr_dbname ());
+ SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ()));
+ exit (E_GRP_UPDATE);
+ }
}
- return val;
+#endif /* SHADOWGRP */
}
/*
* (unique) gid (turn off -g). Based on the RedHat's
* patch from shadow-utils-970616-9.
*/
- fflg++;
+ fflg = true;
break;
case 'g':
- gflg++;
- group_id = get_gid (optarg);
+ gflg = true;
+ if ( (get_gid (optarg, &group_id) == 0)
+ || (group_id == (gid_t)-1)) {
+ fprintf (stderr,
+ _("%s: invalid group ID '%s'\n"),
+ Prog, optarg);
+ exit (E_BAD_ARG);
+ }
break;
case 'h':
- usage ();
+ usage (E_SUCCESS);
break;
case 'K':
/*
cp = strchr (optarg, '=');
if (NULL == cp) {
fprintf (stderr,
- _
- ("%s: -K requires KEY=VALUE\n"),
+ _("%s: -K requires KEY=VALUE\n"),
Prog);
exit (E_BAD_ARG);
}
}
break;
case 'o':
- oflg++;
+ oflg = true;
break;
case 'p':
- pflg++;
+ pflg = true;
group_passwd = optarg;
break;
case 'r':
- rflg++;
+ rflg = true;
break;
default:
- usage ();
+ usage (E_USAGE);
}
}
* Check the flags consistency
*/
if (optind != argc - 1) {
- usage ();
+ usage (E_USAGE);
}
group_name = argv[optind];
{
/* -o does not make sense without -g */
if (oflg && !gflg) {
- usage ();
+ usage (E_USAGE);
}
check_new_name ();
/* The group already exist */
if (fflg) {
/* OK, no need to do anything */
- fail_exit (E_SUCCESS);
+ exit (E_SUCCESS);
}
- fprintf (stderr, _("%s: group %s exists\n"), Prog, group_name);
- fail_exit (E_NAME_IN_USE);
+ fprintf (stderr,
+ _("%s: group '%s' already exists\n"),
+ Prog, group_name);
+ exit (E_NAME_IN_USE);
}
if (gflg && (getgrgid (group_id) != NULL)) {
/* Continue with this GID */
} else if (fflg) {
/* Turn off -g, we can use any GID */
- gflg = 0;
+ gflg = false;
} else {
- fprintf (stderr, _("%s: GID %u is not unique\n"),
- Prog, (unsigned int) group_id);
- fail_exit (E_GID_IN_USE);
+ fprintf (stderr,
+ _("%s: GID '%lu' already exists\n"),
+ Prog, (unsigned long int) group_id);
+ exit (E_GID_IN_USE);
}
}
}
*/
static void check_perms (void)
{
+#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
- int retval = PAM_SUCCESS;
+ pam_handle_t *pamh = NULL;
+ int retval;
struct passwd *pampw;
pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
- if (pampw == NULL) {
- retval = PAM_USER_UNKNOWN;
+ if (NULL == pampw) {
+ fprintf (stderr,
+ _("%s: Cannot determine your user name.\n"),
+ Prog);
+ exit (1);
}
- if (retval == PAM_SUCCESS) {
- retval = pam_start ("groupadd", pampw->pw_name,
- &conv, &pamh);
- }
+ retval = pam_start ("groupadd", pampw->pw_name, &conv, &pamh);
- if (retval == PAM_SUCCESS) {
+ if (PAM_SUCCESS == retval) {
retval = pam_authenticate (pamh, 0);
- if (retval != PAM_SUCCESS) {
- pam_end (pamh, retval);
- }
}
- if (retval == PAM_SUCCESS) {
+ if (PAM_SUCCESS == retval) {
retval = pam_acct_mgmt (pamh, 0);
- if (retval != PAM_SUCCESS) {
- pam_end (pamh, retval);
- }
}
- if (retval != PAM_SUCCESS) {
+ if (NULL != pamh) {
+ (void) pam_end (pamh, retval);
+ }
+ if (PAM_SUCCESS != retval) {
fprintf (stderr, _("%s: PAM authentication failed\n"), Prog);
exit (1);
}
#endif /* USE_PAM */
+#endif /* ACCT_TOOLS_SETUID */
}
/*
#ifdef WITH_AUDIT
audit_help_open ();
#endif
+ atexit (do_cleanups);
+
/*
* Get my name so that I can use it to report errors.
*/
Prog = Basename (argv[0]);
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
+ (void) setlocale (LC_ALL, "");
+ (void) bindtextdomain (PACKAGE, LOCALEDIR);
+ (void) textdomain (PACKAGE);
OPENLOG ("groupadd");
if (!gflg) {
if (find_new_gid (rflg, &group_id, NULL) < 0) {
- fprintf (stderr, _("%s: can't create group\n"), Prog);
- fail_exit (E_GID_IN_USE);
+ exit (E_GID_IN_USE);
}
}
nscd_flush_cache ("group");
-#ifdef USE_PAM
- pam_end (pamh, PAM_SUCCESS);
-#endif /* USE_PAM */
-
exit (E_SUCCESS);
- /* NOT REACHED */
+ /*@notreached@*/
}