ACC SHELL

Path : /proc/self/root/usr/share/YaST2/modules/
File Upload :
Current File : //proc/self/root/usr/share/YaST2/modules/Kerberos.ycp

/**
 * File:	modules/Kerberos.ycp
 * Package:	Configuration of kerberos-client
 * Summary:	Data for configuration of kerberos-client, i/o functions.
 * Authors:	Jiri Suchomel <jsuchome@suse.cz>
 *
 * $Id: Kerberos.ycp 57772 2009-06-26 13:23:16Z jsuchome $
 *
 * Representation of the configuration of kerberos-client.
 * Input and output routines.
 */

{

module "Kerberos";

textdomain "kerberos";

import "FileUtils";
import "Hostname";
import "Label";
import "Mode";
import "Package";
import "Pam";
import "Progress";
import "Report";
import "Service";
import "Stage";
import "Summary";

/**
 * Required packages for this module to operate
 */
global list<string> required_packages = ["pam_krb5", "krb5", "krb5-client"];

global boolean write_only = false;

// if pam_krb5 module is used for login
global boolean use_pam_krb = false;

// if pam_unix is in /etc/pam.d/login
boolean pam_unix_present = false;

// default realm and domain name
global string default_realm = "";
global string default_domain = "";

global string dns_default_realm = "";
global string dns_kdc		= "";

// adress of KDC (key distribution centre) server for default realm
global string kdc = "";
string admin_server = "";

// used for pkinit-nss (feature 302132)
global string trusted_servers	= "";

global string clockskew = "300";

global boolean pam_modified = false;
global boolean ssh_modified = false;
global boolean modified = false;

// advanced krb5.conf settings (pam section)
global string ticket_lifetime = "1d";
global string renew_lifetime = "1d";
global string forwardable = "true";
global string proxiable = "false";
// obsolete, do not use
global string retain_after_close = "false";
global boolean ssh_support = false;
global string minimum_uid = "1";

// deprecated at this scope: now present in ExpertSettings map
global string use_shmem	= "sshd";
global string mappings	= "";

// --krb5-ignore_unknown_principals for pam-config
global boolean ignore_unknown	= true;

// section in /etc/ssh/ssh_config file for storing krb support
string ssh_section = "*";

// packages to install (openssh etc.)
global list<string> packages = [];

// if DNS can be used for retrieving configuration data
global boolean dns_available	= false;

// if DNS is used for retrieving configuration data
global boolean dns_used		= false;

/**
  map with the settings configurable in the expert tabs
 */
global map<string,any> ExpertSettings	= $[];

// backup of original ExpertSettings
map<string,any> OrigExpertSettings	= $[];

/**
 * Data was modified?
 * @return true if modified
 */
global define boolean Modified() ``{
    y2debug("modified=%1",modified);
    return (modified || pam_modified || ssh_modified);
};


/**
 * Get all the Kerberos configuration from a map.
 * @param settings imported map
 * @return	success
 */
global define boolean Import (map settings) ``{
    use_pam_krb		= settings["pam_login", "use_kerberos"]:false;
    map client		= settings["kerberos_client"]:$[];

    default_domain	= client["default_domain"]:"";
    default_realm	= client["default_realm"]:"";
    kdc			= client["kdc_server"]:"";
    clockskew		= client["clockskew"]:clockskew;
    ssh_support		= client["ssh_support"]:false;
    ignore_unknown	= client["ignore_unknown"]:ignore_unknown;
    ticket_lifetime	= client["ticket_lifetime"]:"1d";
    renew_lifetime	= client["renew_lifetime"]:"1d";
    minimum_uid		= client["minimum_uid"]:minimum_uid;
    forwardable		= (client["forwardable"]:true) ? "true": "false";
    proxiable		= (client["proxiable"]:false) ?   "true" :  "false";
    use_shmem		= client["use_shmem"]:use_shmem;
    mappings		= client["mappings"]:"";
    trusted_servers	= client["trusted_servers"]:"";
    ExpertSettings	= client["ExpertSettings"]:$[];
    if (!haskey (ExpertSettings, "use_shmem") && haskey (client, "use_shmem"))
	ExpertSettings["use_shmem"]	= use_shmem;
    pam_modified	= true;
    modified		= true;
    ssh_modified	= true;
    return true;
}


/**
 * Dump the Kerberos settings to a map, for autoinstallation use.
 * @return map with settings
 */
global define map Export () ``{

    map export_map	= $[
	"pam_login": $[
		"use_kerberos"		: use_pam_krb,
	],
	"kerberos_client": $[
		"default_domain"	: default_domain,
		"default_realm"		: default_realm,
		"kdc_server"		: kdc,
		"clockskew"		: clockskew,
		"ssh_support"		: ssh_support,
		"ignore_unknown"	: ignore_unknown,
		"ticket_lifetime"	: ticket_lifetime,
                "renew_lifetime"	: renew_lifetime,
                "minimum_uid"		: minimum_uid,
		"forwardable"		: forwardable == "true",
                "proxiable"		: proxiable == "true",
		"ExpertSettings"	: ExpertSettings,
	]
    ];
    if (mappings != "")
	export_map["kerberos_client","mappings"]	= mappings;
    if (trusted_servers	!= "")
	export_map["kerberos_client","trusted_servers"]	= trusted_servers;
    return export_map;
}

/**
 * Reads the item values from the /etc/krb5.conf via ini agent
 * If the item doesn't exist, returns the default value
 * @param path_to_value path for agent (.etc.krb5_conf)
 * @param def_value default value for the key (path)
 * @return list string the values for given key
 */
global list<string> ReadKrb5ConfValues (path path_to_value, string def_value) ``{

    list<string> value = (list<string>) SCR::Read (path_to_value);
    if (value == nil)
        value = [def_value];
    return value;
}

/**
 * Reads the item value from the /etc/krb5.conf via ini agent
 * If the item doesn't exist, returns the default value
 * @param path_to_value path for agent (.etc.krb5_conf)
 * @param def_value default value for the key (path)
 * @return string the value
 */
global string ReadKrb5ConfValue (path path_to_value, string def_value) {

    list vals	= ReadKrb5ConfValues (path_to_value, def_value);
    return vals[0]:def_value;
}

/**
 * Deprecated variant to ReadKrb5ConfValue
 * @deprecated
 */
global string ReadFile (path path_to_value, string def_value) {

    y2warning ("This function is deprecated, use ReadKrb5ConfValue instead.");
    return ReadKrb5ConfValue (path_to_value, def_value);
}

/**
 * Write list of values for given key to /etc/krb5.conf
 * Do not write anything for empty and nil values
 * @param path_to_value path for agent (.etc.krb5_conf)
 * @param value
 * @return false when nothing was written, success of write otherwise
 */
global boolean WriteKrb5ConfValues (path path_to_value, list<string> values) {

    if (values == nil || values == [])
	return SCR::Write (path_to_value, nil);
    return SCR::Write (path_to_value, values);
}

/**
 * Write (possible) multiple values of given key to /etc/krb5.conf
 * @param values values separated by spaces
 */
global boolean WriteKrb5ConfValuesAsString (path path_to_value, string values) {

    return WriteKrb5ConfValues (path_to_value,
	filter (string val, splitstring (values, " \t"), ``(val != "")));
}
/**
 * Write the item value to /etc/krb5.conf
 * Do not write anything for empty and nil values
 * @param path_to_value path for agent (.etc.krb5_conf)
 * @param value
 * @return false when nothing was written, success of write otherwise
 */
global boolean WriteKrb5ConfValue (path path_to_value, string value) {

    if (value == nil || value == "")
	return SCR::Write (path_to_value, nil);
    return WriteKrb5ConfValues (path_to_value, [value]);
}

/**
 * Deprecated variant to WriteKrb5ConfValue
 * @deprecated
 */
global define boolean WriteFile (path path_to_value, string value) {

    y2warning ("This function is deprecated, use WriteKrb5ConfValue instead.");
    return WriteKrb5ConfValue (path_to_value, value);
}



/**
 * Reads Kerberos settings from the SCR
 * @return success
 */
global define boolean Read () ``{

    map pam_query	= Pam::Query ("krb5");
    use_pam_krb		= size (pam_query) > 0;
    if (use_pam_krb) // if krb is not enabled, ignore_unknown is true by default
	ignore_unknown	= contains (pam_query["account"]:[], "ignore_unknown_principals");

    // now read the settings from /etc/krb5.conf
    if (FileUtils::Exists ("/etc/krb5.conf"))
    {
        y2debug("krb5.conf sections: %1", SCR::Dir(.etc.krb5_conf.s));

        default_realm = ReadKrb5ConfValue(.etc.krb5_conf.v.libdefaults.default_realm,"");

        clockskew = ReadKrb5ConfValue (.etc.krb5_conf.v.libdefaults.clockskew, "300");

	if (size (default_realm) > 0)
	{
	    path realm	= add (.etc.krb5_conf.v, default_realm);
	    kdc		= mergestring (ReadKrb5ConfValues (add (realm, "kdc"),""), " ");
	    admin_server = mergestring (ReadKrb5ConfValues(add (realm, "admin_server"),""), " ");
	    default_domain = ReadKrb5ConfValue (add (realm,"default_domain"), "");
	}
	if (admin_server == kdc)
	    admin_server = ""; // we could replace it in Write in this case...

	path pam_p	= .etc.krb5_conf.v.pam;
	ticket_lifetime = ReadKrb5ConfValue (add(pam_p,"ticket_lifetime"),"1d");
	renew_lifetime	= ReadKrb5ConfValue (add (pam_p,"renew_lifetime"),"1d");
	forwardable	= ReadKrb5ConfValue (add (pam_p,"forwardable"), "true");
	proxiable	= ReadKrb5ConfValue (add (pam_p,"proxiable"), "false");
	minimum_uid	= ReadKrb5ConfValue (add (pam_p, "minimum_uid"), "1");

	foreach (string key, [ "keytab", "ccache_dir", "ccname_template",
	    "mappings", "existing_ticket", "external", "validate", "use_shmem",
	    "addressless", "debug", "debug_sensitive",
	    "initial_prompt", "subsequent_prompt", "banner"],
	{
	    string val	= ReadKrb5ConfValue (add (pam_p, key), nil);
	    if (val != nil)
		ExpertSettings[key]	= val;
	});
	if (!haskey (ExpertSettings, "use_shmem"))
	    ExpertSettings["use_shmem"]	= "sshd";
	use_shmem	= ExpertSettings["use_shmem"]:"sshd";
	if (!haskey (ExpertSettings, "external"))
	    ExpertSettings["external"]	= "sshd";

	OrigExpertSettings	= ExpertSettings;

	trusted_servers	= ReadKrb5ConfValue (.etc.krb5_conf.v.pkinit.trusted_servers, "");
    }
    else
        SCR::Execute (.target.bash, sformat("/usr/bin/touch /etc/krb5.conf"));

    // propose some good values, if there are install defaults
    if (default_domain == "")
    {
	default_domain	= Hostname::CurrentDomain ();
	// workaround for bug#393951
	if (default_domain == "" && Stage::cont ())
	{
	    map out = (map) SCR::Execute (.target.bash_output, "domainname");
	    if (out["exit"]:0 == 0)
		default_domain	= deletechars (out["stdout"]:"", "\n");
	}
    }

    // now, check DNS status (Fate 301812)
    if (FileUtils::Exists ("/usr/bin/dig") && default_domain != "" &&
	!Mode::test ())
    {
	map out = (map) SCR::Execute (.target.bash_output, sformat ("dig TXT _kerberos.%1 +short", default_domain));
	dns_default_realm = deletechars (out["stdout"]:"", "\n\"");
	if (dns_default_realm != "")
	{
	    out = (map) SCR::Execute (.target.bash_output, sformat ("dig SRV _kerberos._udp.%1 +short", default_domain));
	    list split = splitstring (deletechars (out["stdout"]:"", "\n")," ");
	    dns_kdc	= split[3]:"";
	    if (dns_kdc != "" &&
		substring (dns_kdc, size (dns_kdc) - 1, 1) == ".")
		dns_kdc	= substring (dns_kdc, 0, size (dns_kdc) - 1);
	    if (dns_kdc != "")
		dns_available = true;
	    // empty domain_realm section and no kdc entry defined and
	    // values via DNS are available -> DNS is used
	    if (kdc == "" &&
		(!contains ((list)SCR::Dir(.etc.krb5_conf.s), "domain_realm") ||
		SCR::Dir(.etc.krb5_conf.v.domain_realm) == []))
	    {
		dns_used	= true;
		kdc		= dns_kdc;
		default_realm	= dns_default_realm;
		y2milestone ("kdc by DNS %1, default_domain: %2",
		    dns_kdc, dns_default_realm);
		y2milestone ("DNS is used for Kerberos data");
	    }
	}
    }

    if ((default_realm == "" || default_realm == "MY.REALM" ||
	 default_realm == "EXAMPLE.COM") &&
        default_domain != "")
    {
        default_realm = toupper (default_domain);
    }
    if ((kdc == "" || kdc == "MY.COMPUTER" || kdc == "kerberos.example.com") &&
	FileUtils::Exists ("/usr/bin/ypwhich"))
    {
        map out = (map) SCR::Execute (.target.bash_output, "/usr/bin/ypwhich");
        kdc = deletechars (out["stdout"]:"", "\n");
    }
    if ((kdc == "" || kdc == "MY.COMPUTER" || kdc == "kerberos.example.com") &&
	FileUtils::Exists ("/usr/bin/host"))
    {
	string proposed	= "kdc." + Hostname::CurrentDomain ();
	map m = (map)SCR::Execute (.target.bash_output,
            sformat ("LANG=C /usr/bin/host %1 | /bin/grep address", proposed));
        if (m["exit"]:1 == 0)
	    kdc	= proposed;
	y2milestone ("no kdc defined, proposing: %1", kdc);
    }

    // read ssh support
    y2debug ("ssh_config sections: %1", SCR::Dir (.etc.ssh.ssh_config.s));
    string hostname = Hostname::CurrentHostname ();

    ssh_support = nil;
    foreach (string sec, SCR::Dir (.etc.ssh.ssh_config.s), ``{
	// according to ssh man page, first value is taken:
	if (ssh_support != nil)
	    return;

	list cont = SCR::Dir (add(.etc.ssh.ssh_config.v, sec));
	y2debug ("section %1 contains: %2", sec, cont);

	if ((sec == "*" || sec == hostname) &&
	   (contains (cont, "GSSAPIAuthentication") &&
	    contains (cont, "GSSAPIDelegateCredentials")))
	{
	    ssh_support =
		(SCR::Read (add (add (.etc.ssh.ssh_config.v, sec),
		    "GSSAPIAuthentication")) == "yes") &&
		(SCR::Read (add (add (.etc.ssh.ssh_config.v, sec),
		    "GSSAPIDelegateCredentials")) == "yes");
	    ssh_section = sec;
	}
    });
    if (ssh_support == nil)
	ssh_support = false;

    return true;
}


/**
 * Saves Kerberos configuration.
 * (No parameters because it is too short to abort)
 * @return true on success
 */
global define boolean Write () ``{

    boolean pam_installed = false;
    boolean ret = true;

    // dialog caption
    string caption = _("Saving Kerberos Client Configuration");

    integer no_stages = 3;
    list<string> stages =  [
	    // progress stage label
	    _("Write PAM settings"),
	    // progress stage label
	    _("Write Kerberos client settings"),
	    // progress stage label
	    _("Write OpenSSH settings"),
    ];
    list steps = [
	    // progress step label
	    _("Writing PAM settings..."),
	    // progress step label
	    _("Writing Kerberos client settings..."),
	    // progress step label
	    _("Writing OpenSSH settings..."),
	    // final progress step label
	    _("Finished")
    ];
    if (packages != [])
    {
	// progress stage label
	stages = prepend (stages, _("Install required packages"));
	// progress step label
	steps = prepend (steps, _("Installing required packages..."));
	no_stages = no_stages + 1;
    }

    Progress::New(caption, " ", no_stages, stages, steps, "");

    if (packages != [])
    {
	y2debug ("packages to install: %1", packages);
	Progress::NextStage ();
	list<string> to_install = [];
	// check if packages are avialable...
	foreach (string p, packages, ``{
	    if (Package::Available (p) == true)
		to_install = add (to_install, p);
	});
	Package::DoInstallAndRemove (to_install, []);

	packages = [];
    }

    Progress::NextStage ();

    // -- pam settings
    if (pam_modified || write_only)
    {
        if (use_pam_krb)
        {
	    Pam::Add ("krb5");
	    // If ldap is configured we need to change it to ldap-account_only
	    if (Pam::Enabled("ldap"))
		Pam::Add("ldap-account_only");
	    if (ignore_unknown)
		Pam::Add ("krb5-ignore_unknown_principals");
	    else
		Pam::Remove ("krb5-ignore_unknown_principals");
        }
        else
        {
	    // if ldap-account_only is used we need to change it to ldap
	    if (Pam::Enabled("ldap-account_only"))
		Pam::Add("ldap");
	    Pam::Remove ("krb5");
        }
    }

    // -- write to /etc/krb5.conf
    Progress::NextStage ();

    if (modified && !dns_used)
    {
	// change the default realm name
	WriteKrb5ConfValue (.etc.krb5_conf.v.libdefaults.default_realm, default_realm);

	// write the mapping domain-realm
	if (default_domain != "")
	{
	    string domain = default_domain;
	    if ( findfirstof (domain, ".") != 0)
		domain = "." + domain;
	    WriteKrb5ConfValue (add(.etc.krb5_conf.v.domain_realm,domain),
		default_realm);
	}

	WriteKrb5ConfValue (.etc.krb5_conf.v.libdefaults.clockskew, clockskew);

	if (contains (SCR::Dir(.etc.krb5_conf.s), default_realm))
	{
	    // update the default realm settings
	    WriteKrb5ConfValuesAsString (add (add (.etc.krb5_conf.v, default_realm),"kdc"),kdc);
	    if (default_domain != "" && default_domain != nil)
		WriteKrb5ConfValue (add (add (.etc.krb5_conf.v, default_realm),
		    "default_domain"), default_domain);
	    if (admin_server == "")
		// save only when the entry was mising or same as KDC
		WriteKrb5ConfValuesAsString (add (add (.etc.krb5_conf.v, default_realm),
		    "admin_server"),kdc);
	}
	else if (default_realm != "")
	{
	    // specify the type of this subsection
	    SCR::Write( add (.etc.krb5_conf.st.realms, default_realm), [1]);
	    // write the settings of the new default realm
	    WriteKrb5ConfValuesAsString (
		add (add(.etc.krb5_conf.v.realms,default_realm),"kdc"), kdc);
	    if (default_domain != "" && default_domain != nil)
		WriteKrb5ConfValue (add (add (.etc.krb5_conf.v.realms, default_realm),
		    "default_domain"), default_domain);
	    if (admin_server == "")
		WriteKrb5ConfValuesAsString (add (add (.etc.krb5_conf.v.realms, default_realm),
		    "admin_server"), kdc);
	}
    }
    if (modified)
    {
	/*
	    3. Yes, if the user chooses DNS you need to remove the domain_realm
	    section (so that the domain->realm mapping can be read through DNS)
	    and at least the complete sub-section describing the realm
	    (maybe even the whole [realms]-section).
	*/
	if (dns_used)
	{
	    y2milestone ("DNS set to use: removing domain info from krb5.conf");
	    WriteKrb5ConfValue (.etc.krb5_conf.s.domain_realm, nil);
	    WriteKrb5ConfValue (add (.etc.krb5_conf.s, default_realm), nil);
	    // write the default realm name
	    WriteKrb5ConfValue (.etc.krb5_conf.v.libdefaults.default_realm,
		default_realm);
	}

	// write advanced settings
	path pam_sect = .etc.krb5_conf.v.pam;
	if (!contains (SCR::Dir(.etc.krb5_conf.s), "pam"))
	{
	    // specify the type of new subsection
	    SCR::Write(.etc.krb5_conf.st.appdefaults.pam, [1]);
	    pam_sect = .etc.krb5_conf.v.appdefaults.pam;
	}

	WriteKrb5ConfValue (add (pam_sect, "ticket_lifetime"), ticket_lifetime);
	WriteKrb5ConfValue (add (pam_sect, "renew_lifetime"), renew_lifetime);
	WriteKrb5ConfValue (add (pam_sect, "forwardable"), forwardable);
	WriteKrb5ConfValue (add (pam_sect, "proxiable"), proxiable);
	WriteKrb5ConfValue (add (pam_sect, "minimum_uid"), minimum_uid);

	foreach (string key, any value, ExpertSettings, {
	    path pth	= add (pam_sect, key);
	    if (is (value, boolean))
	    {
		WriteKrb5ConfValue (pth, value == true ? "true" : "false");
		return;
	    }
	    // rest is string
	    if (value != "")
		WriteKrb5ConfValue (pth, (string) value);
	    // removin
	    else if (OrigExpertSettings[key]:"" != "")
		WriteKrb5ConfValue (pth, nil);
	});

	if (trusted_servers != "" &&
	    Package::Installed ("krb5-plugin-preauth-pkinit-nss"))
	{
	    path pkinit_sect	= .etc.krb5_conf.v.pkinit;
	    if (!contains (SCR::Dir(.etc.krb5_conf.s), "pkinit"))
	    {
		SCR::Write (.etc.krb5_conf.st.appdefaults.pkinit, [1]);
		pkinit_sect	= .etc.krb5_conf.v.appdefaults.pkinit;
	    }
	    WriteKrb5ConfValue (
		add (pkinit_sect, "trusted_servers"), trusted_servers);
	    if (FileUtils::Exists ("/etc/pam_pkcs11/pam_pkcs11.conf"))
	    {
		SCR::Write (add (add (.etc.pam_pkcs11_conf.v.pam_pkcs11, "mapper ms"), "domainname"), default_realm);
		SCR::Write (add (add (.etc.pam_pkcs11_conf.v.pam_pkcs11, "mapper ms"), "domainnickname"), default_domain);
		SCR::Write (.etc.pam_pkcs11_conf, nil);
	    }
	}

	// write the changes now
	SCR::Write (.etc.krb5_conf, nil);

	// unmount agent; otherwise it won't be available to read new created
	// sections (it will treat DEFAULT_REALM as a subsection of [realms])
	SCR::UnmountAgent (.etc.krb5_conf);
    }

    // -- write openssh settings
    Progress::NextStage ();

    if (ssh_modified)
    {
	string write = ssh_support ? "yes": "no";
	SCR::Write (add (add (.etc.ssh.ssh_config.v, ssh_section),
	    "GSSAPIAuthentication"), write);
	SCR::Write (add (add (.etc.ssh.ssh_config.v, ssh_section),
	    "GSSAPIDelegateCredentials"), write);
	SCR::Write (.etc.ssh.ssh_config, nil);
	y2milestone ("/etc/ssh/ssh_config modified");
    }

    // final stage
    Progress::NextStage ();

    return ret;
}

/**
 * Create a textual summary
 * @return summary of the current configuration
 */
global define list Summary() ``{

    string summary = "";
    string nc = Summary::NotConfigured ();
    // summary header
    summary = Summary::AddHeader(summary, _("PAM Login"));

    // summary item
    summary = Summary::AddLine(summary, (use_pam_krb) ? _("Use Kerberos") :
	// summary item
	_("Do Not Use Kerberos"));

    // summary header
    summary = Summary::AddHeader(summary, _("Default Realm"));
    summary = Summary::AddLine(summary, (default_realm != "") ? default_realm : nc);

    // summary header
    summary = Summary::AddHeader(summary, _("Default Domain"));
    summary = Summary::AddLine(summary, (default_domain != "") ? default_domain : nc);

    // summary header
    summary = Summary::AddHeader(summary, _("KDC Server Address"));
    summary = Summary::AddLine(summary, (kdc != "") ? kdc : nc);

    // summary header
    summary = Summary::AddHeader(summary, _("Clock Skew"));
    summary = Summary::AddLine(summary, (clockskew != "") ? clockskew : nc);

    return [ summary, [] ];
}

/**
 * Create a short textual summary
 * @return summary of the current configuration
 */
global define string ShortSummary() ``{

    string summary = "";
    string nc = Summary::NotConfigured ();
    // summary text, %1 is value
    summary = sformat(_("<b>KDC Server</b>: %1<br>"), (kdc != "") ? kdc : nc) +
    // summary text, %1 is value
    sformat (_("<b>Default Domain</b>: %1<br>"), (default_domain != "") ?
	default_domain : nc) +
    // summary text, %1 is value
    sformat (_("<b>Default Realm</b>: %1<br>"), (default_realm != "") ?
	default_realm : nc) +
    // summary text (yes/no follows)
    sformat(_("<b>Kerberos Authentication Enabled</b>: %1"), use_pam_krb ?
	// summary value
	_("Yes") :
	// summary value
	_("No"));
    if (dns_used)
	// summary line
	summary	= summary + "<br>" + _("Configuration Acquired via DNS");

    return summary;
}

/**
 * Return required packages for auto-installation
 * @return map of packages to be installed and to be removed
 */
global define map AutoPackages() {
    return ($[
	"install": required_packages,
	"remove": []
    ]);
}

/**
 * Validation function for time-related values
 */
global boolean ValidateTimeEntries (string key, string val) {
    if (!regexpmatch (val, "^([0-9]+)[dmh]$") &&
	!regexpmatch (val, "^([0-9]+)$"))
    {
	if (key == "clockskew")
	    // error popup (wrong format of entered value)
	    Report::Error (_("Clock skew is invalid.
Try again.
"));
	else
	    // error popup (wrong format of entered value)
	    Report::Error (_("Lifetime is invalid.
Try again."));
	return false;
    }
    return true;
}


/* EOF */
}

ACC SHELL 2018