dcavalca / rpms / grub2

Forked from rpms/grub2 3 years ago
Clone
8631a2
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
8631a2
From: Hans de Goede <hdegoede@redhat.com>
8631a2
Date: Tue, 12 Jun 2018 13:25:16 +0200
8631a2
Subject: [PATCH] Add grub-set-bootflag utility
8631a2
8631a2
This commit adds a new grub-set-bootflag utility, which can be used
8631a2
to set known bootflags in the grubenv: boot_success or menu_show_once.
8631a2
8631a2
grub-set-bootflag is different from grub-editenv in 2 ways:
8631a2
8631a2
1) It is intended to be executed by regular users through pkexec, so
8631a2
running as root if the polkit policy allows this. As such it is written
8631a2
to not use any existing grubenv related code for easy auditing.
8631a2
8631a2
2) Since it can be executed by regular users it only allows setting
8631a2
(assigning a value of 1 to) bootflags which it knows about. Currently
8631a2
those are just boot_success and menu_show_once.
8631a2
8631a2
This commit also adds a couple of example systemd and polkit files which
8631a2
show how this can be used to set boot_success from a user-session:
8631a2
8631a2
docs/grub-boot-success.service
8631a2
docs/grub-boot-success.timer
8631a2
docs/org.gnu.grub.policy
8631a2
8631a2
The 2 grub-boot-success.systemd files should be placed in /lib/systemd/user
8631a2
and a symlink to grub-boot-success.timer should be added to
8631a2
/lib/systemd/user/timers.target.wants.
8631a2
8631a2
The org.gnu.grub.policy polkit file should be placed in
8631a2
/usr/share/polkit-1/actions.
8631a2
8631a2
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
8631a2
---
8631a2
 Makefile.util.def              |   7 ++
8631a2
 util/grub-set-bootflag.c       | 158 +++++++++++++++++++++++++++++++++++++++++
8631a2
 .gitignore                     |   2 +
8631a2
 conf/Makefile.extra-dist       |   3 +
8631a2
 docs/grub-boot-success.service |   6 ++
8631a2
 docs/grub-boot-success.timer   |   5 ++
8631a2
 docs/org.gnu.grub.policy       |  20 ++++++
8631a2
 util/grub-set-bootflag.1       |  20 ++++++
8631a2
 8 files changed, 221 insertions(+)
8631a2
 create mode 100644 util/grub-set-bootflag.c
8631a2
 create mode 100644 docs/grub-boot-success.service
8631a2
 create mode 100644 docs/grub-boot-success.timer
8631a2
 create mode 100644 docs/org.gnu.grub.policy
8631a2
 create mode 100644 util/grub-set-bootflag.1
8631a2
8631a2
diff --git a/Makefile.util.def b/Makefile.util.def
8631a2
index 5a8c390a1da..5da55393291 100644
8631a2
--- a/Makefile.util.def
8631a2
+++ b/Makefile.util.def
8631a2
@@ -1435,3 +1435,10 @@ program = {
8631a2
   ldadd = grub-core/gnulib/libgnu.a;
8631a2
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
8631a2
 };
8631a2
+
8631a2
+program = {
8631a2
+  name = grub-set-bootflag;
8631a2
+  installdir = sbin;
8631a2
+  mansection = 1;
8631a2
+  common = util/grub-set-bootflag.c;
8631a2
+};
8631a2
diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c
8631a2
new file mode 100644
8631a2
index 00000000000..f8dc310909a
8631a2
--- /dev/null
8631a2
+++ b/util/grub-set-bootflag.c
8631a2
@@ -0,0 +1,158 @@
8631a2
+/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */
8631a2
+/*
8631a2
+ *  GRUB  --  GRand Unified Bootloader
8631a2
+ *  Copyright (C) 2018 Free Software Foundation, Inc.
8631a2
+ *
8631a2
+ *  GRUB is free software: you can redistribute it and/or modify
8631a2
+ *  it under the terms of the GNU General Public License as published by
8631a2
+ *  the Free Software Foundation, either version 3 of the License, or
8631a2
+ *  (at your option) any later version.
8631a2
+ *
8631a2
+ *  GRUB is distributed in the hope that it will be useful,
8631a2
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
8631a2
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8631a2
+ *  GNU General Public License for more details.
8631a2
+ *
8631a2
+ *  You should have received a copy of the GNU General Public License
8631a2
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
8631a2
+ */
8631a2
+
8631a2
+/*
8631a2
+ * NOTE this gets run by users as root (through pkexec), so this does not
8631a2
+ * use any grub library / util functions to allow for easy auditing.
8631a2
+ * The grub headers are only included to get certain defines.
8631a2
+ */
8631a2
+
8631a2
+#include <config-util.h>     /* For *_DIR_NAME defines */
8631a2
+#include <grub/types.h>
8631a2
+#include <grub/lib/envblk.h> /* For GRUB_ENVBLK_DEFCFG define */
8631a2
+#include <stdio.h>
8631a2
+#include <string.h>
8631a2
+#include <unistd.h>
8631a2
+
8631a2
+#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG
8631a2
+#define GRUBENV_SIZE 1024
8631a2
+
8631a2
+const char *bootflags[] = {
8631a2
+  "boot_success",
8631a2
+  "menu_show_once",
8631a2
+  NULL
8631a2
+};
8631a2
+
8631a2
+static void usage(void)
8631a2
+{
8631a2
+  int i;
8631a2
+
8631a2
+  fprintf (stderr, "Usage: 'grub-set-bootflag <bootflag>', where <bootflag> is one of:\n");
8631a2
+  for (i = 0; bootflags[i]; i++)
8631a2
+    fprintf (stderr, "  %s\n", bootflags[i]);
8631a2
+}
8631a2
+
8631a2
+int main(int argc, char *argv[])
8631a2
+{
8631a2
+  /* NOTE buf must be at least the longest bootflag length + 4 bytes */
8631a2
+  char env[GRUBENV_SIZE + 1], buf[64], *s;
8631a2
+  const char *bootflag;
8631a2
+  int i, len, ret;
8631a2
+  FILE *f;
8631a2
+
8631a2
+  if (argc != 2)
8631a2
+    {
8631a2
+      usage();
8631a2
+      return 1;
8631a2
+    }
8631a2
+
8631a2
+  for (i = 0; bootflags[i]; i++)
8631a2
+    if (!strcmp (argv[1], bootflags[i]))
8631a2
+      break;
8631a2
+  if (!bootflags[i])
8631a2
+    {
8631a2
+      fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]);
8631a2
+      usage();
8631a2
+      return 1;
8631a2
+    }
8631a2
+
8631a2
+  bootflag = bootflags[i];
8631a2
+  len = strlen (bootflag);
8631a2
+
8631a2
+  f = fopen (GRUBENV, "r");
8631a2
+  if (!f)
8631a2
+    {
8631a2
+      perror ("Error opening " GRUBENV " for reading");
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  ret = fread (env, 1, GRUBENV_SIZE, f);
8631a2
+  fclose (f);
8631a2
+  if (ret != GRUBENV_SIZE)
8631a2
+    {
8631a2
+      perror ("Error reading from " GRUBENV);
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  /* 0 terminate env */
8631a2
+  env[GRUBENV_SIZE] = 0;
8631a2
+
8631a2
+  if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE)))
8631a2
+    {
8631a2
+      fprintf (stderr, "Error invalid environment block\n");
8631a2
+      return 1;
8631a2
+    }
8631a2
+
8631a2
+  /* Find a pre-existing definition of the bootflag */
8631a2
+  s = strstr (env, bootflag);
8631a2
+  while (s && s[len] != '=')
8631a2
+    s = strstr (s + len, bootflag);
8631a2
+
8631a2
+  if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n'))
8631a2
+    {
8631a2
+      fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag);
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  /* No pre-existing bootflag? -> find free space */
8631a2
+  if (!s)
8631a2
+    {
8631a2
+      for (i = 0; i < (len + 3); i++)
8631a2
+        buf[i] = '#';
8631a2
+      buf[i] = 0;
8631a2
+      s = strstr (env, buf);
8631a2
+    }
8631a2
+
8631a2
+  if (!s)
8631a2
+    {
8631a2
+      fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag);
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */
8631a2
+  snprintf(buf, sizeof(buf), "%s=1\n", bootflag);
8631a2
+  memcpy(s, buf, len + 3);
8631a2
+
8631a2
+  /* "r+", don't truncate so that the diskspace stays reserved */
8631a2
+  f = fopen (GRUBENV, "r+");
8631a2
+  if (!f)
8631a2
+    {
8631a2
+      perror ("Error opening " GRUBENV " for writing");
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  ret = fwrite (env, 1, GRUBENV_SIZE, f);
8631a2
+  if (ret != GRUBENV_SIZE)
8631a2
+    {
8631a2
+      perror ("Error writing to " GRUBENV);
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  ret = fflush (f);
8631a2
+  if (ret)
8631a2
+    {
8631a2
+      perror ("Error flushing " GRUBENV);
8631a2
+      return 1;     
8631a2
+    }
8631a2
+
8631a2
+  fsync (fileno (f));
8631a2
+  fclose (f);
8631a2
+
8631a2
+  return 0;
8631a2
+}
8631a2
diff --git a/.gitignore b/.gitignore
8631a2
index 42475592123..6c4cfc53781 100644
8631a2
--- a/.gitignore
8631a2
+++ b/.gitignore
8631a2
@@ -111,6 +111,8 @@ grub-*.tar.*
8631a2
 /grub*-rpm-sort.8
8631a2
 /grub*-script-check
8631a2
 /grub*-script-check.1
8631a2
+/grub*-set-bootflag
8631a2
+/grub*-set-bootflag.1
8631a2
 /grub*-set-default
8631a2
 /grub*-set-default.8
8631a2
 /grub*-setsetpassword
8631a2
diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist
8631a2
index 39eb94bded6..5946ec24a65 100644
8631a2
--- a/conf/Makefile.extra-dist
8631a2
+++ b/conf/Makefile.extra-dist
8631a2
@@ -14,6 +14,9 @@ EXTRA_DIST += util/import_unicode.py
8631a2
 EXTRA_DIST += docs/autoiso.cfg
8631a2
 EXTRA_DIST += docs/grub.cfg
8631a2
 EXTRA_DIST += docs/osdetect.cfg
8631a2
+EXTRA_DIST += docs/org.gnu.grub.policy
8631a2
+EXTRA_DIST += docs/grub-boot-success.service
8631a2
+EXTRA_DIST += docs/grub-boot-success.timer
8631a2
 
8631a2
 EXTRA_DIST += conf/i386-cygwin-img-ld.sc
8631a2
 
8631a2
diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service
8631a2
new file mode 100644
8631a2
index 00000000000..c8c91c34d49
8631a2
--- /dev/null
8631a2
+++ b/docs/grub-boot-success.service
8631a2
@@ -0,0 +1,6 @@
8631a2
+[Unit]
8631a2
+Description=Mark boot as successful
8631a2
+
8631a2
+[Service]
8631a2
+Type=oneshot
8631a2
+ExecStart=/usr/bin/pkexec /usr/sbin/grub2-set-bootflag boot_success
8631a2
diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer
8631a2
new file mode 100644
8631a2
index 00000000000..221b532781b
8631a2
--- /dev/null
8631a2
+++ b/docs/grub-boot-success.timer
8631a2
@@ -0,0 +1,5 @@
8631a2
+[Unit]
8631a2
+Description=Mark boot as successful after the user session has run 2 minutes
8631a2
+
8631a2
+[Timer]
8631a2
+OnActiveSec=2min
8631a2
diff --git a/docs/org.gnu.grub.policy b/docs/org.gnu.grub.policy
8631a2
new file mode 100644
8631a2
index 00000000000..18391efc8e7
8631a2
--- /dev/null
8631a2
+++ b/docs/org.gnu.grub.policy
8631a2
@@ -0,0 +1,20 @@
8631a2
+
8631a2
+
8631a2
+<policyconfig>
8631a2
+  <vendor>GNU GRUB</vendor>
8631a2
+  <vendor_url>https://www.gnu.org/software/grub/</vendor_url>
8631a2
+  <action id="org.gnu.grub.set-bootflag">
8631a2
+    
8631a2
+          - A normal active user on the local machine does not need permission
8631a2
+            to set bootflags to show the menu / mark current boot successful.
8631a2
+     -->
8631a2
+    <description>Set GRUB bootflags</description>
8631a2
+    <message>Authentication is required to modify the bootloaders bootflags</message>
8631a2
+    <defaults>
8631a2
+      <allow_any>no</allow_any>
8631a2
+      <allow_inactive>no</allow_inactive>
8631a2
+      <allow_active>yes</allow_active>
8631a2
+    </defaults>
8631a2
+    <annotate key="org.freedesktop.policykit.exec.path">/usr/sbin/grub2-set-bootflag</annotate>
8631a2
+  </action>
8631a2
+</policyconfig>
8631a2
diff --git a/util/grub-set-bootflag.1 b/util/grub-set-bootflag.1
8631a2
new file mode 100644
8631a2
index 00000000000..57801da22a0
8631a2
--- /dev/null
8631a2
+++ b/util/grub-set-bootflag.1
8631a2
@@ -0,0 +1,20 @@
8631a2
+.TH GRUB-SET-BOOTFLAG 1 "Tue Jun 12 2018"
8631a2
+.SH NAME
8631a2
+\fBgrub-set-bootflag\fR \(em Set a bootflag in the GRUB environment block.
8631a2
+
8631a2
+.SH SYNOPSIS
8631a2
+\fBgrub-set-bootflag\fR <\fIBOOTFLAG\fR>
8631a2
+
8631a2
+.SH DESCRIPTION
8631a2
+\fBgrub-set-bootflag\fR is a command line to set bootflags in GRUB's
8631a2
+stored environment.
8631a2
+
8631a2
+.SH COMMANDS
8631a2
+.TP
8631a2
+\fBBOOTFLAG\fR
8631a2
+.RS 7
8631a2
+Bootflag to set, one of \fIboot_success\fR or \fIshow_menu_once\fR.
8631a2
+.RE
8631a2
+
8631a2
+.SH SEE ALSO
8631a2
+.BR "info grub"