summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/sudoers/visudo.c128
1 files changed, 74 insertions, 54 deletions
diff --git a/plugins/sudoers/visudo.c b/plugins/sudoers/visudo.c
index e738e123b..05af2c8cd 100644
--- a/plugins/sudoers/visudo.c
+++ b/plugins/sudoers/visudo.c
@@ -72,12 +72,13 @@
struct sudoersfile {
TAILQ_ENTRY(sudoersfile) entries;
- char *path;
- char *tpath;
- bool modified;
- bool doedit;
- int fd;
- int errorline;
+ char *opath; /* original path we opened */
+ char *dpath; /* destination path to write to */
+ char *tpath; /* editor temporary file path */
+ bool modified; /* true if the user modified the file */
+ bool doedit; /* true when editing (not just checking) sudoers */
+ int fd; /* fd of the original file (if it exists) */
+ int errorline; /* line number when there is a syntax error */
};
TAILQ_HEAD(sudoersfile_list, sudoersfile);
@@ -305,7 +306,7 @@ main(int argc, char *argv[])
if (!sp->doedit)
continue;
if (sp != TAILQ_FIRST(&sudoerslist)) {
- printf(_("press return to edit %s: "), sp->path);
+ printf(_("press return to edit %s: "), sp->opath);
while ((ch = getchar()) != EOF && ch != '\n')
continue;
}
@@ -347,7 +348,7 @@ visudo_track_error(const char *file, int line, int column, const char *fmt,
if (sp->errorline > 0)
continue; /* preserve the first error */
- if (strcmp(file, sp->path) == 0 ||
+ if (strcmp(file, sp->opath) == 0 ||
(sp->tpath != NULL && strcmp(file, sp->tpath) == 0)) {
sp->errorline = line;
break;
@@ -489,19 +490,19 @@ edit_sudoers(struct sudoersfile *sp, char *editor, int editor_argc,
debug_decl(edit_sudoers, SUDOERS_DEBUG_UTIL);
if (fstat(sp->fd, &sb) == -1)
- sudo_fatal(U_("unable to stat %s"), sp->path);
+ sudo_fatal(U_("unable to stat %s"), sp->opath);
orig_size = sb.st_size;
mtim_get(&sb, orig_mtim);
/* Create the temp file if needed and set timestamp. */
if (sp->tpath == NULL) {
- if (asprintf(&sp->tpath, "%s.tmp", sp->path) == -1)
+ if (asprintf(&sp->tpath, "%s.tmp", sp->dpath) == -1)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
tfd = open(sp->tpath, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRUSR);
if (tfd < 0)
sudo_fatal("%s", sp->tpath);
- /* Copy sp->path -> sp->tpath and reset the mtime. */
+ /* Copy sp->opath -> sp->tpath and reset the mtime. */
if (orig_size != 0) {
char buf[4096], lastch = '\0';
ssize_t nread;
@@ -564,19 +565,19 @@ edit_sudoers(struct sudoersfile *sp, char *editor, int editor_argc,
*/
if (stat(sp->tpath, &sb) == -1) {
sudo_warnx(U_("unable to stat temporary file (%s), %s unchanged"),
- sp->tpath, sp->path);
+ sp->tpath, sp->opath);
goto done;
}
if (sb.st_size == 0 && orig_size != 0) {
/* Avoid accidental zeroing of main sudoers file. */
if (sp == TAILQ_FIRST(&sudoerslist)) {
sudo_warnx(U_("zero length temporary file (%s), %s unchanged"),
- sp->tpath, sp->path);
+ sp->tpath, sp->opath);
goto done;
}
}
} else {
- sudo_warnx(U_("editor (%s) failed, %s unchanged"), editor, sp->path);
+ sudo_warnx(U_("editor (%s) failed, %s unchanged"), editor, sp->opath);
goto done;
}
@@ -644,12 +645,12 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
fp = fopen(sp->tpath, "r+");
if (fp == NULL)
sudo_fatalx(U_("unable to re-open temporary file (%s), %s unchanged."),
- sp->tpath, sp->path);
+ sp->tpath, sp->opath);
/* Clean slate for each parse */
if (!init_defaults())
sudo_fatalx("%s", U_("unable to initialize sudoers default values"));
- init_parser_ext(sp->path, true, quiet ? 0 : 2);
+ init_parser_ext(sp->opath, true, quiet ? 0 : 2);
sp->errorline = -1;
/* Parse the sudoers temp file(s) */
@@ -696,7 +697,7 @@ reparse_sudoers(char *editor, int editor_argc, char **editor_argv,
if ((sp = TAILQ_NEXT(last, entries)) != NULL) {
bool modified = false;
do {
- printf(_("press return to edit %s: "), sp->path);
+ printf(_("press return to edit %s: "), sp->opath);
while ((ch = getchar()) != EOF && ch != '\n')
continue;
edit_sudoers(sp, editor, editor_argc, editor_argv, -1);
@@ -741,18 +742,18 @@ install_sudoers(struct sudoersfile *sp, bool set_owner, bool set_mode)
if (fstat(sp->fd, &sb) == 0) {
if (set_owner) {
if (sb.st_uid != sudoers_uid || sb.st_gid != sudoers_gid) {
- if (chown(sp->path, sudoers_uid, sudoers_gid) != 0) {
+ if (chown(sp->opath, sudoers_uid, sudoers_gid) != 0) {
sudo_warn(U_("unable to set (uid, gid) of %s to (%u, %u)"),
- sp->path, (unsigned int)sudoers_uid,
+ sp->opath, (unsigned int)sudoers_uid,
(unsigned int)sudoers_gid);
}
}
}
if (set_mode) {
if ((sb.st_mode & ACCESSPERMS) != sudoers_mode) {
- if (chmod(sp->path, sudoers_mode) != 0) {
+ if (chmod(sp->opath, sudoers_mode) != 0) {
sudo_warn(U_("unable to change mode of %s to 0%o"),
- sp->path, (unsigned int)sudoers_mode);
+ sp->opath, (unsigned int)sudoers_mode);
}
}
}
@@ -762,13 +763,13 @@ install_sudoers(struct sudoersfile *sp, bool set_owner, bool set_mode)
}
/*
- * Change mode and ownership of temp file so when
- * we move it to sp->path things are kosher.
+ * Change mode and ownership of temp file before moving it into place
+ * to avoid a race condition.
*/
if (!set_owner || !set_mode) {
/* Preserve owner/perms of the existing file. */
if (fstat(sp->fd, &sb) == -1)
- sudo_fatal(U_("unable to stat %s"), sp->path);
+ sudo_fatal(U_("unable to stat %s"), sp->opath);
}
if (set_owner) {
if (chown(sp->tpath, sudoers_uid, sudoers_gid) != 0) {
@@ -798,34 +799,35 @@ install_sudoers(struct sudoersfile *sp, bool set_owner, bool set_mode)
/*
* Now that we know sp->tpath parses correctly, it needs to be
- * rename(2)'d to sp->path. If the rename(2) fails we try using
- * mv(1) in case sp->tpath and sp->path are on different file systems.
+ * rename(2)'d to sp->dpath. If the rename(2) fails we try using
+ * mv(1) in case sp->tpath and sp->dpath are on different file systems.
*/
- if (rename(sp->tpath, sp->path) == 0) {
+ if (rename(sp->tpath, sp->dpath) == 0) {
free(sp->tpath);
sp->tpath = NULL;
} else {
if (errno == EXDEV) {
char *av[4];
sudo_warnx(U_("%s and %s not on the same file system, using mv to rename"),
- sp->tpath, sp->path);
+ sp->tpath, sp->dpath);
/* Build up argument vector for the command */
av[0] = sudo_basename(_PATH_MV);
av[1] = sp->tpath;
- av[2] = sp->path;
+ av[2] = sp->dpath;
av[3] = NULL;
/* And run it... */
if (run_command(_PATH_MV, av, false) != 0) {
sudo_warnx(U_("command failed: '%s %s %s', %s unchanged"),
- _PATH_MV, sp->tpath, sp->path, sp->path);
+ _PATH_MV, sp->tpath, sp->dpath, sp->opath);
goto done;
}
free(sp->tpath);
sp->tpath = NULL;
} else {
- sudo_warn(U_("error renaming %s, %s unchanged"), sp->tpath, sp->path);
+ sudo_warn(U_("error renaming %s, %s unchanged"), sp->tpath,
+ sp->opath);
goto done;
}
}
@@ -1091,9 +1093,9 @@ check_syntax(const char *path, bool quiet, bool strict, bool check_owner,
ok = false;
}
TAILQ_FOREACH(sp, &sudoerslist, entries) {
- if (check_file(sp->path, quiet, check_owner, check_mode)) {
+ if (check_file(sp->opath, quiet, check_owner, check_mode)) {
if (!quiet)
- (void) printf(_("%s: parsed OK\n"), sp->path);
+ (void) printf(_("%s: parsed OK\n"), sp->opath);
} else {
ok = false;
}
@@ -1112,10 +1114,10 @@ lock_sudoers(struct sudoersfile *entry)
if (!sudo_lock_file(entry->fd, SUDO_TLOCK)) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
- sudo_warnx(U_("%s busy, try again later"), entry->path);
+ sudo_warnx(U_("%s busy, try again later"), entry->opath);
debug_return_bool(false);
}
- sudo_warn(U_("unable to lock %s"), entry->path);
+ sudo_warn(U_("unable to lock %s"), entry->opath);
(void) fputs(_("Edit anyway? [y/N]"), stdout);
ch = getchar();
if (tolower(ch) != 'y')
@@ -1138,6 +1140,12 @@ new_sudoers(const char *path, bool doedit)
int fd = -1;
debug_decl(new_sudoersfile, SUDOERS_DEBUG_UTIL);
+ /* We always write to the first file in the colon-separated path. */
+ len = strcspn(path, ":");
+ entry = calloc(1, sizeof(*entry));
+ if (entry == NULL || (entry->dpath = strndup(path, len)) == NULL)
+ sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+
/* Open the first file found in the colon-separated path. */
path_end = path + strlen(path);
for (cp = sudo_strsplit(path, path_end, ":", &ep);
@@ -1154,27 +1162,38 @@ new_sudoers(const char *path, bool doedit)
/* Open in write mode for file locking. */
fd = open(fname, checkonly ? O_RDONLY : O_RDWR);
- if (fd != -1 || errno != ENOENT)
+ if (fd != -1) {
+ /* Store the path we actually opened. */
+ if ((entry->opath = strdup(fname)) == NULL) {
+ sudo_fatalx(U_("%s: %s"), __func__,
+ U_("unable to allocate memory"));
+ }
break;
- }
+ }
- /* If more than one file is specified, we always write to the first one. */
- len = strcspn(path, ":");
- entry = calloc(1, sizeof(*entry));
- if (entry == NULL || (entry->path = strndup(path, len)) == NULL)
- sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
- if (fd == -1 && errno == ENOENT) {
+ /* If the file exists but we can't open it, that is a fatal error. */
+ if (errno != ENOENT) {
+ sudo_warn("%s", fname);
+ goto bad;
+ }
+ }
+ if (fd == -1) {
if (!checkonly) {
- /* Create the first file in the path. */
- fd = open(entry->path, O_RDWR|O_CREAT, sudoers_mode);
+ /* No sudoers file, create the destination file for editing. */
+ fd = open(entry->dpath, O_RDWR|O_CREAT, sudoers_mode);
+ }
+ if (fd == -1) {
+ sudo_warn("%s", entry->dpath);
+ goto bad;
}
+ entry->opath = entry->dpath;
}
- if (fd == -1 || fstat(fd, &sb) == -1) {
- sudo_warn("%s", entry->path);
+ if (fstat(fd, &sb) == -1) {
+ sudo_warn("%s", entry->opath);
goto bad;
}
if (!S_ISREG(sb.st_mode)) {
- sudo_warnx(U_("%s is not a regular file"), entry->path);
+ sudo_warnx(U_("%s is not a regular file"), entry->opath);
goto bad;
}
entry->fd = fd;
@@ -1187,7 +1206,9 @@ new_sudoers(const char *path, bool doedit)
bad:
if (fd != -1)
close(fd);
- free(entry->path);
+ if (entry->opath != entry->dpath)
+ free(entry->opath);
+ free(entry->dpath);
free(entry);
debug_return_ptr(NULL);
}
@@ -1207,7 +1228,7 @@ open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
/* Check for existing entry using the first file in path. */
len = strcspn(path, ":");
TAILQ_FOREACH(entry, &sudoerslist, entries) {
- if (strncmp(path, entry->path, len) == 0 && entry->path[len] == '\0')
+ if (strncmp(path, entry->opath, len) == 0 && entry->opath[len] == '\0')
break;
}
if (entry == NULL) {
@@ -1220,7 +1241,7 @@ open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
if ((entry = new_sudoers(path, doedit)) == NULL)
debug_return_ptr(NULL);
if ((fp = fdopen(entry->fd, "r")) == NULL)
- sudo_fatal("%s", entry->path);
+ sudo_fatal("%s", entry->opath);
TAILQ_INSERT_TAIL(&sudoerslist, entry, entries);
} else {
/* Already exists, open .tmp version if there is one. */
@@ -1229,15 +1250,14 @@ open_sudoers(const char *path, char **outfile, bool doedit, bool *keepopen)
sudo_fatal("%s", entry->tpath);
} else {
if ((fp = fdopen(entry->fd, "r")) == NULL)
- sudo_fatal("%s", entry->path);
+ sudo_fatal("%s", entry->opath);
rewind(fp);
}
}
if (keepopen != NULL)
*keepopen = true;
if (outfile != NULL) {
- /* XXX - if path is a list, entry->path may not exist yet. */
- *outfile = sudo_rcstr_dup(entry->path);
+ *outfile = sudo_rcstr_dup(entry->opath);
if (*outfile == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
}