[PATCH] Require minimum random data entropy when creating LUKS (#1073679)

Anne Mulhern amulhern at redhat.com
Tue Sep 9 13:48:36 UTC 2014



----- Original Message -----
From: "Vratislav Podzimek" <vpodzime at redhat.com>
To: anaconda-patches at lists.fedorahosted.org
Sent: Friday, August 29, 2014 8:20:03 AM
Subject: [PATCH] Require minimum random data entropy when creating LUKS	(#1073679)

Creating new LUKS format requires high-quality random data and such quality may
be specified by minimum entropy of kernel's random data pool required. We can
tell blivet what should be the minimum entropy available when creating LUKS and
we can provide callbacks which inform users about what's happening and how they
can help to increase the available entropy.

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 po/POTFILES.in                                    |   3 +
 pyanaconda/install.py                             |   5 +-
 pyanaconda/kickstart.py                           |   3 +-
 pyanaconda/ui/gui/spokes/custom.py                |  26 +++---
 pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade |  66 ++++++++++++++
 pyanaconda/ui/gui/spokes/lib/entropy_dialog.py    |  56 ++++++++++++
 pyanaconda/ui/lib/entropy.py                      | 103 ++++++++++++++++++++++
 7 files changed, 250 insertions(+), 12 deletions(-)
 create mode 100644 pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
 create mode 100644 pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
 create mode 100644 pyanaconda/ui/lib/entropy.py

diff --git a/po/POTFILES.in b/po/POTFILES.in
index daf7460..a1ca868 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -48,6 +48,7 @@ pyanaconda/ui/categories/user_settings.py
 
 # Common stuff
 pyanaconda/ui/lib/space.py
+pyanaconda/ui/lib/entropy.py
 
 # Text interface
 pyanaconda/ui/tui/hubs/summary.py
@@ -96,6 +97,7 @@ pyanaconda/ui/gui/spokes/lib/passphrase.py
 pyanaconda/ui/gui/spokes/lib/refresh.py
 pyanaconda/ui/gui/spokes/lib/resize.py
 pyanaconda/ui/gui/spokes/lib/summary.py
+pyanaconda/ui/gui/spokes/lib/entropy.py
 
 # Custom widgets.
 widgets/src/BaseWindow.c
@@ -134,6 +136,7 @@ pyanaconda/ui/gui/spokes/lib/passphrase.glade
 pyanaconda/ui/gui/spokes/lib/refresh.glade
 pyanaconda/ui/gui/spokes/lib/resize.glade
 pyanaconda/ui/gui/spokes/lib/summary.glade
+pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
 pyanaconda/ui/gui/main.glade
 pyanaconda/ui/gui/hubs/summary.glade
 pyanaconda/ui/gui/hubs/progress.glade
diff --git a/pyanaconda/install.py b/pyanaconda/install.py
index 18043f1..d3ad6be 100644
--- a/pyanaconda/install.py
+++ b/pyanaconda/install.py
@@ -29,6 +29,7 @@ from pyanaconda import iutil
 from pyanaconda import timezone
 from pyanaconda.i18n import _
 from pyanaconda.threads import threadMgr
+from pyanaconda.ui.lib.entropy import wait_for_entropy
 import logging
 import blivet
 log = logging.getLogger("anaconda")
@@ -178,10 +179,12 @@ def doInstall(storage, payload, ksdata, instClass):
     # callbacks for blivet
     message_clbk = lambda clbk_data: progress_message(clbk_data.msg)
     step_clbk = lambda clbk_data: progress_step(clbk_data.msg)
+    entropy_wait_clbk = lambda msg, ent: wait_for_entropy(msg, ent, ksdata)
     callbacks_reg = callbacks.create_new_callbacks_register(create_format_pre=message_clbk,
                                                             create_format_post=step_clbk,
                                                             resize_format_pre=message_clbk,
-                                                            resize_format_post=step_clbk)
+                                                            resize_format_post=step_clbk,
+                                                            wait_for_entropy=entropy_wait_clbk)
 
     turnOnFilesystems(storage, mountOnly=flags.flags.dirInstall, callbacks=callbacks_reg)
     write_storage_late = (flags.flags.livecdInstall or ksdata.ostreesetup.seen
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index cb86c9f..b8797b7 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -273,6 +273,7 @@ class AutoPart(commands.autopart.F21_AutoPart):
 
     def execute(self, storage, ksdata, instClass):
         from blivet.partitioning import doAutoPartition
+        from blivet.devicelibs.crypto import MIN_CREATE_ENTROPY
         from pyanaconda.storage_utils import sanity_check
 
         if not self.autopart:
@@ -300,7 +301,7 @@ class AutoPart(commands.autopart.F21_AutoPart):
         if self.type is not None:
             storage.autoPartType = self.type
 
-        doAutoPartition(storage, ksdata)
+        doAutoPartition(storage, ksdata, min_luks_entropy=MIN_CREATE_ENTROPY)
         errors = sanity_check(storage)
         if errors:
             raise PartitioningError("autopart failed:\n" + "\n".join(error.message for error in errors))
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py
index 8380c3e..8e926ab 100644
--- a/pyanaconda/ui/gui/spokes/custom.py
+++ b/pyanaconda/ui/gui/spokes/custom.py
@@ -58,7 +58,7 @@ from blivet.partitioning import doAutoPartition
 from blivet.errors import StorageError
 from blivet.errors import NoDisksError
 from blivet.errors import NotEnoughFreeSpaceError
-from blivet.devicelibs import raid
+from blivet.devicelibs import raid, crypto
 from blivet.devices import LUKSDevice
 
 from pyanaconda.storage_utils import ui_storage_logger, device_type_from_autopart
@@ -860,9 +860,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._back_already_clicked = False
 
         # dictionaries for many, many pieces of information about the device and
-        # requested changes
-        new_device_info = dict()
-        old_device_info = dict()
+        # requested changes, minimum required entropy for LUKS creation is
+        # always the same
+        new_device_info = {"min_luks_entropy": crypto.MIN_CREATE_ENTROPY}
+        old_device_info = {"min_luks_entropy": crypto.MIN_CREATE_ENTROPY}
 
         new_device_info["device"] = device
         use_dev = device.raw_device
@@ -1009,7 +1010,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                                       device_type, size,
                                                       disks=device.disks,
                                                       encrypted=encrypted,
-                                                      raid_level=raid_level)
+                                                      raid_level=raid_level,
+                                                      min_luks_entropy=crypto.MIN_CREATE_ENTROPY)
 
         # CONTAINER
         changed_container = False
@@ -1546,7 +1548,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
     @ui_storage_logged
     def _add_device(self, dev_info):
         factory = devicefactory.get_device_factory(self._storage_playground,
-                                                   dev_info["device_type"], dev_info["size"])
+                                                   dev_info["device_type"], dev_info["size"],
+                                                   min_luks_entropy=crypto.MIN_CREATE_ENTROPY)
         container = factory.get_container()
         if container:
             # don't override user-initiated changes to a defined container
@@ -1623,8 +1626,9 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         self._back_already_clicked = False
 
         ## gather data about the added mountpoint
+        # minimum entropy required for LUKS creation is always the same
         # TODO: use instance of a class with properly named attributes
-        dev_info = dict()
+        dev_info = {"min_luks_entropy": crypto.MIN_CREATE_ENTROPY}
 
         # create a device of the default type, using any disks, with an
         # appropriate fstype and mountpoint
@@ -1753,7 +1757,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
                                         container_name=cont_name,
                                         container_encrypted=cont_encrypted,
                                         container_raid_level=cont_raid,
-                                        container_size=cont_size)
+                                        container_size=cont_size,
+                                        min_luks_entropy=crypto.MIN_CREATE_ENTROPY)
             factory.configure()
 
         self._remove_empty_parents(device)
@@ -2190,7 +2195,8 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
             self._storage_playground.setUpBootLoader(early=True)
 
             refreshAutoSwapSize(self._storage_playground)
-            doAutoPartition(self._storage_playground, self.data)
+            doAutoPartition(self._storage_playground, self.data,
+                            min_luks_entropy=crypto.MIN_CREATE_ENTROPY)
         except NoDisksError as e:
             # No handling should be required for this.
             log.error("doAutoPartition failed: %s", e)
@@ -2311,7 +2317,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
         with ui_storage_logger():
             factory = devicefactory.get_device_factory(self._storage_playground,
                                                      device_type,
-                                                     0)
+                                                     0, min_luks_entropy=crypto.MIN_CREATE_ENTROPY)
             container = factory.get_container(device=_device)
             default_container_name = getattr(container, "name", None)
             if container:
diff --git a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
new file mode 100644
index 0000000..cb98a2a
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.glade
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+  <requires lib="gtk+" version="3.0"/>
+  <object class="GtkWindow" id="entropyDialog">
+    <property name="can_focus">False</property>
+    <property name="type">popup</property>
+    <property name="modal">True</property>
+    <property name="window_position">center</property>
+    <property name="type_hint">dialog</property>
+    <property name="deletable">False</property>
+    <child>
+      <object class="GtkFrame" id="frame1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="label_xalign">0</property>
+        <property name="shadow_type">etched-out</property>
+        <child>
+          <object class="GtkBox" id="mainBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">6</property>
+            <property name="margin_right">6</property>
+            <property name="margin_top">6</property>
+            <property name="margin_bottom">6</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="messageLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="valign">center</property>
+                <property name="vexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">The system doesn't have enough random data entropy for the actions it needs to execute. To shorten the time of waiting for enough entropy, please move your mouse or type on the keyboard.</property>
+                <property name="wrap">True</property>
+                <property name="width_chars">60</property>
+                <property name="max_width_chars">60</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkProgressBar" id="progressBar">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="show_text">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="label_item">
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
new file mode 100644
index 0000000..6f55483
--- /dev/null
+++ b/pyanaconda/ui/gui/spokes/lib/entropy_dialog.py
@@ -0,0 +1,56 @@
+# Dialog for waiting for enough random data entropy
+#
+# Copyright (C) 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Vratislav Podzimek <vpodzime at redhat.com>
+#
+
+from gi.repository import Gtk, GLib
+
+from pyanaconda.ui.gui import GUIObject
+from blivet.util import get_current_entropy
+
+__all__ = ["EntropyDialog"]
+
+class EntropyDialog(GUIObject):
+    builderObjects = ["entropyDialog"]
+    mainWidgetName = "entropyDialog"
+    uiFile = "spokes/lib/entropy_dialog.glade"
+
+    def __init__(self, data, desired_entropy):
+        GUIObject.__init__(self, data)
+        self._desired_entropy = desired_entropy
+        self._progress_bar = self.builder.get_object("progressBar")
+
+    def run(self):
+        self.window.show_all()
+        GLib.timeout_add(250, self._update_progress)
+        Gtk.main()
+        self.window.destroy()
+
+    def _update_progress(self):
+        current_entropy = get_current_entropy()
+        current_fraction = min(float(current_entropy) / self._desired_entropy, 1.0)
+        self._progress_bar.set_fraction(current_fraction)
+
+        if current_entropy >= self._desired_entropy:
+            Gtk.main_quit()
+            # remove the method from idle queue
+            return False
+        else:
+            # keep updating
+            return True
diff --git a/pyanaconda/ui/lib/entropy.py b/pyanaconda/ui/lib/entropy.py
new file mode 100644
index 0000000..4d892ee
--- /dev/null
+++ b/pyanaconda/ui/lib/entropy.py
@@ -0,0 +1,103 @@
+#
+# Copyright (C) 2013  Red Hat, Inc.
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# the GNU General Public License v.2, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY expressed or implied, including the implied warranties of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+# Public License for more details.  You should have received a copy of the
+# GNU General Public License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
+# source code or documentation are not subject to the GNU General Public
+# License and may only be used or replicated with the express permission of
+# Red Hat, Inc.
+#
+# Red Hat Author(s): Vratislav Podzimek <vpodzime at redhat.com>
+#
+
+"""
+Helper module with functions for informing user we are waiting for random
+data entropy.
+
+"""
+
+import time
+import select
+import sys
+import termios
+
+from gi.repository import Gtk
+from pyanaconda.ui.gui.spokes.lib.entropy_dialog import EntropyDialog
+from pyanaconda.ui.gui.utils import gtk_action_wait
+from pyanaconda.progress import progress_message
+from blivet.util import get_current_entropy
+
+from pyanaconda.i18n import _
+
+def wait_for_entropy(msg, desired_entropy, ksdata):
+    """
+    Show UI dialog/message for waiting for desired random data entropy.
+
+    :param ksdata: kickstart data
+    :type ksdata: pykickstart.base.BaseHandler
+    :param desired_entropy: entropy level to wait for
+    :type desired_entropy: int
+
+    """
+
+    progress_message(_("The system needs more random data entropy"))
+
+    (succ, _args) = Gtk.init_check(None)
+    if succ:
+        # Gtk initialized, run GUI dialog
+        _gui_wait(ksdata, desired_entropy)
+    else:
+        _tui_wait(msg, desired_entropy)
+
+ at gtk_action_wait
+def _gui_wait(ksdata, desired_entropy):
+    """Show dialog with waiting for entropy"""
+
+    dialog = EntropyDialog(ksdata, desired_entropy)
+    dialog.run()
+
+def _tui_wait(msg, desired_entropy):
+    """Tell user we are waiting for entropy"""
+
+    print(msg)
+    print(_("Entropy can be increased by typing on keyboard"))
+    fd = sys.stdin.fileno()
+    old = termios.tcgetattr(fd)
+    new = termios.tcgetattr(fd)

The above line could be:

new = old[:]

I think.

+    new[3] = new[3] & ~termios.ICANON & ~termios.ECHO
+    new[6][termios.VMIN] = 1
+    termios.tcsetattr(fd, termios.TCSANOW, new)
+
+    # wait for the entropy to become high enough
+    cur_entr = get_current_entropy()
+    while cur_entr < desired_entropy:
+        print(_("Available entropy: %(av_entr)s, Required entropy: %(req_entr)s [%(pct)d %%]")
+                % {"av_entr": cur_entr, "req_entr": desired_entropy,
+                   "pct": int((float(cur_entr) / desired_entropy) * 100)})
+        time.sleep(1)
+        cur_entr = get_current_entropy()
+
+    # print the final state as well
+    print(_("Available entropy: %(av_entr)s, Required entropy: %(req_entr)s [%(pct)d %%]")
+            % {"av_entr": cur_entr, "req_entr": desired_entropy,
+               "pct": int((float(cur_entr) / desired_entropy) * 100)})
+
+    print("Done")
+    # we are done, just read everything from the input buffer and revert the
+    # termios state
+    print("Running select")
+    while sys.stdin in select.select([sys.stdin], [], [], 0)[0]:

It seems wierd to me that a timeout of 0 seconds will always be enough here.

- mulhern

+        print("Data available")
+        _in_data = sys.stdin.read(1)
+        print("Read")
+
+    print("Restoring termios")
+    termios.tcsetattr(fd, termios.TCSAFLUSH, old)
-- 
1.9.3

_______________________________________________
anaconda-patches mailing list
anaconda-patches at lists.fedorahosted.org
https://lists.fedorahosted.org/mailman/listinfo/anaconda-patches

- mulhern


More information about the anaconda-patches mailing list