Annotation of embedaddon/sudo/plugins/sudoers/visudo.c, revision 1.1.1.5
1.1 misho 1: /*
1.1.1.4 misho 2: * Copyright (c) 1996, 1998-2005, 2007-2013
1.1 misho 3: * Todd C. Miller <Todd.Miller@courtesan.com>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: *
17: * Sponsored in part by the Defense Advanced Research Projects
18: * Agency (DARPA) and Air Force Research Laboratory, Air Force
19: * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20: */
21:
22: /*
23: * Lock the sudoers file for safe editing (ala vipw) and check for parse errors.
24: */
25:
26: #define _SUDO_MAIN
27:
28: #ifdef __TANDEM
29: # include <floss.h>
30: #endif
31:
32: #include <config.h>
33:
34: #include <sys/types.h>
35: #include <sys/stat.h>
36: #include <sys/socket.h>
37: #include <sys/time.h>
1.1.1.4 misho 38: #include <sys/uio.h>
1.1 misho 39: #ifndef __TANDEM
40: # include <sys/file.h>
41: #endif
42: #include <sys/wait.h>
43: #include <stdio.h>
44: #ifdef STDC_HEADERS
45: # include <stdlib.h>
46: # include <stddef.h>
47: #else
48: # ifdef HAVE_STDLIB_H
49: # include <stdlib.h>
50: # endif
51: #endif /* STDC_HEADERS */
52: #ifdef HAVE_STRING_H
53: # include <string.h>
54: #endif /* HAVE_STRING_H */
55: #ifdef HAVE_STRINGS_H
56: # include <strings.h>
57: #endif /* HAVE_STRINGS_H */
58: #ifdef HAVE_UNISTD_H
59: #include <unistd.h>
60: #endif /* HAVE_UNISTD_H */
61: #include <stdarg.h>
62: #include <ctype.h>
63: #include <pwd.h>
64: #include <grp.h>
65: #include <signal.h>
66: #include <errno.h>
67: #include <fcntl.h>
68: #include <netinet/in.h>
69: #include <arpa/inet.h>
70: #if TIME_WITH_SYS_TIME
71: # include <time.h>
72: #endif
1.1.1.5 ! misho 73: #ifdef HAVE_GETOPT_LONG
! 74: # include <getopt.h>
! 75: # else
! 76: # include "compat/getopt.h"
! 77: #endif /* HAVE_GETOPT_LONG */
1.1 misho 78:
79: #include "sudoers.h"
80: #include "parse.h"
81: #include "redblack.h"
82: #include "gettext.h"
83: #include "sudoers_version.h"
1.1.1.2 misho 84: #include "sudo_conf.h"
1.1 misho 85: #include <gram.h>
86:
87: struct sudoersfile {
88: struct sudoersfile *prev, *next;
89: char *path;
90: char *tpath;
91: int fd;
92: int modified;
93: int doedit;
94: };
95: TQ_DECLARE(sudoersfile)
96:
97: /*
98: * Function prototypes
99: */
100: static void quit(int);
101: static char *get_args(char *);
102: static char *get_editor(char **);
103: static void get_hostname(void);
1.1.1.2 misho 104: static int whatnow(void);
105: static int check_aliases(bool, bool);
106: static bool check_syntax(char *, bool, bool, bool);
107: static bool edit_sudoers(struct sudoersfile *, char *, char *, int);
108: static bool install_sudoers(struct sudoersfile *, bool);
1.1 misho 109: static int print_unused(void *, void *);
1.1.1.4 misho 110: static bool reparse_sudoers(char *, char *, bool, bool);
1.1 misho 111: static int run_command(char *, char **);
112: static void setup_signals(void);
113: static void help(void) __attribute__((__noreturn__));
114: static void usage(int);
1.1.1.4 misho 115: static void visudo_cleanup(void);
1.1 misho 116:
1.1.1.4 misho 117: extern void sudoerserror(const char *);
118: extern void sudoersrestart(FILE *);
1.1 misho 119:
120: /*
121: * External globals exported by the parser
122: */
123: extern struct rbtree *aliases;
1.1.1.4 misho 124: extern FILE *sudoersin;
1.1 misho 125: extern char *sudoers, *errorfile;
1.1.1.2 misho 126: extern int errorlineno;
127: extern bool parse_error;
1.1 misho 128:
129: /*
130: * Globals
131: */
132: struct sudo_user sudo_user;
133: struct passwd *list_pw;
134: static struct sudoersfile_list sudoerslist;
135: static struct rbtree *alias_freelist;
1.1.1.2 misho 136: static bool checkonly;
1.1.1.5 ! misho 137: static const char short_opts[] = "cf:hqsV";
! 138: static struct option long_opts[] = {
! 139: { "check", no_argument, NULL, 'c' },
! 140: { "file", required_argument, NULL, 'f' },
! 141: { "help", no_argument, NULL, 'h' },
! 142: { "quiet", no_argument, NULL, 'q' },
! 143: { "strict", no_argument, NULL, 's' },
! 144: { "version", no_argument, NULL, 'V' },
! 145: { NULL, no_argument, NULL, '\0' },
! 146: };
1.1 misho 147:
1.1.1.4 misho 148: __dso_public int main(int argc, char *argv[]);
149:
1.1 misho 150: int
151: main(int argc, char *argv[])
152: {
153: struct sudoersfile *sp;
154: char *args, *editor, *sudoers_path;
1.1.1.2 misho 155: int ch, exitcode = 0;
156: bool quiet, strict, oldperms;
157: debug_decl(main, SUDO_DEBUG_MAIN)
158:
1.1 misho 159: #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
1.1.1.2 misho 160: {
161: extern char *malloc_options;
162: malloc_options = "AFGJPR";
163: }
1.1 misho 164: #endif
165:
166: #if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)
167: setprogname(argc > 0 ? argv[0] : "visudo");
168: #endif
169:
1.1.1.4 misho 170: sudoers_setlocale(SUDOERS_LOCALE_USER, NULL);
1.1 misho 171: bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have visudo domain */
172: textdomain("sudoers");
173:
174: if (argc < 1)
175: usage(1);
176:
1.1.1.4 misho 177: /* Register fatal/fatalx callback. */
178: fatal_callback_register(visudo_cleanup);
179:
1.1.1.2 misho 180: /* Read sudo.conf. */
1.1.1.4 misho 181: sudo_conf_read(NULL);
1.1.1.2 misho 182:
1.1 misho 183: /*
184: * Arg handling.
185: */
1.1.1.2 misho 186: checkonly = oldperms = quiet = strict = false;
1.1 misho 187: sudoers_path = _PATH_SUDOERS;
1.1.1.5 ! misho 188: while ((ch = getopt_long(argc, argv, short_opts, long_opts, NULL)) != -1) {
1.1 misho 189: switch (ch) {
190: case 'V':
1.1.1.5 ! misho 191: (void) printf(_("%s version %s\n"), getprogname(),
! 192: PACKAGE_VERSION);
! 193: (void) printf(_("%s grammar version %d\n"), getprogname(),
! 194: SUDOERS_GRAMMAR_VERSION);
1.1.1.2 misho 195: goto done;
1.1 misho 196: case 'c':
1.1.1.3 misho 197: checkonly = true; /* check mode */
1.1 misho 198: break;
199: case 'f':
200: sudoers_path = optarg; /* sudoers file path */
1.1.1.2 misho 201: oldperms = true;
1.1 misho 202: break;
203: case 'h':
204: help();
205: break;
206: case 's':
1.1.1.3 misho 207: strict = true; /* strict mode */
1.1 misho 208: break;
209: case 'q':
1.1.1.5 ! misho 210: quiet = true; /* quiet mode */
1.1 misho 211: break;
212: default:
213: usage(1);
214: }
215: }
1.1.1.2 misho 216: /* There should be no other command line arguments. */
217: if (argc - optind != 0)
1.1 misho 218: usage(1);
219:
220: sudo_setpwent();
221: sudo_setgrent();
222:
223: /* Mock up a fake sudo_user struct. */
224: user_cmnd = "";
225: if ((sudo_user.pw = sudo_getpwuid(getuid())) == NULL)
1.1.1.4 misho 226: fatalx(_("you do not exist in the %s database"), "passwd");
1.1 misho 227: get_hostname();
228:
229: /* Setup defaults data structures. */
230: init_defaults();
231:
1.1.1.2 misho 232: if (checkonly) {
233: exitcode = check_syntax(sudoers_path, quiet, strict, oldperms) ? 0 : 1;
234: goto done;
235: }
1.1 misho 236:
237: /*
1.1.1.4 misho 238: * Parse the existing sudoers file(s) to highlight any existing
239: * errors and to pull in editor and env_editor conf values.
1.1 misho 240: */
1.1.1.4 misho 241: if ((sudoersin = open_sudoers(sudoers_path, true, NULL)) == NULL)
242: exit(1);
1.1.1.3 misho 243: init_parser(sudoers_path, false);
1.1.1.4 misho 244: sudoersparse();
1.1 misho 245: (void) update_defaults(SETDEF_GENERIC|SETDEF_HOST|SETDEF_USER);
246:
247: editor = get_editor(&args);
248:
249: /* Install signal handlers to clean up temp files if we are killed. */
250: setup_signals();
251:
252: /* Edit the sudoers file(s) */
253: tq_foreach_fwd(&sudoerslist, sp) {
254: if (!sp->doedit)
255: continue;
256: if (sp != tq_first(&sudoerslist)) {
257: printf(_("press return to edit %s: "), sp->path);
258: while ((ch = getchar()) != EOF && ch != '\n')
259: continue;
260: }
261: edit_sudoers(sp, editor, args, -1);
262: }
263:
1.1.1.4 misho 264: /*
265: * Check edited files for a parse error, re-edit any that fail
266: * and install the edited files as needed.
267: */
268: if (reparse_sudoers(editor, args, strict, quiet)) {
269: tq_foreach_fwd(&sudoerslist, sp) {
270: (void) install_sudoers(sp, oldperms);
271: }
1.1 misho 272: }
273:
1.1.1.2 misho 274: done:
275: sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
276: exit(exitcode);
1.1 misho 277: }
278:
279: /*
280: * List of editors that support the "+lineno" command line syntax.
281: * If an entry starts with '*' the tail end of the string is matched.
282: * No other wild cards are supported.
283: */
284: static char *lineno_editors[] = {
285: "ex",
286: "nex",
287: "vi",
288: "nvi",
289: "vim",
290: "elvis",
291: "*macs",
292: "mg",
293: "vile",
294: "jove",
295: "pico",
296: "nano",
297: "ee",
298: "joe",
299: "zile",
300: NULL
301: };
302:
303: /*
304: * Edit each sudoers file.
1.1.1.2 misho 305: * Returns true on success, else false.
1.1 misho 306: */
1.1.1.2 misho 307: static bool
1.1 misho 308: edit_sudoers(struct sudoersfile *sp, char *editor, char *args, int lineno)
309: {
310: int tfd; /* sudoers temp file descriptor */
1.1.1.2 misho 311: bool modified; /* was the file modified? */
1.1 misho 312: int ac; /* argument count */
313: char **av; /* argument vector for run_command */
314: char *cp; /* scratch char pointer */
315: char buf[PATH_MAX*2]; /* buffer used for copying files */
316: char linestr[64]; /* string version of lineno */
317: struct timeval tv, tv1, tv2; /* time before and after edit */
318: struct timeval orig_mtim; /* starting mtime of sudoers file */
319: off_t orig_size; /* starting size of sudoers file */
320: ssize_t nread; /* number of bytes read */
321: struct stat sb; /* stat buffer */
1.1.1.2 misho 322: bool rval = false; /* return value */
323: debug_decl(edit_sudoers, SUDO_DEBUG_UTIL)
1.1 misho 324:
325: if (fstat(sp->fd, &sb) == -1)
1.1.1.4 misho 326: fatal(_("unable to stat %s"), sp->path);
1.1 misho 327: orig_size = sb.st_size;
328: mtim_get(&sb, &orig_mtim);
329:
330: /* Create the temp file if needed and set timestamp. */
331: if (sp->tpath == NULL) {
332: easprintf(&sp->tpath, "%s.tmp", sp->path);
333: tfd = open(sp->tpath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
334: if (tfd < 0)
1.1.1.4 misho 335: fatal("%s", sp->tpath);
1.1 misho 336:
337: /* Copy sp->path -> sp->tpath and reset the mtime. */
338: if (orig_size != 0) {
339: (void) lseek(sp->fd, (off_t)0, SEEK_SET);
340: while ((nread = read(sp->fd, buf, sizeof(buf))) > 0)
341: if (write(tfd, buf, nread) != nread)
1.1.1.4 misho 342: fatal(_("write error"));
1.1 misho 343:
344: /* Add missing newline at EOF if needed. */
345: if (nread > 0 && buf[nread - 1] != '\n') {
346: buf[0] = '\n';
347: if (write(tfd, buf, 1) != 1)
1.1.1.4 misho 348: fatal(_("write error"));
1.1 misho 349: }
350: }
351: (void) close(tfd);
352: }
353: (void) touch(-1, sp->tpath, &orig_mtim);
354:
355: /* Does the editor support +lineno? */
356: if (lineno > 0)
357: {
358: char *editor_base = strrchr(editor, '/');
359: if (editor_base != NULL)
360: editor_base++;
361: else
362: editor_base = editor;
363: if (*editor_base == 'r')
364: editor_base++;
365:
366: for (av = lineno_editors; (cp = *av) != NULL; av++) {
367: /* We only handle a leading '*' wildcard. */
368: if (*cp == '*') {
369: size_t blen = strlen(editor_base);
370: size_t clen = strlen(++cp);
371: if (blen >= clen) {
372: if (strcmp(cp, editor_base + blen - clen) == 0)
373: break;
374: }
375: } else if (strcmp(cp, editor_base) == 0)
376: break;
377: }
378: /* Disable +lineno if editor doesn't support it. */
379: if (cp == NULL)
380: lineno = -1;
381: }
382:
383: /* Find the length of the argument vector */
384: ac = 3 + (lineno > 0);
385: if (args) {
1.1.1.2 misho 386: bool wasblank;
1.1 misho 387:
388: ac++;
1.1.1.2 misho 389: for (wasblank = false, cp = args; *cp; cp++) {
1.1 misho 390: if (isblank((unsigned char) *cp))
1.1.1.2 misho 391: wasblank = true;
1.1 misho 392: else if (wasblank) {
1.1.1.2 misho 393: wasblank = false;
1.1 misho 394: ac++;
395: }
396: }
397: }
398:
399: /* Build up argument vector for the command */
400: av = emalloc2(ac, sizeof(char *));
401: if ((av[0] = strrchr(editor, '/')) != NULL)
402: av[0]++;
403: else
404: av[0] = editor;
405: ac = 1;
406: if (lineno > 0) {
407: (void) snprintf(linestr, sizeof(linestr), "+%d", lineno);
408: av[ac++] = linestr;
409: }
410: if (args) {
411: for ((cp = strtok(args, " \t")); cp; (cp = strtok(NULL, " \t")))
412: av[ac++] = cp;
413: }
414: av[ac++] = sp->tpath;
415: av[ac++] = NULL;
416:
417: /*
418: * Do the edit:
419: * We cannot check the editor's exit value against 0 since
420: * XPG4 specifies that vi's exit value is a function of the
421: * number of errors during editing (?!?!).
422: */
423: gettimeofday(&tv1, NULL);
424: if (run_command(editor, av) != -1) {
425: gettimeofday(&tv2, NULL);
426: /*
427: * Sanity checks.
428: */
429: if (stat(sp->tpath, &sb) < 0) {
430: warningx(_("unable to stat temporary file (%s), %s unchanged"),
431: sp->tpath, sp->path);
1.1.1.2 misho 432: goto done;
1.1 misho 433: }
434: if (sb.st_size == 0 && orig_size != 0) {
435: warningx(_("zero length temporary file (%s), %s unchanged"),
436: sp->tpath, sp->path);
1.1.1.2 misho 437: sp->modified = true;
438: goto done;
1.1 misho 439: }
440: } else {
441: warningx(_("editor (%s) failed, %s unchanged"), editor, sp->path);
1.1.1.2 misho 442: goto done;
1.1 misho 443: }
444:
445: /* Set modified bit if use changed the file. */
1.1.1.2 misho 446: modified = true;
1.1 misho 447: mtim_get(&sb, &tv);
448: if (orig_size == sb.st_size && timevalcmp(&orig_mtim, &tv, ==)) {
449: /*
450: * If mtime and size match but the user spent no measurable
451: * time in the editor we can't tell if the file was changed.
452: */
453: timevalsub(&tv1, &tv2);
454: if (timevalisset(&tv2))
1.1.1.2 misho 455: modified = false;
1.1 misho 456: }
457:
458: /*
459: * If modified in this edit session, mark as modified.
460: */
461: if (modified)
462: sp->modified = modified;
463: else
464: warningx(_("%s unchanged"), sp->tpath);
465:
1.1.1.2 misho 466: rval = true;
467: done:
468: debug_return_bool(rval);
1.1 misho 469: }
470:
471: /*
472: * Parse sudoers after editing and re-edit any ones that caused a parse error.
473: */
1.1.1.4 misho 474: static bool
1.1.1.2 misho 475: reparse_sudoers(char *editor, char *args, bool strict, bool quiet)
1.1 misho 476: {
477: struct sudoersfile *sp, *last;
478: FILE *fp;
479: int ch;
1.1.1.2 misho 480: debug_decl(reparse_sudoers, SUDO_DEBUG_UTIL)
1.1 misho 481:
482: /*
483: * Parse the edited sudoers files and do sanity checking
484: */
1.1.1.4 misho 485: while ((sp = tq_first(&sudoerslist)) != NULL) {
1.1 misho 486: last = tq_last(&sudoerslist);
487: fp = fopen(sp->tpath, "r+");
488: if (fp == NULL)
1.1.1.4 misho 489: fatalx(_("unable to re-open temporary file (%s), %s unchanged."),
1.1 misho 490: sp->tpath, sp->path);
491:
492: /* Clean slate for each parse */
493: init_defaults();
494: init_parser(sp->path, quiet);
495:
1.1.1.4 misho 496: /* Parse the sudoers temp file(s) */
497: sudoersrestart(fp);
498: if (sudoersparse() && !parse_error) {
1.1 misho 499: warningx(_("unabled to parse temporary file (%s), unknown error"),
500: sp->tpath);
1.1.1.2 misho 501: parse_error = true;
1.1 misho 502: errorfile = sp->path;
503: }
1.1.1.4 misho 504: fclose(sudoersin);
1.1 misho 505: if (!parse_error) {
1.1.1.3 misho 506: if (!check_defaults(SETDEF_ALL, quiet) ||
1.1 misho 507: check_aliases(strict, quiet) != 0) {
1.1.1.2 misho 508: parse_error = true;
1.1.1.3 misho 509: errorfile = NULL;
1.1 misho 510: }
511: }
512:
513: /*
1.1.1.4 misho 514: * Got an error, prompt the user for what to do now.
1.1 misho 515: */
516: if (parse_error) {
517: switch (whatnow()) {
1.1.1.4 misho 518: case 'Q':
519: parse_error = false; /* ignore parse error */
520: break;
521: case 'x':
522: visudo_cleanup(); /* discard changes */
523: debug_return_bool(false);
524: case 'e':
525: default:
526: /* Edit file with the parse error */
527: tq_foreach_fwd(&sudoerslist, sp) {
528: if (errorfile == NULL || strcmp(sp->path, errorfile) == 0) {
529: edit_sudoers(sp, editor, args, errorlineno);
530: if (errorfile != NULL)
531: break;
532: }
1.1 misho 533: }
1.1.1.4 misho 534: if (errorfile != NULL && sp == NULL) {
535: fatalx(_("internal error, unable to find %s in list!"),
536: sudoers);
537: }
538: break;
1.1 misho 539: }
540: }
541:
542: /* If any new #include directives were added, edit them too. */
543: for (sp = last->next; sp != NULL; sp = sp->next) {
544: printf(_("press return to edit %s: "), sp->path);
545: while ((ch = getchar()) != EOF && ch != '\n')
546: continue;
547: edit_sudoers(sp, editor, args, errorlineno);
548: }
549:
1.1.1.4 misho 550: /* If all sudoers files parsed OK we are done. */
551: if (!parse_error)
552: break;
553: }
554:
555: debug_return_bool(true);
1.1 misho 556: }
557:
558: /*
559: * Set the owner and mode on a sudoers temp file and
1.1.1.2 misho 560: * move it into place. Returns true on success, else false.
1.1 misho 561: */
1.1.1.2 misho 562: static bool
563: install_sudoers(struct sudoersfile *sp, bool oldperms)
1.1 misho 564: {
565: struct stat sb;
1.1.1.2 misho 566: bool rval = false;
567: debug_decl(install_sudoers, SUDO_DEBUG_UTIL)
568:
569: if (!sp->modified) {
570: /*
571: * No changes but fix owner/mode if needed.
572: */
573: (void) unlink(sp->tpath);
574: if (!oldperms && fstat(sp->fd, &sb) != -1) {
575: if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID)
576: ignore_result(chown(sp->path, SUDOERS_UID, SUDOERS_GID));
577: if ((sb.st_mode & 0777) != SUDOERS_MODE)
578: ignore_result(chmod(sp->path, SUDOERS_MODE));
579: }
580: rval = true;
581: goto done;
582: }
1.1 misho 583:
584: /*
585: * Change mode and ownership of temp file so when
586: * we move it to sp->path things are kosher.
587: */
588: if (oldperms) {
589: /* Use perms of the existing file. */
590: if (fstat(sp->fd, &sb) == -1)
1.1.1.4 misho 591: fatal(_("unable to stat %s"), sp->path);
1.1 misho 592: if (chown(sp->tpath, sb.st_uid, sb.st_gid) != 0) {
593: warning(_("unable to set (uid, gid) of %s to (%u, %u)"),
594: sp->tpath, (unsigned int)sb.st_uid, (unsigned int)sb.st_gid);
595: }
596: if (chmod(sp->tpath, sb.st_mode & 0777) != 0) {
597: warning(_("unable to change mode of %s to 0%o"), sp->tpath,
598: (unsigned int)(sb.st_mode & 0777));
599: }
600: } else {
601: if (chown(sp->tpath, SUDOERS_UID, SUDOERS_GID) != 0) {
602: warning(_("unable to set (uid, gid) of %s to (%u, %u)"),
603: sp->tpath, SUDOERS_UID, SUDOERS_GID);
1.1.1.2 misho 604: goto done;
1.1 misho 605: }
606: if (chmod(sp->tpath, SUDOERS_MODE) != 0) {
607: warning(_("unable to change mode of %s to 0%o"), sp->tpath,
608: SUDOERS_MODE);
1.1.1.2 misho 609: goto done;
1.1 misho 610: }
611: }
612:
613: /*
614: * Now that sp->tpath is sane (parses ok) it needs to be
615: * rename(2)'d to sp->path. If the rename(2) fails we try using
616: * mv(1) in case sp->tpath and sp->path are on different file systems.
617: */
618: if (rename(sp->tpath, sp->path) == 0) {
619: efree(sp->tpath);
620: sp->tpath = NULL;
621: } else {
622: if (errno == EXDEV) {
623: char *av[4];
624: warningx(_("%s and %s not on the same file system, using mv to rename"),
625: sp->tpath, sp->path);
626:
627: /* Build up argument vector for the command */
628: if ((av[0] = strrchr(_PATH_MV, '/')) != NULL)
629: av[0]++;
630: else
631: av[0] = _PATH_MV;
632: av[1] = sp->tpath;
633: av[2] = sp->path;
634: av[3] = NULL;
635:
636: /* And run it... */
637: if (run_command(_PATH_MV, av)) {
638: warningx(_("command failed: '%s %s %s', %s unchanged"),
639: _PATH_MV, sp->tpath, sp->path, sp->path);
640: (void) unlink(sp->tpath);
641: efree(sp->tpath);
642: sp->tpath = NULL;
1.1.1.2 misho 643: goto done;
1.1 misho 644: }
645: efree(sp->tpath);
646: sp->tpath = NULL;
647: } else {
648: warning(_("error renaming %s, %s unchanged"), sp->tpath, sp->path);
649: (void) unlink(sp->tpath);
1.1.1.2 misho 650: goto done;
1.1 misho 651: }
652: }
1.1.1.2 misho 653: rval = true;
654: done:
655: debug_return_bool(rval);
1.1 misho 656: }
657:
658: /* STUB */
659: void
660: init_envtables(void)
661: {
662: return;
663: }
664:
665: /* STUB */
1.1.1.2 misho 666: bool
1.1 misho 667: user_is_exempt(void)
668: {
1.1.1.2 misho 669: return false;
1.1 misho 670: }
671:
672: /* STUB */
673: void
674: sudo_setspent(void)
675: {
676: return;
677: }
678:
679: /* STUB */
680: void
681: sudo_endspent(void)
682: {
683: return;
684: }
685:
686: /* STUB */
687: int
688: group_plugin_query(const char *user, const char *group, const struct passwd *pw)
689: {
1.1.1.2 misho 690: return false;
1.1 misho 691: }
692:
1.1.1.4 misho 693: /* STUB */
694: struct interface *get_interfaces(void)
695: {
696: return NULL;
697: }
698:
1.1 misho 699: /*
700: * Assuming a parse error occurred, prompt the user for what they want
701: * to do now. Returns the first letter of their choice.
702: */
1.1.1.2 misho 703: static int
1.1 misho 704: whatnow(void)
705: {
706: int choice, c;
1.1.1.2 misho 707: debug_decl(whatnow, SUDO_DEBUG_UTIL)
1.1 misho 708:
709: for (;;) {
710: (void) fputs(_("What now? "), stdout);
711: choice = getchar();
712: for (c = choice; c != '\n' && c != EOF;)
713: c = getchar();
714:
715: switch (choice) {
716: case EOF:
717: choice = 'x';
718: /* FALLTHROUGH */
719: case 'e':
720: case 'x':
721: case 'Q':
1.1.1.2 misho 722: debug_return_int(choice);
1.1 misho 723: default:
724: (void) puts(_("Options are:\n"
725: " (e)dit sudoers file again\n"
726: " e(x)it without saving changes to sudoers file\n"
727: " (Q)uit and save changes to sudoers file (DANGER!)\n"));
728: }
729: }
730: }
731:
732: /*
733: * Install signal handlers for visudo.
734: */
735: static void
736: setup_signals(void)
737: {
1.1.1.2 misho 738: sigaction_t sa;
739: debug_decl(setup_signals, SUDO_DEBUG_UTIL)
1.1 misho 740:
1.1.1.2 misho 741: /*
742: * Setup signal handlers to cleanup nicely.
743: */
1.1.1.5 ! misho 744: memset(&sa, 0, sizeof(sa));
1.1.1.2 misho 745: sigemptyset(&sa.sa_mask);
746: sa.sa_flags = SA_RESTART;
747: sa.sa_handler = quit;
748: (void) sigaction(SIGTERM, &sa, NULL);
749: (void) sigaction(SIGHUP, &sa, NULL);
750: (void) sigaction(SIGINT, &sa, NULL);
751: (void) sigaction(SIGQUIT, &sa, NULL);
752:
753: debug_return;
1.1 misho 754: }
755:
756: static int
757: run_command(char *path, char **argv)
758: {
759: int status;
760: pid_t pid, rv;
1.1.1.2 misho 761: debug_decl(run_command, SUDO_DEBUG_UTIL)
1.1 misho 762:
1.1.1.2 misho 763: switch (pid = sudo_debug_fork()) {
1.1 misho 764: case -1:
1.1.1.4 misho 765: fatal(_("unable to execute %s"), path);
1.1 misho 766: break; /* NOTREACHED */
767: case 0:
768: sudo_endpwent();
769: sudo_endgrent();
770: closefrom(STDERR_FILENO + 1);
771: execv(path, argv);
772: warning(_("unable to run %s"), path);
773: _exit(127);
774: break; /* NOTREACHED */
775: }
776:
777: do {
778: rv = waitpid(pid, &status, 0);
779: } while (rv == -1 && errno == EINTR);
780:
1.1.1.2 misho 781: if (rv != -1)
782: rv = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
783: debug_return_int(rv);
1.1 misho 784: }
785:
1.1.1.2 misho 786: static bool
787: check_owner(const char *path, bool quiet)
1.1 misho 788: {
789: struct stat sb;
1.1.1.2 misho 790: bool ok = true;
791: debug_decl(check_owner, SUDO_DEBUG_UTIL)
792:
793: if (stat(path, &sb) == 0) {
794: if (sb.st_uid != SUDOERS_UID || sb.st_gid != SUDOERS_GID) {
795: ok = false;
796: if (!quiet) {
797: fprintf(stderr,
798: _("%s: wrong owner (uid, gid) should be (%u, %u)\n"),
799: path, SUDOERS_UID, SUDOERS_GID);
800: }
801: }
802: if ((sb.st_mode & 07777) != SUDOERS_MODE) {
803: ok = false;
804: if (!quiet) {
805: fprintf(stderr, _("%s: bad permissions, should be mode 0%o\n"),
806: path, SUDOERS_MODE);
807: }
808: }
809: }
810: debug_return_bool(ok);
811: }
812:
813: static bool
814: check_syntax(char *sudoers_path, bool quiet, bool strict, bool oldperms)
815: {
816: bool ok = false;
817: debug_decl(check_syntax, SUDO_DEBUG_UTIL)
1.1 misho 818:
819: if (strcmp(sudoers_path, "-") == 0) {
1.1.1.4 misho 820: sudoersin = stdin;
1.1 misho 821: sudoers_path = "stdin";
1.1.1.4 misho 822: } else if ((sudoersin = fopen(sudoers_path, "r")) == NULL) {
1.1 misho 823: if (!quiet)
824: warning(_("unable to open %s"), sudoers_path);
1.1.1.2 misho 825: goto done;
1.1 misho 826: }
827: init_parser(sudoers_path, quiet);
1.1.1.4 misho 828: if (sudoersparse() && !parse_error) {
1.1 misho 829: if (!quiet)
830: warningx(_("failed to parse %s file, unknown error"), sudoers_path);
1.1.1.2 misho 831: parse_error = true;
1.1 misho 832: errorfile = sudoers_path;
833: }
1.1.1.3 misho 834: if (!parse_error) {
835: if (!check_defaults(SETDEF_ALL, quiet) ||
836: check_aliases(strict, quiet) != 0) {
837: parse_error = true;
838: errorfile = NULL;
839: }
1.1 misho 840: }
1.1.1.2 misho 841: ok = !parse_error;
842:
843: if (parse_error) {
844: if (!quiet) {
1.1 misho 845: if (errorlineno != -1)
846: (void) printf(_("parse error in %s near line %d\n"),
847: errorfile, errorlineno);
1.1.1.4 misho 848: else if (errorfile != NULL)
1.1 misho 849: (void) printf(_("parse error in %s\n"), errorfile);
850: }
1.1.1.2 misho 851: } else {
852: struct sudoersfile *sp;
853:
854: /* Parsed OK, check mode and owner. */
1.1.1.5 ! misho 855: if (oldperms || check_owner(sudoers_path, quiet)) {
! 856: if (!quiet)
! 857: (void) printf(_("%s: parsed OK\n"), sudoers_path);
! 858: } else {
1.1.1.2 misho 859: ok = false;
1.1.1.5 ! misho 860: }
1.1.1.2 misho 861: tq_foreach_fwd(&sudoerslist, sp) {
1.1.1.5 ! misho 862: if (oldperms || check_owner(sp->path, quiet)) {
! 863: if (!quiet)
! 864: (void) printf(_("%s: parsed OK\n"), sp->path);
! 865: } else {
1.1.1.2 misho 866: ok = false;
1.1.1.5 ! misho 867: }
1.1 misho 868: }
869: }
870:
1.1.1.2 misho 871: done:
872: debug_return_bool(ok);
1.1 misho 873: }
874:
875: /*
876: * Used to open (and lock) the initial sudoers file and to also open
877: * any subsequent files #included via a callback from the parser.
878: */
879: FILE *
1.1.1.2 misho 880: open_sudoers(const char *path, bool doedit, bool *keepopen)
1.1 misho 881: {
882: struct sudoersfile *entry;
883: FILE *fp;
1.1.1.2 misho 884: int open_flags;
885: debug_decl(open_sudoers, SUDO_DEBUG_UTIL)
886:
887: if (checkonly)
888: open_flags = O_RDONLY;
889: else
890: open_flags = O_RDWR | O_CREAT;
1.1 misho 891:
892: /* Check for existing entry */
893: tq_foreach_fwd(&sudoerslist, entry) {
894: if (strcmp(path, entry->path) == 0)
895: break;
896: }
897: if (entry == NULL) {
1.1.1.2 misho 898: entry = ecalloc(1, sizeof(*entry));
1.1 misho 899: entry->path = estrdup(path);
1.1.1.2 misho 900: /* entry->modified = 0; */
1.1 misho 901: entry->prev = entry;
1.1.1.2 misho 902: /* entry->next = NULL; */
903: entry->fd = open(entry->path, open_flags, SUDOERS_MODE);
904: /* entry->tpath = NULL; */
1.1 misho 905: entry->doedit = doedit;
906: if (entry->fd == -1) {
907: warning("%s", entry->path);
908: efree(entry);
1.1.1.2 misho 909: debug_return_ptr(NULL);
1.1 misho 910: }
1.1.1.2 misho 911: if (!checkonly && !lock_file(entry->fd, SUDO_TLOCK))
1.1.1.4 misho 912: fatalx(_("%s busy, try again later"), entry->path);
1.1 misho 913: if ((fp = fdopen(entry->fd, "r")) == NULL)
1.1.1.4 misho 914: fatal("%s", entry->path);
1.1 misho 915: tq_append(&sudoerslist, entry);
916: } else {
917: /* Already exists, open .tmp version if there is one. */
918: if (entry->tpath != NULL) {
919: if ((fp = fopen(entry->tpath, "r")) == NULL)
1.1.1.4 misho 920: fatal("%s", entry->tpath);
1.1 misho 921: } else {
922: if ((fp = fdopen(entry->fd, "r")) == NULL)
1.1.1.4 misho 923: fatal("%s", entry->path);
1.1 misho 924: rewind(fp);
925: }
926: }
927: if (keepopen != NULL)
1.1.1.2 misho 928: *keepopen = true;
929: debug_return_ptr(fp);
1.1 misho 930: }
931:
932: static char *
933: get_editor(char **args)
934: {
935: char *Editor, *EditorArgs, *EditorPath, *UserEditor, *UserEditorArgs;
1.1.1.2 misho 936: debug_decl(get_editor, SUDO_DEBUG_UTIL)
1.1 misho 937:
938: /*
939: * Check VISUAL and EDITOR environment variables to see which editor
940: * the user wants to use (we may not end up using it though).
941: * If the path is not fully-qualified, make it so and check that
942: * the specified executable actually exists.
943: */
944: UserEditorArgs = NULL;
945: if ((UserEditor = getenv("VISUAL")) == NULL || *UserEditor == '\0')
946: UserEditor = getenv("EDITOR");
947: if (UserEditor && *UserEditor == '\0')
948: UserEditor = NULL;
949: else if (UserEditor) {
950: UserEditorArgs = get_args(UserEditor);
951: if (find_path(UserEditor, &Editor, NULL, getenv("PATH"), 0) == FOUND) {
952: UserEditor = Editor;
953: } else {
954: if (def_env_editor) {
955: /* If we are honoring $EDITOR this is a fatal error. */
1.1.1.4 misho 956: fatalx(_("specified editor (%s) doesn't exist"), UserEditor);
1.1 misho 957: } else {
958: /* Otherwise, just ignore $EDITOR. */
959: UserEditor = NULL;
960: }
961: }
962: }
963:
964: /*
965: * See if we can use the user's choice of editors either because
966: * we allow any $EDITOR or because $EDITOR is in the allowable list.
967: */
968: Editor = EditorArgs = EditorPath = NULL;
969: if (def_env_editor && UserEditor) {
970: Editor = UserEditor;
971: EditorArgs = UserEditorArgs;
972: } else if (UserEditor) {
973: struct stat editor_sb;
974: struct stat user_editor_sb;
975: char *base, *userbase;
976:
977: if (stat(UserEditor, &user_editor_sb) != 0) {
978: /* Should never happen since we already checked above. */
1.1.1.4 misho 979: fatal(_("unable to stat editor (%s)"), UserEditor);
1.1 misho 980: }
981: EditorPath = estrdup(def_editor);
982: Editor = strtok(EditorPath, ":");
983: do {
984: EditorArgs = get_args(Editor);
985: /*
986: * Both Editor and UserEditor should be fully qualified but
987: * check anyway...
988: */
989: if ((base = strrchr(Editor, '/')) == NULL)
990: continue;
991: if ((userbase = strrchr(UserEditor, '/')) == NULL) {
992: Editor = NULL;
993: break;
994: }
995: base++, userbase++;
996:
997: /*
998: * We compare the basenames first and then use stat to match
999: * for sure.
1000: */
1001: if (strcmp(base, userbase) == 0) {
1002: if (stat(Editor, &editor_sb) == 0 && S_ISREG(editor_sb.st_mode)
1003: && (editor_sb.st_mode & 0000111) &&
1004: editor_sb.st_dev == user_editor_sb.st_dev &&
1005: editor_sb.st_ino == user_editor_sb.st_ino)
1006: break;
1007: }
1008: } while ((Editor = strtok(NULL, ":")));
1009: }
1010:
1011: /*
1012: * Can't use $EDITOR, try each element of def_editor until we
1013: * find one that exists, is regular, and is executable.
1014: */
1015: if (Editor == NULL || *Editor == '\0') {
1016: efree(EditorPath);
1017: EditorPath = estrdup(def_editor);
1018: Editor = strtok(EditorPath, ":");
1019: do {
1020: EditorArgs = get_args(Editor);
1021: if (sudo_goodpath(Editor, NULL))
1022: break;
1023: } while ((Editor = strtok(NULL, ":")));
1024:
1025: /* Bleah, none of the editors existed! */
1026: if (Editor == NULL || *Editor == '\0')
1.1.1.4 misho 1027: fatalx(_("no editor found (editor path = %s)"), def_editor);
1.1 misho 1028: }
1029: *args = EditorArgs;
1.1.1.2 misho 1030: debug_return_str(Editor);
1.1 misho 1031: }
1032:
1033: /*
1034: * Split out any command line arguments and return them.
1035: */
1036: static char *
1037: get_args(char *cmnd)
1038: {
1039: char *args;
1.1.1.2 misho 1040: debug_decl(get_args, SUDO_DEBUG_UTIL)
1.1 misho 1041:
1042: args = cmnd;
1043: while (*args && !isblank((unsigned char) *args))
1044: args++;
1045: if (*args) {
1046: *args++ = '\0';
1047: while (*args && isblank((unsigned char) *args))
1048: args++;
1049: }
1.1.1.2 misho 1050: debug_return_str(*args ? args : NULL);
1.1 misho 1051: }
1052:
1053: /*
1054: * Look up the hostname and set user_host and user_shost.
1055: */
1056: static void
1057: get_hostname(void)
1058: {
1.1.1.4 misho 1059: char *p, thost[HOST_NAME_MAX + 1];
1.1.1.2 misho 1060: debug_decl(get_hostname, SUDO_DEBUG_UTIL)
1.1 misho 1061:
1.1.1.2 misho 1062: if (gethostname(thost, sizeof(thost)) != -1) {
1063: thost[sizeof(thost) - 1] = '\0';
1064: user_host = estrdup(thost);
1065:
1066: if ((p = strchr(user_host, '.'))) {
1067: *p = '\0';
1068: user_shost = estrdup(user_host);
1069: *p = '.';
1070: } else {
1071: user_shost = user_host;
1072: }
1.1 misho 1073: } else {
1.1.1.2 misho 1074: user_host = user_shost = "localhost";
1.1 misho 1075: }
1.1.1.5 ! misho 1076: user_runhost = user_host;
! 1077: user_srunhost = user_shost;
1.1.1.2 misho 1078: debug_return;
1.1 misho 1079: }
1080:
1.1.1.2 misho 1081: static bool
1082: alias_remove_recursive(char *name, int type)
1.1 misho 1083: {
1084: struct member *m;
1085: struct alias *a;
1.1.1.2 misho 1086: bool rval = true;
1087: debug_decl(alias_remove_recursive, SUDO_DEBUG_ALIAS)
1.1 misho 1088:
1.1.1.4 misho 1089: if ((a = alias_remove(name, type)) != NULL) {
1.1 misho 1090: tq_foreach_fwd(&a->members, m) {
1091: if (m->type == ALIAS) {
1.1.1.2 misho 1092: if (!alias_remove_recursive(m->name, type))
1093: rval = false;
1.1 misho 1094: }
1095: }
1096: rbinsert(alias_freelist, a);
1.1.1.4 misho 1097: }
1.1.1.2 misho 1098: debug_return_bool(rval);
1.1 misho 1099: }
1100:
1101: static int
1102: check_alias(char *name, int type, int strict, int quiet)
1103: {
1104: struct member *m;
1105: struct alias *a;
1.1.1.2 misho 1106: int errors = 0;
1107: debug_decl(check_alias, SUDO_DEBUG_ALIAS)
1.1 misho 1108:
1.1.1.4 misho 1109: if ((a = alias_get(name, type)) != NULL) {
1.1 misho 1110: /* check alias contents */
1111: tq_foreach_fwd(&a->members, m) {
1112: if (m->type == ALIAS)
1.1.1.2 misho 1113: errors += check_alias(m->name, type, strict, quiet);
1.1 misho 1114: }
1.1.1.4 misho 1115: alias_put(a);
1.1 misho 1116: } else {
1117: if (!quiet) {
1118: if (errno == ELOOP) {
1.1.1.5 ! misho 1119: warningx(strict ?
1.1 misho 1120: _("Error: cycle in %s_Alias `%s'") :
1.1.1.5 ! misho 1121: _("Warning: cycle in %s_Alias `%s'"),
! 1122: type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :
! 1123: type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
! 1124: "Unknown", name);
1.1 misho 1125: } else {
1.1.1.5 ! misho 1126: warningx(strict ?
1.1 misho 1127: _("Error: %s_Alias `%s' referenced but not defined") :
1.1.1.5 ! misho 1128: _("Warning: %s_Alias `%s' referenced but not defined"),
! 1129: type == HOSTALIAS ? "Host" : type == CMNDALIAS ? "Cmnd" :
! 1130: type == USERALIAS ? "User" : type == RUNASALIAS ? "Runas" :
! 1131: "Unknown", name);
1.1 misho 1132: }
1133: }
1.1.1.2 misho 1134: errors++;
1.1 misho 1135: }
1136:
1.1.1.2 misho 1137: debug_return_int(errors);
1.1 misho 1138: }
1139:
1140: /*
1141: * Iterate through the sudoers datastructures looking for undefined
1142: * aliases or unused aliases.
1143: */
1144: static int
1.1.1.2 misho 1145: check_aliases(bool strict, bool quiet)
1.1 misho 1146: {
1147: struct cmndspec *cs;
1148: struct member *m, *binding;
1149: struct privilege *priv;
1150: struct userspec *us;
1151: struct defaults *d;
1.1.1.2 misho 1152: int atype, errors = 0;
1153: debug_decl(check_aliases, SUDO_DEBUG_ALIAS)
1.1 misho 1154:
1155: alias_freelist = rbcreate(alias_compare);
1156:
1157: /* Forward check. */
1158: tq_foreach_fwd(&userspecs, us) {
1159: tq_foreach_fwd(&us->users, m) {
1160: if (m->type == ALIAS) {
1.1.1.2 misho 1161: errors += check_alias(m->name, USERALIAS, strict, quiet);
1.1 misho 1162: }
1163: }
1164: tq_foreach_fwd(&us->privileges, priv) {
1165: tq_foreach_fwd(&priv->hostlist, m) {
1166: if (m->type == ALIAS) {
1.1.1.2 misho 1167: errors += check_alias(m->name, HOSTALIAS, strict, quiet);
1.1 misho 1168: }
1169: }
1170: tq_foreach_fwd(&priv->cmndlist, cs) {
1171: tq_foreach_fwd(&cs->runasuserlist, m) {
1172: if (m->type == ALIAS) {
1.1.1.2 misho 1173: errors += check_alias(m->name, RUNASALIAS, strict, quiet);
1.1 misho 1174: }
1175: }
1176: if ((m = cs->cmnd)->type == ALIAS) {
1.1.1.2 misho 1177: errors += check_alias(m->name, CMNDALIAS, strict, quiet);
1.1 misho 1178: }
1179: }
1180: }
1181: }
1182:
1183: /* Reverse check (destructive) */
1184: tq_foreach_fwd(&userspecs, us) {
1185: tq_foreach_fwd(&us->users, m) {
1186: if (m->type == ALIAS) {
1.1.1.2 misho 1187: if (!alias_remove_recursive(m->name, USERALIAS))
1188: errors++;
1.1 misho 1189: }
1190: }
1191: tq_foreach_fwd(&us->privileges, priv) {
1192: tq_foreach_fwd(&priv->hostlist, m) {
1193: if (m->type == ALIAS) {
1.1.1.2 misho 1194: if (!alias_remove_recursive(m->name, HOSTALIAS))
1195: errors++;
1.1 misho 1196: }
1197: }
1198: tq_foreach_fwd(&priv->cmndlist, cs) {
1199: tq_foreach_fwd(&cs->runasuserlist, m) {
1200: if (m->type == ALIAS) {
1.1.1.2 misho 1201: if (!alias_remove_recursive(m->name, RUNASALIAS))
1202: errors++;
1.1 misho 1203: }
1204: }
1205: if ((m = cs->cmnd)->type == ALIAS) {
1.1.1.2 misho 1206: if (!alias_remove_recursive(m->name, CMNDALIAS))
1207: errors++;
1.1 misho 1208: }
1209: }
1210: }
1211: }
1212: tq_foreach_fwd(&defaults, d) {
1213: switch (d->type) {
1214: case DEFAULTS_HOST:
1215: atype = HOSTALIAS;
1216: break;
1217: case DEFAULTS_USER:
1218: atype = USERALIAS;
1219: break;
1220: case DEFAULTS_RUNAS:
1221: atype = RUNASALIAS;
1222: break;
1223: case DEFAULTS_CMND:
1224: atype = CMNDALIAS;
1225: break;
1226: default:
1227: continue; /* not an alias */
1228: }
1229: tq_foreach_fwd(&d->binding, binding) {
1230: for (m = binding; m != NULL; m = m->next) {
1231: if (m->type == ALIAS) {
1.1.1.2 misho 1232: if (!alias_remove_recursive(m->name, atype))
1233: errors++;
1.1 misho 1234: }
1235: }
1236: }
1237: }
1238: rbdestroy(alias_freelist, alias_free);
1239:
1240: /* If all aliases were referenced we will have an empty tree. */
1241: if (!no_aliases() && !quiet)
1242: alias_apply(print_unused, strict ? "Error" : "Warning");
1243:
1.1.1.2 misho 1244: debug_return_int(strict ? errors : 0);
1.1 misho 1245: }
1246:
1247: static int
1248: print_unused(void *v1, void *v2)
1249: {
1250: struct alias *a = (struct alias *)v1;
1251: char *prefix = (char *)v2;
1252:
1.1.1.4 misho 1253: warningx_nodebug(_("%s: unused %s_Alias %s"), prefix,
1.1 misho 1254: a->type == HOSTALIAS ? "Host" : a->type == CMNDALIAS ? "Cmnd" :
1255: a->type == USERALIAS ? "User" : a->type == RUNASALIAS ? "Runas" :
1256: "Unknown", a->name);
1257: return 0;
1258: }
1259:
1260: /*
1261: * Unlink any sudoers temp files that remain.
1262: */
1.1.1.4 misho 1263: static void
1264: visudo_cleanup(void)
1.1 misho 1265: {
1266: struct sudoersfile *sp;
1267:
1268: tq_foreach_fwd(&sudoerslist, sp) {
1269: if (sp->tpath != NULL)
1270: (void) unlink(sp->tpath);
1271: }
1.1.1.4 misho 1272: sudo_endpwent();
1273: sudo_endgrent();
1.1 misho 1274: }
1275:
1276: /*
1277: * Unlink sudoers temp files (if any) and exit.
1278: */
1279: static void
1280: quit(int signo)
1281: {
1.1.1.4 misho 1282: struct sudoersfile *sp;
1283: struct iovec iov[4];
1284:
1285: tq_foreach_fwd(&sudoerslist, sp) {
1286: if (sp->tpath != NULL)
1287: (void) unlink(sp->tpath);
1288: }
1.1 misho 1289:
1290: #define emsg " exiting due to signal: "
1.1.1.4 misho 1291: iov[0].iov_base = (char *)getprogname();
1292: iov[0].iov_len = strlen(iov[0].iov_base);
1293: iov[1].iov_base = emsg;
1294: iov[1].iov_len = sizeof(emsg) - 1;
1295: iov[2].iov_base = strsignal(signo);
1296: iov[2].iov_len = strlen(iov[2].iov_base);
1297: iov[3].iov_base = "\n";
1298: iov[3].iov_len = 1;
1299: ignore_result(writev(STDERR_FILENO, iov, 4));
1.1 misho 1300: _exit(signo);
1301: }
1302:
1303: static void
1304: usage(int fatal)
1305: {
1306: (void) fprintf(fatal ? stderr : stdout,
1307: "usage: %s [-chqsV] [-f sudoers]\n", getprogname());
1308: if (fatal)
1309: exit(1);
1310: }
1311:
1312: static void
1313: help(void)
1314: {
1315: (void) printf(_("%s - safely edit the sudoers file\n\n"), getprogname());
1316: usage(0);
1317: (void) puts(_("\nOptions:\n"
1.1.1.5 ! misho 1318: " -c, --check check-only mode\n"
! 1319: " -f, --file=file specify sudoers file location\n"
! 1320: " -h, --help display help message and exit\n"
! 1321: " -q, --quiet less verbose (quiet) syntax error messages\n"
! 1322: " -s, --strict strict syntax checking\n"
! 1323: " -V, --version display version information and exit"));
1.1 misho 1324: exit(0);
1325: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>