1 /*
2 
3                           Firewall Builder
4 
5                  Copyright (C) 2008 NetCitadel, LLC
6 
7   Author:  Vadim Kurland     vadim@fwbuilder.org
8 
9   $Id$
10 
11   This program is free software which we release under the GNU General Public
12   License. You may redistribute and/or modify this program under the terms
13   of that license as published by the Free Software Foundation; either
14   version 2 of the License, or (at your option) any later version.
15 
16   This program is distributed in the hope that it will be useful,
17   but WITHOUT ANY WARRANTY; without even the implied warranty of
18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   GNU General Public License for more details.
20 
21   To get a copy of the GNU General Public License, write to the Free Software
22   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23 
24 */
25 
26 #include "config.h"
27 #include "global.h"
28 #include "utils.h"
29 #include "utils_no_qt.h"
30 
31 #include "CompilerDriver.h"
32 #include "Configlet.h"
33 #include "FWBSettings.h"
34 #include "FWWindow.h"
35 #include "FirewallInstaller.h"
36 #include "SSHSession.h"
37 #include "SSHUnx.h"
38 #include "instDialog.h"
39 
40 #include "fwbuilder/Resources.h"
41 #include "fwbuilder/FWObjectDatabase.h"
42 #include "fwbuilder/Firewall.h"
43 #include "fwbuilder/XMLTools.h"
44 #include "fwbuilder/Interface.h"
45 #include "fwbuilder/Management.h"
46 
47 #include <QDir>
48 #include <QFileInfo>
49 #include <QMessageBox>
50 #include <QString>
51 #include <QTextCodec>
52 #include <QTextStream>
53 #include <QTimer>
54 #include <QtDebug>
55 #include <QSettings>
56 
57 #ifndef _WIN32
58 #  include <unistd.h>     // for access(2) and getdomainname
59 #endif
60 
61 #include <errno.h>
62 #include <iostream>
63 
64 
65 using namespace std;
66 using namespace libfwbuilder;
67 
68 
parseManifestLine(const QString & line,QString * local_file_name,QString * remote_file_name,bool * main_script)69 bool FirewallInstaller::parseManifestLine(const QString &line,
70                                           QString *local_file_name,
71                                           QString *remote_file_name,
72                                           bool *main_script)
73 {
74     // generated IOS and PIX scripts use '!' as a comment which places
75     // manifest marker at offset of 1 char from the beginning of the
76     // line
77     if (line.indexOf(MANIFEST_MARKER) == -1) return false;
78 
79     if (fwbdebug)
80 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
81         qDebug("Manifest line: '%s'", line.toAscii().constData());
82 #else
83         qDebug("Manifest line: '%s'", line.toLatin1().constData());
84 #endif
85     QString workline = line.split(MANIFEST_MARKER)[1].trimmed();
86     if (workline.startsWith("*"))
87     {
88         *main_script = true;
89         workline = workline.remove(0, 1).trimmed();
90     }
91 
92     QString local, remote;
93     int i = 0;
94 
95     for(i=0; i<workline.size(); i++)
96     {
97         // qDebug() << "1: " << workline.at(i);
98 
99         if (workline.at(i) == ' ' && workline.at(i-1) != '\\')
100         {
101             i++;
102             break;
103         } else
104             local.append(workline.at(i));
105     }
106 
107     //qDebug() << "local=" << local;
108 
109     for(; i<workline.size(); i++)
110     {
111         // qDebug() << "2: " << workline.at(i);
112 
113         if (workline.at(i) == ' ' && workline.at(i-1) != '\\')
114             break;
115         else
116             remote.append(workline.at(i));
117     }
118 
119     // qDebug() << "remote=" << remote;
120 
121     *local_file_name = fwcompiler::CompilerDriver::unescapeFileName(local);
122     *remote_file_name = fwcompiler::CompilerDriver::unescapeFileName(remote);
123 
124     if (fwbdebug)
125         qDebug() << "local_name:"
126             #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
127                  << local_file_name->toAscii().constData()
128             #else
129                  << local_file_name->toLatin1().constData()
130             #endif
131                  << "remote_name:"
132             #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
133                  << remote_file_name->toAscii().constData()
134             #else
135                  << remote_file_name->toLatin1().constData()
136             #endif
137                  << "main_script:"
138                  << *main_script;
139 
140     return true;
141 }
142 
143 /*
144  * FirewallInstaller::readManifest reads manifest from the generated
145  * script and applies logic to decide the path and name of all files
146  * that will be copied to the firewall.
147 
148 Manifest format:
149 
150 # files: * local_file_name remote_file_name
151 
152 the '*' is optional and marks the "main" script, that is, the script
153 that should be executed on the firewall to activate policy. The part
154 '# files:' is manifest marker and must be reproduced just so. Parts
155 are separated by one or more spaces. Remote name is optional, if it is
156 missing, it is assumed to be equal to the local name. This provides
157 for backwards compatibility with previous versions where manifest did
158 not include remote name. Manifest can consit of multiple lines to
159 describe multiple files, although only one line can have '*'.
160 
161 Installation process is controlled by several variables that the user
162 can change in the "advanced" dialog for the firewall platform:
163 
164 Tab "Compiler":
165  - output file name
166  - script name on the firewall
167  - for PF and ipfilter additionally .conf file name on the firewall
168 
169 Tab "Installer":
170  - directory on the firewall where script should be installed
171  - command that installer should execute on the firewall
172 
173 These variables have default values if input fields are left blank
174 in the dialog as follows:
175 
176 output file name: the name of the firewall object, plus extension
177 ".fw". For PF two files are generated: <firewall>.fw and
178 <firewall>.conf; for ipfilter files <firewall>.fw, <firewall>-ipf.conf
179 and <firewall>-nat.conf are generated.
180 
181 script name on the firewall: the same as the output file name
182 
183 directory on the firewall: "/etc"
184 
185 command that installer executes to activate policy: installer runs
186 script <firewall>.fw
187 
188 If user enters alternative name in the "script name on the firewall",
189 it is used when generated script is copied to the firewall. There are
190 two input fields in the dialogs for PF and ipf where user can enter
191 alternative name for the .fw script and .conf file. The name can be
192 relative or absolute path. If it is a relative path or just a file
193 name, it is treated as a file name in the directory specified by the
194 "directory on the firewall" input field in the "Installer" tab. If the
195 name is an absolute path, the directory entered in "directory on the
196 firewall..." input field is ignored.  If user entered alternative name
197 for the script on the firewall, the command that installer should
198 execute to activate it must be entered as well. If the alternative
199 name was entered as an absolute path, activation command should take
200 this into account and use the same absolute path. The command can
201 start with "sudo " if user account used to copy and activate policy is
202 not root.
203 
204 */
205 
206 
readManifest(const QString & script,QMap<QString,QString> * all_files)207 bool FirewallInstaller::readManifest(const QString &script,
208                                      QMap<QString, QString> *all_files)
209 {
210     if (fwbdebug)
211         qDebug("FirewallInstaller::readManifest");
212 
213     // Read generated config file (cnf->script), find manifest
214     // and schedule copying of all files listed there.
215     QFile cf(script);
216     if (cf.open(QIODevice::ReadOnly ))
217     {
218         QTextStream stream(&cf);
219         QString line;
220         do
221         {
222             line = stream.readLine();
223             if (line.isNull()) break;
224             QString local_name;
225             QString remote_name;
226             bool main_script = false;
227             if (parseManifestLine(line, &local_name, &remote_name, &main_script))
228             {
229                 QFileInfo loc_file_info(local_name);
230                 if (!loc_file_info.isAbsolute())
231                 {
232                     QFileInfo cnf_file_info(cnf->script);
233                     if (cnf_file_info.isAbsolute())
234                         local_name = cnf_file_info.dir().path() + "/" + local_name;
235                 }
236 
237                 if (remote_name.isEmpty())
238                 {
239                     QFileInfo loc_file_info(local_name);
240                     remote_name = cnf->fwdir + "/" + loc_file_info.fileName();
241                 }
242 
243                 // This is the manifest line with "*", it marks the main script
244                 // we should run.
245                 if (main_script)
246                 {
247                     // Override directory variable if remote file name
248                     // is an absolute path. This is used later to
249                     // replace %FWDIR% macro
250 
251                     // Override fwbscript as well
252                     // This is used later to replace %FWSCRIPT% macro
253                     // getDestinationDir() returns corrected directory
254                     // depending on the user (root/regular) and temp install
255                     // flag setting
256 
257                     QFileInfo rem_file_info(remote_name);
258                     if (rem_file_info.isAbsolute())
259                     {
260                         cnf->fwdir = rem_file_info.dir().path();
261                         cnf->remote_script = getDestinationDir(cnf->fwdir) +
262                             rem_file_info.fileName();
263                     } else
264                     {
265                         cnf->remote_script = getDestinationDir(cnf->fwdir) +
266                             remote_name;
267                     }
268                 }
269                 (*all_files)[local_name] = remote_name;
270             }
271         } while  (!line.isNull());
272         cf.close();
273 
274         if (cnf->remote_script.isEmpty())
275         {
276             // manifest did not include line with '*'
277             cnf->remote_script = getDestinationDir(cnf->fwdir) + cnf->script;
278         }
279 
280         // Now that we have found the main script and know its
281         // location (in case user provided absolute path for the
282         // remote file name variable) we can update remote path for
283         // all files
284         QMap<QString, QString>::iterator it;
285         for (it=all_files->begin(); it!=all_files->end(); ++it)
286         {
287             QString local_name = it.key();
288             QString remote_name = it.value();
289             QFileInfo rem_file_info(remote_name);
290             if (rem_file_info.isAbsolute())
291                 (*all_files)[local_name] =
292                     getDestinationDir(rem_file_info.dir().path()) + rem_file_info.fileName();
293             else
294                 (*all_files)[local_name] =
295                     getDestinationDir(cnf->fwdir) + remote_name;
296         }
297 
298         // make sure remote_name has '/' as a separator. If the program
299         // runs on windows, QFileInfo may return path with native
300         // separators '\'
301         cnf->remote_script = QDir::fromNativeSeparators(cnf->remote_script);
302         for (it=all_files->begin(); it!=all_files->end(); ++it)
303         {
304             QString local_name = it.key();
305             (*all_files)[local_name] = QDir::fromNativeSeparators(it.value());
306         }
307         return true;
308     } else
309     {
310         QMessageBox::critical(
311             inst_dlg, "Firewall Builder",
312             tr("Generated script file %1 not found.").arg(script),
313             tr("&Continue") );
314         return false;
315     }
316 }
317 
packInstallJobsList(Firewall *)318 bool FirewallInstaller::packInstallJobsList(Firewall*)
319 {
320     return false;
321 }
322 
packSSHArgs(QStringList & args)323 void FirewallInstaller::packSSHArgs(QStringList &args)
324 {
325     QString ssh = st->getSSHPath();
326 
327     QStringList ssh_argv;
328     parseCommandLine(ssh, ssh_argv);
329 
330 #ifdef _WIN32
331     args += ssh_argv;
332 
333     /*
334      * putty ignores protocol and port specified in the session file if
335      * command line option -ssh is given.
336      *
337      * On the other hand,the sign of session usage is an empty user name,
338      * so we can check for that. If user name is empty, then putty will
339      * use current Windows account name to log in to the firewall and this
340      * is unlikely to work anyway. This seems to be a decent workaround.
341      */
342     if (ssh.contains("plink.exe", Qt::CaseInsensitive))
343     {
344         args.push_back("-ssh");
345         args.push_back("-t");
346 
347         if (cnf->putty_session.isEmpty())
348         {
349             args.push_back("-load");
350             args.push_back("fwb_session_with_keepalive");
351         }
352 
353 
354         if (!cnf->pwd.isEmpty())
355         {
356             args.push_back("-pw");
357             args.push_back(cnf->pwd);
358         }
359     }
360 
361 #else
362 
363     args.push_back(argv0.c_str());
364 
365     args.push_back("-X");   // fwbuilder works as ssh wrapper
366 
367     args += ssh_argv;
368 
369     args.push_back("-o");
370     args.push_back(QString("ServerAliveInterval=%1").arg(st->getSSHTimeout()));
371 
372     args.push_back("-t");
373     args.push_back("-t");
374 
375 #endif
376 
377     if (!cnf->sshArgs.isEmpty())
378         args += cnf->sshArgs.split(" ", QString::SkipEmptyParts);
379 
380     if (cnf->verbose) args.push_back("-v");
381 
382     if (!cnf->user.isEmpty())
383     {
384         args.push_back("-l");
385         args.push_back(cnf->user);
386     }
387 
388     if (!cnf->putty_session.isEmpty())
389     {
390         args.push_back(cnf->putty_session);
391     } else
392     {
393         args.push_back(cnf->maddr);
394     }
395 }
396 
packSCPArgs(const QString & local_name,const QString & remote_name,QStringList & args)397 void FirewallInstaller::packSCPArgs(const QString &local_name,
398                                     const QString &remote_name,
399                                     QStringList &args)
400 {
401     if (fwbdebug)
402         qDebug() << "packSCPArgs"
403                  << "local_name=" << local_name
404                  << "remote_name=" << remote_name;
405 
406 
407     QString file_with_path = getFullPath(local_name);
408     QString scp = st->getSCPPath();
409 
410     QStringList scp_argv;
411     parseCommandLine(scp, scp_argv);
412 
413     QString mgmt_addr = cnf->maddr;
414 
415     /*
416      * bug #2618686 "built-in installer can not handle ipv6 management
417      * address". If cnf->maddr is ipv6 address, it needs to be placed in
418      * [ ] for scp (otherwise scp interprets ':' as a separator between
419      * host name and port number).
420      * Note that this is only necessary for scp; ssh takes ipv6 addresses
421      * without [ ] just fine.
422      */
423 
424     try
425     {
426 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
427         InetAddr addr(AF_INET6, cnf->maddr.toAscii().constData());
428 #else
429         InetAddr addr(AF_INET6, cnf->maddr.toLatin1().constData());
430 #endif
431         if (fwbdebug)
432             qDebug("SCP will talk to the firewall using address %s ( %s )",
433 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
434                    cnf->maddr.toAscii().constData(),
435 #else
436                    cnf->maddr.toLatin1().constData(),
437 #endif
438                    addr.toString().c_str());
439         /*
440          * It looks like if cnf->maddr is a host name, then InetAddr
441          * does not fail but just creates address '::'.
442          * InetAddr throws exception if it is given ipv4 address.
443          * Only add [ ] if this is legitimate ipv6 address (not '::')
444          */
445         if (!addr.isAny())
446             mgmt_addr = '[' + cnf->maddr + ']';
447 
448     } catch(FWException &ex)
449     {
450         // Assume cnf->maddr is ipv4 or host name
451         ;
452     }
453 
454 
455 #ifdef _WIN32
456     args += scp_argv;
457 
458     /*
459      * See #1832. plink.exe and pscp.exe do not try to interpret target
460      * host name as session name if they see "-load session_name" on the
461      * command line. This means if user wants to use session, we can not
462      * load session fwb_session_with_keepalive.
463      */
464     if (scp.contains("pscp.exe", Qt::CaseInsensitive))
465     {
466         if (cnf->putty_session.isEmpty())
467         {
468             args.push_back("-load");
469             args.push_back("fwb_session_with_keepalive");
470         }
471 
472         if (!cnf->pwd.isEmpty())
473         {
474             args.push_back("-pw");
475             args.push_back(cnf->pwd);
476         }
477     }
478 
479 #else
480 
481     args.push_back(argv0.c_str());
482     args.push_back("-Y");   // fwbuilder works as scp wrapper
483 
484     args += scp_argv;
485 
486     args.push_back("-o");
487     // "3" here is the default value of ServerAliveCountMax parameter
488     // This way, overall timeout will be the same for ssh and scp
489     args.push_back(QString("ConnectTimeout=%1").arg(st->getSSHTimeout() * 3));
490 
491 #endif
492 
493     if (!cnf->scpArgs.isEmpty())
494         args += cnf->scpArgs.split(" ", QString::SkipEmptyParts);
495 
496     args.push_back("-q");
497 
498     args.push_back(file_with_path);
499 
500     /*
501      * bug #2618772: "test install" option does not work.  To fix, I
502      * put macro for the temp directory in in res/os/host_os.xml XML
503      * elements root/test/copy reg_user/test/copy. That macro
504      * is read and processed by getDestinationDir()
505      *
506      * Also note that pscp.exe supports "-l user" command line arg,
507      * but unix scp does not. Both support user@target format though
508      * and it works with sessions for pscp.exe
509      */
510 
511     QString user_spec;
512     if (!cnf->user.isEmpty()) user_spec = cnf->user + "@";
513 
514     QString target;
515 
516     if (!cnf->putty_session.isEmpty())
517         target = QString("%1%2:%3").arg(user_spec)
518             .arg(cnf->putty_session)
519             .arg(fwcompiler::CompilerDriver::escapeFileName(remote_name));
520     else
521         target = QString("%1%2:%3").arg(user_spec)
522             .arg(mgmt_addr)
523             .arg(fwcompiler::CompilerDriver::escapeFileName(remote_name));
524 
525     args.push_back(target);
526 
527     if (fwbdebug)
528         qDebug() << "args=" << args;
529 }
530 
531 /*
532  * take next job from job_list and execute it.
533  *
534  * Note that this slot is called when SSHSession emits signal
535  * sessionFinished.  This happens outside of control of the
536  * instDialog. If user clicked Cancel or Finish button (even though
537  * Finish should not be active, but still), runJobs() should not
538  * continue. Check for this condition using instDialog::isFinished()
539  *
540  */
runJobs()541 void FirewallInstaller::runJobs()
542 {
543     if (fwbdebug) qDebug("FirewallInstaller::runJobs");
544 
545     if (inst_dlg->isFinished()) return;
546 
547     if (job_list.size()==0)
548     {
549         if (fwbdebug) qDebug("FirewallInstaller::runJobs:  job list is empty");
550         QTimer::singleShot( 1000, inst_dlg, SLOT(mainLoopInstall()));
551         return;
552     }
553 
554     instJob current_job = job_list.front();
555     job_list.pop_front();
556 
557     switch (current_job.job)
558     {
559     case COPY_FILE:
560         copyFile(current_job.argument1, current_job.argument2);
561         break;
562 
563     case EXECUTE_COMMAND:
564         executeCommand(current_job.argument1);
565         break;
566 
567     case ACTIVATE_POLICY:
568         activatePolicy(current_job.argument1, current_job.argument2);
569         break;
570 
571     case RUN_EXTERNAL_SCRIPT:
572         executeExternalInstallScript(current_job.argument1, current_job.argument2);
573         break;
574     }
575 }
576 
577 /*
578  * copyFile starts background process and returns. Process object
579  * emits signal finished() which will be connected to slot
580  * commandFinished(). This slot checks termination status of the process
581  * and if it was successfull, it schedules call to runJobs()
582  */
copyFile(const QString & local_name,const QString & remote_name)583 void FirewallInstaller::copyFile(const QString &local_name, const QString &remote_name)
584 {
585     //QString platform = cnf->fwobj->getStr("platform").c_str();
586 
587 //    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("latin1"));
588 
589     QStringList args;
590     packSCPArgs(local_name, remote_name, args);
591 
592     inst_dlg->addToLog( tr("Copying %1 -> %2:%3\n")
593                     #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
594                         .arg(QString::fromUtf8(local_name.toAscii().constData()))
595                     #else
596                         .arg(QString::fromUtf8(local_name.toLatin1().constData()))
597                     #endif
598                         .arg(cnf->maddr)
599                     #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
600                         .arg(QString::fromUtf8(remote_name.toAscii().constData())));
601                     #else
602                         .arg(QString::fromUtf8(remote_name.toLatin1().constData())));
603                     #endif
604 
605     if (cnf->verbose) inst_dlg->displayCommand(args);
606     qApp->processEvents();
607 
608     // Need session for scp copy because we need to enter password
609     runSSHSession( new SSHUnx(inst_dlg,
610                               cnf->fwobj->getName().c_str(),
611                               args,
612                               cnf->pwd,
613                               "",
614                               list<string>()), true );
615 }
616 
executeExternalInstallScript(const QString & command,const QString & script_args)617 void FirewallInstaller::executeExternalInstallScript(const QString &command,
618                                                      const QString &script_args)
619 {
620     FWObjectDatabase *db = cnf->fwobj->getRoot();
621     assert(db);
622 
623     QString wdir = getFileDir( mw->getRCS()->getFileName() );
624     QStringList args;
625     //args.push_back(command.trimmed());
626 
627     args.push_back("-f");
628     args.push_back(db->getFileName().c_str());
629 
630     if (wdir!="")
631     {
632         args.push_back("-d");
633         args.push_back(wdir);
634     }
635 
636     args += script_args.trimmed().split(" ", QString::SkipEmptyParts);
637 
638     args.push_back(cnf->fwobj->getName().c_str());
639 
640     if (cnf->verbose) inst_dlg->displayCommand(args);
641     qApp->processEvents();
642 
643     inst_dlg->setUpProcessToInstall();
644     if (!inst_dlg->executeCommand(command.trimmed(), args))
645         QTimer::singleShot( 0, inst_dlg, SLOT(mainLoopInstall()));
646 }
647 
executeCommand(const QString & cmd)648 void FirewallInstaller::executeCommand(const QString &cmd)
649 {
650     QStringList args;
651     packSSHArgs(args);
652     args.push_back( cmd );
653     if (cnf->verbose) inst_dlg->displayCommand(args);
654     qApp->processEvents();
655 
656     QString path = args.at(0);
657     args.pop_front();
658 
659     inst_dlg->setUpProcessToInstall();
660     if (!inst_dlg->executeCommand(path, args))
661         QTimer::singleShot( 0, inst_dlg, SLOT(mainLoopInstall()));
662 }
663 
664 // ************************************************************************
665 
activatePolicy(const QString &,const QString &)666 void FirewallInstaller::activatePolicy(const QString&,  const QString&)
667 {
668     QTimer::singleShot( 0, this, SLOT(runJobs()));
669 }
670 
671 /*
672  * parameter intermediate: if true, then this session is part of the
673  * set required to complete install on single firewall, such as when
674  * we need to copy several files and then activate policy. If this
675  * parameter is false, the session is final and installer terminates
676  * when it finishes. This only applies to when session finishes
677  * successfully. If session finishes with an error, we always
678  * terminate installer.
679  */
runSSHSession(SSHSession * s,bool intermediate)680 void FirewallInstaller::runSSHSession(SSHSession *s, bool intermediate)
681 {
682     if (fwbdebug) qDebug("FirewallInstaller::runSSHSession()");
683 
684     session = s;
685 
686     session->setOptions(cnf);
687     session->setFWBPrompt(fwb_prompt);
688 
689     connect(session,SIGNAL(printStdout_sign(const QString&)),
690             inst_dlg, SLOT(addToLog(const QString&)));
691 
692     if (intermediate)
693         connect(session,SIGNAL(sessionFinished_sign()),
694                 this, SLOT(runJobs()));
695     else
696         connect(session,SIGNAL(sessionFinished_sign()),
697                 inst_dlg, SLOT(installerSuccess()));
698 
699     connect(session,SIGNAL(sessionFatalError_sign()),
700             inst_dlg, SLOT(installerError()));
701 
702     connect(session,SIGNAL(updateProgressBar_sign(int,bool)),
703             inst_dlg, SLOT(updateProgressBar(int,bool)));
704 
705     session->startSession();
706 }
707 
getFullPath(const QString & file)708 QString FirewallInstaller::getFullPath(const QString &file )
709 {
710     if (QDir::isRelativePath(file)) return cnf->wdir + "/" + file;
711     else return file;
712 }
713 
714 
715 /*
716  * This method builds and returns activation command
717  * This method is used for all firewall platforms but PIX
718  */
getActivationCmd()719 QString FirewallInstaller::getActivationCmd()
720 {
721     if (!cnf->activationCmd.isEmpty())
722     {
723         return cnf->activationCmd;
724     }
725 
726     FWOptions *fwopt = cnf->fwobj->getOptionsObject();
727 
728     QString configlet_name = "installer_commands_";
729     if (cnf->user=="root") configlet_name += "root";
730     else                   configlet_name += "reg_user";
731 
732     string host_os = cnf->fwobj->getStr("host_OS");
733     string os_family = Resources::os_res[host_os]->
734         getResourceStr("/FWBuilderResources/Target/family");
735 
736     // installer configlets should be different for each OS, but if
737     // some OS can use the same script, it will be placed in the file
738     // under os_family name. For example:
739     // for linksys/sveasoft configlet is in src/res/configlets/sveasoft
740     // but since linux24 and openwrt can use the same script, it is
741     // located in src/res/configlets/linux24 (openwrt.xml file defines
742     // family as "linux24")
743     Configlet configlet(host_os, os_family, configlet_name);
744     configlet.removeComments();
745     configlet.collapseEmptyStrings(true);
746 
747     // test run and rollback were deprecated in 4.2.0. On Linux, BSD
748     // and PIX rollback was implemented by rebooting firewall which is
749     // too heavy-handed and it did not work on BSD at all.
750     configlet.setVariable("test", false);
751     configlet.setVariable("run", true);
752     configlet.setVariable("with_rollback", false);
753     configlet.setVariable("no_rollback", true);
754 
755     configlet.setVariable("firewall_name",
756                           QString::fromUtf8(cnf->fwobj->getName().c_str()));
757 
758     configlet.setVariable("with_compression",  cnf->compressScript);
759     configlet.setVariable("no_compression",  ! cnf->compressScript);
760 
761     // On FreeBSD where we can generate either shell script or rc.conf
762     // file, installation commands differ.
763     //
764     // TODO: find more generic way to do this so that GUI installer does not
765     // have to be aware of the differences in generated file format.
766     configlet.setVariable("rc_conf_format",
767                           fwopt->getBool("generate_rc_conf_file"));
768     configlet.setVariable("shell_script_format",
769                           ! fwopt->getBool("generate_rc_conf_file"));
770 
771     replaceMacrosInCommand(&configlet);
772 
773     return configlet.expand().trimmed();
774 }
775 
replaceMacrosInCommand(Configlet * conf)776 void FirewallInstaller::replaceMacrosInCommand(Configlet *conf)
777 {
778 /* replace macros in activation commands:
779  *
780  * {{$fwbpromp}}   -- "magic" prompt that installer uses to detect when it is logged in
781  * {{$fwdir}}      -- directory on the firewall
782  * {{$fwscript}}   -- script name on the firewall
783  * {{$rbtimeout}}  -- rollbak timeout
784  */
785 
786 /*
787  * TODO: it does not make sense to split remote_script and then
788  * reassemble it again from the file name and cnf.fwdir. We should set
789  * variable $remote_script and use it in the configlets instead, but
790  * keep $fwbscript and $fwdir for backwards compatibility
791  */
792 
793 /*
794  * remote_script is a full path, which in case of Cisco can be
795  * something like "flash:file.fw". This means we have a problem with
796  * QFileInfo that interprets it as path:filename on Window or just
797  * file name with no directory path on Unix. As the result, fwbscript
798  * becomes just "file.fw" on Windows and stays "flash:file.fw" on
799  * Unix.
800  */
801 
802 /*
803  * TODO: there must be a better place to fill cnd.fwscript than
804  * this.  All I need to do is fill it before calling summary() and
805  * before launching installer that uses it in
806  * FirewallInstaller::replaceMacrosInCommand()
807  */
808 
809     QString fwscript = fwcompiler::CompilerDriver::escapeFileName(
810         QFileInfo(cnf->remote_script).fileName());
811 
812     if (fwscript.indexOf(":")!=-1) fwscript = fwscript.section(':', 1, 1);
813 
814     cnf->fwscript = fwscript;
815 
816     if (fwbdebug)
817     {
818         qDebug() << "Macro substitutions:";
819         qDebug() << "  $fwdir=" << cnf->fwdir;
820         qDebug() << "  cnf->script=" << cnf->script;
821         qDebug() << "  cnf->remote_script=" << cnf->remote_script;
822         qDebug() << "  $fwscript=" << cnf->fwscript;
823         qDebug() << "  $firewall_name=" << QString::fromUtf8(
824             cnf->fwobj->getName().c_str());
825     }
826 
827     conf->setVariable("fwbprompt", fwb_prompt);
828     conf->setVariable("fwdir", cnf->fwdir);
829     conf->setVariable("fwscript", cnf->fwscript);
830     conf->setVariable("firewall_name",
831                       QString::fromUtf8(cnf->fwobj->getName().c_str()));
832 }
833 
834 
835 /*
836  * Returned directory path always ends with separator ("/")
837  */
getDestinationDir(const QString & fwdir)838 QString FirewallInstaller::getDestinationDir(const QString &fwdir)
839 {
840     QString dir = fwdir;
841 
842     if (fwbdebug)
843         qDebug() << "FirewallInstaller::getDestinationDir:  "
844                  << "destination directory=" << dir
845                  << "cnf->fwdir=" << cnf->fwdir;
846 
847     if (!dir.endsWith(QDir::separator())) return dir + "/";
848     return dir;
849 }
850 
getGeneratedFileFullPath(Firewall * fw)851 QString FirewallInstaller::getGeneratedFileFullPath(Firewall *fw)
852 {
853 /* bug #1617501: "Install fails after compile". The "output file"
854  * setting that user enters in the "Compiler" tab of fw advanced
855  * dialog can be either local or absolute path.
856  */
857     QString generated_file = getGeneratedFileName(fw);
858     QFileInfo gen_file_info(generated_file);
859     if (!gen_file_info.isAbsolute())
860     {
861         QFileInfo fwb_file_info = QFileInfo(mw->getRCS()->getFileName());
862         generated_file = fwb_file_info.dir().path() + "/" + generated_file;
863     }
864     return QDir::toNativeSeparators(generated_file);
865 }
866 
getGeneratedFileName(Firewall * fw)867 QString FirewallInstaller::getGeneratedFileName(Firewall *fw)
868 {
869     FWOptions  *fwopt = fw->getOptionsObject();
870     QString generated_file;
871     QString ofname = QString::fromUtf8(fwopt->getStr("output_file").c_str()).trimmed();
872     if (!ofname.isEmpty())
873     {
874         generated_file = ofname;
875     } else
876         generated_file = QString::fromUtf8(fw->getName().c_str()) + ".fw";
877     return generated_file;
878 }
879 
terminate()880 void FirewallInstaller::terminate()
881 {
882     if (session != NULL)
883     {
884         session->terminate();
885     }
886 }
887 
888