2 vipw, vigr edit the password or group file
3 with -s will edit shadow or gshadow file
5 Copyright (c) 1997 , Guy Maor <maor@ece.utexas.edu>
6 Copyright (c) 1999 - 2000, Marek Michałkiewicz
7 Copyright (c) 2002 - 2006, Tomasz Kłoczko
8 Copyright (c) 2007 - 2011, Nicolas François
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 Boston, MA 02110-1301, USA. */
33 #include <selinux/selinux.h>
34 #endif /* WITH_SELINUX */
39 #include <sys/types.h>
45 #include "prototypes.h"
50 #include "exitcodes.h"
56 #define MSG_WARN_EDIT_OTHER_FILE _( \
57 "You have modified %s.\n"\
58 "You may need to modify %s for consistency.\n"\
59 "Please use the command '%s' to do so.\n")
66 static const char *filename, *fileeditname;
67 static bool filelocked = false;
68 static bool createedit = false;
69 static int (*unlock) (void);
70 static bool quiet = false;
72 static const char *user = NULL;
73 static bool tcb_mode = false;
74 #define SHADOWTCB_SCRATCHDIR ":tmp"
77 /* local function prototypes */
78 static void usage (int status);
79 static int create_backup_file (FILE *, const char *, struct stat *);
80 static void vipwexit (const char *msg, int syserr, int ret);
81 static void vipwedit (const char *, int (*)(void), int (*)(void));
84 * usage - display usage message and exit
86 static void usage (int status)
88 FILE *usageout = (E_SUCCESS != status) ? stderr : stdout;
89 (void) fputs (_("Usage: vipw [options]\n"
91 "Options:\n"), usageout);
92 (void) fputs (_(" -g, --group edit group database\n"), usageout);
93 (void) fputs (_(" -h, --help display this help message and exit\n"), usageout);
94 (void) fputs (_(" -p, --passwd edit passwd database\n"), usageout);
95 (void) fputs (_(" -q, --quiet quiet mode\n"), usageout);
96 (void) fputs (_(" -R, --root CHROOT_DIR directory to chroot into\n"), usageout);
97 (void) fputs (_(" -s, --shadow edit shadow or gshadow database\n"), usageout);
99 (void) fputs (_(" -u, --user which user's tcb shadow file to edit\n"), usageout);
100 #endif /* WITH_TCB */
101 (void) fputs (_("\n"), usageout);
108 static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
116 bkfp = fopen (backup, "w");
123 if (fseeko (fp, 0, SEEK_SET) == 0)
124 while ((c = getc (fp)) != EOF) {
125 if (putc (c, bkfp) == EOF) {
129 if ((EOF != c) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
134 if (fsync (fileno (bkfp)) != 0) {
135 (void) fclose (bkfp);
139 if (fclose (bkfp) != 0) {
144 ub.actime = sb->st_atime;
145 ub.modtime = sb->st_mtime;
146 if ( (utime (backup, &ub) != 0)
147 || (chmod (backup, sb->st_mode) != 0)
148 || (chown (backup, sb->st_uid, sb->st_gid) != 0)) {
158 static void vipwexit (const char *msg, int syserr, int ret)
163 if (unlink (fileeditname) != 0) {
164 fprintf (stderr, _("%s: failed to remove %s\n"), Prog, fileeditname);
169 if ((*unlock) () == 0) {
170 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname);
171 SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
176 fprintf (stderr, "%s: %s", Prog, msg);
179 fprintf (stderr, ": %s", strerror (err));
181 (void) fputs ("\n", stderr);
183 fprintf (stdout, _("%s: %s is unchanged\n"), Prog,
189 #ifndef DEFAULT_EDITOR
190 #define DEFAULT_EDITOR "vi"
197 vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
201 struct stat st1, st2;
204 /* FIXME: the following should have variable sizes */
205 char filebackup[1024], fileedit[1024];
208 snprintf (filebackup, sizeof filebackup, "%s-", file);
211 if ( (mkdir (TCB_DIR "/" SHADOWTCB_SCRATCHDIR, 0700) != 0)
212 && (errno != EEXIST)) {
213 vipwexit (_("failed to create scratch directory"), errno, 1);
215 if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
216 vipwexit (_("failed to drop privileges"), errno, 1);
218 snprintf (fileedit, sizeof fileedit,
219 TCB_DIR "/" SHADOWTCB_SCRATCHDIR "/.vipw.shadow.%s",
222 #endif /* WITH_TCB */
223 snprintf (fileedit, sizeof fileedit, "%s.edit", file);
226 #endif /* WITH_TCB */
227 unlock = file_unlock;
229 fileeditname = fileedit;
231 if (access (file, F_OK) != 0) {
232 vipwexit (file, 1, 1);
235 /* if SE Linux is enabled then set the context of all new files
236 to be the context of the file we are editing */
237 if (is_selinux_enabled () != 0) {
238 security_context_t passwd_context=NULL;
240 if (getfilecon (file, &passwd_context) < 0) {
241 vipwexit (_("Couldn't get file context"), errno, 1);
243 ret = setfscreatecon (passwd_context);
244 freecon (passwd_context);
246 vipwexit (_("setfscreatecon () failed"), errno, 1);
249 #endif /* WITH_SELINUX */
251 if (tcb_mode && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE)) {
252 vipwexit (_("failed to gain privileges"), errno, 1);
254 #endif /* WITH_TCB */
255 if (file_lock () == 0) {
256 vipwexit (_("Couldn't lock file"), errno, 5);
260 if (tcb_mode && (shadowtcb_drop_priv () == SHADOWTCB_FAILURE)) {
261 vipwexit (_("failed to drop privileges"), errno, 1);
263 #endif /* WITH_TCB */
265 /* edited copy has same owners, perm */
266 if (stat (file, &st1) != 0) {
267 vipwexit (file, 1, 1);
269 f = fopen (file, "r");
271 vipwexit (file, 1, 1);
274 if (tcb_mode && (shadowtcb_gain_priv () == SHADOWTCB_FAILURE))
275 vipwexit (_("failed to gain privileges"), errno, 1);
276 #endif /* WITH_TCB */
277 if (create_backup_file (f, fileedit, &st1) != 0) {
278 vipwexit (_("Couldn't make backup"), errno, 1);
283 editor = getenv ("VISUAL");
284 if (NULL == editor) {
285 editor = getenv ("EDITOR");
287 if (NULL == editor) {
288 editor = DEFAULT_EDITOR;
293 vipwexit ("fork", 1, 1);
294 } else if (0 == pid) {
295 /* use the system() call to invoke the editor so that it accepts
296 command line args in the EDITOR and VISUAL environment vars */
299 buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
300 snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
301 "%s %s", editor, fileedit);
302 if (system (buf) != 0) {
303 fprintf (stderr, "%s: %s: %s\n", Prog, editor,
312 pid = waitpid (pid, &status, WUNTRACED);
313 if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
314 /* The child (editor) was suspended.
316 kill (getpid (), SIGSTOP);
317 /* wake child when resumed */
325 || (WIFEXITED (status) == 0)
326 || (WEXITSTATUS (status) != 0)) {
327 vipwexit (editor, 1, 1);
330 if (stat (fileedit, &st2) != 0) {
331 vipwexit (fileedit, 1, 1);
333 if (st1.st_mtime == st2.st_mtime) {
337 /* unset the fscreatecon */
338 if (is_selinux_enabled () != 0) {
339 if (setfscreatecon (NULL) != 0) {
340 vipwexit (_("setfscreatecon () failed"), errno, 1);
343 #endif /* WITH_SELINUX */
346 * XXX - here we should check fileedit for errors; if there are any,
347 * ask the user what to do (edit again, save changes anyway, or quit
348 * without saving). Use pwck or grpck to do the check. --marekm
353 f = fopen (fileedit, "r");
355 vipwexit (_("failed to open scratch file"), errno, 1);
357 if (unlink (fileedit) != 0) {
358 vipwexit (_("failed to unlink scratch file"), errno, 1);
360 if (shadowtcb_drop_priv () == SHADOWTCB_FAILURE) {
361 vipwexit (_("failed to drop privileges"), errno, 1);
363 if (stat (file, &st1) != 0) {
364 vipwexit (_("failed to stat edited file"), errno, 1);
366 to_rename = malloc (strlen (file) + 2);
367 if (NULL == to_rename) {
368 vipwexit (_("failed to allocate memory"), errno, 1);
370 snprintf (to_rename, strlen (file) + 2, "%s+", file);
371 if (create_backup_file (f, to_rename, &st1) != 0) {
373 vipwexit (_("failed to create backup file"), errno, 1);
377 #endif /* WITH_TCB */
378 to_rename = fileedit;
381 #endif /* WITH_TCB */
383 link (file, filebackup);
384 if (rename (to_rename, file) == -1) {
386 _("%s: can't restore %s: %s (your changes are in %s)\n"),
387 Prog, file, strerror (errno), to_rename);
392 #endif /* WITH_TCB */
399 if (shadowtcb_gain_priv () == SHADOWTCB_FAILURE) {
400 vipwexit (_("failed to gain privileges"), errno, 1);
403 #endif /* WITH_TCB */
405 if ((*file_unlock) () == 0) {
406 fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, fileeditname);
407 SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
410 SYSLOG ((LOG_INFO, "file %s edited", fileeditname));
413 int main (int argc, char **argv)
415 bool editshadow = false;
419 Prog = Basename (argv[0]);
421 (void) setlocale (LC_ALL, "");
422 (void) bindtextdomain (PACKAGE, LOCALEDIR);
423 (void) textdomain (PACKAGE);
425 process_root_flag ("-R", argc, argv);
427 do_vipw = (strcmp (Prog, "vigr") != 0);
429 OPENLOG (do_vipw ? "vipw" : "vigr");
433 * Parse the command line options.
436 static struct option long_options[] = {
437 {"group", no_argument, NULL, 'g'},
438 {"help", no_argument, NULL, 'h'},
439 {"passwd", no_argument, NULL, 'p'},
440 {"quiet", no_argument, NULL, 'q'},
441 {"root", required_argument, NULL, 'R'},
442 {"shadow", no_argument, NULL, 's'},
444 {"user", required_argument, NULL, 'u'},
445 #endif /* WITH_TCB */
446 {NULL, 0, NULL, '\0'}
448 while ((c = getopt_long (argc, argv,
451 #else /* !WITH_TCB */
453 #endif /* !WITH_TCB */
454 long_options, NULL)) != -1) {
468 case 'R': /* no-op, handled in process_root_flag () */
477 #endif /* WITH_TCB */
487 if (getdef_bool ("USE_TCB") && (NULL != user)) {
488 if (shadowtcb_set_user (user) == SHADOWTCB_FAILURE) {
490 _("%s: failed to find tcb directory for %s\n"),
492 return E_SHADOW_NOTFOUND;
496 #endif /* WITH_TCB */
497 vipwedit (spw_dbname (), spw_lock, spw_unlock);
498 printf (MSG_WARN_EDIT_OTHER_FILE,
503 vipwedit (pw_dbname (), pw_lock, pw_unlock);
504 if (spw_file_present ()) {
505 printf (MSG_WARN_EDIT_OTHER_FILE,
514 vipwedit (sgr_dbname (), sgr_lock, sgr_unlock);
515 printf (MSG_WARN_EDIT_OTHER_FILE,
520 #endif /* SHADOWGRP */
521 vipwedit (gr_dbname (), gr_lock, gr_unlock);
523 if (sgr_file_present ()) {
524 printf (MSG_WARN_EDIT_OTHER_FILE,
530 #endif /* SHADOWGRP */
533 nscd_flush_cache ("passwd");
534 nscd_flush_cache ("group");