|
|
eefaf6 |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
eefaf6 |
From: Hans de Goede <hdegoede@redhat.com>
|
|
|
eefaf6 |
Date: Fri, 22 Nov 2019 11:54:27 +0100
|
|
|
eefaf6 |
Subject: [PATCH] grub-set-bootflag: Write new env to tmpfile and then rename
|
|
|
eefaf6 |
|
|
|
eefaf6 |
Make the grubenv writing code in grub-set-bootflag more robust by
|
|
|
eefaf6 |
writing the modified grubenv to a tmpfile first and then renaming the
|
|
|
eefaf6 |
tmpfile over the old grubenv (following symlinks).
|
|
|
eefaf6 |
|
|
|
eefaf6 |
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
|
eefaf6 |
---
|
|
|
eefaf6 |
util/grub-set-bootflag.c | 87 +++++++++++++++++++++++++++++++++++++++++++-----
|
|
|
eefaf6 |
1 file changed, 78 insertions(+), 9 deletions(-)
|
|
|
eefaf6 |
|
|
|
eefaf6 |
diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
|
|
|
eefaf6 |
index 32f1c104a2b..d3b80a0d204 100644
|
|
|
eefaf6 |
--- a/util/grub-set-bootflag.c
|
|
|
eefaf6 |
+++ b/util/grub-set-bootflag.c
|
|
|
eefaf6 |
@@ -26,7 +26,9 @@
|
|
|
eefaf6 |
#include <config-util.h> /* For *_DIR_NAME defines */
|
|
|
eefaf6 |
#include <grub/types.h>
|
|
|
eefaf6 |
#include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
|
|
|
eefaf6 |
+#include <limits.h>
|
|
|
eefaf6 |
#include <stdio.h>
|
|
|
eefaf6 |
+#include <stdlib.h>
|
|
|
eefaf6 |
#include <string.h>
|
|
|
eefaf6 |
#include <unistd.h>
|
|
|
eefaf6 |
|
|
|
eefaf6 |
@@ -52,8 +54,10 @@ int main(int argc, char *argv[])
|
|
|
eefaf6 |
{
|
|
|
eefaf6 |
/* NOTE buf must be at least the longest bootflag length + 4 bytes */
|
|
|
eefaf6 |
char env[GRUBENV_SIZE + 1], buf[64], *s;
|
|
|
eefaf6 |
+ /* +1 for 0 termination, +6 for "XXXXXX" in tmp filename */
|
|
|
eefaf6 |
+ char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 6 + 1];
|
|
|
eefaf6 |
const char *bootflag;
|
|
|
eefaf6 |
- int i, len, ret;
|
|
|
eefaf6 |
+ int i, fd, len, ret;
|
|
|
eefaf6 |
FILE *f;
|
|
|
eefaf6 |
|
|
|
eefaf6 |
if (argc != 2)
|
|
|
eefaf6 |
@@ -75,7 +79,32 @@ int main(int argc, char *argv[])
|
|
|
eefaf6 |
bootflag = bootflags[i];
|
|
|
eefaf6 |
len = strlen (bootflag);
|
|
|
eefaf6 |
|
|
|
eefaf6 |
- f = fopen (GRUBENV, "r");
|
|
|
eefaf6 |
+ /*
|
|
|
eefaf6 |
+ * Really become root. setuid avoids an user killing us, possibly leaking
|
|
|
eefaf6 |
+ * the tmpfile. setgid avoids the new grubenv's gid being that of the user.
|
|
|
eefaf6 |
+ */
|
|
|
eefaf6 |
+ ret = setuid(0);
|
|
|
eefaf6 |
+ if (ret)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error setuid(0) failed");
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ ret = setgid(0);
|
|
|
eefaf6 |
+ if (ret)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error setgid(0) failed");
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ /* Canonicalize GRUBENV filename, resolving symlinks, etc. */
|
|
|
eefaf6 |
+ if (!realpath(GRUBENV, env_filename))
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error canonicalizing " GRUBENV " filename");
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ f = fopen (env_filename, "r");
|
|
|
eefaf6 |
if (!f)
|
|
|
eefaf6 |
{
|
|
|
eefaf6 |
perror ("Error opening " GRUBENV " for reading");
|
|
|
eefaf6 |
@@ -129,30 +158,70 @@ int main(int argc, char *argv[])
|
|
|
eefaf6 |
snprintf(buf, sizeof(buf), "%s=1\n", bootflag);
|
|
|
eefaf6 |
memcpy(s, buf, len + 3);
|
|
|
eefaf6 |
|
|
|
eefaf6 |
- /* "r+", don't truncate so that the diskspace stays reserved */
|
|
|
eefaf6 |
- f = fopen (GRUBENV, "r+");
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ /*
|
|
|
eefaf6 |
+ * Create a tempfile for writing the new env. Use the canonicalized filename
|
|
|
eefaf6 |
+ * for the template so that the tmpfile is in the same dir / on same fs.
|
|
|
eefaf6 |
+ */
|
|
|
eefaf6 |
+ snprintf(tmp_filename, sizeof(tmp_filename), "%sXXXXXX", env_filename);
|
|
|
eefaf6 |
+ fd = mkstemp(tmp_filename);
|
|
|
eefaf6 |
+ if (fd == -1)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Creating tmpfile failed");
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ f = fdopen (fd, "w");
|
|
|
eefaf6 |
if (!f)
|
|
|
eefaf6 |
{
|
|
|
eefaf6 |
- perror ("Error opening " GRUBENV " for writing");
|
|
|
eefaf6 |
+ perror ("Error fdopen of tmpfile failed");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
return 1;
|
|
|
eefaf6 |
}
|
|
|
eefaf6 |
|
|
|
eefaf6 |
ret = fwrite (env, 1, GRUBENV_SIZE, f);
|
|
|
eefaf6 |
if (ret != GRUBENV_SIZE)
|
|
|
eefaf6 |
{
|
|
|
eefaf6 |
- perror ("Error writing to " GRUBENV);
|
|
|
eefaf6 |
+ perror ("Error writing tmpfile");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
return 1;
|
|
|
eefaf6 |
}
|
|
|
eefaf6 |
|
|
|
eefaf6 |
ret = fflush (f);
|
|
|
eefaf6 |
if (ret)
|
|
|
eefaf6 |
{
|
|
|
eefaf6 |
- perror ("Error flushing " GRUBENV);
|
|
|
eefaf6 |
+ perror ("Error flushing tmpfile");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
return 1;
|
|
|
eefaf6 |
}
|
|
|
eefaf6 |
|
|
|
eefaf6 |
- fsync (fileno (f));
|
|
|
eefaf6 |
- fclose (f);
|
|
|
eefaf6 |
+ ret = fsync (fileno (f));
|
|
|
eefaf6 |
+ if (ret)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error syncing tmpfile");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ ret = fclose (f);
|
|
|
eefaf6 |
+ if (ret)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error closing tmpfile");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
+
|
|
|
eefaf6 |
+ /*
|
|
|
eefaf6 |
+ * And finally rename the tmpfile with the new env over the old env, the
|
|
|
eefaf6 |
+ * linux kernel guarantees that this is atomic (from a syscall pov).
|
|
|
eefaf6 |
+ */
|
|
|
eefaf6 |
+ ret = rename(tmp_filename, env_filename);
|
|
|
eefaf6 |
+ if (ret)
|
|
|
eefaf6 |
+ {
|
|
|
eefaf6 |
+ perror ("Error renaming tmpfile to " GRUBENV " failed");
|
|
|
eefaf6 |
+ unlink(tmp_filename);
|
|
|
eefaf6 |
+ return 1;
|
|
|
eefaf6 |
+ }
|
|
|
eefaf6 |
|
|
|
eefaf6 |
return 0;
|
|
|
eefaf6 |
}
|