]> granicus.if.org Git - shadow/blobdiff - src/vipw.c
* src/usermod.c (update_gshadow): is_member was computed twice.
[shadow] / src / vipw.c
index 037808aa79daaa319a5856a0d773bdb0d29dd0a9..75e59c887fe13aadad05c8a472edae4380725949 100644 (file)
@@ -1,11 +1,11 @@
 /*
   vipw, vigr  edit the password or group file
   with -s will edit shadow or gshadow file
+
   Copyright (c) 1997       , Guy Maor <maor@ece.utexas.edu>
   Copyright (c) 1999 - 2000, Marek Michałkiewicz
   Copyright (c) 2002 - 2006, Tomasz Kłoczko
-  Copyright (c) 2007 - 2008, Nicolas François
+  Copyright (c) 2007 - 2010, Nicolas François
   All rights reserved.
 
   This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,9 @@
 
 #include <errno.h>
 #include <getopt.h>
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif                         /* WITH_SELINUX */
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <utime.h>
 #include "defines.h"
-#include "exitcodes.h"
 #include "groupio.h"
 #include "nscd.h"
 #include "prototypes.h"
 #include "pwio.h"
 #include "sgroupio.h"
 #include "shadowio.h"
+/*@-exitarg@*/
+#include "exitcodes.h"
+#ifdef WITH_TCB
+#include <tcb.h>
+#include "tcbfuncs.h"
+#endif                         /* WITH_TCB */
 
 #define MSG_WARN_EDIT_OTHER_FILE _( \
        "You have modified %s.\n"\
 /*
  * Global variables
  */
-static const char *progname, *filename, *fileeditname;
+const char *Prog;
+
+static const char *filename, *fileeditname;
 static bool filelocked = false;
 static bool createedit = false;
 static int (*unlock) (void);
 static bool quiet = false;
+#ifdef WITH_TCB
+static const char *user = NULL;
+static bool tcb_mode = false;
+#define SHADOWTCB_SCRATCHDIR ":tmp"
+#endif                         /* WITH_TCB */
 
 /* local function prototypes */
-static void usage (void);
+static void usage (int status);
 static int create_backup_file (FILE *, const char *, struct stat *);
 static void vipwexit (const char *msg, int syserr, int ret);
 static void vipwedit (const char *, int (*)(void), int (*)(void));
@@ -68,19 +83,22 @@ static void vipwedit (const char *, int (*)(void), int (*)(void));
 /*
  * usage - display usage message and exit
  */
-static void usage (void)
+static void usage (int status)
 {
-       (void) 
-       fputs (_("Usage: vipw [options]\n"
-                "\n"
-                "Options:\n"
-                "  -g, --group                   edit group database\n"
-                "  -h, --help                    display this help message and exit\n"
-                "  -p, --passwd                  edit passwd database\n"
-                "  -q, --quiet                   quiet mode\n"
-                "  -s, --shadow                  edit shadow or gshadow database\n"
-                "\n"), stderr);
-       exit (E_USAGE);
+       FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
+       (void) fputs (_("Usage: vipw [options]\n"
+                       "\n"
+                       "Options:\n"), usageout);
+       (void) fputs (_("  -g, --group                   edit group database\n"), usageout);
+       (void) fputs (_("  -h, --help                    display this help message and exit\n"), usageout);
+       (void) fputs (_("  -p, --passwd                  edit passwd database\n"), usageout);
+       (void) fputs (_("  -q, --quiet                   quiet mode\n"), usageout);
+       (void) fputs (_("  -s, --shadow                  edit shadow or gshadow database\n"), usageout);
+#ifdef WITH_TCB
+       (void) fputs (_("  -u, --user                    which user's tcb shadow file to edit\n"), usageout);
+#endif                         /* WITH_TCB */
+       (void) fputs (_("\n"), usageout);
+       exit (status);
 }
 
 /*
@@ -112,6 +130,11 @@ static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
                unlink (backup);
                return -1;
        }
+       if (fsync (fileno (bkfp)) != 0) {
+               (void) fclose (bkfp);
+               unlink (backup);
+               return -1;
+       }
        if (fclose (bkfp) != 0) {
                unlink (backup);
                return -1;
@@ -137,26 +160,26 @@ static void vipwexit (const char *msg, int syserr, int ret)
 
        if (createedit) {
                if (unlink (fileeditname) != 0) {
-                       fprintf (stderr, _("%s: failed to remove %s\n"), progname, fileeditname);
+                       fprintf (stderr, _("%s: failed to remove %s\n"), Prog, fileeditname);
                        /* continue */
                }
        }
        if (filelocked) {
                if ((*unlock) () == 0) {
-                       fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
+                       fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname);
                        SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
                        /* continue */
                }
        }
        if (NULL != msg) {
-               fprintf (stderr, "%s: %s", progname, msg);
+               fprintf (stderr, "%s: %s", Prog, msg);
        }
        if (0 != syserr) {
                fprintf (stderr, ": %s", strerror (err));
        }
        (void) fputs ("\n", stderr);
        if (!quiet) {
-               fprintf (stdout, _("%s: %s is unchanged\n"), progname,
+               fprintf (stdout, _("%s: %s is unchanged\n"), Prog,
                         filename);
        }
        exit (ret);
@@ -177,10 +200,29 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
        struct stat st1, st2;
        int status;
        FILE *f;
+       /* FIXME: the following should have variable sizes */
        char filebackup[1024], fileedit[1024];
+       char *to_rename;
 
        snprintf (filebackup, sizeof filebackup, "%s-", file);
-       snprintf (fileedit, sizeof fileedit, "%s.edit", file);
+#ifdef WITH_TCB
+       if (tcb_mode) {
+               if (   (mkdir (TCB_DIR "/" SHADOWTCB_SCRATCHDIR, 0700) != 0)
+                   && (errno != EEXIST)) {
+                       vipwexit (_("failed to create scratch directory"), errno, 1);
+               }
+               if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
+                       vipwexit (_("failed to drop privileges"), errno, 1);
+               }
+               snprintf (fileedit, sizeof fileedit,
+                         TCB_DIR "/" SHADOWTCB_SCRATCHDIR "/.vipw.shadow.%s",
+                         user);
+       } else {
+#endif                         /* WITH_TCB */
+               snprintf (fileedit, sizeof fileedit, "%s.edit", file);
+#ifdef WITH_TCB
+       }
+#endif                         /* WITH_TCB */
        unlock = file_unlock;
        filename = file;
        fileeditname = fileedit;
@@ -188,10 +230,36 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
        if (access (file, F_OK) != 0) {
                vipwexit (file, 1, 1);
        }
+#ifdef WITH_SELINUX
+       /* if SE Linux is enabled then set the context of all new files
+          to be the context of the file we are editing */
+       if (is_selinux_enabled () != 0) {
+               security_context_t passwd_context=NULL;
+               int ret = 0;
+               if (getfilecon (file, &passwd_context) < 0) {
+                       vipwexit (_("Couldn't get file context"), errno, 1);
+               }
+               ret = setfscreatecon (passwd_context);
+               freecon (passwd_context);
+               if (0 != ret) {
+                       vipwexit (_("setfscreatecon () failed"), errno, 1);
+               }
+       }
+#endif                         /* WITH_SELINUX */
+#ifdef WITH_TCB
+       if (tcb_mode && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) {
+               vipwexit (_("failed to gain privileges"), errno, 1);
+       }
+#endif                         /* WITH_TCB */
        if (file_lock () == 0) {
                vipwexit (_("Couldn't lock file"), errno, 5);
        }
        filelocked = true;
+#ifdef WITH_TCB
+       if (tcb_mode && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) {
+               vipwexit (_("failed to drop privileges"), errno, 1);
+       }
+#endif                         /* WITH_TCB */
 
        /* edited copy has same owners, perm */
        if (stat (file, &st1) != 0) {
@@ -201,9 +269,14 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
        if (NULL == f) {
                vipwexit (file, 1, 1);
        }
+#ifdef WITH_TCB
+       if (tcb_mode && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE))
+               vipwexit (_("failed to gain privileges"), errno, 1);
+#endif                         /* WITH_TCB */
        if (create_backup_file (f, fileedit, &st1) != 0) {
                vipwexit (_("Couldn't make backup"), errno, 1);
        }
+       (void) fclose (f);
        createedit = true;
 
        editor = getenv ("VISUAL");
@@ -226,8 +299,8 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
                snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
                          "%s %s", editor, fileedit);
                if (system (buf) != 0) {
-                       fprintf (stderr, "%s: %s: %s\n", progname, editor,
-                                strerror (errno));
+                       fprintf (stderr, "%s: %s: %s\n", Prog, editor,
+                                strerror (errno));
                        exit (1);
                } else {
                        exit (0);
@@ -239,7 +312,7 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
                if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
                        /* The child (editor) was suspended.
                         * Suspend vipw. */
-                       kill (getpid (), WSTOPSIG(status));
+                       kill (getpid (), SIGSTOP);
                        /* wake child when resumed */
                        kill (pid, SIGCONT);
                } else {
@@ -259,6 +332,14 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
        if (st1.st_mtime == st2.st_mtime) {
                vipwexit (0, 0, 0);
        }
+#ifdef WITH_SELINUX
+       /* unset the fscreatecon */
+       if (is_selinux_enabled () != 0) {
+               if (setfscreatecon (NULL) != 0) {
+                       vipwexit (_("setfscreatecon () failed"), errno, 1);
+               }
+       }
+#endif                         /* WITH_SELINUX */
 
        /*
         * XXX - here we should check fileedit for errors; if there are any,
@@ -266,17 +347,62 @@ vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
         * without saving). Use pwck or grpck to do the check.  --marekm
         */
        createedit = false;
+#ifdef WITH_TCB
+       if (tcb_mode) {
+               f = fopen (fileedit, "r");
+               if (NULL == f) {
+                       vipwexit (_("failed to open scratch file"), errno, 1);
+               }
+               if (unlink (fileedit) != 0) {
+                       vipwexit (_("failed to unlink scratch file"), errno, 1);
+               }
+               if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
+                       vipwexit (_("failed to drop privileges"), errno, 1);
+               }
+               if (stat (file, &st1) != 0) {
+                       vipwexit (_("failed to stat edited file"), errno, 1);
+               }
+               to_rename = malloc (strlen (file) + 2);
+               if (NULL == to_rename) {
+                       vipwexit (_("failed to allocate memory"), errno, 1);
+               }
+               snprintf (to_rename, strlen (file) + 2, "%s+", file);
+               if (create_backup_file (f, to_rename, &st1) != 0) {
+                       free (to_rename);
+                       vipwexit (_("failed to create backup file"), errno, 1);
+               }
+               (void) fclose (f);
+       } else {
+#endif                         /* WITH_TCB */
+               to_rename = fileedit;
+#ifdef WITH_TCB
+       }
+#endif                         /* WITH_TCB */
        unlink (filebackup);
        link (file, filebackup);
-       if (rename (fileedit, file) == -1) {
+       if (rename (to_rename, file) == -1) {
                fprintf (stderr,
                         _("%s: can't restore %s: %s (your changes are in %s)\n"),
-                        progname, file, strerror (errno), fileedit);
+                        Prog, file, strerror (errno), to_rename);
+#ifdef WITH_TCB
+               if (tcb_mode) {
+                       free (to_rename);
+               }
+#endif                         /* WITH_TCB */
                vipwexit (0, 0, 1);
        }
 
+#ifdef WITH_TCB
+       if (tcb_mode) {
+               free (to_rename);
+               if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) {
+                       vipwexit (_("failed to gain privileges"), errno, 1);
+               }
+       }
+#endif                         /* WITH_TCB */
+
        if ((*file_unlock) () == 0) {
-               fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
+               fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname);
                SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
                /* continue */
        }
@@ -293,8 +419,8 @@ int main (int argc, char **argv)
        (void) bindtextdomain (PACKAGE, LOCALEDIR);
        (void) textdomain (PACKAGE);
 
-       progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv);
-       do_vipw = (strcmp (progname, "vigr") != 0);
+       Prog = ((a = strrchr (*argv, '/')) ? a + 1 : *argv);
+       do_vipw = (strcmp (Prog, "vigr") != 0);
 
        OPENLOG (do_vipw ? "vipw" : "vigr");
 
@@ -309,17 +435,24 @@ int main (int argc, char **argv)
                        {"passwd", no_argument, NULL, 'p'},
                        {"quiet", no_argument, NULL, 'q'},
                        {"shadow", no_argument, NULL, 's'},
+#ifdef WITH_TCB
+                       {"user", required_argument, NULL, 'u'},
+#endif                         /* WITH_TCB */
                        {NULL, 0, NULL, '\0'}
                };
-               while ((c =
-                       getopt_long (argc, argv, "ghpqs",
-                                    long_options, NULL)) != -1) {
+               while ((c = getopt_long (argc, argv,
+#ifdef WITH_TCB
+                                        "ghpqsu:",
+#else                          /* !WITH_TCB */
+                                        "ghpqs",
+#endif                         /* !WITH_TCB */
+                                        long_options, NULL)) != -1) {
                        switch (c) {
                        case 'g':
                                do_vipw = false;
                                break;
                        case 'h':
-                               usage ();
+                               usage (E_SUCCESS);
                                break;
                        case 'p':
                                do_vipw = true;
@@ -330,53 +463,69 @@ int main (int argc, char **argv)
                        case 's':
                                editshadow = true;
                                break;
+#ifdef WITH_TCB
+                       case 'u':
+                               user = optarg;
+                               break;
+#endif                         /* WITH_TCB */
                        default:
-                               usage ();
+                               usage (E_USAGE);
                        }
                }
        }
 
        if (do_vipw) {
                if (editshadow) {
-                       vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
+#ifdef WITH_TCB
+                       if (getdef_bool ("USE_TCB") && (NULL != user)) {
+                               if (shadowtcb_set_user (user) == SHADOWTCB_FAILURE) {
+                                       fprintf (stderr,
+                                                _("%s: failed to find tcb directory for %s\n"),
+                                                Prog, user);
+                                       return E_SHADOW_NOTFOUND;
+                               }
+                               tcb_mode = true;
+                       }
+#endif                         /* WITH_TCB */
+                       vipwedit (spw_dbname (), spw_lock, spw_unlock);
                        printf (MSG_WARN_EDIT_OTHER_FILE,
-                               SHADOW_FILE,
-                               PASSWD_FILE,
+                               spw_dbname (),
+                               pw_dbname (),
                                "vipw");
                } else {
-                       vipwedit (PASSWD_FILE, pw_lock, pw_unlock);
+                       vipwedit (pw_dbname (), pw_lock, pw_unlock);
                        if (spw_file_present ()) {
                                printf (MSG_WARN_EDIT_OTHER_FILE,
-                                       PASSWD_FILE,
-                                       SHADOW_FILE,
+                                       pw_dbname (),
+                                       spw_dbname (),
                                        "vipw -s");
                        }
                }
        } else {
 #ifdef SHADOWGRP
                if (editshadow) {
-                       vipwedit (SGROUP_FILE, sgr_lock, sgr_unlock);
+                       vipwedit (sgr_dbname (), sgr_lock, sgr_unlock);
                        printf (MSG_WARN_EDIT_OTHER_FILE,
-                               SGROUP_FILE,
-                               GROUP_FILE,
+                               sgr_dbname (),
+                               gr_dbname (),
                                "vigr");
                } else {
-#endif
-                       vipwedit (GROUP_FILE, gr_lock, gr_unlock);
+#endif                         /* SHADOWGRP */
+                       vipwedit (gr_dbname (), gr_lock, gr_unlock);
 #ifdef SHADOWGRP
                        if (sgr_file_present ()) {
                                printf (MSG_WARN_EDIT_OTHER_FILE,
-                                       GROUP_FILE,
-                                       SGROUP_FILE,
+                                       gr_dbname (),
+                                       sgr_dbname (),
                                        "vigr -s");
                        }
                }
-#endif
+#endif                         /* SHADOWGRP */
        }
 
        nscd_flush_cache ("passwd");
        nscd_flush_cache ("group");
 
-       exit (E_SUCCESS);
+       return E_SUCCESS;
 }