/*
 * OSConfigurator_secuwall.cpp - secunet wall OS configurator implementation
 *
 * Copyright (c) 2008 secunet Security Networks AG
 * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger@swiss-it.ch>
 * Copyright (c) 2008 Reto Buerki <buerki@swiss-it.ch>
 *
 * This work is dual-licensed under:
 *
 * o The terms of the GNU General Public License as published by the Free
 *   Software Foundation, either version 2 of the License, or (at your option)
 *   any later version.
 *
 * o The terms of NetCitadel End User License Agreement
 */

#include "config.h"

#include "OSConfigurator_secuwall.h"

#include "fwbuilder/InetAddr.h"

#include "fwbuilder/Firewall.h"
#include "fwbuilder/FWOptions.h"
#include "fwbuilder/InetAddr.h"
#include "fwbuilder/Interface.h"
#include "fwbuilder/IPv4.h"
#include "fwbuilder/Network.h"
#include "fwbuilder/Address.h"
#include "fwbuilder/Resources.h"
#include "fwbuilder/MultiAddress.h"
#include "fwbuilder/physAddress.h"
#include "fwbuilder/Routing.h"
#include "fwbuilder/Rule.h"
#include "fwbuilder/RuleElement.h"
#include "fwbuilder/FailoverClusterGroup.h"

#include <errno.h>
#include <ctype.h>
#include <assert.h>

#include <iostream>
#include <cstring>

#include <QDir>
#include <QString>
#include <QTextStream>

using namespace libfwbuilder;
using namespace fwcompiler;
using namespace std;

static string sysconfig_dir      = "sysconfig";
static string ssh_dir            = "ssh";
static string networkscripts_dir = sysconfig_dir + "/network-scripts";
static string hosts_filename     = "hosts";
static string dns_filename       = "resolv.conf";
static string nsswitch_filename  = "nsswitch.conf";
static string mgmt_filename      = sysconfig_dir + "/management";
static string network_filename   = sysconfig_dir + "/network";
static string iface_prefix       = "ifcfg-";
static string iface_filename     = networkscripts_dir+"/"+iface_prefix;

static string fwadmin_ssh_key       = "/.ssh/id_rsa.pub";
static string fwadmin_known_hosts   = "/.ssh/known_hosts2";

string OSConfigurator_secuwall::myPlatformName()
{
    return "Secuwall";
}

OSConfigurator_secuwall::OSConfigurator_secuwall(FWObjectDatabase *_db,
                                                 Firewall *fw,
                                                 bool ipv6_policy) :
    OSConfigurator_linux24(_db, fw, ipv6_policy)
{
    s_mapIfaceTypes["ethernet"] = ETHERNET;
    s_mapIfaceTypes["bridge"]   = BRIDGE;
    s_mapIfaceTypes["bonding"]  = BONDING;
    s_mapIfaceTypes["vrrp"]     = VRRP;
    s_mapIfaceTypes["8021q"]    = VLAN;
}

/*
 * in addition to kernel parameters and other standard things
 * OSConfigurator_linux24 does, this class also creates configuration
 * directories, genrates ssh keys and generates files for
 * sysconfig-style configuration. This method is called from
 * CompilerDriver_ipt::run() and does not run in single rule compile
 * mode.
 */
void OSConfigurator_secuwall::processFirewallOptions()
{
    OSConfigurator_linux24::processFirewallOptions();

    if (!createDirStructure())
        abort("Unable to create directory structure");

    generateSSHKeys();
    generateHostsFile();
    generateDNSFile();
    generateNsswitchFile();
    generateManagementFile();
    generateNetworkFile();
    generateInterfaces();
}

int  OSConfigurator_secuwall::prolog()
{
    return OSConfigurator_linux24::prolog();
}

void OSConfigurator_secuwall::epilog()
{
    OSConfigurator_linux24::epilog();
}

bool OSConfigurator_secuwall::createDirStructure() const
{
    QDir directory;
    list<QString> dir_names;
    dir_names.push_back (QString (fw->getName().c_str()));

    QString tmp_name = fw->getName().c_str();
    tmp_name.append("/");
    tmp_name.append(ssh_dir.c_str());
    dir_names.push_back(tmp_name);

    tmp_name = fw->getName().c_str();
    tmp_name.append("/");
    tmp_name.append(sysconfig_dir.c_str());
    dir_names.push_back(tmp_name);

    tmp_name = fw->getName().c_str();
    tmp_name.append("/");
    tmp_name.append(networkscripts_dir.c_str());
    dir_names.push_back(tmp_name);

    list<QString>::const_iterator c_iter = dir_names.begin();
    for (; c_iter != dir_names.end(); c_iter++)
    {
        if (!directory.mkdir(*c_iter))
        {
            /* Check if directory already exists */
            if (!directory.exists())
            {
                cerr << "Error[" << errno << "]: " << strerror(errno);
                cerr << " '" << c_iter->toStdString() << "'"<< endl;
                return false;
            }
        }
    }

    cout << " created directory structure successfully" << endl << flush;

    return true;
}

int OSConfigurator_secuwall::generateManagementFile()
{
    FWOptions* options = fw->getOptionsObject();
    assert(options != NULL);

    QString s, mgm_ip, mgm_iface, vrrp_secret, stream_string;
    bool vrrp_master = false;

    /* Temporary storage for management file content */
    QTextStream stream (&stream_string);

    /* Management IP address */
    stream << "MGM_IP=\"";
    s = mgm_ip = options->getStr("secuwall_mgmt_mgmtaddr").c_str();
    if (s.isEmpty())
    {
        cout << " Warning: no Management IP address specified!" << endl;
    }
    stream << s << "\"" << endl;

    /* Find management & VRRP interfaces */
    FWObjectTypedChildIterator fw_ifaces = fw->findByType(Interface::TYPENAME);
    for (; fw_ifaces != fw_ifaces.end(); ++fw_ifaces)
    {
        Interface *iface = Interface::cast(*fw_ifaces);
        /* Check if it is a management interface */
        if (iface->isManagement())
        {
            /* Found management interface */
            const Address* ipAddr = iface->getAddressObject();
            if (ipAddr != NULL && ipAddr->belongs(InetAddr(mgm_ip.toStdString().c_str())))
            {
                /* Found ip address which is in same subnet as mgm_ip */
                mgm_iface = iface->getName().c_str();
            }
        }

        /* Check if it is a VRRP interface */
        FWObject *failover_group =
            iface->getFirstByType(FailoverClusterGroup::TYPENAME);
        if (failover_group)
        {
            FWOptions *failover_opts =
                FailoverClusterGroup::cast(failover_group)->getOptionsObject();
            if (failover_group->getStr("type") == "vrrp" && failover_opts != NULL)
            {
                vrrp_secret = failover_opts->getStr("vrrp_secret").c_str();
                vrrp_master = iface->getOptionsObject()->getBool("failover_master");;
            }
        }
    }

    stream << "MGM_DEV=\"";
    stream << mgm_iface << "\"" << endl;

    /* Log-Server IP address */
    stream << "LOG_IP=\"";
    stream << options->getStr("secuwall_mgmt_loggingaddr").c_str();
    stream << "\"" << endl;

    /* SNMP-Server IP address */
    stream << "SNMP_IP=\"";
    stream << options->getStr("secuwall_mgmt_snmpaddr").c_str();
    stream << "\"" << endl;

    /* SNMP Community string */
    stream << "SNMP_COM=\"";
    stream << options->getStr("secuwall_mgmt_rosnmp").c_str();
    stream << "\"" << endl;

    /* NTP-Server IP address */
    stream << "NTP_IP=\"";
    stream << options->getStr("secuwall_mgmt_ntpaddr").c_str();
    stream << "\"" << endl;

    /* /var partition */
    stream << "VARPART=\"";
    stream << options->getStr("secuwall_mgmt_varpart").c_str();
    stream << "\"" << endl;

    /* Configuration partition */
    stream << "CFGPART=\"";
    stream << options->getStr("secuwall_mgmt_confpart").c_str();
    stream << "\"" << endl;

    s.clear();
    /* Activate Nagios */
    stream << "NRPE=";
    s = options->getStr("secuwall_mgmt_nagiosaddr").c_str();
    if (!s.isEmpty())
    {
        stream << "yes";
    }
    else
    {
        stream << "no";
    }
    stream << endl;

    /* Nagios-Server IP-Address */
    stream << "NRPE_IP=\"";
    stream << s;
    stream << "\"" << endl;

    /* Activate VRRP */
    stream << "VRRPD=";
    if (options->getBool("cluster_member"))
    {
        stream << "yes";
    }
    else
    {
        stream << "no";
    }
    stream << endl;

    /* VRRP secret */
    stream << "VRRPSECRET=\"";
    stream << vrrp_secret;
    stream << "\"" << endl;

    /* VRRP Master/Slave */
    stream << "VRRPMASTER=";
    if (vrrp_master)
    {
        stream << "yes";
    }
    else
    {
        stream << "no";
    }
    stream << endl;

    /* conntrackd */
    s.clear();
    s = options->getStr("state_sync_interface").c_str();
    stream << "CONNTRACKD=";
    if (s.isEmpty())
    {
        stream << "no";
    }
    else
    {
        stream << "yes";
    }
    stream << endl;

    /* conntrack device */
    stream << "CONN_DEV=\"";
    stream << s;
    stream << "\"" << endl;

    /* Write actual management file */
    string filename = fw->getName() + "/" + mgmt_filename;
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << mgmt_filename << " successfully" << endl << flush;

    return 0;
}

/* Routes are expected to be verified & valid since this step is executed after Route policy compilation */
int OSConfigurator_secuwall::generateNetworkFile()
{
    FWOptions* options = fw->getOptionsObject();
    assert(options != NULL);

    FWObject *routes = fw->getFirstByType(Routing::TYPENAME);
    assert(routes);

    QString s, ifName, gwAddress, stream_string;

    /* Temporary storage for file content */
    QTextStream stream (&stream_string);

    /* Default route */
    RoutingRule* defaultRoute = NULL;

    /* Prepend static content */
    stream << "NETWORKING=yes" << endl;

    /* Find default route */
    FWObjectTypedChildIterator routing_rules = routes->findByType(RoutingRule::TYPENAME);
    for (; routing_rules != routing_rules.end(); ++routing_rules)
    {
        RoutingRule* route = RoutingRule::cast(*routing_rules);
        if (!route->isEmpty() && !route->isDisabled() && route->getRDst()->isAny())
        {
            defaultRoute = route;
            /* There can only be one default route, so we are done */
            break;
        }

    }

    if (defaultRoute != NULL)
    {
        RuleElementRItf* itfrel = defaultRoute->getRItf();
        RuleElementRGtw* gtwrel = defaultRoute->getRGtw();

        FWObject *oRGtw = FWReference::cast(gtwrel->front())->getPointer();
        assert(oRGtw != NULL);
        FWObject *oRItf = FWReference::cast(itfrel->front())->getPointer();
        assert(oRItf != NULL);

        /* Extract Gateway IP address */
        if (Host::cast(oRGtw) != NULL)
        {
            Host *host=Host::cast(oRGtw);
            gwAddress = host->getAddressPtr()->toString().c_str();
        }
        else if (Interface::cast(oRGtw) != NULL)
        {
            Interface *intf=Interface::cast(oRGtw);
            gwAddress = intf->getAddressPtr()->toString().c_str();
        }
        else if (Address::cast(oRGtw)->dimension()==1)
        {
            Address *ipv4 = Address::cast(oRGtw);
            gwAddress = ipv4->getAddressPtr()->toString().c_str();
        }
        /* Extract Interface name */
        ifName = oRItf->getName().c_str();
    }

    /* XXX: not setting gateway since default route will be set by routing rules */
    /* Default Gateway */
    stream << "GATEWAY=\"";
    s = gwAddress;
    {
    // stream << s;
    }
    stream << "\"" << endl;

    /* Gateway interface */
    stream << "GATEWAYDEV=\"";
    s = ifName;
    {
    // stream << s;
    }
    stream << "\"" << endl;

    /* Hostname */
    stream << "HOSTNAME=\"";
    stream << fw->getName().c_str();
    stream << "\"" << endl;

    /* Routing */
    stream << "FORWARD_IPV4=\"";
    if (options->getBool("linux24_ip_forward"))
    {
        stream << "yes";
    }
    else
    {
        stream << "no";
    }
    stream << "\"" << endl;

    /* Write actual network file */
    string filename = fw->getName() + "/" + network_filename;
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << network_filename << " successfully" << endl << flush;

    return 0;
}

int OSConfigurator_secuwall::generateHostsFile()
{
    FWOptions* options = fw->getOptionsObject();
    assert(options != NULL);

    QString s, stream_string;

    /* Temporary storage for file content */
    QTextStream stream (&stream_string);

    /* Prepend static content */
    stream << "127.0.0.1\tlocalhost\n\n# Secuwall hosts" << endl;

    /* TODO: Should entries of every fw interface address be appended?  */

    stream << options->getStr("secuwall_dns_hosts").c_str();
    stream << endl;

    /* Write actual hosts file */
    string filename = fw->getName() + "/" + hosts_filename;
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << hosts_filename << " successfully" << endl << flush;

    return 0;
}

int OSConfigurator_secuwall::generateDNSFile()
{
    FWOptions* options = fw->getOptionsObject();
    assert(options != NULL);

    QString s, stream_string;

    /* Temporary storage for file content */
    QTextStream stream (&stream_string);

    /* Search domains */
    s = options->getStr("secuwall_dns_domains").c_str();
    if (!s.isEmpty())
    {
        /* Replace \n with " " */
        s.replace(QString("\n"), QString(" "));
        stream << "search\t\t" << s << endl;
    }

    /* DNS-Server entries */
    s = options->getStr("secuwall_dns_srv1").c_str();
    if (!s.isEmpty())
        stream << "nameserver\t" << s << endl;

    s = options->getStr("secuwall_dns_srv2").c_str();
    if (!s.isEmpty())
        stream << "nameserver\t" << s << endl;

    s = options->getStr("secuwall_dns_srv3").c_str();
    if (!s.isEmpty())
        stream << "nameserver\t" << s << endl;

    /* Write actual DNS file */
    string filename = fw->getName() + "/" + dns_filename;
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << dns_filename << " successfully" << endl << flush;

    return 0;
}

int OSConfigurator_secuwall::generateNsswitchFile()
{
    FWOptions* options = fw->getOptionsObject();
    assert(options != NULL);

    QString s, stream_string;

    /* Temporary storage for file content */
    QTextStream stream(&stream_string);

    /* Prepend static content */
    stream << "passwd:\t\tfiles\nshadow:\t\tfiles\ngroup:\t\tfiles\n" << endl;

    /* hosts entries */
    stream << "hosts:\t\t";
    s = options->getStr("secuwall_dns_reso1").c_str();
    if (!s.isEmpty() && s != "none")
    {
        stream << s;
    }

    s = options->getStr("secuwall_dns_reso2").c_str();
    if (!s.isEmpty() && s != "none")
    {
        stream << " " << s;
    }

    s = options->getStr("secuwall_dns_reso3").c_str();
    if (!s.isEmpty() && s != "none")
    {
        stream << " " << s;
    }

    s = options->getStr("secuwall_dns_reso4").c_str();
    if (!s.isEmpty() && s != "none")
    {
        stream << " " << s;
    }

    s = options->getStr("secuwall_dns_reso5").c_str();
    if (!s.isEmpty() && s != "none")
    {
        stream << " " << s << endl;
    }

    stream << endl;

    /* Append static content */
    stream << "ethers:\t\tfiles\nnetmasks:\tfiles\nnetworks:\tfiles\nprotocols:\tfiles\nrpc:\t\tfiles\nservices:\tfiles\n";

    /* Write actual nsswitch file */
    string filename = fw->getName() + "/" + nsswitch_filename;
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << nsswitch_filename << " successfully" << endl << flush;

    return 0;
}

int OSConfigurator_secuwall::generateInterfaceFile (Interface * iface, IPv4 * ip_address, int iface_number)
{
    FWOptions* options = NULL;

    QString s, stream_string;

    /* Temporary storage for file content */
    QTextStream stream(&stream_string);

    if (iface->getName().find("*")==string::npos)
        options = iface->getOptionsObject();

    /* Interface name */
    stream << "DEVICE=\"" << iface->getName().c_str();
    if (iface_number > 0)
        stream << ":" << iface_number;
    stream << "\"" << endl;

    /* Boot-Protocol */
    stream << "BOOTPROTO=\"";
    if (iface->isDyn())
    {
        stream << "dhcp";
    }
    else
    {
        stream << "none";
    }
    stream << "\"" << endl;

    /* Address object contains host, network and broadcast address plus netmask */
    const Address* ipAddr;
    if (ip_address != NULL)
        ipAddr = ip_address->getAddressObject();
    else
        ipAddr = iface->getAddressObject();

    if (ipAddr != NULL)
    {
        /* Interface IP Address */
        stream << "IPADDR=\"";
        stream << ipAddr->getAddressPtr()->toString().c_str();
        stream << "\"" << endl;

        /* Netmask */
        stream << "NETMASK=\"";
        stream << ipAddr->getNetmaskPtr()->toString().c_str();
        stream << "\"" << endl;

        /* Network IP Address */
        stream << "NETWORK=\"";
        stream << ipAddr->getNetworkAddressPtr()->toString().c_str();
        stream << "\"" << endl;

        /* Broadcast IP Address */
        stream << "BROADCAST=\"";
        stream << ipAddr->getBroadcastAddressPtr()->toString().c_str();
        stream << "\"" << endl;
    }

    /* Activate on bootup */
    stream << "ONBOOT=\"";
    if (options != NULL && options->getBool("iface_disableboot"))
    {
        stream << "no";
    }
    else
    {
        stream << "yes";
    }
    stream << "\"" << endl;

    /* Link */
    stream << "LINK=\"";
    if (options != NULL)
    {
        stream << options->getStr("iface_options").c_str();
    }
    stream << "\"" << endl;

    /* MAC-Address */
    stream << "MACADDR=\"";
    physAddress* macAddr = iface->getPhysicalAddress();
    if (macAddr != NULL)
    {
        stream << macAddr->getPhysAddress().c_str();
    }
    stream << "\"" << endl;

    /* MTU */
    s.clear();
    stream << "MTU=\"";
    if (options == NULL || (s = options->getStr("iface_mtu").c_str()).isEmpty())
    {
        /* TODO: Extract magic value */
        /* Set to "sane" default: "1500" */
        s = "1500";
    }
    stream << s;
    stream << "\"" << endl;

    /* Activate ARP */
    stream << "ARP=\"";
    if (options != NULL && options->getBool("iface_disablearp"))
    {
        stream << "no";
    }
    else
    {
        stream << "yes";
    }
    stream << "\"" << endl;

    /* Interface type */
    stream << "TYPE=\"";
    if (options == NULL || (s = options->getStr("type").c_str()).isEmpty())
    {
        /* TODO: Extract magic value */
        /* Set ethernet as "sane" default  */
        s = "ethernet";
    }
    stream << s;
    stream << "\"" << endl;

    /* Type-specific parameter handling */
    switch (s_mapIfaceTypes[s.toStdString()])
    {
    case BRIDGE:
        /* Fall-through */
    case BONDING:
        /* Base Device */
        stream << "BASEDEV=\"";
        /* Iterate over all child interfaces */
        if (iface->getChildrenCount() > 0)
        {
            list<FWObject*> l3 = iface->getByType(Interface::TYPENAME);
            if (!l3.empty())
            {
                /* Treat first element seperate to avoid unnecessary whitespaces */
                list<FWObject*>::iterator i = l3.begin();
                Interface *ifaceChild = Interface::cast(*i);
                assert(ifaceChild != NULL);
                stream << ifaceChild->getName().c_str();
                i++;
                /* Process remaining child interfaces */
                for (; i != l3.end(); ++i)
                {
                    Interface *ifaceChild = Interface::cast(*i);
                    assert(ifaceChild != NULL);
                    stream << " " << ifaceChild->getName().c_str();
                }
            }
        }
        stream << "\"" << endl;
        break;
    case VLAN:
        stream << "VLANID=\"";
        if (options != NULL)
        {
            stream << options->getStr("vlan_id").c_str();
        }
        stream << "\"" << endl;
        /* Fall-through */
    case VRRP:
    {
        stream << "BASEDEV=\"";
        if (options->getStr("base_device").empty())
        {
            /* No base device provided */
            abort("No base device specified for " + iface->getName());
        }
        stream << options->getStr("base_device").c_str();
        stream << "\"" << endl;
    }
    break;
    default:
        /* Don't define BASEDEV */
        break;
    }

    if (iface_number > 0)
    {
        /* Base Device for secondary interfaces*/
        stream << "BASEDEV=\"";
        stream << iface->getName().c_str();
        stream << "\"" << endl;
    }

    /* Write actual interface file */
    string filename = fw->getName() + "/" + iface_filename + iface->getName();
    if (iface_number > 0)
    {
        stringstream tmp;
        tmp << ":" << iface_number;
        filename += tmp.str();
    }
    stringToFile(stream.string()->toStdString(), filename);
    generated_Files.push_back(filename);

    cout << " wrote " << filename << " successfully" << endl << flush;

    /* Iterate over all child interfaces and call function recursively. Skip if processing a secondary interface*/
    if (iface->getChildrenCount() > 0 && iface_number == 0)
    {
        FWObjectTypedChildIterator sub_ifaces = iface->findByType(Interface::TYPENAME);
        for (; sub_ifaces != sub_ifaces.end(); ++sub_ifaces)
        {
            Interface *ifaceChild=Interface::cast(*sub_ifaces);
            assert(ifaceChild != NULL);
            generateInterfaceFile(ifaceChild);
        }
    }
    return 0;
}

int OSConfigurator_secuwall::generateInterfaces()
{
    /* Iterate over all top-level interfaces */
    FWObjectTypedChildIterator interfaces = fw->findByType(Interface::TYPENAME);
    for (; interfaces != interfaces.end(); ++interfaces)
    {
        Interface *iface = Interface::cast(*interfaces);
        assert(iface != NULL);
        /* Determine if there are secondary interfaces (ethernet or bonding type with multiple IPs) */
        if (iface->countInetAddresses(true) > 1)
        {
            FWOptions* options = NULL;
            string iface_type;
            if (iface->getName().find("*")==string::npos)
                options = iface->getOptionsObject();

            if (options == NULL || (iface_type = options->getStr("type")).empty())
                iface_type = "ethernet";

            if (s_mapIfaceTypes[iface_type] == ETHERNET ||
                    s_mapIfaceTypes[iface_type] == BONDING)
            {
                int count = 0;
                for (FWObjectTypedChildIterator j = iface->findByType(IPv4::TYPENAME); j != j.end(); ++j)
                {
                    IPv4 *address = IPv4::cast(*j);
                    generateInterfaceFile (iface, address, count);
                    count++;
                }
            }
            else
                abort(
                    "Invalid config: Found interface of type " +
                    iface_type + " with multiple addresses");
        }
        else
            generateInterfaceFile(iface);
    }

    return 0;
}

int OSConfigurator_secuwall::stringToFile(const std::string data,
        const std::string filename,
        const QIODevice::OpenMode mode) const
{
    QFile file (QString (filename.c_str()));

    if (!file.open (mode))
    {
        cerr << "Unable to open file " << filename << endl;
    }

    qint64 byte_count = file.write (data.c_str());
    if (byte_count == -1)
    {
        cerr << "Unable to write data to file " << filename << endl;
    }
    if (data.length() != (unsigned int) byte_count)
    {
        cerr << "Unable to write all data (" << byte_count << " of " << data.length() << " bytes) to file " << filename << endl;
    }
    file.close();
    file.setPermissions (QFile::ReadOwner|QFile::WriteOwner|QFile::ReadGroup|QFile::ReadOther);

    return 0;
}

int OSConfigurator_secuwall::generateSSHKeys()
{
    int i;
    string cmd;
    QString pwd = QDir::currentPath();
    string filename;
    string hostKey_file;
    string fwadmin_keyfilename;
    QFile file (filename.c_str());

    /* TODO: Rewrite with popen for error handling */
    /* Generate RSA Keys */
    filename = fw->getName() + "/" + ssh_dir + "/ssh_host_rsa_key";
    if (!QFile::exists(filename.c_str()))
    {
        cmd = "ssh-keygen -t rsa -f " + pwd.toStdString() + "/" + filename + " -C root@" + fw->getName() + " -P \"\" 2>&1";
        i = system(cmd.c_str());
    }
    else
    {
        cout << " Found existing RSA key: skipping key generation." << endl;
    }
    generated_Files.push_back(filename);
    generated_Files.push_back(filename + ".pub");

    /* Generate DSA Keys */
    filename =  fw->getName() + "/" + ssh_dir + "/ssh_host_dsa_key";
    if (!QFile::exists(filename.c_str()))
    {
        cmd = "ssh-keygen -t dsa -f " + pwd.toStdString() + "/" + filename + " -C root@" + fw->getName() + " -P \"\" 2>&1";
        i = system(cmd.c_str());
    }
    else
    {
        cout << " Found existing DSA key: skipping key generation." << endl;
    }
    generated_Files.push_back(filename);
    generated_Files.push_back(filename + ".pub");

    /* Add RSA pub key of fwadmin to the firewall's known hosts file */
    fwadmin_keyfilename = getenv("HOME");
    fwadmin_keyfilename += fwadmin_ssh_key;
    QFile fwadmin_ssh_keyfile(fwadmin_keyfilename.c_str());
    filename = pwd.toStdString() + "/" + fw->getName() + "/" + ssh_dir + "/authorized_keys2";
    if (!QFile::exists(filename.c_str()))
    {
        if (fwadmin_ssh_keyfile.open(QIODevice::ReadOnly))
        {
            /* Temporary storage for file content */
            QString stream_string;
            QTextStream stream(&stream_string);
            stream << fwadmin_ssh_keyfile.readAll();
            fwadmin_ssh_keyfile.close();
            /* Write actual authorized_keys2 file */
            stringToFile(stream.string()->toStdString(),
                         fw->getName() + "/" + ssh_dir + "/authorized_keys2");
            generated_Files.push_back(fw->getName() + "/" + ssh_dir + "/authorized_keys2");

            QFile::setPermissions (filename.c_str(), QFile::ReadOwner|QFile::ReadGroup);
        }
        else
        {
            cout << " Unable to open " << fwadmin_keyfilename << endl;
        }
    }
    else
    {
        cout << " Found existing key authorization file: skipping addition of management key." << endl;
        generated_Files.push_back(fw->getName() + "/" + ssh_dir + "/authorized_keys2");
    }

    /* Add RSA host key of firewall to the fwadmin's known hosts file */
    string hostKey_filename = getenv("HOME");
    hostKey_filename += fwadmin_known_hosts;
    QFile host_key_file(hostKey_filename.c_str());
    bool keyPresent = false;
    /* Check if hosts key file exists */
    if (host_key_file.exists())
    {
        /* Check if key entry is present */
        host_key_file.open(QIODevice::ReadOnly);
        QTextStream known_hosts(&host_key_file);
        QString tmp;
        while(!known_hosts.atEnd())
        {
            known_hosts >> tmp;
            if (containsFirewallKey(tmp.toStdString()))
                keyPresent = true;
        }
        host_key_file.close();
    }

    if (!keyPresent)
    {
        filename = pwd.toStdString() + "/" + fw->getName() + "/" + ssh_dir + "/ssh_host_rsa_key.pub";
        QFile ssh_keyfile(filename.c_str());
        if (ssh_keyfile.open(QIODevice::ReadOnly))
        {
            /* Temporary storage for file content */
            QString stream_string;
            QTextStream stream(&stream_string);
            stream << fw->getName().c_str() << " ";
            stream << ssh_keyfile.readAll();
            ssh_keyfile.close();

            /* Append entry to authorized_keys2 file */
            stringToFile(stream.string()->toStdString(), hostKey_filename, QIODevice::Append);
        }
        else
        {
            cout << " Unable to open firewall public key" << endl;
        }
    }
    else
    {
        cout << " Found existing authorization entry: skipping addition of firewall key." << endl;
    }

    cout << " generated SSH keys successfully" << endl << flush;

    return 0;
}

/* TODO: Put in utility library */
bool OSConfigurator_secuwall::containsFirewallKey(std::string in) const
{
    string match = "root@"+fw->getName();
    if (match == in)
        return true;
    else
        return false;
}

string OSConfigurator_secuwall::printPathForAllTools(const string &)
{
    return OSConfigurator_linux24::printPathForAllTools("secuwall");
}

std::list<string> OSConfigurator_secuwall::getGeneratedFiles() const
{
    return generated_Files;
}
