Initial commit for Tizen
[profile/extras/shadow-utils.git] / src / vipw.c
1 /*
2   vipw, vigr  edit the password or group file
3   with -s will edit shadow or gshadow file
4  
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 - 2008, Nicolas François
9   All rights reserved.
10
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.
15
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.
20
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.  */
25
26 #include <config.h>
27
28 #ident "$Id: vipw.c 3006 2009-05-25 19:51:23Z nekral-guest $"
29
30 #include <errno.h>
31 #include <getopt.h>
32 #ifdef WITH_SELINUX                                                            
33 #include <selinux/selinux.h>                                                   
34 #endif
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <sys/stat.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <utime.h>
42 #include "defines.h"
43 #include "groupio.h"
44 #include "nscd.h"
45 #include "prototypes.h"
46 #include "pwio.h"
47 #include "sgroupio.h"
48 #include "shadowio.h"
49 /*@-exitarg@*/
50 #include "exitcodes.h"
51
52 #define MSG_WARN_EDIT_OTHER_FILE _( \
53         "You have modified %s.\n"\
54         "You may need to modify %s for consistency.\n"\
55         "Please use the command '%s' to do so.\n")
56
57 /*
58  * Global variables
59  */
60 static const char *progname, *filename, *fileeditname;
61 static bool filelocked = false;
62 static bool createedit = false;
63 static int (*unlock) (void);
64 static bool quiet = false;
65
66 /* local function prototypes */
67 static void usage (void);
68 static int create_backup_file (FILE *, const char *, struct stat *);
69 static void vipwexit (const char *msg, int syserr, int ret);
70 static void vipwedit (const char *, int (*)(void), int (*)(void));
71
72 /*
73  * usage - display usage message and exit
74  */
75 static void usage (void)
76 {
77         (void) 
78         fputs (_("Usage: vipw [options]\n"
79                  "\n"
80                  "Options:\n"
81                  "  -g, --group                   edit group database\n"
82                  "  -h, --help                    display this help message and exit\n"
83                  "  -p, --passwd                  edit passwd database\n"
84                  "  -q, --quiet                   quiet mode\n"
85                  "  -s, --shadow                  edit shadow or gshadow database\n"
86                  "\n"), stderr);
87         exit (E_USAGE);
88 }
89
90 /*
91  *
92  */
93 static int create_backup_file (FILE * fp, const char *backup, struct stat *sb)
94 {
95         struct utimbuf ub;
96         FILE *bkfp;
97         int c;
98         mode_t mask;
99
100         mask = umask (077);
101         bkfp = fopen (backup, "w");
102         (void) umask (mask);
103         if (NULL == bkfp) {
104                 return -1;
105         }
106
107         c = 0;
108         if (fseeko (fp, 0, SEEK_SET) == 0)
109                 while ((c = getc (fp)) != EOF) {
110                         if (putc (c, bkfp) == EOF) {
111                                 break;
112                         }
113                 }
114         if ((EOF != c) || (ferror (fp) != 0) || (fflush (bkfp) != 0)) {
115                 fclose (bkfp);
116                 unlink (backup);
117                 return -1;
118         }
119         if (fsync (fileno (bkfp)) != 0) {
120                 (void) fclose (bkfp);
121                 unlink (backup);
122                 return -1;
123         }
124         if (fclose (bkfp) != 0) {
125                 unlink (backup);
126                 return -1;
127         }
128
129         ub.actime = sb->st_atime;
130         ub.modtime = sb->st_mtime;
131         if (   (utime (backup, &ub) != 0)
132             || (chmod (backup, sb->st_mode) != 0)
133             || (chown (backup, sb->st_uid, sb->st_gid) != 0)) {
134                 unlink (backup);
135                 return -1;
136         }
137         return 0;
138 }
139
140 /*
141  *
142  */
143 static void vipwexit (const char *msg, int syserr, int ret)
144 {
145         int err = errno;
146
147         if (createedit) {
148                 if (unlink (fileeditname) != 0) {
149                         fprintf (stderr, _("%s: failed to remove %s\n"), progname, fileeditname);
150                         /* continue */
151                 }
152         }
153         if (filelocked) {
154                 if ((*unlock) () == 0) {
155                         fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
156                         SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
157                         /* continue */
158                 }
159         }
160         if (NULL != msg) {
161                 fprintf (stderr, "%s: %s", progname, msg);
162         }
163         if (0 != syserr) {
164                 fprintf (stderr, ": %s", strerror (err));
165         }
166         (void) fputs ("\n", stderr);
167         if (!quiet) {
168                 fprintf (stdout, _("%s: %s is unchanged\n"), progname,
169                          filename);
170         }
171         exit (ret);
172 }
173
174 #ifndef DEFAULT_EDITOR
175 #define DEFAULT_EDITOR "vi"
176 #endif
177
178 /*
179  *
180  */
181 static void
182 vipwedit (const char *file, int (*file_lock) (void), int (*file_unlock) (void))
183 {
184         const char *editor;
185         pid_t pid;
186         struct stat st1, st2;
187         int status;
188         FILE *f;
189         char filebackup[1024], fileedit[1024];
190
191         snprintf (filebackup, sizeof filebackup, "%s-", file);
192         snprintf (fileedit, sizeof fileedit, "%s.edit", file);
193         unlock = file_unlock;
194         filename = file;
195         fileeditname = fileedit;
196
197         if (access (file, F_OK) != 0) {
198                 vipwexit (file, 1, 1);
199         }
200 #ifdef WITH_SELINUX
201         /* if SE Linux is enabled then set the context of all new files
202            to be the context of the file we are editing */
203         if (is_selinux_enabled ()) {
204                 security_context_t passwd_context=NULL;
205                 int ret = 0;
206                 if (getfilecon (file, &passwd_context) < 0) {
207                         vipwexit (_("Couldn't get file context"), errno, 1);
208                 }
209                 ret = setfscreatecon (passwd_context);
210                 freecon (passwd_context);
211                 if (0 != ret) {
212                         vipwexit (_("setfscreatecon () failed"), errno, 1);
213                 }
214         }
215 #endif
216         if (file_lock () == 0) {
217                 vipwexit (_("Couldn't lock file"), errno, 5);
218         }
219         filelocked = true;
220
221         /* edited copy has same owners, perm */
222         if (stat (file, &st1) != 0) {
223                 vipwexit (file, 1, 1);
224         }
225         f = fopen (file, "r");
226         if (NULL == f) {
227                 vipwexit (file, 1, 1);
228         }
229         if (create_backup_file (f, fileedit, &st1) != 0) {
230                 vipwexit (_("Couldn't make backup"), errno, 1);
231         }
232         (void) fclose (f);
233         createedit = true;
234
235         editor = getenv ("VISUAL");
236         if (NULL == editor) {
237                 editor = getenv ("EDITOR");
238         }
239         if (NULL == editor) {
240                 editor = DEFAULT_EDITOR;
241         }
242
243         pid = fork ();
244         if (-1 == pid) {
245                 vipwexit ("fork", 1, 1);
246         } else if (0 == pid) {
247                 /* use the system() call to invoke the editor so that it accepts
248                    command line args in the EDITOR and VISUAL environment vars */
249                 char *buf;
250
251                 buf = (char *) malloc (strlen (editor) + strlen (fileedit) + 2);
252                 snprintf (buf, strlen (editor) + strlen (fileedit) + 2,
253                           "%s %s", editor, fileedit);
254                 if (system (buf) != 0) {
255                         fprintf (stderr, "%s: %s: %s\n", progname, editor,
256                                  strerror (errno));
257                         exit (1);
258                 } else {
259                         exit (0);
260                 }
261         }
262
263         for (;;) {
264                 pid = waitpid (pid, &status, WUNTRACED);
265                 if ((pid != -1) && (WIFSTOPPED (status) != 0)) {
266                         /* The child (editor) was suspended.
267                          * Suspend vipw. */
268                         kill (getpid (), WSTOPSIG(status));
269                         /* wake child when resumed */
270                         kill (pid, SIGCONT);
271                 } else {
272                         break;
273                 }
274         }
275
276         if (   (-1 == pid)
277             || (WIFEXITED (status) == 0)
278             || (WEXITSTATUS (status) != 0)) {
279                 vipwexit (editor, 1, 1);
280         }
281
282         if (stat (fileedit, &st2) != 0) {
283                 vipwexit (fileedit, 1, 1);
284         }
285         if (st1.st_mtime == st2.st_mtime) {
286                 vipwexit (0, 0, 0);
287         }
288 #ifdef WITH_SELINUX                                                            
289         /* unset the fscreatecon */                                             
290         if (is_selinux_enabled ()) {
291                 if (setfscreatecon (NULL)) {
292                         vipwexit (_("setfscreatecon () failed"), errno, 1);
293                 }
294         }
295 #endif
296
297         /*
298          * XXX - here we should check fileedit for errors; if there are any,
299          * ask the user what to do (edit again, save changes anyway, or quit
300          * without saving). Use pwck or grpck to do the check.  --marekm
301          */
302         createedit = false;
303         unlink (filebackup);
304         link (file, filebackup);
305         if (rename (fileedit, file) == -1) {
306                 fprintf (stderr,
307                          _("%s: can't restore %s: %s (your changes are in %s)\n"),
308                          progname, file, strerror (errno), fileedit);
309                 vipwexit (0, 0, 1);
310         }
311
312         if ((*file_unlock) () == 0) {
313                 fprintf (stderr, _("%s: failed to unlock %s\n"), progname, fileeditname);
314                 SYSLOG ((LOG_ERR, "failed to unlock %s", fileeditname));
315                 /* continue */
316         }
317         SYSLOG ((LOG_INFO, "file %s edited", fileeditname));
318 }
319
320 int main (int argc, char **argv)
321 {
322         bool editshadow = false;
323         char *a;
324         bool do_vipw;
325
326         (void) setlocale (LC_ALL, "");
327         (void) bindtextdomain (PACKAGE, LOCALEDIR);
328         (void) textdomain (PACKAGE);
329
330         progname = ((a = strrchr (*argv, '/')) ? a + 1 : *argv);
331         do_vipw = (strcmp (progname, "vigr") != 0);
332
333         OPENLOG (do_vipw ? "vipw" : "vigr");
334
335         {
336                 /*
337                  * Parse the command line options.
338                  */
339                 int c;
340                 static struct option long_options[] = {
341                         {"group", no_argument, NULL, 'g'},
342                         {"help", no_argument, NULL, 'h'},
343                         {"passwd", no_argument, NULL, 'p'},
344                         {"quiet", no_argument, NULL, 'q'},
345                         {"shadow", no_argument, NULL, 's'},
346                         {NULL, 0, NULL, '\0'}
347                 };
348                 while ((c =
349                         getopt_long (argc, argv, "ghpqs",
350                                      long_options, NULL)) != -1) {
351                         switch (c) {
352                         case 'g':
353                                 do_vipw = false;
354                                 break;
355                         case 'h':
356                                 usage ();
357                                 break;
358                         case 'p':
359                                 do_vipw = true;
360                                 break;
361                         case 'q':
362                                 quiet = true;
363                                 break;
364                         case 's':
365                                 editshadow = true;
366                                 break;
367                         default:
368                                 usage ();
369                         }
370                 }
371         }
372
373         if (do_vipw) {
374                 if (editshadow) {
375                         vipwedit (SHADOW_FILE, spw_lock, spw_unlock);
376                         printf (MSG_WARN_EDIT_OTHER_FILE,
377                                 SHADOW_FILE,
378                                 PASSWD_FILE,
379                                 "vipw");
380                 } else {
381                         vipwedit (PASSWD_FILE, pw_lock, pw_unlock);
382                         if (spw_file_present ()) {
383                                 printf (MSG_WARN_EDIT_OTHER_FILE,
384                                         PASSWD_FILE,
385                                         SHADOW_FILE,
386                                         "vipw -s");
387                         }
388                 }
389         } else {
390 #ifdef SHADOWGRP
391                 if (editshadow) {
392                         vipwedit (SGROUP_FILE, sgr_lock, sgr_unlock);
393                         printf (MSG_WARN_EDIT_OTHER_FILE,
394                                 SGROUP_FILE,
395                                 GROUP_FILE,
396                                 "vigr");
397                 } else {
398 #endif
399                         vipwedit (GROUP_FILE, gr_lock, gr_unlock);
400 #ifdef SHADOWGRP
401                         if (sgr_file_present ()) {
402                                 printf (MSG_WARN_EDIT_OTHER_FILE,
403                                         GROUP_FILE,
404                                         SGROUP_FILE,
405                                         "vigr -s");
406                         }
407                 }
408 #endif
409         }
410
411         nscd_flush_cache ("passwd");
412         nscd_flush_cache ("group");
413
414         return E_SUCCESS;
415 }
416