[PATCH 3/3] Require root password from kickstart to allow changes

Vratislav Podzimek vpodzime at redhat.com
Wed May 15 12:28:46 UTC 2013


If the password was given in kickstart, we should require retyping it before we
allow user to change it. And if the password is set in kickstart as frozen, we
shouldn't allow changing it at all. If somebody wants to put a password in the
kickstart and allow changes without retyping it, the --allow-changes option can
be used.

With the old UI we didn't allow changes of root password if it was given in
kickstart.

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 anaconda.spec.in                        |  2 +-
 pyanaconda/kickstart.py                 |  2 +-
 pyanaconda/ui/gui/spokes/password.glade | 46 ++++++++++++++-------
 pyanaconda/ui/gui/spokes/password.py    | 72 ++++++++++++++++++++++++++++++---
 pyanaconda/users.py                     | 24 +++++++++++
 5 files changed, 123 insertions(+), 23 deletions(-)

diff --git a/anaconda.spec.in b/anaconda.spec.in
index 823567c..af49f80 100644
--- a/anaconda.spec.in
+++ b/anaconda.spec.in
@@ -21,7 +21,7 @@ Source0: %{name}-%{version}.tar.bz2
 %define gconfversion 2.28.1
 %define intltoolver 0.31.2-3
 %define libnlver 1.0
-%define pykickstartver 1.99.30
+%define pykickstartver 1.99.31
 %define yumver 3.4.3-32
 %define partedver 1.8.1
 %define pypartedver 2.5-2
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py
index 7ebcf04..c316324 100644
--- a/pyanaconda/kickstart.py
+++ b/pyanaconda/kickstart.py
@@ -1197,7 +1197,7 @@ class RepoData(commands.repo.F15_RepoData):
 
         commands.repo.F15_RepoData.__init__(self, *args, **kwargs)
 
-class RootPw(commands.rootpw.F18_RootPw):
+class RootPw(commands.rootpw.F19_RootPw):
     def execute(self, storage, ksdata, instClass, users):
         if not self.password and not flags.automatedInstall:
             self.lock = True
diff --git a/pyanaconda/ui/gui/spokes/password.glade b/pyanaconda/ui/gui/spokes/password.glade
index 75ac957..397db6a 100644
--- a/pyanaconda/ui/gui/spokes/password.glade
+++ b/pyanaconda/ui/gui/spokes/password.glade
@@ -45,6 +45,7 @@
                   <object class="GtkGrid" id="pwgrid">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="row_spacing">6</property>
                     <child>
                       <object class="GtkLabel" id="pwlabel">
                         <property name="visible">True</property>
@@ -57,7 +58,7 @@
                       </object>
                       <packing>
                         <property name="left_attach">0</property>
-                        <property name="top_attach">1</property>
+                        <property name="top_attach">2</property>
                         <property name="width">1</property>
                         <property name="height">1</property>
                       </packing>
@@ -74,7 +75,7 @@
                       </object>
                       <packing>
                         <property name="left_attach">0</property>
-                        <property name="top_attach">2</property>
+                        <property name="top_attach">3</property>
                         <property name="width">1</property>
                         <property name="height">1</property>
                       </packing>
@@ -88,7 +89,7 @@
                       </object>
                       <packing>
                         <property name="left_attach">1</property>
-                        <property name="top_attach">1</property>
+                        <property name="top_attach">2</property>
                         <property name="width">1</property>
                         <property name="height">1</property>
                       </packing>
@@ -103,40 +104,55 @@
                       </object>
                       <packing>
                         <property name="left_attach">1</property>
-                        <property name="top_attach">2</property>
+                        <property name="top_attach">3</property>
                         <property name="width">1</property>
                         <property name="height">1</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkLabel" id="capslock">
+                      <object class="GtkLabel" id="details">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="use_markup">True</property>
+                        <property name="label" translatable="yes">The root account is used for administering the system.  Enter a password for the root user.</property>
+                        <property name="wrap">True</property>
                       </object>
                       <packing>
-                        <property name="left_attach">1</property>
-                        <property name="top_attach">3</property>
-                        <property name="width">1</property>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">2</property>
                         <property name="height">1</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkLabel" id="details">
+                      <object class="GtkLabel" id="oldPwLabel">
                         <property name="visible">True</property>
                         <property name="can_focus">False</property>
-                        <property name="label" translatable="yes">The root account is used for administering the system.  Enter a password for the root user.</property>
-                        <property name="wrap">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">_Old Password:</property>
+                        <property name="use_underline">True</property>
+                        <property name="mnemonic_widget">oldPw</property>
                       </object>
                       <packing>
                         <property name="left_attach">0</property>
-                        <property name="top_attach">0</property>
-                        <property name="width">2</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
                         <property name="height">1</property>
                       </packing>
                     </child>
                     <child>
-                      <placeholder/>
+                      <object class="GtkEntry" id="oldPw">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="visibility">False</property>
+                        <property name="invisible_char">●</property>
+                        <signal name="changed" handler="on_old_pw_changed" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
                     </child>
                   </object>
                   <packing>
diff --git a/pyanaconda/ui/gui/spokes/password.py b/pyanaconda/ui/gui/spokes/password.py
index 1bf55ce..e4749fd 100644
--- a/pyanaconda/ui/gui/spokes/password.py
+++ b/pyanaconda/ui/gui/spokes/password.py
@@ -1,6 +1,6 @@
 # root password spoke class
 #
-# Copyright (C) 2012 Red Hat, Inc.
+# Copyright (C) 2012-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
@@ -17,21 +17,29 @@
 # Red Hat, Inc.
 #
 # Red Hat Author(s): Jesse Keating <jkeating at redhat.com>
+#                    Vratislav Podzimek <vpodzime at redhat.com>
 #
 
 # pylint: disable-msg=E0611
 from gi.repository import Gtk
 
 from pyanaconda.i18n import _, N_
-from pyanaconda.users import cryptPassword, validatePassword
+from pyanaconda.users import cryptPassword, validatePassword, passwordMatchesHash
 from pwquality import PWQError
 
 from pyanaconda.ui.gui.spokes import NormalSpoke
 from pyanaconda.ui.gui.categories.user_settings import UserSettingsCategory
 from pyanaconda.ui.common import FirstbootSpokeMixIn
+from pyanaconda.ui.gui.utils import fancy_set_sensitive, really_show, really_hide
+
+import logging
+log = logging.getLogger("anaconda")
 
 __all__ = ["PasswordSpoke"]
 
+PASSWORD_IN_KS_MSG = _("The password was set by kickstart.")
+PASSWORD_IN_KS_FROZEN_MSG = _("The password was set as frozen by kickstart.")
+OLD_PW_MSG = _("Enter the old password to enable changes")
 
 class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
     builderObjects = ["passwordWindow"]
@@ -56,15 +64,42 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
         # place holders for the text boxes
         self.pw = self.builder.get_object("pw")
         self.confirm = self.builder.get_object("confirm")
+        self.old_pw = self.builder.get_object("oldPw")
+        self.old_pw_label = self.builder.get_object("oldPwLabel")
 
         self._kickstarted = self.data.rootpw.seen
-        if self._kickstarted:
-            self.pw.set_placeholder_text(_("The password was set by kickstart."))
-            self.confirm.set_placeholder_text(_("The password was set by kickstart."))
 
     def refresh(self):
-        self.pw.grab_focus()
+        really_hide(self.old_pw)
+        really_hide(self.old_pw_label)
 
+        if self._kickstarted:
+            if self.data.rootpw.frozen:
+                # frozen password, cannot be changed
+                self.pw.set_placeholder_text(PASSWORD_IN_KS_FROZEN_MSG)
+                self.confirm.set_placeholder_text(PASSWORD_IN_KS_FROZEN_MSG)
+                fancy_set_sensitive(self.pw, False)
+                fancy_set_sensitive(self.confirm, False)
+
+            elif self.data.rootpw.allow_changes:
+                # changes without retyping old password allowed
+                self.pw.set_placeholder_text(PASSWORD_IN_KS_MSG)
+                self.confirm.set_placeholder_text(PASSWORD_IN_KS_MSG)
+                self.pw.grab_focus()
+
+            else:
+                # need to type the old password to allow changes
+                self.old_pw.show()
+                self.old_pw_label.show()
+                self.old_pw.set_placeholder_text(OLD_PW_MSG)
+
+                self.pw.set_placeholder_text(OLD_PW_MSG)
+                self.confirm.set_placeholder_text(OLD_PW_MSG)
+                fancy_set_sensitive(self.pw, False)
+                fancy_set_sensitive(self.confirm, False)
+
+        else:
+            self.pw.grab_focus()
 
     @property
     def status(self):
@@ -144,3 +179,28 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke):
             self.set_warning(self._error)
             self.pw.grab_focus()
             self.window.show_all()
+
+    def on_old_pw_changed(self, entry):
+        text = entry.get_text()
+        matches = False
+
+        if self.data.rootpw.isCrypted:
+            try:
+                matches = passwordMatchesHash(text, self.data.rootpw.password)
+            except ValueError as valerr:
+                # weird hash, cannot compare
+                log.error("Cannot compare password and hash: %s" % valerr)
+        else:
+            matches = text == self.data.rootpw.password
+
+        if matches:
+            # the right password entered, allow changes
+            fancy_set_sensitive(self.pw, True)
+            fancy_set_sensitive(self.confirm, True)
+            self.pw.set_placeholder_text("")
+            self.confirm.set_placeholder_text("")
+        else:
+            fancy_set_sensitive(self.pw, False)
+            fancy_set_sensitive(self.confirm, False)
+            self.pw.set_placeholder_text(OLD_PW_MSG)
+            self.confirm.set_placeholder_text(OLD_PW_MSG)
diff --git a/pyanaconda/users.py b/pyanaconda/users.py
index 212184b..45a94b6 100644
--- a/pyanaconda/users.py
+++ b/pyanaconda/users.py
@@ -114,6 +114,30 @@ def cryptPassword(password, algo=None):
 
     return crypt.crypt (password, saltstr)
 
+def passwordMatchesHash(password, hash_str):
+    """
+    Compares password with a hashed password (with algorithm specification and
+    salt).
+
+    :param password: password to be compared
+    :type password: str
+    :param hash_str: hashed password to be compared
+    :type hash_str: str as returned by crypt.crypt
+    :return: True if the password matches the hash, False otherwise
+    :rtype: bool
+
+    """
+
+    parts = hash_str.rsplit("$", 1)
+    if len(parts) != 2:
+        # weird hash string, cannot check
+        raise ValueError("Invalid hash string '%s', cannot check" % hash_str)
+
+    alg_salt = parts[0]
+    crypted_password = crypt.crypt(password, alg_salt)
+
+    return crypted_password == hash_str
+
 def validatePassword(pw, confirm, minlen=6):
     # Do various steps to validate the password
     # Return an error string, or None for no errors
-- 
1.7.11.7



More information about the anaconda-patches mailing list