/**
 * File:	modules/Security.ycp
 * Package:	Security configuration
 * Summary:	Data for the security configuration
 * Authors:	Michal Svec <msvec@suse.cz>
 *
 * $Id: Security.ycp 20827 2005-01-21 13:49:09Z jsuchome $
 */

{

module "Security";
textdomain "security";

import "PamSettings";
import "Progress";

include "security/levels.ycp";

/**
 * All security settings
 */
global map<string,string> Settings = $[
    "CONSOLE_SHUTDOWN"			: "reboot",
    "CRACKLIB_DICT_PATH"		: "/usr/lib/cracklib_dict",
    "CWD_IN_ROOT_PATH"			: "yes",
    "CWD_IN_USER_PATH"			: "yes",
    "DISPLAYMANAGER_REMOTE_ACCESS"	: "no",
    "ENABLE_SYSRQ"			: "no",
    "FAILLOG_ENAB"			: "yes",
    "FAIL_DELAY"			: "3",
    "GID_MAX"				: "60000",
    "GID_MIN"				: "1000",
    "KDM_SHUTDOWN"			: "all",
    "LASTLOG_ENAB"			: "yes",
    "OBSCURE_CHECKS_ENAB"		: "yes",
    "PASSWD_ENCRYPTION"			: "blowfish",
    "GROUP_ENCRYPTION"			: "des",
    "PASSWD_USE_CRACKLIB"		: "yes",
    "PASS_MAX_DAYS"			: "99999",
    "PASS_MIN_DAYS"			: "0",
    "PASS_MIN_LEN"			: "5",
    "PASS_WARN_AGE"			: "7",
    "PERMISSION_SECURITY"		: "secure",
    "RUN_UPDATEDB_AS"			: "nobody",
    "UID_MAX"				: "60000",
    "UID_MIN"				: "500",
    "SYSTEM_UID_MAX"			: "499",
    "SYSTEM_UID_MIN"			: "100",
    "SYSTEM_GID_MAX"			: "499",
    "SYSTEM_GID_MIN"			: "100",
    "USERADD_CMD"			: "/usr/sbin/useradd.local",
    "USERDEL_PRECMD"			: "/usr/sbin/userdel-pre.local",
    "USERDEL_POSTCMD"			: "/usr/sbin/userdel-post.local",
];

/**
 * Security settings locations
 */
map<string,list<string> > Locations = $[
    ".etc.login_defs" : [
	"FAILLOG_ENAB",
	"FAIL_DELAY",
	"GID_MAX",
	"GID_MIN",
	"LASTLOG_ENAB",
	"PASS_MAX_DAYS",
	"PASS_MIN_DAYS",
	"PASS_WARN_AGE",
	"UID_MAX",
	"UID_MIN",
	"SYSTEM_UID_MAX",
	"SYSTEM_UID_MIN",
	"SYSTEM_GID_MAX",
	"SYSTEM_GID_MIN",
	"USERADD_CMD",
	"USERDEL_PRECMD",
	"USERDEL_POSTCMD",
    ],
    ".sysconfig.suseconfig" : [
	"CWD_IN_ROOT_PATH",
	"CWD_IN_USER_PATH",
    ],
    ".sysconfig.displaymanager" : [
	"DISPLAYMANAGER_REMOTE_ACCESS",
	"KDM_SHUTDOWN",
    ],
    ".sysconfig.security" : [
	"PERMISSION_SECURITY",
    ],
    ".sysconfig.locate" : [
	"RUN_UPDATEDB_AS",
    ],
    ".sysconfig.sysctl" : [
	"ENABLE_SYSRQ",
    ],
];

/*
 * Remaining settings:
 * - CONSOLE_SHUTDOWN (/etc/inittab)
 * - PASSWD_ENCRYPTION (/etc/pam?)
 * - GROUP_ENCRYPTION FIXME cannot be set
 */

/**
 * Number of sigificant characters in the password
 */
global map PasswordMaxLengths = $[
    "des" : 8,
    "md5" : 127,
    "blowfish" : 72,
];

/**
 * List of supported password encryption ciphers
 */
list<string> Ciphers = [
    // "des",
    "md5",
    "blowfish",
];

/**
 * Abort function
 * return boolean return true if abort
 */
global block<boolean> AbortFunction = nil;

/**
 * Check for pending Abort press
 * @return true if pending abort
 */
global define boolean PollAbort() {
    return UI::PollInput() == `abort;
}

/**
 * Abort function
 * @return blah blah lahjk
 */
global define boolean Abort() {
    if(AbortFunction != nil)
	return eval(AbortFunction) == true;
    return false;
}

/**
 * Data was modified?
 */
global boolean modified = false;

global boolean proposal_valid = false;
global boolean write_only = false;


/**
 * Function which returns if the settings were modified
 * @return boolean  settings were modified
 */
global define boolean GetModified () {
    return modified;
}
/**
 * Function sets internal variable, which indicates, that any
 * settings were modified, to "true"
 */


global define void SetModified () {
    modified = true;
}


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

/**
 * Read all security settings
 * @return true on success
 */
global define boolean Read() {

    /* Security read dialog caption */
    string caption = _("Initializing Security Configuration");
    integer steps = 3;

    integer sl = 0*1000;
    sleep(sl);

    Progress::New( caption, " ", steps, [
	    /* Progress stage 1/3 */
	    _("Read security settings"),
	    /* Progress stage 2/3 */
	    _("Read inittab settings"),
	    /* Progress stage 3/3 */
	    _("Read PAM settings"),
	], [
	    /* Progress step 1/4 */
	    _("Reading security settings..."),
	    /* Progress step 2/4 */
	    _("Reading inittab settings..."),
	    /* Progress step 3/4 */
	    _("Reading PAM settings..."),
	    /* Progress step 4/4 */
	    _("Finished")
	],
	""
    );

    Settings = $[];

    /* Read security settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    mapmap(string file, list<string> vars, Locations, {
	maplist(string var, vars, {
	    string val = "";

	    string filename = nil;
	    if(issubstring(file, "sysconfig")) {
		filename = "/etc" + mergestring(splitstring(file, "."), "/");
		y2debug("filename=%1", filename);
	    }

	    if(filename == nil || SCR::Read(.target.size, filename) > 0) {
		val = (string) SCR::Read(topath(file + "." + var));
		y2debug("Reading: %1 (%2)", topath(file + "." + var), val);
	    }
	    if(val == nil) val = "";
	    Settings[var] = val;
	});
	return $[0: 0];
    });
    y2debug("Settings=%1", Settings);

    /* Read inittab settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    list inittab = SCR::Dir(.etc.inittab);
    if(contains(inittab, "ca")) {
	string ca = (string) SCR::Read(.etc.inittab.ca);
	if(issubstring(ca, "/bin/true") || issubstring(ca, "/bin/false"))
	    Settings["CONSOLE_SHUTDOWN"] = "ignore";
	else if(issubstring(ca, "reboot") || issubstring(ca, " -r"))
	    Settings["CONSOLE_SHUTDOWN"] = "reboot";
	else if(issubstring(ca, "halt") || issubstring(ca, " -h"))
	    Settings["CONSOLE_SHUTDOWN"] = "halt";
	else {
	    y2error("Unknown ca status: %1", ca);
	    Settings["CONSOLE_SHUTDOWN"] = "ignore";
	}
    }
    else
	Settings["CONSOLE_SHUTDOWN"] = "ignore";

    y2debug("Settings=%1", Settings);

    /* Read pam settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    /* pam stuff */
    list<string> pam_check_settings = PamSettings::GetValues("pam_pwcheck", "password");

    /* read the password hash settings */
    string method = PamSettings::GetDefaultValue ("CRYPT_FILES");
    if (method == nil || method == "" ||
	!contains (["des","md5","blowfish"],method))
    {
	method = PamSettings::GetHashMethod ();
    }
    if (method == "" || !contains (["des","md5","blowfish"],method))
	method = "des";
    Settings["PASSWD_ENCRYPTION"]	= method;
    Settings["GROUP_ENCRYPTION"]	= PamSettings::GetGroupHashMethod ();

    /* obscure checks */
    boolean obscure = !contains (pam_check_settings, "no_obscure_checks");
    Settings["OBSCURE_CHECKS_ENAB"] = obscure? "yes":"no";

    /* other pam_pwcheck settings */
    Settings ["PASS_MIN_LEN"] = "5";
    Settings ["PASSWD_USE_CRACKLIB"] = "no";
    foreach (string val, pam_check_settings, {
	list lval	= splitstring (val, "=");
	if (issubstring (val, "minlen"))
	    Settings ["PASS_MIN_LEN"] = lval[1]:"5";
	/* use cracklib? */
	if (issubstring (val, "cracklib")) {
	    Settings ["CRACKLIB_DICT_PATH"] = lval[1]:"/usr/lib/cracklib_dict";
	    Settings ["PASSWD_USE_CRACKLIB"] = "yes";
	}
    });

    y2debug("Settings=%1", Settings);

    /* Local permissions hack */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    string perm = Settings["PERMISSION_SECURITY"]:"";
    if(issubstring(perm, "easy")) perm = "easy";
    else if(issubstring(perm, "paranoid")) perm = "paranoid";
    else if(issubstring(perm, "secure")) perm = "secure";
    else perm = "secure";
    Settings["PERMISSION_SECURITY"] = perm;
    y2debug("Settings=%1", Settings);

    sleep(sl);
    if(Abort()) return false;
    modified = false;
    return true;
}

/**
 * Write all security settings
 * @return true on success
 */
global define boolean Write() {

    if(!modified) return true;
    y2milestone("Writing configuration");

    /* Security read dialog caption */
    string caption = _("Saving Security Configuration");
    integer steps = 4;

    integer sl = 0*1000;
    sleep(sl);

    Progress::New( caption, " ", steps, [
	    /* Progress stage 1/4 */
	    _("Write security settings"),
	    /* Progress stage 2/4 */
	    _("Write inittab settings"),
	    /* Progress stage 3/4 */
	    _("Write PAM settings"),
	    /* Progress stage 4/4 */
	    _("Run SuSEconfig"),
	], [
	    /* Progress step 1/5 */
	    _("Writing security settings..."),
	    /* Progress step 2/5 */
	    _("Writing inittab settings..."),
	    /* Progress step 3/5 */
	    _("Writing PAM settings..."),
	    /* Progress step 4/5 */
	    _("Running SuSEconfig"),
	    /* Progress step 5/5 */
	    _("Finished")
	],
	""
    );

    /* Write security settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    y2debug("Settings=%1", Settings);
    Settings["PERMISSION_SECURITY"] = Settings["PERMISSION_SECURITY"]:"" + " local";

    list<string> commitlist = [];
    mapmap(string file, list<string> vars, Locations, {
	maplist(string var, vars, {
	    string val = Settings[var]:"";
	    if(val == nil) val = "";
	    if(val != SCR::Read(topath(file + "." + var))) {
		SCR::Write(topath(file + "." + var), val);
		commitlist = (list<string>) union(commitlist, [ file ]);
	    }
	});
	return $[0: 0];
    });

    maplist(string file, commitlist, {
	SCR::Write(topath(file), nil);
    });

    /* Write inittab settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    string ca = Settings["CONSOLE_SHUTDOWN"]:"ignore";
    if(ca == "reboot")
	SCR::Write(.etc.inittab.ca, ":ctrlaltdel:/sbin/shutdown -r -t 4 now");
    else if(ca == "halt")
	SCR::Write(.etc.inittab.ca, ":ctrlaltdel:/sbin/shutdown -h -t 4 now");
    else
	SCR::Write(.etc.inittab.ca, ":ctrlaltdel:/bin/true");
    SCR::Write(.etc.inittab, nil);

    /* Write pam settings */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    /* pam stuff */
    string encr = Settings["PASSWD_ENCRYPTION"]:"blowfish";
    PamSettings::SetDefaultValue ("CRYPT_FILES", encr);

    /* use cracklib? */
    PamSettings::RemoveValue ("pam_pwcheck", "password", "use_cracklib");
    PamSettings::RemoveValue ("pam_pwcheck", "password", "cracklib");
    if(Settings["PASSWD_USE_CRACKLIB"]:"no" == "yes") {
	string crack = "cracklib";
	string pth = Settings["CRACKLIB_DICT_PATH"]:"/usr/lib/cracklib_dict";
	if (pth != "/usr/lib/cracklib_dict")
	    crack = crack + "=" + pth;
	PamSettings::AddValue ("pam_pwcheck", "password", crack);
    }

    /* save obscure checks when not defualt*/
    PamSettings::RemoveValue ("pam_pwcheck", "password", "no_obscure_checks");
    if(Settings["OBSCURE_CHECKS_ENAB"]:"yes" != "yes")
	PamSettings::AddValue ("pam_pwcheck", "password", "no_obscure_checks");

    /* save min pass length when not default */
    PamSettings::RemoveValue ("pam_pwcheck", "password", "minlen");
    if(Settings["PASS_MIN_LEN"]:"5" != "5")
	PamSettings::AddValue ("pam_pwcheck", "password",
	    sformat ("minlen=%1", Settings["PASS_MIN_LEN"]:"5"));

    PamSettings::Write (false);

    /* enable sysrq? */
    boolean sysrq = Settings["ENABLE_SYSRQ"]:"no" == "yes";
    if(sysrq)
	SCR::Execute(.target.bash, "echo 1 > /proc/sys/kernel/sysrq");
    else
	SCR::Execute(.target.bash, "echo 0 > /proc/sys/kernel/sysrq");

    /* Finish him: SuSEconfig */
    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    SCR::Execute(.target.bash, "/sbin/SuSEconfig --module xdm");
    SCR::Execute(.target.bash, "/sbin/SuSEconfig --module kde3");
    SCR::Execute(.target.bash, "/sbin/SuSEconfig --module kdm3");
    SCR::Execute(.target.bash, "/sbin/SuSEconfig --module permissions");
    SCR::Execute(.target.bash, "/sbin/SuSEconfig --module profiles");

    sleep(sl);
    if(Abort()) return false;
    Progress::NextStage();

    sleep(sl);
    if(Abort()) return false;
    modified = false;
    return true;
}

/**
 * Get all security settings from the first parameter
 * (For use by autoinstallation.)
 * @param settings The YCP structure to be imported.
 * @return boolean True on success
 */
global define boolean Import(map settings) {
    if(settings == $[]) return true;

    modified = true;
    map tmpSettings = $[];
    foreach(string k, string v, Settings, {
	if (!haskey(settings, k))
	    tmpSettings[k] = v;
	else
	    tmpSettings[k] = settings[k]:"";
    });
    Settings = (map<string,string>) eval(tmpSettings);
    return true;
}

/**
 * Dump the security settings to a single map
 * (For use by autoinstallation.)
 * @return map Dumped settings (later acceptable by Import ())
 */
global define map Export() {
    return (map) eval(Settings);
}

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

    /* Determine current settings */
    any current = `custom;
    maplist(string key, map level, Levels, {
	y2debug("%1=%2", key, level);
	if(level == Settings)
	    current = key;
    });
    y2debug("%1=%2", current, Settings);

    /* Summary text */
    string summary = _("Current Security Level: Custom settings");
    if(current != `custom)
	/* Summary text */
	summary = sformat(_("Current Security Level: %1"), LevelsNames[(string) current]:"");

    return [ summary, [] ];
}

/**
 * Create an overview table with all configured cards
 * @return table items
 */
global define list Overview() {
    return [];
}

/* EOF */
}
