shadow/src/vipw.c

261 lines
6.0 KiB
C
Raw Normal View History

/*
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>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include "rcsid.h"
RCSID (PKG_VER "$Id: vipw.c,v 1.11 2005/05/25 18:20:25 kloczek Exp $")
#include "defines.h"
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <utime.h>
#include "prototypes.h"
#include "pwio.h"
#include "shadowio.h"
#include "groupio.h"
#include "sgroupio.h"
#include "nscd.h"
static const char *progname, *filename, *fileeditname;
static int filelocked = 0, createedit = 0;
static int (*unlock) (void);
/* local function prototypes */
static int create_backup_file (FILE *, const char *, struct stat *);
static void vipwexit (const char *, int, int);
static void vipwedit (const char *, int (*)(void), int (*)(void));
static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
{
struct utimbuf ub;
FILE *bkfp;
int c;
mode_t mask;
mask = umask (077);
bkfp = fopen (backup, "w");
umask (mask);
if (!bkfp)
return -1;
c = 0;
if (fseeko (fp, 0, SEEK_SET) == 0)
while ((c = getc (fp)) != EOF) {
if (putc (c, bkfp) == EOF)
break;
}
if (c != EOF || ferror (fp) || fflush (bkfp)) {
fclose (bkfp);
unlink (backup);
return -1;
}
if (fclose (bkfp)) {
unlink (backup);
return -1;
}
ub.actime = sb->st_atime;
ub.modtime = sb->st_mtime;
if (utime (backup, &ub) ||
chmod (backup, sb->st_mode) ||
chown (backup, sb->st_uid, sb->st_gid)) {
unlink (backup);
return -1;
}
return 0;
}
static void vipwexit (const char *msg, int syserr, int ret)
{
int err = errno;
if (createedit)
unlink (fileeditname);
if (filelocked)
(*unlock) ();
if (msg)
fprintf (stderr, "%s: %s", progname, msg);
if (syserr)
fprintf (stderr, ": %s", strerror (err));
fprintf (stderr, _("\n%s: %s is unchanged\n"), progname, filename);
exit (ret);
}
#ifndef DEFAULT_EDITOR
#define DEFAULT_EDITOR "vi"
#endif
static void
vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
{
const char *editor;
pid_t pid;
struct stat st1, st2;
int status;
FILE *f;
char filebackup[1024], fileedit[1024];
snprintf (filebackup, sizeof filebackup, "%s-", file);
snprintf (fileedit, sizeof fileedit, "%s.edit", file);
unlock = file_unlock;
filename = file;
fileeditname = fileedit;
if (access (file, F_OK))
vipwexit (file, 1, 1);
if (!file_lock ())
vipwexit (_("Couldn't lock file"), errno, 5);
filelocked = 1;
/* edited copy has same owners, perm */
if (stat (file, &st1))
vipwexit (file, 1, 1);
if (!(f = fopen (file, "r")))
vipwexit (file, 1, 1);
if (create_backup_file (f, fileedit, &st1))
vipwexit (_("Couldn't make backup"), errno, 1);
createedit = 1;
editor = getenv ("VISUAL");
if (!editor)
editor = getenv ("EDITOR");
if (!editor)
editor = DEFAULT_EDITOR;
if ((pid = fork ()) == -1)
vipwexit ("fork", 1, 1);
else if (!pid) {
/* use the system() call to invoke the editor so that it accepts
command line args in the EDITOR and VISUAL environment vars */
char *buf;
buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
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));
exit (1);
} else
exit (0);
}
for (;;) {
pid = waitpid (pid, &status, WUNTRACED);
if (WIFSTOPPED (status)) {
kill (getpid (), SIGSTOP);
kill (getpid (), SIGCONT);
} else
break;
}
if (pid == -1 || !WIFEXITED (status) || WEXITSTATUS (status))
vipwexit (editor, 1, 1);
if (stat (fileedit, &st2))
vipwexit (fileedit, 1, 1);
if (st1.st_mtime == st2.st_mtime)
vipwexit (0, 0, 0);
/*
* XXX - here we should check fileedit for errors; if there are any,
* ask the user what to do (edit again, save changes anyway, or quit
* without saving). Use pwck or grpck to do the check. --marekm
*/
createedit = 0;
unlink (filebackup);
link (file, filebackup);
if (rename (fileedit, file) == -1) {
fprintf (stderr,
_
("%s: can't restore %s: %s (your changes are in %s)\n"),
progname, file, strerror (errno), fileedit);
vipwexit (0, 0, 1);
}
(*file_unlock) ();
}
int main (int argc, char **argv)
{
int flag;
int editshadow = 0;
char *c;
int e = 1;
int do_vipw;
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
progname = ((c = strrchr (*argv, '/')) ? c + 1 : *argv);
do_vipw = (strcmp (progname, "vigr") != 0);
while ((flag = getopt (argc, argv, "ghps")) != EOF) {
switch (flag) {
case 'p':
do_vipw = 1;
break;
case 'g':
do_vipw = 0;
break;
case 's':
editshadow = 1;
break;
case 'h':
e = 0;
default:
printf (_("Usage:\n\
`vipw' edits /etc/passwd `vipw -s' edits /etc/shadow\n\
`vigr' edits /etc/group `vigr -s' edits /etc/gshadow\n\
"));
exit (e);
}
}
if (do_vipw) {
if (editshadow)
vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
else
vipwedit (PASSWD_FILE, pw_lock, pw_unlock);
} else {
#ifdef SHADOWGRP
if (editshadow)
vipwedit (SGROUP_FILE, sgr_lock, sgr_unlock);
else
#endif
vipwedit (GROUP_FILE, gr_lock, gr_unlock);
}
nscd_flush_cache ("passwd");
nscd_flush_cache ("group");
nscd_flush_cache ("shadow");
return 0;
}