aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEugeni Dodonov <eugeni@mandriva.org>2009-01-07 22:05:09 +0000
committerEugeni Dodonov <eugeni@mandriva.org>2009-01-07 22:05:09 +0000
commit3fe3b3eeb76292c002b7f9880542a6324f5c9231 (patch)
tree20a92fce4e3d8a8eefdc2751159ce55f7c14dea1
parentf7640ddebe3e855635f1c5f2b96ed6d6e7da1d9d (diff)
downloadmsec-3fe3b3eeb76292c002b7f9880542a6324f5c9231.tar
msec-3fe3b3eeb76292c002b7f9880542a6324f5c9231.tar.gz
msec-3fe3b3eeb76292c002b7f9880542a6324f5c9231.tar.bz2
msec-3fe3b3eeb76292c002b7f9880542a6324f5c9231.tar.xz
msec-3fe3b3eeb76292c002b7f9880542a6324f5c9231.zip
Implemented authentication.
-rw-r--r--src/msec/config.py175
-rwxr-xr-xsrc/msec/libmsec.py9
-rwxr-xr-xsrc/msec/msecgui.py224
3 files changed, 366 insertions, 42 deletions
diff --git a/src/msec/config.py b/src/msec/config.py
index 0c5f345..a35bf92 100644
--- a/src/msec/config.py
+++ b/src/msec/config.py
@@ -16,6 +16,7 @@ import gettext
import sys
import traceback
import re
+import os
# security levels
SECURITY_LEVELS = [ "none", "default", "secure" ]
@@ -105,6 +106,46 @@ SETTINGS = {'CHECK_SECURITY' : ("check_security",
'SHELL_TIMEOUT': ("set_shell_timeout", ['*']),
}
+# mandriva security tools
+AUTH_NO_PASSWD = _("No password")
+AUTH_ROOT_PASSWD = _("Root password")
+AUTH_USER_PASSWD = _("User password")
+
+# mandriva drakx tools
+MANDRIVA_TOOLS = {
+ "rpmdrake": _("Software Management"),
+ "mandrivaupdate": _("Mandriva Update"),
+ "drakrpm-edit-media": _("Software Media Manager"),
+ "drak3d": _("Configure 3D Desktop effects"),
+ "xfdrake": _("Graphical Server Configuration"),
+ "drakmouse": _("Mouse Configuration"),
+ "drakkeyboard": _("Keyboard Configuration"),
+ "drakups": _("UPS Configuration"),
+ "drakconnect": _("Network Configuration"),
+ "drakhosts": _("Hosts definitions"),
+ "draknetcenter": _("Network Center"),
+ "drakvpn": _("VPN"),
+ "drakproxy": _("Proxy Configuration"),
+ "drakgw": _("Connection Sharing"),
+ "drakauth": _("Authentication"),
+ "drakbackup": _("Backups"),
+ "drakfont": _("Import fonts"),
+ "draklog": _("Logs"),
+ "drakxservices": _("Services"),
+ "userdrake": _("Users"),
+ "drakclock": _("Date, Clock & Time Zone Settings"),
+ "drakboot": _("Boot Configuration"),
+ }
+
+# drakx tool groups
+MANDRIVA_TOOL_GROUPS = [
+ ( _("Software Management"), ['rpmdrake', 'mandrivaupdate', 'drakrpm-edit-media'] ),
+ ( _("Hardware"), ['drak3d', 'xfdrake', 'drakmouse', 'drakkeyboard', 'drakups'] ),
+ ( _("Network"), ['drakconnect', 'drakhosts', 'draknetcenter', 'drakvpn', 'drakproxy', 'drakgw'] ),
+ ( _("System"), ['drakauth', 'drakbackup', 'drakfont', 'draklog', 'drakxservices', 'userdrake', 'drakclock'] ),
+ ( _("Boot"), ['drakboot'] ),
+ ]
+
def find_callback(param):
'''Finds a callback for security option'''
if param not in SETTINGS:
@@ -147,8 +188,19 @@ class MsecConfig:
self.comments = []
self.log = log
+ def merge(self, newconfig, overwrite=False):
+ """Merges parameters from newconfig to current config"""
+ for opt in newconfig.list_options():
+ if overwrite:
+ self.set(opt, newconfig.get(opt))
+ else:
+ self.get(opt, newconfig.get(opt))
+
def load(self):
"""Loads and parses configuration file"""
+ if not self.config:
+ # No associated file
+ return True
try:
fd = open(self.config)
except:
@@ -193,6 +245,9 @@ class MsecConfig:
def save(self):
"""Saves configuration. Comments go on top"""
+ if not self.config:
+ # No associated file
+ return True
try:
fd = open(self.config, "w")
except:
@@ -277,3 +332,123 @@ class PermConfig(MsecConfig):
return True
# }}}
+# {{{ AuthConfig
+class AuthConfig(MsecConfig):
+ """Msec auth configuration config"""
+ def __init__(self, log, config=MANDRIVA_TOOLS):
+ self.config = config
+ self.options = {}
+ self.log = log
+ self.user_r = re.compile("USER=(.*)")
+ self.auth_root = "USER=root"
+ self.auth_user = "USER=<user>"
+
+ def load(self):
+ """Loads Mandriva auth configuration"""
+ # TODO: this should probably go to libmsec..
+ for app in self.config:
+ # first, lets see if file exists
+ try:
+ link = os.readlink("/etc/pam.d/%s" % app)
+ except:
+ self.log.error(_("Unable to access /etc/pam.d/%s: %s") % (app, sys.exc_value))
+ self.set(app, None)
+ continue
+
+ auth = None
+ # checking auth
+ if link.find("mandriva-console-auth") != -1:
+ auth = AUTH_NO_PASSWD
+ elif link.find("mandriva-simple-auth") != -1:
+ try:
+ # read console.apps data
+ fd = open("/etc/security/console.apps/%s" % app)
+ data = fd.read()
+ fd.close()
+ # locate correspondent user
+ res = self.user_r.findall(data)
+ if res:
+ user = res[0]
+ if user == "root":
+ auth = AUTH_ROOT_PASSWD
+ elif user == "<user>":
+ auth = AUTH_USER_PASSWD
+ else:
+ # unknown authentication
+ self.log.error(_("Unknown authentication scheme for %s: %s") % (app, link))
+ except:
+ self.log.error(_("Error parsing /etc/security/console.apps/%s: %s") % (app, sys.exc_value))
+ else:
+ # unknown pam parameter?
+ self.log.error(_("Unknown authentication scheme for %s: %s") % (app, link))
+ self.set(app, auth)
+ return True
+
+ def list_options(self):
+ """Sorts and returns configuration parameters"""
+ sortedparams = self.options.keys()
+ if sortedparams:
+ sortedparams.sort()
+ return sortedparams
+
+
+ def symlinkf(self, src, target, create=True):
+ """Check if correct symlink exists and creates when necessary."""
+ try:
+ link = os.readlink(target)
+ except:
+ self.log.error(_("Unable to handle symlink from %s to %s: %s") % (src, target, sys.exc_value))
+ link = ""
+ if link == target:
+ return True
+ else:
+ if create:
+ os.unlink(target)
+ os.symlink(src, target)
+ return False
+
+ def replace_auth(self, file, auth):
+ """Replaces PAM authentication in file"""
+ try:
+ lines = []
+ changed = False
+ fd = open(file)
+ for line in fd.readlines():
+ line = line.strip()
+ res = self.user_r.search(line)
+ if res:
+ if line.find(auth) == -1:
+ self.log.debug("Changing <%s> to <%s> in %s" % (line, auth, file))
+ changed = True
+ line = auth
+ lines.append(line)
+ fd.close()
+ if changed:
+ fd = open(file, "w")
+ print >>fd, "\n".join(lines)
+ fd.close()
+ except:
+ traceback.print_exc()
+
+ def save(self):
+ """Saves configuration. Comments go on top"""
+ # TODO: this should probably go to libmsec..
+ link_console_auth = "/etc/pam.d/mandriva-console-auth"
+ link_simple_auth = "/etc/pam.d/mandriva-simple-auth"
+ for app in self.config:
+ auth = self.get(app)
+ file_pam = "/etc/pam.d/%s" % app
+ file_console = "/etc/security/console.apps/%s" % app
+ # well, let's rock
+ if auth == AUTH_NO_PASSWD:
+ self.symlinkf(link_console_auth, file_pam)
+ elif auth == AUTH_ROOT_PASSWD:
+ self.symlinkf(link_simple_auth, file_pam)
+ self.replace_auth(file_console, self.auth_root)
+ elif auth == AUTH_USER_PASSWD:
+ self.symlinkf(link_simple_auth, file_pam)
+ self.replace_auth(file_console, self.auth_user)
+ else:
+ self.log.error(_("Invalid authentication %s for %s!") % (auth, app))
+# }}}
+
diff --git a/src/msec/libmsec.py b/src/msec/libmsec.py
index c1620a6..6689247 100755
--- a/src/msec/libmsec.py
+++ b/src/msec/libmsec.py
@@ -750,6 +750,10 @@ class MSEC:
continue
action(curconfig.get(opt))
+ def base_level(self, param):
+ """Specify a base security level"""
+ pass
+
def create_server_link(self, param):
''' Creates the symlink /etc/security/msec/server to point to /etc/security/msec/server.<SERVER_LEVEL>. The /etc/security/msec/server is used by chkconfig --add to decide to add a service if it is present in the file during the installation of packages.'''
__params__ = ["no", "default", "secure"]
@@ -1761,6 +1765,11 @@ class PERMS:
return self.files
# }}}
+class AUTH:
+ """Mandriva security tools authentication"""
+ def __init__(self, log):
+ """Initializes configuration"""
+ self.log = log
if __name__ == "__main__":
# this should never ever be run directly
diff --git a/src/msec/msecgui.py b/src/msec/msecgui.py
index 2ff68fa..3e5aa48 100755
--- a/src/msec/msecgui.py
+++ b/src/msec/msecgui.py
@@ -25,7 +25,7 @@ except:
version = "development version"
# libmsec
-from libmsec import MSEC, PERMS, Log
+from libmsec import MSEC, PERMS, AUTH, Log
import logging
@@ -65,6 +65,11 @@ The following security profiles are defined in this version:
checks if the system security settings, configured here, were modified.
""")
+AUTH_SECURITY_TEXT=_("""System authentication.
+
+These options control the authentication for different Mandriva tools.
+""")
+
SYSTEM_SECURITY_TEXT=_("""System security options.
These options control the local security configuration, such as the login restrictions,
@@ -106,21 +111,28 @@ class MsecGui:
# common columns
(COLUMN_OPTION, COLUMN_DESCR, COLUMN_VALUE) = range(3)
(COLUMN_PATH, COLUMN_USER, COLUMN_GROUP, COLUMN_PERM, COLUMN_FORCE) = range(5)
+ (COLUMN_APP, COLUMN_DESCR, COLUMN_AUTH) = range(3)
- def __init__(self, log, msec, perms, config, permconfig):
+ def __init__(self, log, msec, perms, auth, config, permconfig, authconfig):
"""Initializes gui"""
self.log = log
self.msec = msec
self.config = config
self.perms = perms
+ self.authconfig = authconfig
self.permconfig = permconfig
- # save original configuration
+ # msec settings
self.oldconfig = {}
for opt in config.list_options():
self.oldconfig[opt] = config.get(opt)
+ # permissions
self.oldperms = {}
for opt in permconfig.list_options():
self.oldperms[opt] = permconfig.get(opt)
+ # auth
+ self.oldauth = {}
+ for opt in authconfig.list_options():
+ self.oldauth[opt] = authconfig.get(opt)
self.window = gtk.Window()
self.window.set_default_size(640, 480)
@@ -142,6 +154,7 @@ class MsecGui:
frame.add(self.notebook)
self.notebook.append_page(self.basic_security_page(), gtk.Label(_("Basic security")))
+ self.notebook.append_page(self.auth_security_page(), gtk.Label(_("Authentication")))
self.notebook.append_page(self.system_security_page(), gtk.Label(_("System security")))
self.notebook.append_page(self.network_security_page(), gtk.Label(_("Network security")))
self.notebook.append_page(self.periodic_security_page(), gtk.Label(_("Periodic checks")))
@@ -180,11 +193,13 @@ class MsecGui:
def ok(self, widget):
"""Ok button"""
+ # TODO: split in smaller functions
# first, let's reset previous msec data
self.msec.reset()
# start buffered logging
self.log.start_buffer()
# are we enforcing a level?
+ # TODO: copy and merge options
if self.enforcing_level:
self.log.debug(">> Enforcing level %s" % self.enforced_level)
curconfig = config.load_defaults(self.log, self.enforced_level)
@@ -220,45 +235,32 @@ class MsecGui:
vbox.pack_start(label, False, False)
# check for changed options
- opt_changes = []
- for opt in self.oldconfig:
- if curconfig.get(opt) != self.oldconfig[opt]:
- opt_changes.append(opt)
-
- if len(opt_changes) > 0:
- # some configuration parameters were changed
- label = gtk.Label(_('<b>MSEC option changed:</b> <i>%s</i>\n') % ", ".join(opt_changes))
- label.set_use_markup(True)
- label.set_line_wrap(True)
- vbox.pack_start(label, False, False)
- else:
- label = gtk.Label(_('<i>No changes in MSEC options.</i>'))
- label.set_use_markup(True)
- vbox.pack_start(label, False, False)
-
- # now checking for permission changes
- perm_changes = []
- for opt in self.oldperms:
- if curperms.get(opt) != self.oldperms[opt]:
- perm_changes.append(opt)
-
- if len(perm_changes) > 0:
- # some configuration parameters were changed
- label = gtk.Label(_('<b>System permissions changed:</b> <i>%s</i>\n') % ", ".join(perm_changes))
- label.set_use_markup(True)
- label.set_line_wrap(True)
- vbox.pack_start(label, False, False)
- else:
- label = gtk.Label(_('<i>No changes in system permissions.</i>'))
+ for name, oldconf, curconf in [ (_("MSEC option changes"), self.oldconfig, curconfig),
+ (_("System permissions changes"), self.oldperms, curperms),
+ (_("System authentication changes"), self.oldauth, self.authconfig),
+ ]:
+ # check for changes
+ opt_changes = []
+ for opt in oldconf:
+ if curconf.get(opt) != oldconf.get(opt):
+ opt_changes.append(opt)
+ if len(opt_changes) > 0:
+ changes = "\n\t" + "\n\t".join(["<b>%s</b> (%s -> %s)" % (param, oldconf.get(param), curconf.get(param)) for param in opt_changes])
+ else:
+ changes = _("no changes")
+ label = gtk.Label(_('<b>%s:</b> <i>%s</i>\n') % (name, changes))
label.set_use_markup(True)
+# label.set_line_wrap(True)
+ label.set_property("xalign", 0.0)
vbox.pack_start(label, False, False)
# see if there were any changes to system files
for msg in messages['info']:
if msg.find(config.MODIFICATIONS_FOUND) != -1 or msg.find(config.MODIFICATIONS_NOT_FOUND) != -1:
- label = gtk.Label('<i>%s</i>' % msg)
+ label = gtk.Label(_("<b>MSEC test run results:</b> <i>%s</i>") % msg)
label.set_line_wrap(True)
label.set_use_markup(True)
+ label.set_property("xalign", 0.0)
vbox.pack_start(label, False, False)
break
@@ -296,12 +298,17 @@ class MsecGui:
self.config.set(opt, curconfig.get(opt))
for perm in curperms.list_options():
self.permconfig.set(perm, curperms.get(perm))
+
# saving the configuration
self.config.save()
self.msec.apply(self.config)
self.msec.commit(True)
+
# saving permissions
self.permconfig.save()
+
+ # saving authentication
+ self.authconfig.save()
# this is done periodically
#self.perms.check_perms(self.permconfig)
#self.perms.commit(True)
@@ -381,6 +388,75 @@ class MsecGui:
)
return sw
+ def create_auth_treeview(self, options):
+ """Creates a treeview for authentication options"""
+ sw = gtk.ScrolledWindow()
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+
+ # list of options
+ lstore = gtk.TreeStore(
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+
+ # treeview
+ treeview = gtk.TreeView(lstore)
+ treeview.set_rules_hint(True)
+ treeview.set_search_column(self.COLUMN_DESCR)
+ treeview.connect('realize', lambda tv: tv.expand_all())
+
+ treeview.connect('row-activated', self.auth_changed, lstore)
+
+ # configuring columns
+
+ # column for option names
+ column = gtk.TreeViewColumn(_('Application'), gtk.CellRendererText(), text=self.COLUMN_APP)
+ column.set_sort_column_id(self.COLUMN_APP)
+ treeview.append_column(column)
+
+ # column for descriptions
+ renderer = gtk.CellRendererText()
+ renderer.set_property('wrap-width', 400)
+ renderer.set_property('wrap-mode', pango.WRAP_WORD_CHAR)
+ column = gtk.TreeViewColumn(_('Description'), renderer, text=self.COLUMN_DESCR)
+ column.set_sort_column_id(self.COLUMN_DESCR)
+ treeview.append_column(column)
+
+ # column for values
+ column = gtk.TreeViewColumn(_('Auth'), gtk.CellRendererText(), text=self.COLUMN_AUTH)
+ column.set_sort_column_id(self.COLUMN_AUTH)
+ treeview.append_column(column)
+
+ sw.add(treeview)
+
+ for name, items in options:
+ # building the option
+ iter = lstore.append(None)
+ lstore.set(iter,
+ self.COLUMN_APP, name,
+ self.COLUMN_DESCR, None,
+ self.COLUMN_AUTH, None,
+ )
+ for option in items:
+ # retreiving option description
+ if not config.MANDRIVA_TOOLS.has_key(option):
+ # invalid option
+ self.log.error(_("Invalid option '%s'!") % option)
+ continue
+ descr = config.MANDRIVA_TOOLS[option]
+ value = self.authconfig.get(option)
+
+ # building the option
+ child_iter = lstore.append(iter)
+ lstore.set(child_iter,
+ self.COLUMN_APP, option,
+ self.COLUMN_DESCR, descr,
+ self.COLUMN_AUTH, value,
+ )
+ return sw
+
+
def basic_security_page(self):
"""Builds the basic security page"""
vbox = gtk.VBox(homogeneous=False)
@@ -423,6 +499,29 @@ class MsecGui:
return vbox
+ def auth_security_page(self):
+ """Builds the authentication page"""
+ vbox = gtk.VBox(homogeneous=False)
+
+ entry = gtk.Label(AUTH_SECURITY_TEXT)
+ entry.set_use_markup(True)
+ vbox.pack_start(entry, False, False)
+
+ sw = gtk.ScrolledWindow()
+ sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+ sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+
+ auth_vbox = gtk.VBox()
+
+ # security levels
+ sw.add_with_viewport(auth_vbox)
+ vbox.add(sw)
+
+ apps = self.create_auth_treeview(config.MANDRIVA_TOOL_GROUPS)
+ auth_vbox.pack_start(apps)
+
+ return vbox
+
def enforce_level(self, widget, options):
"""Enforces a new security level"""
frame = options
@@ -758,15 +857,50 @@ class MsecGui:
dialog.destroy()
return
+ def auth_changed(self, treeview, path, col, model):
+ """Processes an option change"""
+ iter = model.get_iter(path)
+ param = model.get_value(iter, self.COLUMN_APP)
+ descr = model.get_value(iter, self.COLUMN_DESCR)
+ value = model.get_value(iter, self.COLUMN_AUTH)
+
+ if len(path) < 2:
+ # We are clicking on an option group
+ return
+
+ # asks for new parameter value
+ dialog = gtk.Dialog(_("Specify new authentication for %s") % (param),
+ self.window, 0,
+ (gtk.STOCK_OK, gtk.RESPONSE_OK,
+ gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL))
+ label = gtk.Label(_("Modifying <b>%s</b> authentication.\n<i>%s</i>\nCurrent value: <b>%s</b>") % (param, descr, value))
+ label.set_line_wrap(True)
+ label.set_use_markup(True)
+ dialog.vbox.pack_start(label)
+ # combobox parameter
+ entry = gtk.combo_box_new_text()
+ params = [config.AUTH_NO_PASSWD, config.AUTH_ROOT_PASSWD, config.AUTH_USER_PASSWD]
+ for item in params:
+ entry.append_text(item)
+ if value not in params:
+ entry.append_text(value)
+ params.append(value)
+ active = params.index(value)
+ entry.set_active(active)
+ dialog.vbox.pack_start(entry)
+
+ dialog.show_all()
+ response = dialog.run()
+ if response != gtk.RESPONSE_OK:
+ dialog.destroy()
+ return
+
# process new parameter
- if '*' in params:
- newval = entry.get_text()
- else:
- newval = entry.get_active_text()
+ newval = entry.get_active_text()
dialog.destroy()
# update options
- self.config.set(param, newval)
+ self.authconfig.set(param, newval)
model.set(iter, self.COLUMN_VALUE, newval)
@@ -812,19 +946,25 @@ if __name__ == "__main__":
# loading initial config
msec_config = config.MsecConfig(log, config=config.SECURITYCONF)
if not msec_config.load():
- log.info(_("Unable to load config, using default values"))
+ log.info(_("Unable to load config."))
# loading permissions config
perm_conf = config.PermConfig(log, config=config.PERMCONF)
if not perm_conf.load():
- log.info(_("Unable to load permissions, using default values"))
+ log.info(_("Unable to load permissions."))
+
+ # loading auth config
+ auth_conf = config.AuthConfig(log)
+ if not auth_conf.load():
+ log.info(_("Unable to load auth config."))
# creating an msec instance
msec = MSEC(log)
perms = PERMS(log)
+ auth = AUTH(log)
log.info("Starting gui..")
- gui = MsecGui(log, msec, perms, msec_config, perm_conf)
+ gui = MsecGui(log, msec, perms, auth, msec_config, perm_conf, auth_conf)
gtk.main()