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