diff --git a/ChangeLog b/ChangeLog index ef673345..b37e3abd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +2008-12-22 Nicolas François + + * libmisc/audit_help.c: Added audit_logger_message() to log + messages not related to an account. + * lib/prototypes.h, libmisc/cleanup.c, libmisc/cleanup_group.c, + libmisc/cleanup_user.c, libmisc/Makefile.am: Added stack of + cleanup functions to be executed on exit. + * NEWS, src/groupadd.c, src/groupdel.c, src/groupmod.c: Only + report success to audit and syslog when the changes are committed + to the system. Do not log failure for on-memory changes to audit + or syslog. Make sure failures and inconsistencies will be reported + in case of unexpected failures (e.g. malloc failures). Only + specify an audit message if it is not implicitly implied by the + type argument. Removed fail_exit (replaced by atexit(do_cleanups)). + 2008-12-15 Nicolas François * NEWS, src/gpasswd.c: Added support usernames with arbitrary diff --git a/NEWS b/NEWS index 470d4a0c..0ba256f8 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,13 @@ shadow-4.1.2.2 -> shadow-4.1.3 UNRELEASED * Speed improvement in case UID_MAX/SYS_UID_MAX/GID_MAX/SYS_GID_MAX is used for an user/group. This should be noticeable in case of LDAP configured systems. This should impact useradd, groupadd, and newusers +- error handling improvement + * Make sure errors and incomplete changes are reported to syslog and + audit in case of unexpected failures. + * Report system inconsistencies to syslog and audit. + * Only report success to syslog and audit if the changes are really + performed in the system databases. + This is still not complete. - /etc/login.defs * New CREATE_HOME variable to tell useradd to create a home directory by default. @@ -35,9 +42,11 @@ shadow-4.1.2.2 -> shadow-4.1.3 UNRELEASED * Added support for usernames with arbitrary length. - groupadd * audit logging improvements. + * error handling improvement (see above). * Speedup (see "addition of users or groups" above). - groupdel * audit logging improvements. + * error handling improvement (see above). - groupmems * Check if user exist before they are added to groups. * Avoid segfault in case the specified group does not exist in /etc/group. @@ -50,6 +59,9 @@ shadow-4.1.2.2 -> shadow-4.1.3 UNRELEASED * Added support for shadow groups. * Added support long options --add (-a), --delete (-d), --purge (-p), --list (-l), --group (-g). +- groupmod + * audit logging improvements. + * error handling improvement (see above). - newusers * Implement the -r, --system option. * Speedup (see "addition of users or groups" above). diff --git a/lib/prototypes.h b/lib/prototypes.h index 1e77bb2c..5b257aa5 100644 --- a/lib/prototypes.h +++ b/lib/prototypes.h @@ -78,6 +78,37 @@ extern int chown_tree (const char *, uid_t, uid_t, gid_t, gid_t); /* chowntty.c */ extern void chown_tty (const struct passwd *); +/* cleanup.c */ +typedef void (*cleanup_function) (void *arg); +void add_cleanup (cleanup_function pcf, void *arg); +void del_cleanup (cleanup_function pcf); +void do_cleanups (void); + +/* cleanup_group.c */ +struct cleanup_info_mod { + char *audit_msg; + char *action; + char *name; +}; +void cleanup_report_add_group (void *group_name); +void cleanup_report_add_group_group (void *group_name); +#ifdef SHADOWGRP +void cleanup_report_add_group_gshadow (void *group_name); +#endif +void cleanup_report_del_group (void *group_name); +void cleanup_report_del_group_group (void *group_name); +#ifdef SHADOWGRP +void cleanup_report_del_group_gshadow (void *group_name); +#endif +void cleanup_report_mod_passwd (void *cleanup_info); +void cleanup_report_mod_group (void *cleanup_info); +void cleanup_report_mod_gshadow (void *cleanup_info); +void cleanup_unlock_group (void *unused); +#ifdef SHADOWGRP +void cleanup_unlock_gshadow (void *unused); +#endif +void cleanup_unlock_passwd (void *unused); + /* console.c */ extern bool console (const char *); @@ -144,6 +175,7 @@ typedef enum { extern void audit_logger (int type, const char *pgname, const char *op, const char *name, unsigned int id, shadow_audit_result result); +void audit_logger_message (const char *message, shadow_audit_result result); #endif /* limits.c */ diff --git a/libmisc/Makefile.am b/libmisc/Makefile.am index a93f6d66..e8cd77bf 100644 --- a/libmisc/Makefile.am +++ b/libmisc/Makefile.am @@ -14,6 +14,9 @@ libmisc_a_SOURCES = \ chkname.h \ chowndir.c \ chowntty.c \ + cleanup.c \ + cleanup_group.c \ + cleanup_user.c \ console.c \ copydir.c \ entry.c \ diff --git a/libmisc/audit_help.c b/libmisc/audit_help.c index 21e3d8a0..9a58a8b7 100644 --- a/libmisc/audit_help.c +++ b/libmisc/audit_help.c @@ -87,6 +87,21 @@ void audit_logger (int type, const char *pgname, const char *op, } } +void audit_logger_message (const char *message, shadow_audit_result result) +{ + if (audit_fd < 0) { + return; + } else { + audit_log_user_message (audit_fd, + AUDIT_USYS_CONFIG, + message, + NULL, /* hostname */ + NULL, /* addr */ + NULL, /* tty */ + (int) result); + } +} + #else /* WITH_AUDIT */ extern int errno; /* warning: ANSI C forbids an empty source file */ #endif /* WITH_AUDIT */ diff --git a/libmisc/cleanup.c b/libmisc/cleanup.c new file mode 100644 index 00000000..b339a7e9 --- /dev/null +++ b/libmisc/cleanup.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2008 , Nicolas François + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "prototypes.h" + +/* + * The cleanup_functions stack. + */ +#define CLEANUP_FUNCTIONS 10 +static cleanup_function cleanup_functions[CLEANUP_FUNCTIONS]; +static const char * cleanup_function_args[CLEANUP_FUNCTIONS]; + +/* + * - Cleanup functions shall not fail. + * - You should register do_cleanups with atexit. + * - You should add cleanup functions to the stack with add_cleanup when + * an operation is expected to be executed later, and remove it from the + * stack with del_cleanup when it has been executed. + * + **/ + +/* + * do_cleanups - perform the actions stored in the cleanup_functions stack. + * + * It is intended to be used as: + * atexit (do_cleanups); + */ +void do_cleanups (void) +{ + unsigned int i; + + /* Make sure there were no overflow */ + assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-1]); + + i = CLEANUP_FUNCTIONS; + do { + i--; + if (cleanup_functions[i] != NULL) { + cleanup_functions[i] (cleanup_function_args[i]); + } + } while (i>0); +} + +/* + * add_cleanup - Add a cleanup_function to the cleanup_functions stack. + */ +void add_cleanup (cleanup_function pcf, void *arg) +{ + unsigned int i; + assert (NULL != pcf); + + assert (NULL == cleanup_functions[CLEANUP_FUNCTIONS-2]); + + /* Add the cleanup_function at the end of the stack */ + for (i=0; NULL != cleanup_functions[i]; i++); + cleanup_functions[i] = pcf; + cleanup_function_args[i] = arg; +} + +/* + * del_cleanup - Remove a cleanup_function from the cleanup_functions stack. + */ +void del_cleanup (cleanup_function pcf) +{ + unsigned int i; + assert (NULL != pcf); + + /* Find the pcf cleanup function */ + for (i=0; i + +#include +#include + +#include "defines.h" +#include "groupio.h" +#include "sgroupio.h" +#include "prototypes.h" + +/* + * cleanup_report_add_group - Report failure to add a group to the system + * + * It should be registered when it is decided to add a group to the system. + */ +void cleanup_report_add_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_del_group - Report failure to remove a group from the system + * + * It should be registered when it is decided to remove a group from the system. + */ +void cleanup_report_del_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to remove group %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_DEL_GROUP, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +void cleanup_report_mod_group (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + gr_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +void cleanup_report_mod_gshadow (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + sgr_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_add_group_group - Report failure to add a group to group + * + * It should be registered when it is decided to add a group to the + * group database. + */ +void cleanup_report_add_group_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/group", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +#ifdef SHADOWGRP +/* + * cleanup_report_add_group_gshadow - Report failure to add a group to gshadow + * + * It should be registered when it is decided to add a group to the + * gshadow database. + */ +void cleanup_report_add_group_gshadow (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, "failed to add group %s to %s", name, sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/gshadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} +#endif + +/* + * cleanup_report_del_group_group - Report failure to remove a group from the + * regular group database + * + * It should be registered when it is decided to remove a group from the + * regular group database. + */ +void cleanup_report_del_group_group (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, + "failed to remove group %s from %s", + name, gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "removing group from /etc/group", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +#ifdef SHADOWGRP +/* + * cleanup_report_del_group_gshadow - Report failure to remove a group from + * gshadow + * + * It should be registered when it is decided to remove a group from the + * gshadow database. + */ +void cleanup_report_del_group_gshadow (void *group_name) +{ + const char *name = (const char *)group_name; + + SYSLOG ((LOG_ERR, + "failed to remove group %s from %s", + name, sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_GROUP, Prog, + "removing group from /etc/gshadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} +#endif + +/* + * cleanup_unlock_group - Unlock the group file + * + * It should be registered after the group file is successfully locked. + */ +void cleanup_unlock_group (unused void *arg) +{ + if (gr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, gr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking group file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + +#ifdef SHADOWGRP +/* + * cleanup_unlock_gshadow - Unlock the gshadow file + * + * It should be registered after the gshadow file is successfully locked. + */ +void cleanup_unlock_gshadow (unused void *arg) +{ + if (sgr_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, sgr_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking gshadow file", + SHADOW_AUDIT_FAILURE); +#endif + } +} +#endif + diff --git a/libmisc/cleanup_user.c b/libmisc/cleanup_user.c new file mode 100644 index 00000000..6e497512 --- /dev/null +++ b/libmisc/cleanup_user.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2008 , Nicolas François + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the copyright holders or contributors may not be used to + * endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "defines.h" +#include "pwio.h" +#include "shadowio.h" +#include "prototypes.h" + +/* + * cleanup_report_add_user - Report failure to add an user to the system + * + * It should be registered when it is decided to add an user to the system. + */ +void cleanup_report_add_user (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s", name)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +void cleanup_report_mod_passwd (void *cleanup_info) +{ + const struct cleanup_info_mod *info; + info = (const struct cleanup_info_mod *)cleanup_info; + + SYSLOG ((LOG_ERR, + "failed to change %s (%s)", + pw_dbname (), + info->action)); +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + info->audit_msg, + info->name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_add_user_passwd - Report failure to add an user to + * /etc/passwd + * + * It should be registered when it is decided to add an user to the + * /etc/passwd database. + */ +void cleanup_report_add_user_passwd (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, pw_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "adding user to /etc/passwd", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_report_add_user_shadow - Report failure to add an user to + * /etc/shadow + * + * It should be registered when it is decided to add an user to the + * /etc/shadow database. + */ +void cleanup_report_add_user_shadow (void *user_name) +{ + const char *name = (const char *)user_name; + + SYSLOG ((LOG_ERR, "failed to add user %s to %s", name, spw_dbname ())); +#ifdef WITH_AUDIT + audit_logger (AUDIT_ADD_USER, Prog, + "adding user to /etc/shadow", + name, AUDIT_NO_ID, + SHADOW_AUDIT_FAILURE); +#endif +} + +/* + * cleanup_unlock_passwd - Unlock the /etc/passwd database + * + * It should be registered after the passwd database is successfully locked. + */ +void cleanup_unlock_passwd (unused void *arg) +{ + if (pw_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, pw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking passwd file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + +/* + * cleanup_unlock_shadow - Unlock the /etc/shadow database + * + * It should be registered after the shadow database is successfully locked. + */ +void cleanup_unlock_shadow (unused void *arg) +{ + if (spw_unlock () == 0) { + fprintf (stderr, + _("%s: failed to unlock %s\n"), + Prog, spw_dbname ()); + SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ())); +#ifdef WITH_AUDIT + audit_logger_message ("unlocking shadow file", + SHADOW_AUDIT_FAILURE); +#endif + } +} + diff --git a/src/groupadd.c b/src/groupadd.c index 6cc07a49..d4377bd8 100644 --- a/src/groupadd.c +++ b/src/groupadd.c @@ -85,9 +85,7 @@ static bool pflg = false; /* new encrypted password */ #ifdef SHADOWGRP static bool is_shadow_grp; -static bool sgr_locked = false; #endif -static bool gr_locked = false; /* local function prototypes */ static void usage (void); @@ -100,7 +98,6 @@ static void grp_update (void); 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); @@ -180,6 +177,18 @@ static void grp_update (void) 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. */ @@ -198,7 +207,7 @@ static void grp_update (void) fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grp.gr_name); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } #ifdef SHADOWGRP /* @@ -208,17 +217,9 @@ static void grp_update (void) fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, sgr_dbname (), sgrp.sg_name); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } #endif /* SHADOWGRP */ -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "adding group", - 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)); } /* @@ -251,45 +252,60 @@ static void check_new_name (void) */ static void close_files (void) { + /* First, write the changes in the regular group database */ if (gr_close () == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + exit (E_GRP_UPDATE); } - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); #ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/group", + group_name, (unsigned int) group_id, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - gr_locked = false; + 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) { if (sgr_close () == 0) { fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (E_GRP_UPDATE); + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + exit (E_GRP_UPDATE); } - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); #ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); + audit_logger (AUDIT_ADD_GROUP, Prog, + "adding group to /etc/gshadow", + group_name, (unsigned int) group_id, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - sgr_locked = false; + 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); } /* @@ -299,107 +315,53 @@ static void close_files (void) */ static void open_files (void) { + /* First, lock the databases */ if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "locking group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - fail_exit (E_GRP_UPDATE); - } - gr_locked = true; - if (gr_open (O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "opening group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } + add_cleanup (cleanup_unlock_group, NULL); + #ifdef SHADOWGRP if (is_shadow_grp) { if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "locking gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } - sgr_locked = true; + add_cleanup (cleanup_unlock_gshadow, NULL); + } +#endif /* SHADOWGRP */ + + /* + * 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); + + /* An 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); + } + +#ifdef SHADOWGRP + if (is_shadow_grp) { if (sgr_open (O_RDWR) == 0) { fprintf (stderr, - _("%s: cannot open %s\n"), Prog, sgr_dbname ()); + _("%s: cannot open %s\n"), + Prog, sgr_dbname ()); SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "opening gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } } #endif /* SHADOWGRP */ } -/* - * fail_exit - exit with an error code after unlocking files - */ -static void fail_exit (int code) -{ - if (gr_locked) { - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - /* continue */ - } - } -#ifdef SHADOWGRP - if (sgr_locked) { - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_ADD_GROUP, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - /* continue */ - } - } -#endif - -#ifdef WITH_AUDIT - if (code != E_SUCCESS) { - audit_logger (AUDIT_ADD_GROUP, Prog, - "adding group", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); - } -#endif - - exit (code); -} - /* * get_id - validate and get group ID */ @@ -529,10 +491,10 @@ static void check_flags (void) /* The group already exist */ if (fflg) { /* OK, no need to do anything */ - fail_exit (E_SUCCESS); + exit (E_SUCCESS); } fprintf (stderr, _("%s: group '%s' already exists\n"), Prog, group_name); - fail_exit (E_NAME_IN_USE); + exit (E_NAME_IN_USE); } if (gflg && (getgrgid (group_id) != NULL)) { @@ -550,7 +512,7 @@ static void check_flags (void) } else { fprintf (stderr, _("%s: GID '%lu' already exists\n"), Prog, (unsigned long int) group_id); - fail_exit (E_GID_IN_USE); + exit (E_GID_IN_USE); } } } @@ -610,6 +572,8 @@ int main (int argc, char **argv) #ifdef WITH_AUDIT audit_help_open (); #endif + atexit (do_cleanups); + /* * Get my name so that I can use it to report errors. */ @@ -640,8 +604,7 @@ int main (int argc, char **argv) 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); } } diff --git a/src/groupdel.c b/src/groupdel.c index 1968b953..7e54d9a0 100644 --- a/src/groupdel.c +++ b/src/groupdel.c @@ -62,9 +62,7 @@ static gid_t group_id = -1; #ifdef SHADOWGRP static bool is_shadow_grp; -static bool sgr_locked = false; #endif -static bool gr_locked = false; /* * exit status values @@ -92,50 +90,6 @@ static void usage (void) exit (E_USAGE); } -/* - * fail_exit - exit with a failure code after unlocking the files - */ -static void fail_exit (int code) -{ - if (gr_locked) { - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - /* continue */ - } - } -#ifdef SHADOWGRP - if (sgr_locked) { - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - /* continue */ - } - } -#endif - -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "deleting group", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - - exit (code); -} - /* * grp_update - update group file entries * @@ -143,12 +97,28 @@ static void fail_exit (int code) */ static void grp_update (void) { + /* + * To add the group, we need to update /etc/group. + * Make sure failures will be reported. + */ + add_cleanup (cleanup_report_del_group_group, group_name); +#ifdef SHADOWGRP + if (is_shadow_grp) { + /* We also need to update /etc/gshadow */ + add_cleanup (cleanup_report_del_group_gshadow, group_name); + } +#endif + + /* + * Delete the group entry. + */ if (gr_remove (group_name) == 0) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, group_name, gr_dbname ()); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } + #ifdef SHADOWGRP /* * Delete the shadow group entries as well. @@ -158,11 +128,10 @@ static void grp_update (void) fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, group_name, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } } #endif /* SHADOWGRP */ - return; } /* @@ -173,54 +142,64 @@ static void grp_update (void) */ static void close_files (void) { + /* First, write the changes in the regular group database */ + if (gr_close () == 0) { + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + exit (E_GRP_UPDATE); + } + #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_GROUP, Prog, - "deleting group", + "removing group from /etc/group", group_name, (unsigned int) group_id, SHADOW_AUDIT_SUCCESS); #endif - SYSLOG ((LOG_INFO, "remove group '%s'\n", group_name)); + SYSLOG ((LOG_INFO, + "group '%s' removed from %s", + group_name, gr_dbname ())); + del_cleanup (cleanup_report_del_group_group); - if (gr_close () == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); - } - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif - /* continue */ - } - gr_locked = false; + cleanup_unlock_group (NULL); + del_cleanup (cleanup_unlock_group); + + + /* Then, write the changes in the shadow database */ #ifdef SHADOWGRP if (is_shadow_grp) { if (sgr_close () == 0) { fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); + #ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); + audit_logger (AUDIT_DEL_GROUP, Prog, + "removing group from /etc/gshadow", + group_name, (unsigned int) group_id, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - sgr_locked = false; + SYSLOG ((LOG_INFO, + "group '%s' removed from %s", + group_name, sgr_dbname ())); + del_cleanup (cleanup_report_del_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_DEL_GROUP, Prog, + "", + group_name, (unsigned int) group_id, + SHADOW_AUDIT_SUCCESS); +#endif + SYSLOG ((LOG_INFO, "group '%s' removed\n", group_name)); + del_cleanup (cleanup_report_del_group); } /* @@ -230,34 +209,47 @@ static void close_files (void) */ static void open_files (void) { + /* First, lock the databases */ if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } - gr_locked = true; - if (gr_open (O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } + add_cleanup (cleanup_unlock_group, NULL); #ifdef SHADOWGRP if (is_shadow_grp) { if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } - sgr_locked = true; + add_cleanup (cleanup_unlock_gshadow, NULL); + } +#endif + + /* + * Now, if the group is not removed, it's our fault. + * Make sure failures will be reported. + */ + add_cleanup (cleanup_report_del_group, group_name); + + /* An 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); + } +#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 ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } } #endif /* SHADOWGRP */ @@ -295,8 +287,10 @@ static void group_busy (gid_t gid) /* * Can't remove the group. */ - fprintf (stderr, _("%s: cannot remove the primary group of user '%s'\n"), Prog, pwd->pw_name); - fail_exit (E_GROUP_BUSY); + fprintf (stderr, + _("%s: cannot remove the primary group of user '%s'\n"), + Prog, pwd->pw_name); + exit (E_GROUP_BUSY); } /* @@ -321,6 +315,7 @@ int main (int argc, char **argv) #ifdef WITH_AUDIT audit_help_open (); #endif + atexit (do_cleanups); /* * Get my name so that I can use it to report errors. @@ -384,18 +379,13 @@ int main (int argc, char **argv) */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { - fprintf (stderr, _("%s: group '%s' does not exist\n"), - Prog, group_name); -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "deleting group", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif + fprintf (stderr, + _("%s: group '%s' does not exist\n"), + Prog, group_name); exit (E_NOTFOUND); } - group_id = grp->gr_gid; /* LAUS */ + group_id = grp->gr_gid; } #ifdef USE_NIS @@ -409,12 +399,6 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: group '%s' is a NIS group\n"), Prog, group_name); -#ifdef WITH_AUDIT - audit_logger (AUDIT_DEL_GROUP, Prog, - "deleting group", - group_name, AUDIT_NO_ID, - SHADOW_AUDIT_FAILURE); -#endif if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), diff --git a/src/groupmod.c b/src/groupmod.c index d4a277cc..57f5edd4 100644 --- a/src/groupmod.c +++ b/src/groupmod.c @@ -72,16 +72,17 @@ char *Prog; #ifdef SHADOWGRP static bool is_shadow_grp; -static bool sgr_locked = false; #endif /* SHADOWGRP */ -static bool gr_locked = false; -static bool pw_locked = false; static char *group_name; static char *group_newname; static char *group_passwd; static gid_t group_id; static gid_t group_newid; +struct cleanup_info_mod info_passwd; +struct cleanup_info_mod info_group; +struct cleanup_info_mod info_gshadow; + static bool oflg = false, /* permit non-unique group ID to be specified with -g */ gflg = false, /* new ID value for the group */ @@ -90,7 +91,6 @@ static bool /* local function prototypes */ static void usage (void); -static void fail_exit (int); static void new_grent (struct group *); #ifdef SHADOWGRP @@ -100,8 +100,10 @@ static void grp_update (void); static void check_new_gid (void); static void check_new_name (void); static void process_flags (int, char **); -static void close_files (void); +static void lock_files (void); +static void prepare_failure_reports (void); static void open_files (void); +static void close_files (void); static gid_t get_gid (const char *gidstr); static void update_primary_groups (gid_t ogid, gid_t ngid); @@ -123,49 +125,6 @@ static void usage (void) exit (E_USAGE); } -static void fail_exit (int status) -{ - if (gr_locked) { - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, 0); -#endif - /* continue */ - } - } -#ifdef SHADOWGRP - if (sgr_locked) { - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, 0); -#endif - /* continue */ - } - } -#endif /* SHADOWGRP */ - if (pw_locked) { - if (pw_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking passwd file", - group_name, AUDIT_NO_ID, 0); -#endif - /* continue */ - } - } - exit (status); -} - /* * new_grent - updates the values in a group file entry * @@ -209,7 +168,7 @@ static void new_sgent (struct sgrp *sgent) /* * grp_update - update group file entries * - * grp_update() writes the new records to the group files. + * grp_update() updates the new records in the memory databases. */ static void grp_update (void) { @@ -229,17 +188,13 @@ static void grp_update (void) fprintf (stderr, _("%s: group '%s' does not exist in %s\n"), Prog, group_name, gr_dbname ()); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } grp = *ogrp; new_grent (&grp); #ifdef SHADOWGRP - if (is_shadow_grp) { + if ( is_shadow_grp + && (pflg || nflg)) { osgrp = sgr_locate (group_name); if (NULL != osgrp) { sgrp = *osgrp; @@ -262,78 +217,38 @@ static void grp_update (void) fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, gr_dbname (), grp.gr_name); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "adding group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } if (nflg && (gr_remove (group_name) == 0)) { fprintf (stderr, _("%s: cannot remove entry '%s' from %s\n"), Prog, grp.gr_name, gr_dbname ()); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "deleting group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } #ifdef SHADOWGRP /* - * Make sure there was a shadow entry to begin with. Skip down to - * "out" if there wasn't. Can't just return because there might be - * some syslogging to do. + * Make sure there was a shadow entry to begin with. */ - if (NULL == osgrp) { - goto out; + if ( (NULL != osgrp) + && (pflg || nflg)) { + /* + * Write out the new shadow group entries as well. + */ + if (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); + } + if (nflg && (sgr_remove (group_name) == 0)) { + fprintf (stderr, + _("%s: cannot remove entry '%s' from %s\n"), + Prog, group_name, sgr_dbname ()); + exit (E_GRP_UPDATE); + } } - - /* - * Write out the new shadow group entries as well. - */ - 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); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "adding group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); - } - if (is_shadow_grp && nflg && (sgr_remove (group_name) == 0)) { - fprintf (stderr, - _("%s: cannot remove entry '%s' from %s\n"), - Prog, group_name, sgr_dbname ()); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "deleting group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); - } - out: #endif /* SHADOWGRP */ - -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifing group", - group_name, (unsigned int) group_id, 1); -#endif - if (nflg) { - SYSLOG ((LOG_INFO, "change group '%s' to '%s'", - group_name, group_newname)); - } - - if (gflg) { - SYSLOG ((LOG_INFO, "change GID for '%s' to %lu", - nflg ? group_newname : group_name, - (unsigned long) group_newid)); - } } /* @@ -364,12 +279,7 @@ static void check_new_gid (void) */ fprintf (stderr, _("%s: GID '%lu' already exists\n"), Prog, (unsigned long int) group_newid); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modify gid", - NULL, (unsigned int) group_newid, 0); -#endif - fail_exit (E_GID_IN_USE); + exit (E_GID_IN_USE); } /* @@ -398,12 +308,7 @@ static void check_new_name (void) fprintf (stderr, _("%s: group '%s' already exists\n"), Prog, group_newname); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_NAME_IN_USE); + exit (E_NAME_IN_USE); } return; } @@ -414,12 +319,7 @@ static void check_new_name (void) fprintf (stderr, _("%s: invalid group name '%s'\n"), Prog, group_newname); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_BAD_ARG); + exit (E_BAD_ARG); } /* @@ -434,7 +334,7 @@ static gid_t get_gid (const char *gidstr) if (('\0' != *errptr) || (ERANGE == errno) || (val < 0)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, gidstr); - fail_exit (E_BAD_ARG); + exit (E_BAD_ARG); } return (gid_t) val; } @@ -467,11 +367,6 @@ static void process_flags (int argc, char **argv) case 'g': gflg = true; group_newid = get_gid (optarg); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - NULL, (unsigned int) group_newid, 0); -#endif break; case 'n': nflg = true; @@ -510,129 +405,240 @@ static void process_flags (int argc, char **argv) static void close_files (void) { if (gr_close () == 0) { - fprintf (stderr, _("%s: failure while writing changes to %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", gr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "rewrite group file", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + fprintf (stderr, + _("%s: failure while writing changes to %s\n"), + Prog, gr_dbname ()); + exit (E_GRP_UPDATE); } - if (gr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", gr_dbname ())); #ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking group file", - group_name, AUDIT_NO_ID, 0); + audit_logger (AUDIT_USER_ACCT, Prog, + info_group.audit_msg, + group_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - gr_locked = false; + SYSLOG ((LOG_INFO, + "group changed in %s (%s)", + gr_dbname (), info_group.action)); + del_cleanup (cleanup_report_mod_group); + + cleanup_unlock_group (NULL); + del_cleanup (cleanup_unlock_group); + #ifdef SHADOWGRP - if (is_shadow_grp) { + if ( is_shadow_grp + && (pflg || nflg)) { if (sgr_close () == 0) { fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", sgr_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "rewrite gshadow file", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + _("%s: failure while writing changes to %s\n"), + Prog, sgr_dbname ()); + exit (E_GRP_UPDATE); } - if (sgr_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, sgr_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", sgr_dbname ())); #ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking gshadow file", - group_name, AUDIT_NO_ID, 0); + audit_logger (AUDIT_USER_ACCT, Prog, + info_gshadow.audit_msg, + group_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - sgr_locked = false; + SYSLOG ((LOG_INFO, + "group changed in %s (%s)", + sgr_dbname (), info_gshadow.action)); + del_cleanup (cleanup_report_mod_gshadow); + + cleanup_unlock_gshadow (NULL); + del_cleanup (cleanup_unlock_gshadow); } #endif /* SHADOWGRP */ + if (gflg) { if (pw_close () == 0) { fprintf (stderr, - _("%s: failure while writing changes to %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ())); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "rewrite passwd file", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_GRP_UPDATE); + _("%s: failure while writing changes to %s\n"), + Prog, pw_dbname ()); + exit (E_GRP_UPDATE); } - if (pw_unlock () == 0) { - fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ()); - SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ())); #ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "unlocking passwd file", - group_name, AUDIT_NO_ID, 0); + audit_logger (AUDIT_USER_ACCT, Prog, + info_passwd.audit_msg, + group_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); #endif - /* continue */ - } - pw_locked = false; + SYSLOG ((LOG_INFO, + "group changed in %s (%s)", + pw_dbname (), info_passwd.action)); + del_cleanup (cleanup_report_mod_passwd); + + cleanup_unlock_passwd (NULL); + del_cleanup (cleanup_unlock_passwd); } + +#ifdef WITH_AUDIT + audit_logger (AUDIT_USER_ACCT, Prog, + "modifying group", + group_name, AUDIT_NO_ID, + SHADOW_AUDIT_SUCCESS); +#endif } /* - * open_files - lock and open the group files - * - * open_files() opens the two group files. + * prepare_failure_reports - Prepare the cleanup_info structure for logging + * of success and failure to syslog or audit. */ -static void open_files (void) +static void prepare_failure_reports (void) +{ + info_group.name = group_name; + info_gshadow.name = group_name; + info_passwd.name = group_name; + + info_group.audit_msg = xmalloc (512); + info_gshadow.audit_msg = xmalloc (512); + info_passwd.audit_msg = xmalloc (512); + + snprintf (info_group.audit_msg, 511, + "changing %s; ", gr_dbname ()); + snprintf (info_gshadow.audit_msg, 511, + "changing %s; ", sgr_dbname ()); + snprintf (info_passwd.audit_msg, 511, + "changing %s; ", pw_dbname ()); + + info_group.action = info_group.audit_msg + + strlen (info_group.audit_msg); + info_gshadow.action = info_gshadow.audit_msg + + strlen (info_gshadow.audit_msg); + info_passwd.action = info_passwd.audit_msg + + strlen (info_passwd.audit_msg); + + snprintf (info_group.action, 511 - strlen (info_group.audit_msg), + "group %s/%d", group_name, group_id); + snprintf (info_gshadow.action, 511 - strlen (info_group.audit_msg), + "group %s", group_name); + snprintf (info_passwd.action, 511 - strlen (info_group.audit_msg), + "group %s/%d", group_name, group_id); + + if (nflg) { + strncat (info_group.action, ", new name: ", + 511 - strlen (info_group.audit_msg)); + strncat (info_group.action, group_newname, + 511 - strlen (info_group.audit_msg)); + + strncat (info_gshadow.action, ", new name: ", + 511 - strlen (info_gshadow.audit_msg)); + strncat (info_gshadow.action, group_newname, + 511 - strlen (info_gshadow.audit_msg)); + + strncat (info_passwd.action, ", new name: ", + 511 - strlen (info_passwd.audit_msg)); + strncat (info_passwd.action, group_newname, + 511 - strlen (info_passwd.audit_msg)); + } + if (pflg) { + strncat (info_group.action, ", new password", + 511 - strlen (info_group.audit_msg)); + + strncat (info_gshadow.action, ", new password", + 511 - strlen (info_gshadow.audit_msg)); + } + if (gflg) { + strncat (info_group.action, ", new gid: ", + 511 - strlen (info_group.audit_msg)); + snprintf (info_group.action+strlen (info_group.action), + 511 - strlen (info_group.audit_msg), + "%d", group_newid); + + strncat (info_passwd.action, ", new gid: ", + 511 - strlen (info_passwd.audit_msg)); + snprintf (info_passwd.action+strlen (info_passwd.action), + 511 - strlen (info_passwd.audit_msg), + "%d", group_newid); + } + info_group.audit_msg[511] = '\0'; + info_gshadow.audit_msg[511] = '\0'; + info_passwd.audit_msg[511] = '\0'; + +// FIXME: add a system cleanup + add_cleanup (cleanup_report_mod_group, &info_group); + if ( is_shadow_grp + && (pflg || nflg)) { + add_cleanup (cleanup_report_mod_gshadow, &info_gshadow); + } + if (gflg) { + add_cleanup (cleanup_report_mod_passwd, &info_passwd); + } + +} + +/* + * lock_files - lock the accounts databases + * + * lock_files() locks the group, gshadow, and passwd databases. + */ +static void lock_files (void) { if (gr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, gr_dbname ()); - fail_exit (E_GRP_UPDATE); - } - gr_locked = true; - if (gr_open (O_RDWR) == 0) { - fprintf (stderr, _("%s: cannot open %s\n"), Prog, gr_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } + add_cleanup (cleanup_unlock_group, NULL); + #ifdef SHADOWGRP - if (is_shadow_grp) { + if ( is_shadow_grp + && (pflg || nflg)) { if (sgr_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, sgr_dbname ()); - fail_exit (E_GRP_UPDATE); - } - sgr_locked = true; - if (sgr_open (O_RDWR) == 0) { - fprintf (stderr, - _("%s: cannot open %s\n"), - Prog, sgr_dbname ()); - SYSLOG ((LOG_WARN, "cannot open %s", sgr_dbname ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } + add_cleanup (cleanup_unlock_gshadow, NULL); } -#endif /* SHADOWGRP */ +#endif + if (gflg) { if (pw_lock () == 0) { fprintf (stderr, _("%s: cannot lock %s; try again later.\n"), Prog, pw_dbname ()); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } - pw_locked = true; + add_cleanup (cleanup_unlock_passwd, NULL); + } +} + + +/* + * open_files - open the accounts databases + * + * open_files() opens the group, gshadow, and passwd databases. + */ +static void open_files (void) +{ + 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); + } + +#ifdef SHADOWGRP + if ( is_shadow_grp + && (pflg || nflg)) { + 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); + } + } +#endif /* SHADOWGRP */ + + if (gflg) { if (pw_open (O_RDWR) == 0) { fprintf (stderr, _("%s: cannot open %s\n"), Prog, pw_dbname ()); SYSLOG ((LOG_WARN, "cannot open %s", gr_dbname ())); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } } } @@ -651,7 +657,7 @@ void update_primary_groups (gid_t ogid, gid_t ngid) fprintf (stderr, _("%s: user '%s' does not exist in %s\n"), Prog, pwd->pw_name, pw_dbname ()); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } else { npwd = *lpwd; npwd.pw_gid = ngid; @@ -659,7 +665,7 @@ void update_primary_groups (gid_t ogid, gid_t ngid) fprintf (stderr, _("%s: failed to prepare the new %s entry '%s'\n"), Prog, pw_dbname (), npwd.pw_name); - fail_exit (E_GRP_UPDATE); + exit (E_GRP_UPDATE); } } } @@ -691,6 +697,7 @@ int main (int argc, char **argv) #ifdef WITH_AUDIT audit_help_open (); #endif + atexit (do_cleanups); /* * Get my name so that I can use it to report errors. @@ -714,7 +721,7 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); - fail_exit (1); + exit (1); } retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh); @@ -733,7 +740,7 @@ int main (int argc, char **argv) } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM authentication failed\n"), Prog); - fail_exit (1); + exit (1); } #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ @@ -750,27 +757,12 @@ int main (int argc, char **argv) if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - group_name, AUDIT_NO_ID, 0); -#endif - fail_exit (E_NOTFOUND); + exit (E_NOTFOUND); } else { group_id = grp->gr_gid; } } -#ifdef WITH_AUDIT - /* Set new name/id to original if not specified on command line */ - if (!nflg) { - group_newname = group_name; - } - if (!gflg) { - group_newid = group_id; - } -#endif - #ifdef USE_NIS /* * Now make sure it isn't an NIS group. @@ -782,17 +774,12 @@ int main (int argc, char **argv) fprintf (stderr, _("%s: group %s is a NIS group\n"), Prog, group_name); -#ifdef WITH_AUDIT - audit_logger (AUDIT_USER_CHAUTHTOK, Prog, - "modifying group", - group_name, AUDIT_NO_ID, 0); -#endif if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } - fail_exit (E_NOTFOUND); + exit (E_NOTFOUND); } #endif @@ -804,6 +791,14 @@ int main (int argc, char **argv) check_new_name (); } + lock_files (); + + /* + * Now if the group is not changed, it's our fault. + * Make sure failures will be reported. + */ + prepare_failure_reports (); + /* * Do the hard stuff - open the files, create the group entries, * then close and update the files.