1 /*
2  * OSConfigurator_secuwall.cpp - secunet wall OS configurator implementation
3  *
4  * Copyright (c) 2008 secunet Security Networks AG
5  * Copyright (c) 2008 Adrian-Ken Rueegsegger <rueegsegger@swiss-it.ch>
6  * Copyright (c) 2008 Reto Buerki <buerki@swiss-it.ch>
7  *
8  * This work is dual-licensed under:
9  *
10  * o The terms of the GNU General Public License as published by the Free
11  *   Software Foundation, either version 2 of the License, or (at your option)
12  *   any later version.
13  *
14  * o The terms of NetCitadel End User License Agreement
15  */
16 
17 #include "config.h"
18 
19 #include "OSConfigurator_secuwall.h"
20 
21 #include "fwbuilder/InetAddr.h"
22 
23 #include "fwbuilder/Firewall.h"
24 #include "fwbuilder/FWOptions.h"
25 #include "fwbuilder/InetAddr.h"
26 #include "fwbuilder/Interface.h"
27 #include "fwbuilder/IPv4.h"
28 #include "fwbuilder/Network.h"
29 #include "fwbuilder/Address.h"
30 #include "fwbuilder/Resources.h"
31 #include "fwbuilder/MultiAddress.h"
32 #include "fwbuilder/physAddress.h"
33 #include "fwbuilder/Routing.h"
34 #include "fwbuilder/Rule.h"
35 #include "fwbuilder/RuleElement.h"
36 #include "fwbuilder/FailoverClusterGroup.h"
37 #include "fwbuilder/Tools.h"
38 
39 #include <errno.h>
40 #include <ctype.h>
41 #include <assert.h>
42 
43 #include <iostream>
44 #include <cstring>
45 
46 #include <QDir>
47 #include <QString>
48 #include <QTextStream>
49 
50 using namespace libfwbuilder;
51 using namespace fwcompiler;
52 using namespace std;
53 
54 static string sysconfig_dir      = "sysconfig";
55 static string ssh_dir            = "ssh";
56 static string networkscripts_dir = sysconfig_dir + "/network-scripts";
57 static string hosts_filename     = "hosts";
58 static string dns_filename       = "resolv.conf";
59 static string nsswitch_filename  = "nsswitch.conf";
60 static string mgmt_filename      = sysconfig_dir + "/management";
61 static string network_filename   = sysconfig_dir + "/network";
62 static string iface_prefix       = "ifcfg-";
63 static string iface_filename     = networkscripts_dir+"/"+iface_prefix;
64 
65 static string fwadmin_ssh_key       = "/.ssh/id_rsa.pub";
66 static string fwadmin_known_hosts   = "/.ssh/known_hosts2";
67 
myPlatformName()68 string OSConfigurator_secuwall::myPlatformName()
69 {
70     return "Secuwall";
71 }
72 
OSConfigurator_secuwall(FWObjectDatabase * _db,Firewall * fw,bool ipv6_policy)73 OSConfigurator_secuwall::OSConfigurator_secuwall(FWObjectDatabase *_db,
74                                                  Firewall *fw,
75                                                  bool ipv6_policy) :
76     OSConfigurator_linux24(_db, fw, ipv6_policy)
77 {
78     s_mapIfaceTypes["ethernet"] = ETHERNET;
79     s_mapIfaceTypes["bridge"]   = BRIDGE;
80     s_mapIfaceTypes["bonding"]  = BONDING;
81     s_mapIfaceTypes["vrrp"]     = CLUSTER;
82     s_mapIfaceTypes["8021q"]    = VLAN;
83 
84     s_mapIfaceStrings[ETHERNET] = "ethernet";
85     s_mapIfaceStrings[ALIAS]    = "alias";
86     s_mapIfaceStrings[BRIDGE]   = "bridge";
87     s_mapIfaceStrings[BONDING]  = "bonding";
88     s_mapIfaceStrings[CLUSTER]  = "vrrp";
89     s_mapIfaceStrings[VLAN]     = "8021q";
90 
91     /* search all interfaces, this means here: IP endpoints in stack! */
92     list<FWObject *> fw_ifaces = fw->getByTypeDeep(Interface::TYPENAME);
93 
94     for (list<FWObject *>::iterator it = fw_ifaces.begin(); it != fw_ifaces.end(); it++)
95     {
96         Interface *iface = Interface::cast(*it);
97         assert(NULL != iface);
98         /* Check if it is a management interface */
99         if (!iface->getName().empty() && (NULL != iface->getAddressObject()))
100         {
101             m_ifaces.push_back(iface);
102         }
103     }
104 }
105 
106 /*
107  * in addition to kernel parameters and other standard things
108  * OSConfigurator_linux24 does, this class also creates configuration
109  * directories, generates ssh keys and files for sysconfig-style
110  * configuration. This method is called from CompilerDriver_ipt::run()
111  * and does not run in single rule compile mode.
112  */
processFirewallOptions()113 void OSConfigurator_secuwall::processFirewallOptions()
114 {
115     OSConfigurator_linux24::processFirewallOptions();
116 
117     if (!createDirStructure())
118         abort("Unable to create directory structure");
119 
120     FWOptions* options = fw->getOptionsObject();
121     assert(options != NULL);
122 
123     /* Do ssh key generation if not disabled. */
124     if (!options->getBool("secuwall_no_ssh_key_generation"))
125     {
126         generateSSHKeys();
127     }
128 
129     generateHostsFile();
130     generateDNSFile();
131     generateNsswitchFile();
132     generateManagementFile();
133     generateNetworkFile();
134     generateInterfaces();
135 }
136 
createDirStructure() const137 bool OSConfigurator_secuwall::createDirStructure() const
138 {
139     QDir directory;
140     list<QString> dir_names;
141 
142     dir_names.push_back (QString (fw->getName().c_str()));
143 
144     QString tmp_name = fw->getName().c_str();
145     tmp_name.append("/");
146     tmp_name.append(ssh_dir.c_str());
147     dir_names.push_back(tmp_name);
148 
149     tmp_name = fw->getName().c_str();
150     tmp_name.append("/");
151     tmp_name.append(sysconfig_dir.c_str());
152     dir_names.push_back(tmp_name);
153 
154     tmp_name = fw->getName().c_str();
155     tmp_name.append("/");
156     tmp_name.append(networkscripts_dir.c_str());
157     dir_names.push_back(tmp_name);
158 
159     list<QString>::const_iterator c_iter = dir_names.begin();
160     for (; c_iter != dir_names.end(); c_iter++)
161     {
162         if (!directory.mkdir(*c_iter))
163         {
164             /* Check if directory already exists */
165             if (!directory.exists())
166             {
167                 cerr << "Error[" << errno << "]: " << strerror(errno);
168                 cerr << " '" << c_iter->toStdString() << "'"<< endl;
169                 return false;
170             }
171         }
172     }
173 
174     cout << " created directory structure successfully" << endl << flush;
175 
176     return true;
177 }
178 
generateManagementFile()179 int OSConfigurator_secuwall::generateManagementFile()
180 {
181     FWOptions* options = fw->getOptionsObject();
182     assert(options != NULL);
183 
184     QString s, mgm_ip, vrrp_secret, stream_string, snmp_ip;
185     bool vrrp_master = false;
186     vector <string> tmp_v, mgm_iface;
187 
188     /* Temporary storage for management file content */
189     QTextStream stream (&stream_string);
190 
191     /* search Management Interfaces, note: this can be more than one */
192     for (list<Interface *>::iterator it = m_ifaces.begin(); it != m_ifaces.end(); it++)
193     {
194         /* Check if it is a management interface */
195         if ((*it)->isManagement())
196         {
197             mgm_iface.push_back((*it)->getName());
198         }
199     }
200 
201     stream << "MGM_DEV=\"";
202     stream << stringify(mgm_iface, " ").c_str();
203     stream << "\"" << endl;
204 
205     /* lookup Management IP address */
206     mgm_ip = options->getStr("secuwall_mgmt_mgmtaddr").c_str();
207 
208     if (mgm_ip.isEmpty())
209     {
210         /* This is only a warning, if the system is not managed online */
211         cout << " Warning: no Management IP address specified!" << endl;
212     }
213     else
214     {
215         if (mgm_iface.empty())
216         {
217             abort("At least one management interface is needed for Online Management!");
218         }
219         else
220         {
221             stream << "MGM_IP=\"";
222             tmp_v.clear();
223             tokenize(mgm_ip.toStdString(), tmp_v, ",");
224             stream << stringify(tmp_v, " ").c_str();
225             stream << s << "\"" << endl;
226         }
227     }
228 
229     /* Log-Server IP address */
230     stream << "LOG_IP=\"";
231     tmp_v.clear();
232     tokenize(options->getStr("secuwall_mgmt_loggingaddr"), tmp_v, ",");
233     stream << stringify(tmp_v, " ").c_str();
234     stream << "\"" << endl;
235 
236     /* SNMP-Server IP address */
237     snmp_ip = options->getStr("secuwall_mgmt_snmpaddr").c_str();
238     if (!snmp_ip.isEmpty())
239     {
240         if (mgm_iface.empty())
241         {
242             abort("At least one management interface is needed for SNMP!");
243         }
244         else
245         {
246             stream << "SNMP_IP=\"";
247             tmp_v.clear();
248             tokenize(options->getStr("secuwall_mgmt_snmpaddr"), tmp_v, ",");
249             stream << stringify(tmp_v, " ").c_str();
250             stream << "\"" << endl;
251 
252             /* SNMP Community string */
253             stream << "SNMP_COM=\"";
254             stream << options->getStr("secuwall_mgmt_rosnmp").c_str();
255             stream << "\"" << endl;
256         }
257     }
258 
259     /* NTP-Server IP address */
260     stream << "NTP_IP=\"";
261     tmp_v.clear();
262     tokenize(options->getStr("secuwall_mgmt_ntpaddr"), tmp_v, ",");
263     stream << stringify(tmp_v, " ").c_str();
264     stream << "\"" << endl;
265 
266     /* /var partition */
267     stream << "VARPART=\"";
268     stream << options->getStr("secuwall_mgmt_varpart").c_str();
269     stream << "\"" << endl;
270 
271     /* Configuration partition */
272     stream << "CFGPART=\"";
273     stream << options->getStr("secuwall_mgmt_confpart").c_str();
274     stream << "\"" << endl;
275 
276     /* Activate Nagios */
277     stream << "NRPE=";
278     s.clear();
279     s = options->getStr("secuwall_mgmt_nagiosaddr").c_str();
280     if (!s.isEmpty())
281     {
282         stream << "yes" << endl;
283 
284         /* Nagios-Server IP-Address */
285         stream << "NRPE_IP=\"";
286         tmp_v.clear();
287         tokenize(s.toStdString(), tmp_v, ",");
288         stream << stringify(tmp_v, " ").c_str();
289         stream << "\"" << endl;
290     }
291     else
292     {
293         stream << "no" << endl;
294     }
295 
296     /* VRRP interfaces */
297 
298     for (FWObjectTypedChildIterator fw_ifaces = fw->findByType(Interface::TYPENAME);
299              fw_ifaces != fw_ifaces.end(); ++fw_ifaces)
300     {
301         Interface *iface = Interface::cast(*fw_ifaces);
302         /* Check if it is a VRRP interface */
303         FWObject *failover_group =
304             iface->getFirstByType(FailoverClusterGroup::TYPENAME);
305         if (failover_group)
306         {
307             FWOptions *failover_opts =
308                 FailoverClusterGroup::cast(failover_group)->getOptionsObject();
309             if (failover_group->getStr("type") == "vrrp" && failover_opts != NULL)
310             {
311                 vrrp_secret = failover_opts->getStr("vrrp_secret").c_str();
312                 vrrp_master = iface->getOptionsObject()->getBool("failover_master");;
313             }
314         }
315     }
316 
317     /* Activate VRRP */
318     stream << "VRRPD=";
319     if (options->getBool("cluster_member"))
320     {
321         stream << "yes" << endl;
322         /* VRRP secret */
323         stream << "VRRPSECRET=\"";
324         stream << vrrp_secret;
325         stream << "\"" << endl;
326 
327         /* VRRP Master/Slave */
328         stream << "MASTER=";
329         stream << (vrrp_master ? "yes" : "no");
330         stream << endl;
331     }
332     else
333     {
334         stream << "no" << endl;
335     }
336 
337     /* conntrackd */
338     s.clear();
339     s = options->getStr("state_sync_interface").c_str();
340     stream << "CONNTRACKD=";
341     if (!s.isEmpty())
342     {
343         stream << "yes" << endl;
344         /* conntrack device */
345         stream << "CONN_DEV=\"";
346         stream << s;
347         stream << "\"" << endl;
348     }
349     else
350     {
351         stream << "no" << endl;
352     }
353 
354     /* Write actual management file */
355     string filename = fw->getName() + "/" + mgmt_filename;
356     stringToFile(stream.string()->toStdString(), filename);
357 
358     cout << " wrote " << mgmt_filename << " successfully" << endl << flush;
359 
360     return 0;
361 }
362 
363 /* Routes are expected to be verified & valid since this step is executed after Route policy compilation */
generateNetworkFile()364 int OSConfigurator_secuwall::generateNetworkFile()
365 {
366     FWOptions* options = fw->getOptionsObject();
367     assert(options != NULL);
368 
369     FWObject *routes = fw->getFirstByType(Routing::TYPENAME);
370     assert(routes);
371 
372     QString s, ifName, gwAddress, stream_string;
373 
374     /* Temporary storage for file content */
375     QTextStream stream (&stream_string);
376 
377     /* Default route */
378     RoutingRule* defaultRoute = NULL;
379 
380     /* Prepend static content */
381     stream << "NETWORKING=yes" << endl;
382 
383     /* Find default route */
384     FWObjectTypedChildIterator routing_rules = routes->findByType(RoutingRule::TYPENAME);
385     for (; routing_rules != routing_rules.end(); ++routing_rules)
386     {
387         RoutingRule* route = RoutingRule::cast(*routing_rules);
388         if (!route->isEmpty() && !route->isDisabled() && route->getRDst()->isAny())
389         {
390             defaultRoute = route;
391             /* There can only be one default route, so we are done */
392             break;
393         }
394 
395     }
396 
397     if (defaultRoute != NULL)
398     {
399         RuleElementRItf* itfrel = defaultRoute->getRItf();
400         RuleElementRGtw* gtwrel = defaultRoute->getRGtw();
401 
402         FWObject *oRGtw = FWReference::cast(gtwrel->front())->getPointer();
403         assert(oRGtw != NULL);
404         FWObject *oRItf = FWReference::cast(itfrel->front())->getPointer();
405         assert(oRItf != NULL);
406 
407         /* Extract Gateway IP address */
408         if (Host::cast(oRGtw) != NULL)
409         {
410             Host *host=Host::cast(oRGtw);
411             gwAddress = host->getAddressPtr()->toString().c_str();
412         }
413         else if (Interface::cast(oRGtw) != NULL)
414         {
415             Interface *intf=Interface::cast(oRGtw);
416             gwAddress = intf->getAddressPtr()->toString().c_str();
417         }
418         else if (Address::cast(oRGtw)->dimension()==1)
419         {
420             Address *ipv4 = Address::cast(oRGtw);
421             gwAddress = ipv4->getAddressPtr()->toString().c_str();
422         }
423         /* Extract Interface name */
424         ifName = oRItf->getName().c_str();
425     }
426 
427     /* XXX: not setting gateway since default route will be set by routing rules */
428     /* Default Gateway */
429     stream << "GATEWAY=\"";
430     s = gwAddress;
431     {
432     // stream << s;
433     }
434     stream << "\"" << endl;
435 
436     /* Gateway interface */
437     stream << "GATEWAYDEV=\"";
438     s = ifName;
439     {
440     // stream << s;
441     }
442     stream << "\"" << endl;
443 
444     /* Hostname */
445     stream << "HOSTNAME=\"";
446     stream << fw->getName().c_str();
447     stream << "\"" << endl;
448 
449     /* Routing */
450     stream << "FORWARD_IPV4=\"";
451     if (options->getBool("linux24_ip_forward"))
452     {
453         stream << "yes";
454     }
455     else
456     {
457         stream << "no";
458     }
459     stream << "\"" << endl;
460 
461     /* Write actual network file */
462     string filename = fw->getName() + "/" + network_filename;
463     stringToFile(stream.string()->toStdString(), filename);
464 
465     cout << " wrote " << network_filename << " successfully" << endl << flush;
466 
467     return 0;
468 }
469 
generateHostsFile()470 int OSConfigurator_secuwall::generateHostsFile()
471 {
472     FWOptions* options = fw->getOptionsObject();
473     assert(options != NULL);
474 
475     QString s, stream_string;
476 
477     /* Temporary storage for file content */
478     QTextStream stream (&stream_string);
479 
480     /* Prepend static content */
481     stream << "127.0.0.1\tlocalhost\n\n# Secuwall hosts" << endl;
482 
483     /* TODO: Should entries of every fw interface address be appended?  */
484 
485     stream << options->getStr("secuwall_dns_hosts").c_str();
486     stream << endl;
487 
488     /* Write actual hosts file */
489     string filename = fw->getName() + "/" + hosts_filename;
490     stringToFile(stream.string()->toStdString(), filename);
491 
492     cout << " wrote " << hosts_filename << " successfully" << endl << flush;
493 
494     return 0;
495 }
496 
generateDNSFile()497 int OSConfigurator_secuwall::generateDNSFile()
498 {
499     FWOptions* options = fw->getOptionsObject();
500     assert(options != NULL);
501 
502     QString s, stream_string;
503 
504     /* Temporary storage for file content */
505     QTextStream stream (&stream_string);
506 
507     /* Search domains */
508     s = options->getStr("secuwall_dns_domains").c_str();
509     if (!s.isEmpty())
510     {
511         /* Replace \n with " " */
512         s.replace(QString("\n"), QString(" "));
513         stream << "search\t\t" << s << endl;
514     }
515 
516     /* DNS-Server entries */
517     s = options->getStr("secuwall_dns_srv1").c_str();
518     if (!s.isEmpty())
519         stream << "nameserver\t" << s << endl;
520 
521     s = options->getStr("secuwall_dns_srv2").c_str();
522     if (!s.isEmpty())
523         stream << "nameserver\t" << s << endl;
524 
525     s = options->getStr("secuwall_dns_srv3").c_str();
526     if (!s.isEmpty())
527         stream << "nameserver\t" << s << endl;
528 
529     /* Write actual DNS file */
530     string filename = fw->getName() + "/" + dns_filename;
531     stringToFile(stream.string()->toStdString(), filename);
532 
533     cout << " wrote " << dns_filename << " successfully" << endl << flush;
534 
535     return 0;
536 }
537 
generateNsswitchFile()538 int OSConfigurator_secuwall::generateNsswitchFile()
539 {
540     FWOptions* options = fw->getOptionsObject();
541     assert(options != NULL);
542 
543     QString s, stream_string;
544 
545     /* Temporary storage for file content */
546     QTextStream stream(&stream_string);
547 
548     /* Prepend static content */
549     stream << "passwd:\t\tfiles\nshadow:\t\tfiles\ngroup:\t\tfiles\n" << endl;
550 
551     /* hosts entries */
552     stream << "hosts:\t\t";
553     s = options->getStr("secuwall_dns_reso1").c_str();
554     if (!s.isEmpty() && s != "none")
555     {
556         stream << s;
557     }
558 
559     s = options->getStr("secuwall_dns_reso2").c_str();
560     if (!s.isEmpty() && s != "none")
561     {
562         stream << " " << s;
563     }
564 
565     s = options->getStr("secuwall_dns_reso3").c_str();
566     if (!s.isEmpty() && s != "none")
567     {
568         stream << " " << s;
569     }
570 
571     s = options->getStr("secuwall_dns_reso4").c_str();
572     if (!s.isEmpty() && s != "none")
573     {
574         stream << " " << s;
575     }
576 
577     s = options->getStr("secuwall_dns_reso5").c_str();
578     if (!s.isEmpty() && s != "none")
579     {
580         stream << " " << s << endl;
581     }
582 
583     stream << endl;
584 
585     /* Append static content */
586     stream << "ethers:\t\tfiles\nnetmasks:\tfiles\nnetworks:\tfiles\nprotocols:\tfiles\nrpc:\t\tfiles\nservices:\tfiles\n";
587 
588     /* Write actual nsswitch file */
589     string filename = fw->getName() + "/" + nsswitch_filename;
590     stringToFile(stream.string()->toStdString(), filename);
591 
592     cout << " wrote " << nsswitch_filename << " successfully" << endl << flush;
593 
594     return 0;
595 }
596 
generateInterfaceFile(Interface * iface,string name,IPv4 * ip_address,int iface_number)597 int OSConfigurator_secuwall::generateInterfaceFile (Interface * iface, string name, IPv4 * ip_address, int iface_number)
598 {
599     FWOptions* options = NULL;
600     ifaceType itype = ifNotDefined;
601     QString s;
602 
603     /* Temporary storage for file content */
604     QString stream_string;
605     QTextStream stream(&stream_string);
606 
607     assert(iface != NULL);
608 
609     /* fallback for name of the interface */
610     if (name.empty())
611         name = iface->getName();
612 
613     if (name.empty())
614         abort("cannot get name for interface");
615 
616     /* determine the type of the interface */
617     if (iface->getName().find("*") == string::npos)
618         options = iface->getOptionsObject();
619 
620     if (iface_number > 0)
621     {
622         itype = ALIAS;
623     }
624     else if (options == NULL || options->getStr("type").empty())
625     {
626         itype = ETHERNET;
627     }
628     else
629     {
630         itype = s_mapIfaceTypes[options->getStr("type")];
631     }
632 
633     /* shortcut: unconfigured ethernet devices just exist, they don't need a config file */
634     if ((itype == ETHERNET) && (ip_address == NULL) && (iface->getAddressObject() == NULL))
635         return 0;
636 
637     /* Interface name */
638     stream << "DEVICE=\"";
639     stream << name.c_str();
640     if (iface_number > 0)
641         stream << ":" << iface_number;
642     stream << "\"" << endl;
643 
644     /* Boot-Protocol */
645     stream << "BOOTPROTO=\"";
646     if (iface->isDyn())
647     {
648         stream << "dhcp";
649     }
650     else
651     {
652         stream << "none";
653     }
654     stream << "\"" << endl;
655 
656     /* Address object contains host, network and broadcast address plus netmask */
657     const Address* ipAddr = NULL;
658     if (ip_address != NULL)
659         ipAddr = ip_address->getAddressObject();
660 
661     if (ipAddr != NULL)
662     {
663         /* Interface IP Address */
664         stream << "IPADDR=\"";
665         stream << ipAddr->getAddressPtr()->toString().c_str();
666         stream << "\"" << endl;
667 
668         /* Netmask */
669         stream << "NETMASK=\"";
670         stream << ipAddr->getNetmaskPtr()->toString().c_str();
671         stream << "\"" << endl;
672 
673         /* Network IP Address */
674         stream << "NETWORK=\"";
675         stream << ipAddr->getNetworkAddressPtr()->toString().c_str();
676         stream << "\"" << endl;
677 
678         /* Broadcast IP Address */
679         stream << "BROADCAST=\"";
680         stream << ipAddr->getBroadcastAddressPtr()->toString().c_str();
681         stream << "\"" << endl;
682     }
683 
684     /* Activate on bootup */
685     stream << "ONBOOT=\"";
686     if (options != NULL && options->getBool("iface_disableboot"))
687     {
688         stream << "no";
689     }
690     else
691     {
692         stream << "yes";
693     }
694     stream << "\"" << endl;
695 
696     /* Link */
697     stream << "LINK=\"";
698     if (options != NULL)
699     {
700         stream << options->getStr("iface_options").c_str();
701     }
702     stream << "\"" << endl;
703 
704     /* MAC-Address */
705     stream << "MACADDR=\"";
706     physAddress* macAddr = iface->getPhysicalAddress();
707     if (macAddr != NULL)
708     {
709         stream << macAddr->getPhysAddress().c_str();
710     }
711     stream << "\"" << endl;
712 
713     /* MTU */
714     s.clear();
715     stream << "MTU=\"";
716     if (options == NULL || (s = options->getStr("iface_mtu").c_str()).isEmpty())
717     {
718         /* TODO: Extract magic value */
719         /* Set to "sane" default: "1500" */
720         s = "1500";
721     }
722     stream << s;
723     stream << "\"" << endl;
724 
725     /* Activate ARP */
726     stream << "ARP=\"";
727     if (options != NULL && options->getBool("iface_disablearp"))
728     {
729         stream << "no";
730     }
731     else
732     {
733         stream << "yes";
734     }
735     stream << "\"" << endl;
736 
737     /* Interface type */
738     stream << "TYPE=\"";
739     stream << s_mapIfaceStrings[itype].c_str();
740     stream << "\"" << endl;
741 
742     /* get all direct children of type interface */
743     list<FWObject *> basedevs = iface->getByType(Interface::TYPENAME);
744 
745     /* Type-specific parameter handling */
746     switch (itype)
747     {
748     case BRIDGE:
749         /* Fall-through */
750     case BONDING:
751         /* Iterate over all child interfaces */
752         if (basedevs.empty())
753         {
754             abort("No base device specified for " + name);
755         }
756         else
757         {
758             vector<string> devs;
759             for (list<FWObject *>::iterator it = basedevs.begin(); it != basedevs.end(); it++)
760             {
761                 Interface *iface = Interface::cast(*it);
762                 assert(NULL != iface);
763                 if (!(iface->getName().empty()))
764                 {
765                     devs.push_back(iface->getName());
766                     generateInterfaceFile(iface);
767                 }
768             }
769 
770             /* Base Device */
771             stream << "BASEDEV=\"";
772             stream << stringify(devs," ").c_str();
773             stream << "\"" << endl;
774         }
775         break;
776 
777     case VLAN:
778         if (options == NULL || options->getStr("vlan_id").empty())
779         {
780             abort("No VLAN id specified for " + name);
781         }
782 
783         stream << "VLANID=\"";
784         stream << options->getStr("vlan_id").c_str();
785         stream << "\"" << endl;
786 
787         if (iface->getParent() == NULL || iface->getParent()->getName().empty())
788         {
789             /* No base device provided */
790             abort("No base device specified for " + name);
791         }
792 
793         stream << "BASEDEV=\"";
794         stream << iface->getParent()->getName().c_str();
795         stream << "\"" << endl;
796 
797         generateInterfaceFile(Interface::cast(iface->getParent()));
798         break;
799 
800     case CLUSTER:
801         if (options->getStr("base_device").empty())
802         {
803             /* No base device provided */
804             abort("No base device specified for " + name);
805         }
806 
807         stream << "BASEDEV=\"";
808         stream << options->getStr("base_device").c_str();
809         stream << "\"" << endl;
810         break;
811 
812     case ALIAS:
813         /* Base Device for secondary interfaces*/
814         stream << "BASEDEV=\"";
815         stream << name.c_str();
816         stream << "\"" << endl;
817         break;
818 
819     default:
820         /* Don't define BASEDEV */
821         break;
822     }
823 
824 
825     /* Write actual interface file */
826     string filename = fw->getName() + "/" + iface_filename + name;
827     if (iface_number > 0)
828     {
829         stringstream tmp;
830         tmp << ":" << iface_number;
831         filename += tmp.str();
832     }
833     stringToFile(stream.string()->toStdString(), filename);
834 
835     cout << " wrote " << filename << " successfully" << endl << flush;
836 
837     return 0;
838 }
839 
840 template <class T>
toString(const T & t)841 inline std::string toString (const T& t)
842 {
843     std::stringstream ss;
844     ss << t;
845     return ss.str();
846 }
847 
generateInterfaces()848 int OSConfigurator_secuwall::generateInterfaces()
849 {
850     /* clean up possibly stale interface files */
851     string nwdir = fw->getName() + "/" + networkscripts_dir;
852 
853     QDir d(nwdir.c_str());
854     QStringList entries = d.entryList();
855 
856     for (QStringList::ConstIterator entry=entries.begin(); entry!=entries.end(); ++entry)
857     {
858         if (*entry != "." && *entry != "..")
859         {
860             d.remove(*entry);
861         }
862     }
863 
864     int vrrp_count = 0;
865     /* Iterate over all top-level interfaces */
866     for (list<Interface *>::iterator it = m_ifaces.begin(); it != m_ifaces.end(); it++)
867     {
868         string ifname = (*it)->getName();
869         FWOptions *options = (*it)->getOptionsObject();
870 
871         /* rename handling for our vrrp "devices" */
872         if ((options != NULL) && options->getBool("cluster_interface"))
873         {
874             ifname = "vrrp" + ::toString(vrrp_count++);
875         }
876 
877         /* Iterate over all addresses */
878         FWObjectTypedChildIterator j = (*it)->findByType(IPv4::TYPENAME);
879         int count = 0;
880         for (; j != j.end(); ++j, ++count)
881         {
882             IPv4 *address = IPv4::cast(*j);
883             generateInterfaceFile (*it, ifname, address, count);
884         }
885     }
886 
887     return 0;
888 }
889 
stringToFile(const std::string data,const std::string filename,const QIODevice::OpenMode mode) const890 int OSConfigurator_secuwall::stringToFile(const std::string data,
891         const std::string filename,
892         const QIODevice::OpenMode mode) const
893 {
894     QFile file (QString (filename.c_str()));
895 
896     if (!file.open (mode))
897     {
898         cerr << "Unable to open file " << filename << endl;
899     }
900 
901     qint64 byte_count = file.write (data.c_str());
902     if (byte_count == -1)
903     {
904         cerr << "Unable to write data to file " << filename << endl;
905     }
906     if (data.length() != (unsigned int) byte_count)
907     {
908         cerr << "Unable to write all data (" << byte_count << " of " << data.length() << " bytes) to file " << filename << endl;
909     }
910     file.close();
911     file.setPermissions (QFile::ReadOwner|QFile::WriteOwner|QFile::ReadGroup|QFile::ReadOther);
912 
913     return 0;
914 }
915 
generateSSHKeys()916 int OSConfigurator_secuwall::generateSSHKeys()
917 {
918     int i;
919     string cmd;
920     QString pwd = QDir::currentPath();
921     string filename;
922     string hostKey_file;
923     string fwadmin_keyfilename;
924     QFile file (filename.c_str());
925 
926     /* TODO: Rewrite with popen for error handling */
927     /* Generate RSA Keys */
928     filename = fw->getName() + "/" + ssh_dir + "/ssh_host_rsa_key";
929     if (!QFile::exists(filename.c_str()))
930     {
931         cmd = "ssh-keygen -t rsa -b 2048 -f " + pwd.toStdString() + "/" + filename + " -C root@" + fw->getName() + " -P \"\" 2>&1";
932         i = system(cmd.c_str());
933     }
934     else
935     {
936         cout << " Found existing RSA key: skipping key generation." << endl;
937     }
938 
939     /* Generate DSA Keys */
940     filename =  fw->getName() + "/" + ssh_dir + "/ssh_host_dsa_key";
941     if (!QFile::exists(filename.c_str()))
942     {
943         cmd = "ssh-keygen -t dsa -f " + pwd.toStdString() + "/" + filename + " -C root@" + fw->getName() + " -P \"\" 2>&1";
944         i = system(cmd.c_str());
945     }
946     else
947     {
948         cout << " Found existing DSA key: skipping key generation." << endl;
949     }
950 
951     Q_UNUSED(i);
952 
953     /* Add RSA pub key of fwadmin to the firewall's known hosts file */
954     fwadmin_keyfilename = getenv("HOME");
955     fwadmin_keyfilename += fwadmin_ssh_key;
956     QFile fwadmin_ssh_keyfile(fwadmin_keyfilename.c_str());
957     filename = pwd.toStdString() + "/" + fw->getName() + "/" + ssh_dir + "/authorized_keys2";
958     if (!QFile::exists(filename.c_str()))
959     {
960         if (fwadmin_ssh_keyfile.open(QIODevice::ReadOnly))
961         {
962             /* Temporary storage for file content */
963             QString stream_string;
964             QTextStream stream(&stream_string);
965             stream << fwadmin_ssh_keyfile.readAll();
966             fwadmin_ssh_keyfile.close();
967             /* Write actual authorized_keys2 file */
968             stringToFile(stream.string()->toStdString(),
969                          fw->getName() + "/" + ssh_dir + "/authorized_keys2");
970 
971             QFile::setPermissions (filename.c_str(), QFile::ReadOwner|QFile::ReadGroup);
972         }
973         else
974         {
975             cout << " Unable to open " << fwadmin_keyfilename << endl;
976         }
977     }
978     else
979     {
980         cout << " Found existing key authorization file: skipping addition of management key." << endl;
981     }
982 
983     /* Add RSA host key of firewall to the fwadmin's known hosts file */
984     string hostKey_filename = getenv("HOME");
985     hostKey_filename += fwadmin_known_hosts;
986     QFile host_key_file(hostKey_filename.c_str());
987     bool keyPresent = false;
988     /* Check if hosts key file exists */
989     if (host_key_file.exists())
990     {
991         /* Check if key entry is present */
992         host_key_file.open(QIODevice::ReadOnly);
993         QTextStream known_hosts(&host_key_file);
994         QString tmp;
995         while(!known_hosts.atEnd())
996         {
997             known_hosts >> tmp;
998             if (containsFirewallKey(tmp.toStdString()))
999                 keyPresent = true;
1000         }
1001         host_key_file.close();
1002     }
1003 
1004     if (!keyPresent)
1005     {
1006         filename = pwd.toStdString() + "/" + fw->getName() + "/" + ssh_dir + "/ssh_host_rsa_key.pub";
1007         QFile ssh_keyfile(filename.c_str());
1008         if (ssh_keyfile.open(QIODevice::ReadOnly))
1009         {
1010             /* Temporary storage for file content */
1011             QString stream_string;
1012             QTextStream stream(&stream_string);
1013             stream << fw->getName().c_str() << " ";
1014             stream << ssh_keyfile.readAll();
1015             ssh_keyfile.close();
1016 
1017             /* Append entry to authorized_keys2 file */
1018             stringToFile(stream.string()->toStdString(), hostKey_filename, QIODevice::Append);
1019         }
1020         else
1021         {
1022             cout << " Unable to open firewall public key" << endl;
1023         }
1024     }
1025     else
1026     {
1027         cout << " Found existing authorization entry: skipping addition of firewall key." << endl;
1028     }
1029 
1030     cout << " generated SSH keys successfully" << endl << flush;
1031 
1032     return 0;
1033 }
1034 
1035 /* TODO: Put in utility library */
containsFirewallKey(string in) const1036 bool OSConfigurator_secuwall::containsFirewallKey(string in) const
1037 {
1038     string match = "root@"+fw->getName();
1039     if (match == in)
1040         return true;
1041     else
1042         return false;
1043 }
1044 
printPathForAllTools(const string &)1045 string OSConfigurator_secuwall::printPathForAllTools(const string &)
1046 {
1047     return OSConfigurator_linux24::printPathForAllTools("secuwall");
1048 }
1049 
getGeneratedFiles() const1050 map<string, string> OSConfigurator_secuwall::getGeneratedFiles() const
1051 {
1052     map<string, string> files;
1053     return files;
1054 }
1055