"""
Configuration Parsers for Krb5
==============================
Below parsers are included:
Krb5Configuration - files ``/etc/krb5.conf`` and ``/etc/krb5.conf.d/*``
-----------------------------------------------------------------------
Krb5LocalauthPlugin - file ``/var/lib/sss/pubconf/krb5.include.d/localauth_plugin``
-----------------------------------------------------------------------------------
"""
from insights.core import Parser
from insights.core.exceptions import SkipComponent
from insights.core.plugins import parser
from insights.specs import Specs
from insights.util import parse_bool
def _handle_key_value(t_dict, key, value):
"""
Function to handle key has multi value, and return the values as list.
"""
if key in t_dict:
val = t_dict[key]
if isinstance(val, str):
val = [val]
val.append(value)
return val
return value
[docs]
class Krb5ConfBase(Parser, dict):
"""
Base Class to process the Kerberos relevant configurations.
The Kerberos Configuration are generally in .ini format. it is like an
ordinary .ini file except that values can include a multiple line key-value
pair 'relation' that starts with a '{' and end with a '}' on a trailing line.
So we track whether we're in curly braces by setting `is_squ` when we enter
a relation, and clearing it when we leave. Please fill in the remainder of
the logic here.
"""
RESERVED_ATTRS = None
[docs]
def parse_content(self, content):
if self.RESERVED_ATTRS is None:
raise RuntimeError('Not applied when "RESERVED_ATTRS" does not exist')
for key in self.RESERVED_ATTRS:
setattr(self, key, [])
dict_all = {}
is_squ = False
section_name = ""
squ_value = {}
squ_section_name = ""
section_value = {}
unchangeable_tags = []
for line in content:
line = line.strip()
if not line or line.startswith('#'):
# skip empty and commented lines
continue
if line.startswith(self.RESERVED_ATTRS):
key, value = [i.strip() for i in line.split(None, 1)]
if key in self.RESERVED_ATTRS:
getattr(self, key).append(value)
continue
if is_squ:
# If in {} sub_section, get the key_value pair
if "=" in line:
key, value = [i.strip() for i in line.split('=', 1)]
if key not in unchangeable_tags:
value = value.split()[0] if value else value
squ_value[key] = _handle_key_value(squ_value, key, value)
if line.endswith("*"):
unchangeable_tags.append(key)
# The {} sub_section should end with },
# if it is, set the whole value to the sub_section name,
# and clean the flag
else:
section_value[squ_section_name] = squ_value
is_squ = False
squ_section_name = ""
squ_value = {}
else:
# [XXX] means a section, get the section name and the value
# format is dict.
if line.startswith("[") and line.endswith("]"):
# If first section, just get the section name,
# if not, set the value to the former section and
# get the section name
section_name = line.strip("[]")
section_value = {}
if section_name:
dict_all[section_name] = section_value
# key value format is XXX = YYY, store as dict
elif "=" in line and not line.endswith("{"):
key, value = [i.strip() for i in line.split('=', 1)]
if key not in unchangeable_tags:
value = value.split()[0] if value else value
section_value[key] = _handle_key_value(section_value, key, value)
if line.endswith("*"):
unchangeable_tags.append(key)
# The {} sub_section should start with format XXX = {
else:
is_squ = True
squ_section_name = line.split("=")[0].strip()
if dict_all:
self.update(dict_all)
elif not any(getattr(self, key) for key in self.RESERVED_ATTRS):
raise SkipComponent()
@property
def data(self):
"""
Keep backward compatibility. The "data" atrribute is deprecated,
the parser itself is dictionary.
.. warning::
This will be removed from 3.8.0.
"""
return self
[docs]
def sections(self):
"""
Return a list of section names.
"""
return sorted(self.keys())
[docs]
def has_section(self, section):
"""
Indicate whether the named section is present in the configuration.
Return True if the given section is present, and False if not present.
"""
return section in self
[docs]
def options(self, section):
"""
Return a list of option names for the given section name.
"""
return sorted(self[section].keys()) if self.has_section(section) else []
[docs]
def has_option(self, section, option):
"""
Check for the existence of a given option in a given section.
Return True if the given option is present, and False if not present.
"""
return False if section not in self else option in self[section]
[docs]
def getboolean(self, section, option):
"""Parse option as bool
Returns None is not a krb5.conf boolean string.
"""
return parse_bool(self[section][option], default=None)
[docs]
@parser(Specs.krb5)
class Krb5Configuration(Krb5ConfBase):
"""
Krb5 Configuration are ``/etc/krb5.conf`` and ``/etc/krb5.conf.d/*``.
See :class:`Krb5ConfBase` for details.
Attributes:
includedir (list): The directory list that `krb5.conf` includes via
`includedir` directive
include (list): The configuration file list that `krb5.conf` includes
via `include` directive
module (list): The module list that `krb5.conf` specifed via `module`
directive
Sample content::
include /etc/krb5test.conf
[realms]
dns_lookup_realm = false
ticket_lifetime = 24h
default_ccache_name = KEYRING:persistent:%{uid}
EXAMPLE.COM = {
kdc = kerberos.example.com
admin_server = kerberos.example.com
}
pam = {
debug = false
krb4_convert = false
ticket_lifetime = 36000
}
[libdefaults]
dns_lookup_realm = false
dnsdsd = false
ticket_lifetime = 24h
EXAMPLE.COM = {
kdc = kerberos2.example.com
admin_server = kerberos2.example.com
}
Example:
>>> type(krb5_conf)
<class 'insights.parsers.krb5.Krb5Configuration'>
>>> krb5_conf["libdefaults"]["dnsdsd"]
'false'
>>> krb5_conf["realms"]["EXAMPLE.COM"]["kdc"]
'kerberos.example.com'
>>> krb5_conf.sections()
['libdefaults', 'realms']
>>> krb5_conf.has_section("realms")
True
>>> krb5_conf.has_option("realms", "nosuchoption")
False
>>> krb5_conf.options("libdefaults")
['EXAMPLE.COM', 'dns_lookup_realm', 'dnsdsd', 'ticket_lifetime']
>>> krb5_conf.include
['/etc/krb5test.conf']
"""
RESERVED_ATTRS = ("includedir", "include", "module")
[docs]
@parser(Specs.krb5_localauth_plugin)
class Krb5LocalauthPlugin(Krb5ConfBase):
"""
Krb5 Configuration parser for `/var/lib/sss/pubconf/krb5.include.d/localauth_plugin`
Sample input::
[plugins]
localauth = {
module = sssd:/usr/lib64/sssd/modules/sssd_krb5_localauth_plugin.so
}
Examples:
>>> type(krb5_LP)
<class 'insights.parsers.krb5.Krb5LocalauthPlugin'>
>>> krb5_LP['plugins']['localauth']['module']
'sssd:/usr/lib64/sssd/modules/sssd_krb5_localauth_plugin.so'
"""
RESERVED_ATTRS = ()