]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/vipw.c
hwclock: free temporary variable before return
[thirdparty/util-linux.git] / login-utils / vipw.c
CommitLineData
6dbe3af9
KZ
1/*
2 * Copyright (c) 1987 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
726f69e2
KZ
32 *
33 * Updated Thu Oct 12 09:56:55 1995 by faith@cs.unc.edu with security
34 * patches from Zefram <A.Main@dcs.warwick.ac.uk>
35 *
06eafe47 36 * Updated Thu Nov 9 21:58:53 1995 by Martin Schulze
fd6b7a7f
KZ
37 * <joey@finlandia.infodrom.north.de>. Support for vigr.
38 *
39 * Martin Schulze's patches adapted to Util-Linux by Nicolai Langfeldt.
7eda085c 40 *
b50945d4 41 * 1999-02-22 Arkadiusz Miśkiewicz <misiek@pld.ORG.PL>
7eda085c
KZ
42 * - added Native Language Support
43 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
44 * - fixed strerr(errno) in gettext calls
6dbe3af9
KZ
45 */
46
06eafe47
SK
47#include <errno.h>
48#include <fcntl.h>
49#include <paths.h>
6dbe3af9 50#include <pwd.h>
46b6bcca 51#include <shadow.h>
06eafe47 52#include <signal.h>
6dbe3af9
KZ
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
06eafe47 56#include <sys/file.h>
6dbe3af9 57#include <sys/param.h>
06eafe47
SK
58#include <sys/resource.h>
59#include <sys/stat.h>
6dbe3af9 60#include <sys/time.h>
06eafe47 61#include <sys/types.h>
6dbe3af9 62#include <sys/wait.h>
6dbe3af9 63#include <unistd.h>
6c9f102f 64#include <getopt.h>
6dbe3af9 65
06eafe47 66#include "c.h"
1b1af0c1 67#include "fileutils.h"
439cdf1e 68#include "closestream.h"
06eafe47 69#include "nls.h"
fd6b7a7f 70#include "setpwnam.h"
8abcf290 71#include "strutils.h"
23925360 72#include "xalloc.h"
51924a4e 73#include "rpmatch.h"
6dbe3af9 74
48d7b13a 75#ifdef HAVE_LIBSELINUX
06eafe47 76# include <selinux/selinux.h>
d03dd608
KZ
77#endif
78
fd6b7a7f 79#define FILENAMELEN 67
6dbe3af9 80
06eafe47
SK
81enum {
82 VIPW,
83 VIGR
84};
2ba641e5
SK
85static int program;
86static char orig_file[FILENAMELEN]; /* original file /etc/passwd or /etc/group */
87static char *tmp_file; /* tmp file */
6dbe3af9 88
092bc0ed 89void pw_error (char *, int, int);
6dbe3af9 90
06eafe47
SK
91static void pw_init(void)
92{
6dbe3af9
KZ
93 struct rlimit rlim;
94
95 /* Unlimited resource limits. */
96 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
97 (void)setrlimit(RLIMIT_CPU, &rlim);
98 (void)setrlimit(RLIMIT_FSIZE, &rlim);
99 (void)setrlimit(RLIMIT_STACK, &rlim);
100 (void)setrlimit(RLIMIT_DATA, &rlim);
101 (void)setrlimit(RLIMIT_RSS, &rlim);
102
103 /* Don't drop core (not really necessary, but GP's). */
104 rlim.rlim_cur = rlim.rlim_max = 0;
105 (void)setrlimit(RLIMIT_CORE, &rlim);
106
107 /* Turn off signals. */
108 (void)signal(SIGALRM, SIG_IGN);
109 (void)signal(SIGHUP, SIG_IGN);
110 (void)signal(SIGINT, SIG_IGN);
111 (void)signal(SIGPIPE, SIG_IGN);
112 (void)signal(SIGQUIT, SIG_IGN);
113 (void)signal(SIGTERM, SIG_IGN);
114 (void)signal(SIGTSTP, SIG_IGN);
115 (void)signal(SIGTTOU, SIG_IGN);
116
330dcf98
TS
117 /* Set SIGCHLD to default for waitpid. */
118 (void)signal(SIGCHLD, SIG_DFL);
119
6dbe3af9
KZ
120 /* Create with exact permissions. */
121 (void)umask(0);
122}
123
46b6bcca 124static FILE * pw_tmpfile(int lockfd)
06eafe47 125{
46b6bcca
SK
126 FILE *fd;
127 char *tmpname = NULL;
b9dcd384 128 int res;
c07ebfa1 129
bde91c85 130 if ((fd = xfmkstemp(&tmpname, "/etc", ".vipw")) == NULL) {
46b6bcca
SK
131 ulckpwdf();
132 err(EXIT_FAILURE, _("can't open temporary file"));
c07ebfa1
KZ
133 }
134
46b6bcca 135 tmp_file = tmpname;
b9dcd384 136 res = ul_copy_file(lockfd, fileno(fd));
cabbf61f 137 if (res == UL_COPY_READ_ERROR)
b9dcd384 138 pw_error(orig_file, 1, 1);
cabbf61f 139 else if (res == UL_COPY_WRITE_ERROR)
b9dcd384 140 pw_error(tmp_file, 1, 1);
46b6bcca 141 return fd;
6dbe3af9
KZ
142}
143
46b6bcca 144static void pw_write(void)
06eafe47
SK
145{
146 char tmp[FILENAMELEN + 4];
147
2605d883 148 snprintf(tmp, sizeof(tmp), "%s%s", orig_file, ".OLD");
e8f26419 149 unlink(tmp);
18be404b
KZ
150
151 if (link(orig_file, tmp))
152 warn(_("%s: create a link to %s failed"), orig_file, tmp);
d03dd608 153
48d7b13a 154#ifdef HAVE_LIBSELINUX
4ba66edf 155 if (is_selinux_enabled() > 0) {
ca27216a 156 char *passwd_context = NULL;
06eafe47 157 int ret = 0;
ca27216a 158
06eafe47
SK
159 if (getfilecon(orig_file, &passwd_context) < 0) {
160 warnx(_("Can't get context for %s"), orig_file);
161 pw_error(orig_file, 1, 1);
162 }
163 ret = setfilecon(tmp_file, passwd_context);
164 freecon(passwd_context);
165 if (ret != 0) {
166 warnx(_("Can't set context for %s"), tmp_file);
167 pw_error(tmp_file, 1, 1);
168 }
d03dd608
KZ
169 }
170#endif
171
e8f26419
KZ
172 if (rename(tmp_file, orig_file) == -1) {
173 int errsv = errno;
81c8a46f 174 errx(EXIT_FAILURE,
46b6bcca 175 ("cannot write %s: %s (your changes are still in %s)"),
81c8a46f 176 orig_file, strerror(errsv), tmp_file);
e8f26419
KZ
177 }
178 unlink(tmp_file);
46b6bcca 179 free(tmp_file);
76839e97 180 tmp_file = NULL;
6dbe3af9
KZ
181}
182
ccb07d4d 183static void pw_edit(void)
06eafe47 184{
6dbe3af9
KZ
185 int pstat;
186 pid_t pid;
467d50fa 187 char *p, *editor, *tk;
6dbe3af9 188
467d50fa 189 editor = getenv("EDITOR");
23925360 190 editor = xstrdup(editor ? editor : _PATH_VI);
467d50fa
KZ
191
192 tk = strtok(editor, " \t");
193 if (tk && (p = strrchr(tk, '/')) != NULL)
6dbe3af9 194 ++p;
06eafe47 195 else
6dbe3af9
KZ
196 p = editor;
197
2b6fc908 198 pid = fork();
9b59ecf0
MP
199 if (pid < 0)
200 err(EXIT_FAILURE, _("fork failed"));
201
2b6fc908 202 if (!pid) {
1b10fa0e 203 execlp(editor, p, tmp_file, (char *)NULL);
7bc5eeee 204 errexec(editor);
6dbe3af9
KZ
205 }
206 for (;;) {
06eafe47 207 pid = waitpid(pid, &pstat, WUNTRACED);
330dcf98 208 if (pid != -1 && WIFSTOPPED(pstat)) {
06eafe47
SK
209 /* the editor suspended, so suspend us as well */
210 kill(getpid(), SIGSTOP);
211 kill(pid, SIGCONT);
212 } else {
213 break;
214 }
6dbe3af9 215 }
330dcf98 216 if (pid == -1)
6dbe3af9 217 pw_error(editor, 1, 1);
330dcf98
TS
218 else if (!WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) {
219 warnx("%s: unsuccessful execution", editor);
220 pw_error(editor, 0, 1);
221 }
23925360
KZ
222
223 free(editor);
6dbe3af9
KZ
224}
225
506e6d26
SK
226void __attribute__((__noreturn__))
227pw_error(char *name, int err, int eval)
06eafe47 228{
6dbe3af9 229 if (err) {
6dbe3af9 230 if (name)
330dcf98 231 warn("%s", name);
81c8a46f
SK
232 else
233 warn(NULL);
6dbe3af9 234 }
81c8a46f 235 warnx(_("%s unchanged"), orig_file);
76839e97
KZ
236
237 if (tmp_file)
238 unlink(tmp_file);
46b6bcca 239 ulckpwdf();
6dbe3af9
KZ
240 exit(eval);
241}
242
06eafe47 243static void edit_file(int is_shadow)
c07ebfa1
KZ
244{
245 struct stat begin, end;
46b6bcca
SK
246 int passwd_file, ch_ret;
247 FILE *tmp_fd;
c07ebfa1
KZ
248
249 pw_init();
c07ebfa1 250
46b6bcca
SK
251 /* acquire exclusive lock */
252 if (lckpwdf() < 0)
253 err(EXIT_FAILURE, _("cannot get lock"));
254
a16f45d4 255 passwd_file = open(orig_file, O_RDONLY | O_CLOEXEC, 0);
46b6bcca 256 if (passwd_file < 0)
289dcc90 257 err(EXIT_FAILURE, _("cannot open %s"), orig_file);
46b6bcca
SK
258 tmp_fd = pw_tmpfile(passwd_file);
259
260 if (fstat(fileno(tmp_fd), &begin))
c07ebfa1 261 pw_error(tmp_file, 1, 1);
d03dd608 262
ccb07d4d 263 pw_edit();
d03dd608 264
46b6bcca 265 if (fstat(fileno(tmp_fd), &end))
c07ebfa1 266 pw_error(tmp_file, 1, 1);
69a826f1
SK
267 /* Some editors, such as Vim with 'writebackup' mode enabled,
268 * use "atomic save" in which the old file is deleted and a new
269 * one with the same name created in its place. */
270 if (end.st_nlink == 0) {
271 if (close_stream(tmp_fd) != 0)
272 err(EXIT_FAILURE, _("write error"));
a16f45d4 273 tmp_fd = fopen(tmp_file, "r" UL_CLOEXECSTR);
76839e97 274 if (!tmp_fd)
69a826f1
SK
275 err(EXIT_FAILURE, _("cannot open %s"), tmp_file);
276 if (fstat(fileno(tmp_fd), &end))
277 pw_error(tmp_file, 1, 1);
278 }
c07ebfa1 279 if (begin.st_mtime == end.st_mtime) {
81c8a46f 280 warnx(_("no changes made"));
c07ebfa1
KZ
281 pw_error((char *)NULL, 0, 0);
282 }
46b6bcca 283 /* pw_tmpfile() will create the file with mode 600 */
e8f26419 284 if (!is_shadow)
46b6bcca 285 ch_ret = fchmod(fileno(tmp_fd), 0644);
e8f26419 286 else
46b6bcca
SK
287 ch_ret = fchmod(fileno(tmp_fd), 0400);
288 if (ch_ret < 0)
289 err(EXIT_FAILURE, "%s: %s", _("cannot chmod file"), orig_file);
439cdf1e
SK
290 if (close_stream(tmp_fd) != 0)
291 err(EXIT_FAILURE, _("write error"));
46b6bcca
SK
292 pw_write();
293 close(passwd_file);
294 ulckpwdf();
c07ebfa1
KZ
295}
296
86be6a32 297static void __attribute__((__noreturn__)) usage(void)
cc8e9934 298{
86be6a32 299 FILE *out = stdout;
cc8e9934
SK
300 fputs(USAGE_HEADER, out);
301 fprintf(out, " %s\n", program_invocation_short_name);
451dbcfa
BS
302
303 fputs(USAGE_SEPARATOR, out);
304 fputs(_("Edit the password or group file.\n"), out);
305
cc8e9934 306 fputs(USAGE_OPTIONS, out);
bad4c729
MY
307 fprintf(out, USAGE_HELP_OPTIONS(16));
308 fprintf(out, USAGE_MAN_TAIL("vipw(8)"));
86be6a32 309 exit(EXIT_SUCCESS);
cc8e9934
SK
310}
311
06eafe47
SK
312int main(int argc, char *argv[])
313{
6c9f102f
SK
314 int c;
315 static const struct option longopts[] = {
316 {"version", no_argument, NULL, 'V'},
317 {"help", no_argument, NULL, 'h'},
318 {NULL, 0, NULL, 0}
319 };
320
e8f26419
KZ
321 setlocale(LC_ALL, "");
322 bindtextdomain(PACKAGE, LOCALEDIR);
323 textdomain(PACKAGE);
2c308875 324 close_stdout_atexit();
e8f26419 325
81c8a46f 326 if (!strcmp(program_invocation_short_name, "vigr")) {
e8f26419
KZ
327 program = VIGR;
328 xstrncpy(orig_file, GROUP_FILE, sizeof(orig_file));
e8f26419
KZ
329 } else {
330 program = VIPW;
331 xstrncpy(orig_file, PASSWD_FILE, sizeof(orig_file));
e8f26419
KZ
332 }
333
124a9030 334 while ((c = getopt_long(argc, argv, "Vh", longopts, NULL)) != -1) {
6c9f102f
SK
335 switch (c) {
336 case 'V':
2c308875 337 print_version(EXIT_SUCCESS);
6c9f102f 338 case 'h':
86be6a32 339 usage();
6c9f102f
SK
340 default:
341 errtryhelp(EXIT_FAILURE);
342 }
124a9030 343 }
e8f26419
KZ
344
345 edit_file(0);
346
124a9030
KZ
347 if (program == VIGR)
348 xstrncpy(orig_file, SGROUP_FILE, sizeof(orig_file));
349 else
350 xstrncpy(orig_file, SHADOW_FILE, sizeof(orig_file));
e8f26419
KZ
351
352 if (access(orig_file, F_OK) == 0) {
353 char response[80];
354
4cd4b687 355 fputs((program == VIGR)
e8f26419 356 ? _("You are using shadow groups on this system.\n")
4cd4b687 357 : _("You are using shadow passwords on this system.\n"), stdout);
11b86e17 358
4c5e1f8e
SK
359 /* TRANSLATORS: this program uses for y and n rpmatch(3),
360 * which means they can be translated. */
e8f26419
KZ
361 printf(_("Would you like to edit %s now [y/n]? "), orig_file);
362
34a9b655 363 fflush(stdout);
74ce680a
SK
364 if (fgets(response, sizeof(response), stdin) &&
365 rpmatch(response) == RPMATCH_YES)
366 edit_file(1);
e8f26419 367 }
9b59ecf0 368 exit(EXIT_SUCCESS);
6dbe3af9 369}