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