Since glibc now has SHA-256 and SHA-512 support for crypt(3), and the userspace tools like authconfig and libuser all support it, we should offer users the option of which encoding algorithm they want to user for passwords. Via kickstart, users can use the following options for the auth command: --enablemd5 -or- --passalgo=md5 MD5 --passalgo=sha256 SHA-256 --passalgo-sha512 SHA-512 In the text and gtk user interfaces, the root password screen now shows a combo box of some sort that lets users pick the algorithm. The default is MD5 since we've always done that. All user accounts specified in the kickstart file as well as the root password will be encrypted using the specified algorithm. The /etc/libuser.conf file on the target system will have the crypt_style setting modified based on the user's choice. And passwords in /etc/shadow will be much bigger if the user chooses SHA-256 or SHA-512. --- instdata.py | 68 +++++++--- iw/account_gui.py | 193 ++++++++++++-------------- textw/userauth_text.py | 117 ++++++++++------- ui/account.glade | 355 ++++++++++++++++++++++++++++++++++++++++++++++++ ui/autopart.glade | 2 - users.py | 60 +++++--- 6 files changed, 599 insertions(+), 196 deletions(-) create mode 100644 ui/account.glade diff --git a/instdata.py b/instdata.py index b90af8d..5fde6d3 100644 --- a/instdata.py +++ b/instdata.py @@ -150,12 +150,40 @@ class InstallData: def setUpgrade (self, bool): self.upgrade = bool - def write(self): - if self.auth.find("--enablemd5"): - useMD5 = True + # Update the auth string to contain the correct password algorithm opts + # 'algo' can be any of 'md5', 'sha256', 'sha512'. The empty string or + # None for algo gives md5. + def updatePassAlgo(self, algo): + newauth = '' + + for arg in self.auth.lower().split(' '): + if arg != '--enablemd5' and arg != '--passalgo=md5' and \ + arg != '--passalgo=sha256' and arg != '--passalgo=sha512': + newauth += ' ' + arg + + if algo is None or algo == '': + algo = 'md5' + + if algo != 'md5' and algo != 'sha256' and algo != 'sha512': + algo = 'md5' + + newauth += ' --passalgo=%s' % (algo,) + self.auth = newauth.strip() + + # Reads the auth string and returns a string indicating our desired + # password encoding algorithm. + def getPassAlgo(self): + if self.auth.find("--enablemd5") != -1 or \ + self.auth.find("--passalgo=md5") != -1: + return 'md5' + elif self.auth.find("--passalgo=sha256") != -1: + return 'sha256' + elif self.auth.find("--passalgo=sha512") != -1: + return 'sha512' else: - useMD5 = False + return None + def write(self): self.instLanguage.write (self.anaconda.rootPath) if not self.isHeadless: @@ -175,16 +203,21 @@ class InstallData: except RuntimeError, msg: log.error("Error running %s: %s", args, msg) - self.network.write (self.anaconda.rootPath) - self.firewall.write (self.anaconda.rootPath) + self.network.write (self.anaconda.rootPath) + self.firewall.write (self.anaconda.rootPath) self.security.write (self.anaconda.rootPath) self.users = users.Users() + # make sure crypt_style in libuser.conf matches the salt we're using + users.createLuserConf(self.anaconda.rootPath, + algoname=self.getPassAlgo()) + # User should already exist, just without a password. self.users.setRootPassword(self.rootPassword["password"], - self.rootPassword["isCrypted"], useMD5, - self.rootPassword["lock"]) + self.rootPassword["isCrypted"], + self.rootPassword["lock"], + algo=self.getPassAlgo()) self.users.reset() @@ -202,19 +235,20 @@ class InstallData: root=self.anaconda.rootPath) for ud in self.ksdata.user.userList: - if not self.users.createUser(ud.name, ud.password, ud.isCrypted, - ud.groups, ud.homedir, ud.shell, - ud.uid, ud.lock, + if not self.users.createUser(name=ud.name, + password=ud.password, + isCrypted=ud.isCrypted, + groups=ud.groups, + homedir=ud.homedir, + shell=ud.shell, + uid=ud.uid, + algo=self.getPassAlgo(), + lock=ud.lock, root=self.anaconda.rootPath): log.error("User %s already exists, not creating." % ud.name) def writeKS(self, filename): - if self.auth.find("--enablemd5"): - useMD5 = True - else: - useMD5 = False - f = open(filename, "w") f.write("# Kickstart file automatically generated by anaconda.\n\n") @@ -243,7 +277,7 @@ class InstallData: if self.rootPassword["isCrypted"]: args = " --iscrypted %s" % self.rootPassword["password"] else: - args = " --iscrypted %s" % users.cryptPassword(self.rootPassword["password"], useMD5) + args = " --iscrypted %s" % users.cryptPassword(self.rootPassword["password"], algo=self.getPassAlgo()) if self.rootPassword["lock"]: args += " --lock" diff --git a/iw/account_gui.py b/iw/account_gui.py index c4eb334..2afb151 100644 --- a/iw/account_gui.py +++ b/iw/account_gui.py @@ -1,7 +1,8 @@ # -# account_gui.py: gui root password and user creation dialog +# account_gui.py: gui root password and crypt algorithm dialog # -# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. +# Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, Red Hat Inc. +# 2006, 2007, 2008 # All rights reserved. # # This program is free software; you can redistribute it and/or modify @@ -19,34 +20,88 @@ # import gtk +import gobject import string import gui from iw_gui import * -from rhpl.translate import _, N_ +from rhpl.translate import _ from flags import flags import cracklib -def handleCapsLockRelease(window, event, label): - if event.keyval == gtk.keysyms.Caps_Lock and event.state & gtk.gdk.LOCK_MASK: - if label.get_text() == "": - label.set_text(_("<b>Caps Lock is on.</b>")) - label.set_use_markup(True) - else: - label.set_text("") - class AccountWindow (InstallWindow): + def getScreen(self, anaconda): + self.anaconda = anaconda + self.rootPassword = anaconda.id.rootPassword + self.intf = anaconda.intf + + (self.xml, self.align) = gui.getGladeWidget("account.glade", + "account_align") + self.icon = self.xml.get_widget("icon") + self.capslock = self.xml.get_widget("capslock") + self.pwlabel = self.xml.get_widget("pwlabel") + self.pw = self.xml.get_widget("pw") + self.confirmlabel = self.xml.get_widget("confirmlabel") + self.confirm = self.xml.get_widget("confirm") + self.algorithms = self.xml.get_widget("algorithms") + + # load the icon + gui.readImageFromFile("root-password.png", image=self.icon) + + # populate encoding algorithm combo box + store = gtk.ListStore(gobject.TYPE_STRING) + cell = gtk.CellRendererText() + self.algorithms.set_model(store) + self.algorithms.pack_start(cell, True) + self.algorithms.set_attributes(cell, text=0) + + self.algorithms.append_text("MD5") + self.algorithms.append_text("SHA-256") + self.algorithms.append_text("SHA-512") + + # connect hotkeys + self.pwlabel.set_text_with_mnemonic(_("Root _Password:")) + self.pwlabel.set_mnemonic_widget(self.pw) + self.confirmlabel.set_text_with_mnemonic(_("_Confirm:")) + self.confirmlabel.set_mnemonic_widget(self.confirm) + + # watch for Caps Lock so we can warn the user + self.intf.icw.window.connect("key-release-event", + lambda w, e: self.handleCapsLockRelease(w, e, self.capslock)) + + # we might have a root password already + if not self.rootPassword['isCrypted']: + self.pw.set_text(self.rootPassword['password']) + self.confirm.set_text(self.rootPassword['password']) + + return self.align + + def passwordError(self): + self.pw.set_text("") + self.confirm.set_text("") + self.pw.grab_focus() + raise gui.StayOnScreen + + def handleCapsLockRelease(self, window, event, label): + if event.keyval == gtk.keysyms.Caps_Lock and \ + event.state & gtk.gdk.LOCK_MASK: + if label.get_text() == "": + label.set_text("<b>" + _("Caps Lock is on.") + "</b>") + label.set_use_markup(True) + else: + label.set_text("") + + def getSelectedAlgo(self): + model = self.algorithms.get_model() + active = self.algorithms.get_active() + + if active < 0: + algo = "md5" + else: + algo = model[active][0].lower().replace('-', '') - windowTitle = N_("Set Root Password") + return algo def getNext (self): - def passwordError(): - self.pw.set_text("") - self.confirm.set_text("") - self.pw.grab_focus() - raise gui.StayOnScreen - - if not self.__dict__.has_key("pw"): return None - pw = self.pw.get_text() confirm = self.confirm.get_text() @@ -56,119 +111,45 @@ class AccountWindow (InstallWindow): "and confirm it by typing it a second " "time to continue."), custom_icon="error") - passwordError() + self.passwordError() if pw != confirm: self.intf.messageWindow(_("Error with Password"), _("The passwords you entered were " "different. Please try again."), custom_icon="error") - passwordError() + self.passwordError() if len(pw) < 6: self.intf.messageWindow(_("Error with Password"), _("The root password must be at least " "six characters long."), custom_icon="error") - passwordError() + self.passwordError() msg = cracklib.FascistCheck(pw) if msg is not None: ret = self.intf.messageWindow(_("Weak Password"), _("Weak password provided: %s" "\n\n" - "Would you like to continue with this " - "password?" % (msg, )), + "Would you like to continue with " + "this password?" % (msg, )), type = "yesno") if ret == 0: - passwordError() - - allowed = string.digits + string.ascii_letters + string.punctuation + " " + self.passwordError() + + legal = string.digits + string.ascii_letters + string.punctuation + " " for letter in pw: - if letter not in allowed: + if letter not in legal: self.intf.messageWindow(_("Error with Password"), _("Requested password contains " "non-ASCII characters, which are " "not allowed."), custom_icon="error") - passwordError() + self.passwordError() self.rootPassword["password"] = self.pw.get_text() self.rootPassword["isCrypted"] = False - return None - - def setFocus (self, area, data): - self.pw.grab_focus () - - # AccountWindow tag="accts" - def getScreen (self, anaconda): - self.rootPassword = anaconda.id.rootPassword - self.intf = anaconda.intf + self.anaconda.id.updatePassAlgo(self.getSelectedAlgo()) - self.capsLabel = gtk.Label() - self.capsLabel.set_alignment(0.0, 0.5) - - self.intf.icw.window.connect("key-release-event", - lambda w, e: handleCapsLockRelease(w, e, self.capsLabel)) - - self.passwords = {} - - box = gtk.VBox () - box.set_border_width(5) - - hbox = gtk.HBox() - pix = gui.readImageFromFile ("root-password.png") - if pix: - hbox.pack_start (pix, False) - - label = gui.WrappingLabel (_("The root account is used for " - "administering the system. Enter " - "a password for the root user.")) - label.set_line_wrap(True) - label.set_size_request(350, -1) - label.set_alignment(0.0, 0.5) - hbox.pack_start(label, False) - - box.pack_start(hbox, False) - - table = gtk.Table (3, 2) - table.set_size_request(365, -1) - table.set_row_spacings (5) - table.set_col_spacings (5) - - pass1 = gui.MnemonicLabel (_("Root _Password: ")) - pass1.set_alignment (0.0, 0.5) - table.attach (pass1, 0, 1, 0, 1, gtk.FILL, 0, 10) - pass2 = gui.MnemonicLabel (_("_Confirm: ")) - pass2.set_alignment (0.0, 0.5) - table.attach (pass2, 0, 1, 1, 2, gtk.FILL, 0, 10) - self.pw = gtk.Entry (128) - pass1.set_mnemonic_widget(self.pw) - - self.pw.connect ("activate", lambda widget, box=box: box.emit("focus", gtk.DIR_TAB_FORWARD)) - self.pw.connect ("map-event", self.setFocus) - self.pw.set_visibility (False) - self.confirm = gtk.Entry (128) - pass2.set_mnemonic_widget(self.confirm) - self.confirm.connect ("activate", lambda widget, box=box: self.ics.setGrabNext(1)) - self.confirm.set_visibility (False) - table.attach (self.pw, 1, 2, 0, 1, gtk.FILL|gtk.EXPAND, 5) - table.attach (self.confirm, 1, 2, 1, 2, gtk.FILL|gtk.EXPAND, 5) - table.attach (self.capsLabel, 1, 2, 2, 3, gtk.FILL|gtk.EXPAND, 5) - - hbox = gtk.HBox() - hbox.pack_start(table, False) - - box.pack_start (hbox, False) - - # root password statusbar - self.rootStatus = gtk.Label ("") - wrapper = gtk.HBox(0, False) - wrapper.pack_start (self.rootStatus) - box.pack_start (wrapper, False) - - if not self.rootPassword["isCrypted"]: - self.pw.set_text(self.rootPassword["password"]) - self.confirm.set_text(self.rootPassword["password"]) - - return box + return None diff --git a/textw/userauth_text.py b/textw/userauth_text.py index 81331d5..b359782 100644 --- a/textw/userauth_text.py +++ b/textw/userauth_text.py @@ -1,7 +1,7 @@ # # userauth_text.py: text mode authentication setup dialogs # -# Copyright (C) 2000, 2001, 2002 Red Hat, Inc. All rights reserved. +# Copyright (C) 2000, 2001, 2002, 2008 Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,79 +22,100 @@ from constants_text import * from rhpl.translate import _ import cracklib -def has_bad_chars(pw): - allowed = string.digits + string.ascii_letters + string.punctuation + " " - for letter in pw: - if letter not in allowed: - return 1 - return 0 - class RootPasswordWindow: def __call__ (self, screen, anaconda): - toplevel = GridFormHelp (screen, _("Root Password"), "rootpw", 1, 3) + toplevel = GridFormHelp(screen, _("Root Password"), "rootpw", 1, 6) + + toplevel.add(TextboxReflowed(55, + _("Pick a root password. You must " + "type it twice to ensure you know " + "it and do not make a typing " + "mistake. ")), + 0, 0, (0, 0, 0, 1)) + + algogrid = Grid(1, 4) + algogroup = RadioGroup() + + algogrid.setField(TextboxReflowed(55, + _("Select default system password " + "encoding algorithm:")), + 0, 0, (0, 0, 0, 1)) + md5Cb = algogroup.add(_("MD5"), "md5", True) + algogrid.setField(md5Cb, 0, 1, growx=1, anchorLeft=1) - toplevel.add (TextboxReflowed(37, _("Pick a root password. You must " - "type it twice to ensure you know " - "it and do not make a typing mistake. " - "Remember that the " - "root password is a critical part " - "of system security!")), 0, 0, (0, 0, 0, 1)) + sha256Cb = algogroup.add(_("SHA-256"), "sha256", False) + algogrid.setField(sha256Cb, 0, 2, growx=1, anchorLeft=1) + + sha512Cb = algogroup.add(_("SHA-512"), "sha512", False) + algogrid.setField(sha512Cb, 0, 3, growx=1, anchorLeft=1) + + toplevel.add(algogrid, 0, 1, (0, 0, 0, 1)) if anaconda.id.rootPassword["isCrypted"]: anaconda.id.rootPassword["password"] = "" - entry1 = Entry (24, password = 1, text = anaconda.id.rootPassword["password"]) - entry2 = Entry (24, password = 1, text = anaconda.id.rootPassword["password"]) - passgrid = Grid (2, 2) - passgrid.setField (Label (_("Password:")), 0, 0, (0, 0, 1, 0), anchorLeft = 1) - passgrid.setField (Label (_("Password (confirm):")), 0, 1, (0, 0, 1, 0), anchorLeft = 1) - passgrid.setField (entry1, 1, 0) - passgrid.setField (entry2, 1, 1) - toplevel.add (passgrid, 0, 1, (0, 0, 0, 1)) - - bb = ButtonBar (screen, (TEXT_OK_BUTTON, TEXT_BACK_BUTTON)) - toplevel.add (bb, 0, 2, growx = 1) + entry1 = Entry(24, password=1, + text=anaconda.id.rootPassword["password"]) + entry2 = Entry(24, password=1, + text=anaconda.id.rootPassword["password"]) + passgrid = Grid(2, 2) + passgrid.setField(Label(_("Password:")), 0, 0, (0, 0, 1, 0), + anchorLeft=1) + passgrid.setField(Label(_("Password (confirm):")), 0, 1, (0, 0, 1, 0), + anchorLeft=1) + passgrid.setField(entry1, 1, 0) + passgrid.setField(entry2, 1, 1) + toplevel.add(passgrid, 0, 2, (0, 0, 0, 1)) + + bb = ButtonBar(screen, (TEXT_OK_BUTTON, TEXT_BACK_BUTTON)) + toplevel.add(bb, 0, 3, growx = 1) while 1: - toplevel.setCurrent (entry1) - result = toplevel.run () - rc = bb.buttonPressed (result) + toplevel.setCurrent(entry1) + result = toplevel.run() + rc = bb.buttonPressed(result) if rc == TEXT_BACK_CHECK: screen.popWindow() return INSTALL_BACK - if len (entry1.value ()) < 6: + if len(entry1.value()) < 6: ButtonChoiceWindow(screen, _("Password Length"), - _("The root password must be at least 6 characters " - "long."), - buttons = [ TEXT_OK_BUTTON ], width = 50) - elif entry1.value () != entry2.value (): + _("The root password must be at least 6 characters long."), + buttons = [ TEXT_OK_BUTTON ], width = 50) + elif entry1.value() != entry2.value(): ButtonChoiceWindow(screen, _("Password Mismatch"), - _("The passwords you entered were different. Please " - "try again."), - buttons = [ TEXT_OK_BUTTON ], width = 50) - elif has_bad_chars(entry1.value()): + _("The passwords you entered were different. Please " + "try again."), buttons = [ TEXT_OK_BUTTON ], width = 50) + elif self.hasBadChars(entry1.value()): ButtonChoiceWindow(screen, _("Error with Password"), - _("Requested password contains non-ASCII characters, " - "which are not allowed."), - buttons = [ TEXT_OK_BUTTON ], width = 50) + _("Requested password contains non-ASCII characters, " + "which are not allowed."), + buttons = [ TEXT_OK_BUTTON ], width = 50) else: msg = cracklib.FascistCheck(entry1.value()) if msg is not None: ret = anaconda.intf.messageWindow(_("Weak Password"), - _("Weak password provided: %s" - "\n\n" - "Would you like to continue with this " - "password?" % (msg, )), - type = "yesno", default="no") + _("Weak password provided: %s\n\n" + "Would you like to continue with this password?" + % (msg, )), + type = "yesno", default="no") if ret == 1: break else: break - entry1.set ("") - entry2.set ("") + entry1.set("") + entry2.set("") screen.popWindow() anaconda.id.rootPassword["password"] = entry1.value() anaconda.id.rootPassword["isCrypted"] = False + anaconda.id.updatePassAlgo(algogroup.getSelection()) return INSTALL_OK + + def hasBadChars(self, pw): + allowed = string.digits + string.ascii_letters + \ + string.punctuation + " " + for letter in pw: + if letter not in allowed: + return True + return False diff --git a/ui/account.glade b/ui/account.glade new file mode 100644 index 0000000..cd394d1 --- /dev/null +++ b/ui/account.glade @@ -0,0 +1,355 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="account_window"> + <property name="border_width">18</property> + <property name="title" translatable="yes" context="yes"></property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + + <child> + <widget class="GtkAlignment" id="account_align"> + <property name="width_request">400</property> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="account_box"> + <property name="border_width">5</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">10</property> + + <child> + <widget class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkHBox" id="desc_box"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkImage" id="icon"> + <property name="visible">True</property> + <property name="pixbuf">root-password.png</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="desc"> + <property name="visible">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="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment3"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkVBox" id="algovbox"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="label" translatable="yes">Select default system password encoding algorithm:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">True</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">40</property> + <property name="right_padding">600</property> + + <child> + <widget class="GtkComboBox" id="algorithms"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkAlignment" id="alignment4"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">0</property> + <property name="right_padding">0</property> + + <child> + <widget class="GtkTable" id="table1"> + <property name="width_request">365</property> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">5</property> + <property name="column_spacing">5</property> + + <child> + <widget class="GtkLabel" id="pwlabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">Root Password:</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="confirmlabel"> + <property name="visible">True</property> + <property name="label" translatable="yes">Confirm:</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="capslock"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="pw"> + <property name="width_request">256</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â?¢</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="confirm"> + <property name="width_request">256</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">False</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char">â?¢</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/ui/autopart.glade b/ui/autopart.glade index f607a43..3e20709 100644 --- a/ui/autopart.glade +++ b/ui/autopart.glade @@ -9,8 +9,6 @@ <property name="type">GTK_WINDOW_TOPLEVEL</property> <property name="window_position">GTK_WIN_POS_NONE</property> <property name="modal">False</property> - <property name="default_width">440</property> - <property name="default_height">250</property> <property name="resizable">True</property> <property name="destroy_with_parent">False</property> <property name="decorated">True</property> diff --git a/users.py b/users.py index fe5751f..799fa12 100644 --- a/users.py +++ b/users.py @@ -30,39 +30,51 @@ import os.path import logging log = logging.getLogger("anaconda") -def createLuserConf(instPath): +def createLuserConf(instPath, algoname='md5'): """Writes a libuser.conf for instPath.""" - (fd, fn) = tempfile.mkstemp(prefix="libuser.") + if os.getenv("LIBUSER_CONF") and \ + os.access(os.environ["LIBUSER_CONF"], os.R_OK): + fn = os.environ["LIBUSER_CONF"] + fd = open(fn, 'w') + else: + (fp, fn) = tempfile.mkstemp(prefix="libuser.") + fd = os.fdopen(fp, 'w') + buf = """ [defaults] skeleton = %(instPath)s/etc/skel mailspooldir = %(instPath)s/var/mail -crypt_style = md5 +crypt_style = %(algo)s modules = files shadow create_modules = files shadow [files] directory = %(instPath)s/etc [shadow] directory = %(instPath)s/etc -""" % {"instPath": instPath} - os.write(fd, buf) - os.close(fd) +""" % {"instPath": instPath, "algo": algoname} + fd.write(buf) + fd.close() os.environ["LIBUSER_CONF"] = fn -def cryptPassword(password, useMD5): - if useMD5: - salt = "$1$" - saltLen = 8 - else: - salt = "" - saltLen = 2 +# These are explained in crypt/crypt-entry.c in glibc's code. The prefixes +# we use for the different crypt salts: +# $1$ MD5 +# $5$ SHA256 +# $6$ SHA512 +def cryptPassword(password, algo=None): + salts = {'md5': '$1$', 'sha256': '$5$', 'sha512': '$6$', None: ''} + saltstr = salts[algo] + saltlen = 2 + + if algo == 'md5' or algo == 'sha256' or algo == 'sha512': + saltlen = 16 - for i in range(saltLen): - salt = salt + random.choice (string.letters + - string.digits + './') + for i in range(saltlen): + saltstr = saltstr + random.choice (string.letters + + string.digits + './') - return crypt.crypt (password, salt) + return crypt.crypt (password, saltstr) class Users: def __init__ (self): @@ -72,8 +84,8 @@ class Users: os.unsetenv("LIBUSER_CONF") self.admin = libuser.admin() - def createUser (self, name, password=None, isCrypted=False, groups=[], - homedir=None, shell=None, uid=None, lock=False, + def createUser (self, name=None, password=None, isCrypted=False, groups=[], + homedir=None, shell=None, uid=None, algo=None, lock=False, root="/mnt/sysimage"): childpid = os.fork() @@ -108,9 +120,11 @@ class Users: if password: if isCrypted: - self.admin.setpassUser(userEnt, password, isCrypted) + self.admin.setpassUser(userEnt, password, True) else: - self.admin.setpassUser(userEnt, cryptPassword(password, True), isCrypted) + self.admin.setpassUser(userEnt, + cryptPassword(password, algo=algo), + True) if lock: self.admin.lockUser(userEnt) @@ -136,13 +150,13 @@ class Users: else: return False - def setRootPassword(self, password, isCrypted, useMD5, lock): + def setRootPassword(self, password, isCrypted, lock, algo=None): rootUser = self.admin.lookupUserByName("root") if isCrypted: self.admin.setpassUser(rootUser, password, True) else: - self.admin.setpassUser(rootUser, cryptPassword(password, useMD5), True) + self.admin.setpassUser(rootUser, cryptPassword(password, algo=algo), True) if lock: self.admin.lockUser(rootUser) -- 1.5.4.1 _______________________________________________ Anaconda-devel-list mailing list Anaconda-devel-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/anaconda-devel-list