Codebase list firetools / 776b108
New upstream version 0.9.58 Reiner Herrmann 5 years ago
10 changed file(s) with 666 addition(s) and 329 deletion(s). Raw diff Collapse all Expand all
55 Documentation and support: http://firejail.wordpress.com
66 License: GPL v2 or later
77
8 Setting up a compilation environment on Debian/Ubuntu:
9 $ sudo apt-get install build-essential qt5-default qt5-qmake qtbase5-dev-tools libqt5svg5 git
8 Setting up a compilation environment:
9 (Debian/Ubuntu)$ sudo apt-get install build-essential qt5-default qt5-qmake qtbase5-dev-tools libqt5svg5 git
10 (CentOS 7)$ sudo yum install gcc-c++ qt5-qtbase-devel qt5-qtsvg.x86_64 git
1011
1112 Compile and install:
1213 $ git clone https://github.com/netblue30/firetools
1314 $ cd firetools
14 $ ./configure --prefix=/usr && make && sudo make install
15 On CentOS 7 use "./configure --with-qmake=/usr/lib64/qt4/bin/qmake"
15 (Debian/Ubuntu)$ ./configure --prefix=/usr && make && sudo make install
16 (CentOS 7)$ ./configure --prefix=/usr --with-qmake=/usr/lib64/qt5/bin/qmake && make && sudo make install
17
18
19 Maintainer:
20 - netblue30 (netblue30@yahoo.com)
21
22 Committers:
23 - Fred-Barclay (https://github.com/Fred-Barclay)
24 - Reiner Herrmann (https://github.com/reinerh - Debian/Ubuntu maintainer)
25 - smithsohu (https://github.com/smitsohu)
26 - startx2017 (https://github.com/startx2017) - LTS and *bugfixes branches maintainer)
27 - netblue30 (netblue30@yahoo.com)
1628
1729 Firetools Authors:
1830
2537 - improve visual style
2638 - reduce margins, improve usability
2739 - add signal-desktop icon
40 dmio (https://github.com/dmio)
41 - detect and switch qt versions with autotools
42 helmutg (https://github.com/helmutg)
43 - Use correct host-prefixed qmake to allow cross-building
44 Piraty (https://github.com/Piraty)
45 - use system's icons when available
2846 Reiner Herrmann
2947 - Debian and Ubuntu integration
3048 - various fixes
49 Topi Miettinen (https://github.com/topimiettinen)
50 - change labels to black to be visible with dark themes and various other fixes
3151 Warren Togami (https://github.com/wtogami)
3252 - rewrite of mkrpm.sh, Fedora packaging cleanup
33 Piraty (https://github.com/Piraty)
34 - use system's icons when available
35 Topi Miettinen (https://github.com/topimiettinen)
36 - change labels to black to be visible with dark themes and various other fixes
37 dmio (https://github.com/dmio)
38 - detect and switch qt versions with autotools
3953
4054 Terminal icon (gnome-terminal.png) taken from Gnome project, license LGPL v3 or CC BY-SA 3.0.
4155
0 firetools (0.9.58) baseline; urgency=low
1 * split network interface stats in a separate window
2 * detect --net=none in network interface stats
3 * support for Firejail LTS versions
4 * bugfixes
5 -- netblue30 <netblue30@yahoo.com> Thu, 24 Jan 2019 08:00:00 -0500
6
07 firetools (0.9.52) baseline; urgency=low
18 * modif: moving to a grayscale color scheme
29 * feature: firewall support in stats window
00 #! /bin/sh
11 # Guess values for system-dependent variables and create Makefiles.
2 # Generated by GNU Autoconf 2.69 for firetools 0.9.52.
2 # Generated by GNU Autoconf 2.69 for firetools 0.9.58.
33 #
44 # Report bugs to <netblue30@yahoo.com>.
55 #
579579 # Identity of this package.
580580 PACKAGE_NAME='firetools'
581581 PACKAGE_TARNAME='firetools'
582 PACKAGE_VERSION='0.9.52'
583 PACKAGE_STRING='firetools 0.9.52'
582 PACKAGE_VERSION='0.9.58'
583 PACKAGE_STRING='firetools 0.9.58'
584584 PACKAGE_BUGREPORT='netblue30@yahoo.com'
585585 PACKAGE_URL='http://firejail.wordpress.com'
586586
623623
624624 ac_subst_vars='LTLIBOBJS
625625 LIBOBJS
626 QMAKE
626627 HAVE_FATAL_WARNINGS
627628 EGREP
628629 GREP
12491250 # Omit some internal or obsolete options to make the list less imposing.
12501251 # This message is too long to be a string in the A/UX 3.1 sh.
12511252 cat <<_ACEOF
1252 \`configure' configures firetools 0.9.52 to adapt to many kinds of systems.
1253 \`configure' configures firetools 0.9.58 to adapt to many kinds of systems.
12531254
12541255 Usage: $0 [OPTION]... [VAR=VALUE]...
12551256
13111312
13121313 if test -n "$ac_init_help"; then
13131314 case $ac_init_help in
1314 short | recursive ) echo "Configuration of firetools 0.9.52:";;
1315 short | recursive ) echo "Configuration of firetools 0.9.58:";;
13151316 esac
13161317 cat <<\_ACEOF
13171318
14051406 test -n "$ac_init_help" && exit $ac_status
14061407 if $ac_init_version; then
14071408 cat <<\_ACEOF
1408 firetools configure 0.9.52
1409 firetools configure 0.9.58
14091410 generated by GNU Autoconf 2.69
14101411
14111412 Copyright (C) 2012 Free Software Foundation, Inc.
17451746 This file contains any messages produced by compilers while
17461747 running configure, to aid debugging if configure makes a mistake.
17471748
1748 It was created by firetools $as_me 0.9.52, which was
1749 It was created by firetools $as_me 0.9.58, which was
17491750 generated by GNU Autoconf 2.69. Invocation command line was
17501751
17511752 $ $0 $@
38433844
38443845 else
38453846
3846 QMAKE=`which qmake`
3847 if test -n "$ac_tool_prefix"; then
3848 # Extract the first word of "${ac_tool_prefix}qmake", so it can be a program name with args.
3849 set dummy ${ac_tool_prefix}qmake; ac_word=$2
3850 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3851 $as_echo_n "checking for $ac_word... " >&6; }
3852 if ${ac_cv_path_QMAKE+:} false; then :
3853 $as_echo_n "(cached) " >&6
3854 else
3855 case $QMAKE in
3856 [\\/]* | ?:[\\/]*)
3857 ac_cv_path_QMAKE="$QMAKE" # Let the user override the test with a path.
3858 ;;
3859 *)
3860 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3861 for as_dir in $PATH
3862 do
3863 IFS=$as_save_IFS
3864 test -z "$as_dir" && as_dir=.
3865 for ac_exec_ext in '' $ac_executable_extensions; do
3866 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3867 ac_cv_path_QMAKE="$as_dir/$ac_word$ac_exec_ext"
3868 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3869 break 2
3870 fi
3871 done
3872 done
3873 IFS=$as_save_IFS
3874
3875 ;;
3876 esac
3877 fi
3878 QMAKE=$ac_cv_path_QMAKE
3879 if test -n "$QMAKE"; then
3880 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5
3881 $as_echo "$QMAKE" >&6; }
3882 else
3883 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3884 $as_echo "no" >&6; }
3885 fi
3886
3887
3888 fi
3889 if test -z "$ac_cv_path_QMAKE"; then
3890 ac_pt_QMAKE=$QMAKE
3891 # Extract the first word of "qmake", so it can be a program name with args.
3892 set dummy qmake; ac_word=$2
3893 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
3894 $as_echo_n "checking for $ac_word... " >&6; }
3895 if ${ac_cv_path_ac_pt_QMAKE+:} false; then :
3896 $as_echo_n "(cached) " >&6
3897 else
3898 case $ac_pt_QMAKE in
3899 [\\/]* | ?:[\\/]*)
3900 ac_cv_path_ac_pt_QMAKE="$ac_pt_QMAKE" # Let the user override the test with a path.
3901 ;;
3902 *)
3903 as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
3904 for as_dir in $PATH
3905 do
3906 IFS=$as_save_IFS
3907 test -z "$as_dir" && as_dir=.
3908 for ac_exec_ext in '' $ac_executable_extensions; do
3909 if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
3910 ac_cv_path_ac_pt_QMAKE="$as_dir/$ac_word$ac_exec_ext"
3911 $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
3912 break 2
3913 fi
3914 done
3915 done
3916 IFS=$as_save_IFS
3917
3918 ;;
3919 esac
3920 fi
3921 ac_pt_QMAKE=$ac_cv_path_ac_pt_QMAKE
3922 if test -n "$ac_pt_QMAKE"; then
3923 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_QMAKE" >&5
3924 $as_echo "$ac_pt_QMAKE" >&6; }
3925 else
3926 { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
3927 $as_echo "no" >&6; }
3928 fi
3929
3930 if test "x$ac_pt_QMAKE" = x; then
3931 QMAKE=""
3932 else
3933 case $cross_compiling:$ac_tool_warned in
3934 yes:)
3935 { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
3936 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
3937 ac_tool_warned=yes ;;
3938 esac
3939 QMAKE=$ac_pt_QMAKE
3940 fi
3941 else
3942 QMAKE="$ac_cv_path_QMAKE"
3943 fi
3944
38473945
38483946
38493947 fi
44184516 # report actual input values of CONFIG_FILES etc. instead of their
44194517 # values after options handling.
44204518 ac_log="
4421 This file was extended by firetools $as_me 0.9.52, which was
4519 This file was extended by firetools $as_me 0.9.58, which was
44224520 generated by GNU Autoconf 2.69. Invocation command line was
44234521
44244522 CONFIG_FILES = $CONFIG_FILES
44724570 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
44734571 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
44744572 ac_cs_version="\\
4475 firetools config.status 0.9.52
4573 firetools config.status 0.9.58
44764574 configured by $0, generated by GNU Autoconf 2.69,
44774575 with options \\"\$ac_cs_config\\"
44784576
00 AC_PREREQ([2.68])
1 AC_INIT(firetools, 0.9.52, netblue30@yahoo.com, , http://firejail.wordpress.com)
1 AC_INIT(firetools, 0.9.58, netblue30@yahoo.com, , http://firejail.wordpress.com)
22 AC_CONFIG_SRCDIR([src/firetools/main.cpp])
33 #AC_CONFIG_HEADERS([config.h])
44
3535 QMAKE="$qmake"
3636 ],
3737 [
38 QMAKE=`which qmake`
38 AC_PATH_TOOL([QMAKE],[qmake])
3939 ]
4040 )
4141 echo "trying $QMAKE"
22 Architecture: amd64
33 Maintainer: netblue30 <netblue30@yahoo.com>
44 Installed-Size: 340
5 Depends: libqtgui4, libqt4-svg, libc6
5 Depends: libqt5gui5, libqt5svg5, libc6
66 Section: admin
77 Priority: extra
88 Homepage: http://firejail.sourceforge.net
00 #!/bin/bash
1 VER="0.9.52"
1 VER="0.9.58"
22
33 cd ~
44 rm -fr rpmbuild
8989 /usr/lib/firetools/fstats
9090 /usr/lib/firetools/uihelp
9191 /usr/lib/firetools/uimenus
92
92
9393 %changelog
94
95 * Thu Jan 24 2018 netblue30 <netblue30@yahoo.com> 0.9.58-1
9496
9597 * Fri Mar 2 2018 netblue30 <netblue30@yahoo.com> 0.9.52-1
9698
107109 * Mon Oct 24 2016 netblue30 <netblue30@yahoo.com> 0.9.44-1
108110 - support for firejail --x11 detection
109111 - bugfixes
110
112
111113 * Sun May 29 2016 netblue30 <netblue30@yahoo.com> 0.9.40-1
112114 - Grsecurity support
113115 - updated the default application list
4545 exit(1);
4646 }
4747
48 // check if we have permission to run firejail
49 char *testrun = run_program("firejail exit 2>&1");
50 if (!testrun || strstr(testrun, "Error")) {
51 QMessageBox::warning(this, tr("Firejail Launcher"),
52 tr("<br/>Cannot run <b>Firejail</b> sandbox, you may not have<br/>the correct permissions to access this program.<br/><br/><br/>"));
53 exit(1);
54 }
55
4856 // check svg support
4957 #if QT_VERSION >= 0x050000
5058 QList<QByteArray> flist = QImageReader::supportedImageFormats();
139139 printf("print_files path %s\n", path);
140140
141141 char *cmd;
142 if (asprintf(&cmd, "firejail --ls=%d %s 2>&1", pid_, path) == -1)
142 if (asprintf(&cmd, "firejail --quiet --ls=%d %s 2>&1", pid_, path) == -1)
143143 errExit("asprintf");
144144
145145 // clear table
4444 static int getX11Display(pid_t pid);
4545
4646
47 StatsDialog::StatsDialog(): QDialog(), mode_(MODE_TOP), pid_(0), uid_(0),
47 // find the first child process for the specified pid
48 // return -1 if not found
49 static int find_child(int id) {
50 int i;
51 for (i = 0; i < max_pids; i++) {
52 if (pids[i].level == 2 && pids[i].parent == id)
53 return i;
54 }
55
56 return -1;
57 }
58
59 StatsDialog::StatsDialog(): QDialog(), mode_(MODE_TOP), pid_(0), uid_(0), lts_(false),
4860 pid_initialized_(false), pid_seccomp_(false), pid_caps_(QString("")), pid_noroot_(false),
4961 pid_cpu_cores_(QString("")), pid_protocol_(QString("")), pid_name_(QString("")),
5062 profile_(QString("")), pid_x11_(0),
51 have_join_(true), caps_cnt_(64), graph_type_(GRAPH_4MIN), no_network_(false) {
63 have_join_(true), caps_cnt_(64), graph_type_(GRAPH_4MIN), net_none_(false) {
5264
5365 // clean storage area
5466 cleanStorage();
67
68 // detect LTS version
69 char *str = run_program("firejail --version");
70 if (str && strstr(str, "LTS"))
71 lts_ = true;
5572
5673 procView_ = new QTextBrowser;
5774 procView_->setOpenLinks(false);
86103 }
87104
88105 // detect the number of capabilities supported by the current kernel
89 char *str = run_program("firejail --debug-caps");
106 str = run_program("firejail --debug-caps");
90107 if (!str)
91108 return;
92109 int val;
96113 caps_cnt_ = val;
97114 }
98115
99 struct stat s;
100 if (getuid() != 0 && stat("/proc/sys/kernel/grsecurity", &s) == 0)
101 no_network_ = true;
102
103116 thread_ = new PidThread();
104117 connect(thread_, SIGNAL(cycleReady()), this, SLOT(cycleReady()));
105
106118 }
107119
108120 StatsDialog::~StatsDialog() {
114126 storage_dns_ = "";
115127 storage_caps_ = "";
116128 storage_seccomp_ = "";
129 storage_intro_ = "";
130 storage_network_ = "";
117131 }
118132
119133 QString StatsDialog::header() {
133147 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"shut\">Shutdown</a>";
134148 if (have_join_ && uid_ == getuid())
135149 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"join\">Join</a>";
136 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"fmgr\">File Manager</a>";
150 if (!lts_)
151 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"fmgr\">File Manager</a>";
137152 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"tree\">Process Tree</a>";
138 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"dns\">DNS</a>";
153 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"network\">Network</a>";
139154 msg += "</td></tr></table>";
140155 }
141156
147162 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"shut\">Shutdown</a>";
148163 if (have_join_ && uid_ == getuid())
149164 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"join\">Join</a>";
150 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"fmgr\">File Manager</a>";
165 if (!lts_)
166 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"fmgr\">File Manager</a>";
151167 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"tree\">Process Tree</a>";
152 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"dns\">DNS</a>";
168 msg += " &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href=\"network\">Network</a>";
153169 msg += "</td></tr></table>";
154170 }
155171
156
172 msg += "<hr>";
157173 return msg;
158174 }
159175
160176 void StatsDialog::updateTop() {
161 QString msg = header() + "<hr>";
177 QString msg = header();
162178 msg += "<table><tr><td width=\"5\"></td><td><b>Sandbox List</b></td></tr></table><br/>\n";
163179 msg += "<table><tr><td width=\"5\"></td><td width=\"60\">PID</td/><td width=\"60\">CPU<br/>(%)</td><td>Memory<br/>(KiB)&nbsp;&nbsp;</td><td>RX<br/>(KB/s)&nbsp;&nbsp;</td><td>TX<br/>(KB/s)&nbsp;&nbsp;</td><td>Command</td>\n";
164180
198214
199215 if (arg_debug)
200216 printf("reading firewall configuration\n");
201 QString msg = header() + "<hr>";
217 QString msg = header() + storage_intro_;
202218
203219 char *cmd;
204220 if (asprintf(&cmd, "firejail --netfilter.print=%d", pid_) != -1) {
221237
222238 if (arg_debug)
223239 printf("reading process tree configuration\n");
224 QString msg = header();
225 msg += "<hr><table><tr><td width=\"5\"></td><td>";
240 QString msg = header() + storage_intro_;
241 msg += "<table><tr><td width=\"5\"></td><td>";
226242
227243 char *str = 0;
228244 char *cmd;
264280 if (msg.isEmpty()) {
265281 if (arg_debug)
266282 printf("reading seccomp configuration\n");
267 QString msg = header();
268 msg += "<hr><table><tr><td width=\"5\"></td><td>";
283 QString msg = header() + storage_intro_;
284 msg += "<table><tr><td width=\"5\"></td><td>";
269285
270286 char *str = 0;
271287 char *cmd;
309325 if (msg.isEmpty()) {
310326 if (arg_debug)
311327 printf("reading caps configuration\n");
312 msg = header();
313 msg += "<hr><table><tr><td width=\"5\"></td><td>";
328 msg = header() + storage_intro_;
329 msg += "<table><tr><td width=\"5\"></td><td>";
314330
315331 char *str = 0;
316332 char *cmd;
343359 }
344360 }
345361
346 void StatsDialog::updateDns() {
362 static QString get_dns(int pid) {
363 QString rv;
364
365 char *str = 0;
366 char *cmd;
367 if (asprintf(&cmd, "firejail --dns.print=%d", pid) != -1) {
368 str = run_program(cmd);
369 char *ptr = str;
370
371 // htmlize!
372 while (*ptr != 0) {
373 if (*ptr == '\n') {
374 *ptr = '\0';
375 bool skip = false;
376 if (*str == '#')
377 skip = true;
378 if (!skip)
379 rv += QString(str) + "<br/>\n";
380 ptr++;
381
382 while (*ptr == ' ') {
383 if (!skip)
384 rv += "&nbsp;&nbsp;";
385 ptr++;
386 }
387 str = ptr;
388 continue;
389 }
390 ptr++;
391 }
392 }
393 free(cmd);
394 return rv;
395 }
396
397 // build the network interface list for firejail versions 0.9.56 or older, including 0.9.56-LTS
398 static QString get_interfaces_old(int pid) {
399 QString rv;
400
401 char *fname;
402 if (asprintf(&fname, "/run/firejail/network/%d-netmap", pid) == -1)
403 errExit("asprintf");
404
405 FILE *fp = fopen(fname, "r");
406 if (fp) {
407 char buf[4096];
408 int i = -1;
409 while (fgets(buf, 4096, fp)) {
410 i++;
411 char *ptr = strchr(buf, '\n');
412 if (ptr)
413 *ptr = '\0';
414
415 // extract parent device
416 ptr = strchr(buf, ':');
417 if (!ptr)
418 continue;
419 char *parent_dev = buf;
420 *ptr = '\0';
421 ptr++;
422 char *child_dev = ptr;
423
424 QString str;
425 str.sprintf("%s (parent device %s", child_dev, parent_dev);
426
427 // detect bridge device
428 char *sysfile;
429 if (asprintf(&sysfile, "/sys/class/net/%s/bridge", parent_dev) == -1)
430 errExit("asprintf");
431 struct stat s;
432 if (stat(sysfile, &s) == 0)
433 str += ", bridge)";
434 else
435 str += ")";
436 free(sysfile);
437
438 rv += str + "<br/>";
439 }
440 fclose(fp);
441 }
442 free(fname);
443
444 return rv;
445 }
446
447 // build the network interface list for firejail versions 0.9.57 and up
448 // returns an empty string if --net.print is not available in the currently installed firejail version
449 static QString get_interfaces_new(int pid) {
450 QString rv;
451 char *str = 0;
452 char *cmd;
453 if (asprintf(&cmd, "firejail --net.print=%d 2>&1", pid) != -1) {
454 str = run_program(cmd);
455 free(cmd);
456
457 // htmlize!
458 char *ptr = strtok(str, "\n");
459 if (!ptr || strncmp(ptr, "Error", 5) == 0)
460 goto errexit;
461 while ((ptr = strtok(NULL, "\n")) != NULL) {
462 if (strncmp(ptr, "Error", 5) == 0)
463 goto errexit;
464 if (strncmp(ptr, "Interface ", 10) == 0)
465 continue;
466 if (strncmp(ptr, "lo ", 3) == 0)
467 continue;
468
469 // parse the interface line, example
470 //eth0-12202 c6:7f:d1:a9:3d:bc 192.168.1.82 255.255.255.0 UP
471 // ifname
472 char *ifname = ptr;
473 while (*ptr != ' ' && *ptr != '\0')
474 ptr++;
475 if (*ptr == '\0')
476 goto errexit;
477 *ptr = '\0';
478 ptr++;
479
480 // skip mac address
481 while (*ptr == ' ')
482 ptr++;
483 while (*ptr != ' ' && *ptr != '\0')
484 ptr++;
485 if (*ptr == '\0')
486 goto errexit;
487 while (*ptr == ' ')
488 ptr++;
489
490 // ip address
491 char *ip = ptr;
492 while (*ptr != ' ' && *ptr != '\0')
493 ptr++;
494 if (*ptr == '\0')
495 goto errexit;
496 *ptr = '\0';
497 ptr++;
498 while (*ptr == ' ')
499 ptr++;
500
501 // extract mask...
502 char *mask = ptr;
503 while (*ptr != ' ' && *ptr != '\0')
504 ptr++;
505 if (*ptr == '\0')
506 goto errexit;
507 *ptr = '\0';
508 // ... and build a CIDR addrss
509 uint32_t mask_uint32;
510 if (atoip(mask, &mask_uint32))
511 goto errexit;
512 int bits = mask2bits(mask_uint32);
513 rv += QString(ifname) + "&nbsp;&nbsp;&nbsp;" + QString(ip) + "/" +
514 QString::number(bits) + "<br/>";
515 }
516 }
517
518 return rv;
519
520 errexit:
521 return QString(); // empty string
522 }
523
524 void StatsDialog::updateNetwork() {
525 int cycle = Db::instance().getCycle();
526 assert(cycle < DbPid::MAXCYCLE);
347527 DbPid *dbptr = Db::instance().findPid(pid_);
348528 if (!dbptr) {
349529 mode_ = MODE_TOP;
350530 return;
351531 }
352532
353 QString msg = storage_dns_;
354 if (msg.isEmpty()) {
533 // DNS
534 QString msg = header() + storage_intro_;
535 if (storage_dns_.isEmpty()) {
355536 if (arg_debug)
356537 printf("reading dns configuration\n");
357538
358 msg = header();
359 msg += "<hr><table><tr><td width=\"5\"></td><td>";
360
361 char *str = 0;
362 char *cmd;
363 if (asprintf(&cmd, "firejail --dns.print=%d", pid_) != -1) {
364 str = run_program(cmd);
365 char *ptr = str;
366
367 // htmlize!
368 while (*ptr != 0) {
369 if (*ptr == '\n') {
370 *ptr = '\0';
371 msg += QString(str) + "<br/>\n";
372 ptr++;
373
374 while (*ptr == ' ') {
375 msg += "&nbsp;&nbsp;";
376 ptr++;
377 }
378 str = ptr;
379 continue;
380 }
381 ptr++;
382 }
383 }
384 free(cmd);
385
386 msg += "</td></tr></table>";
387 procView_->setHtml(msg);
388 storage_dns_ = msg;
389 }
390 }
391
392 void StatsDialog::kernelSecuritySettings() {
393 if (arg_debug)
394 printf("Checking security settings for pid %d\n", pid_);
395
396 // reset all
397 pid_seccomp_ = false;
398 pid_caps_ = QString("");
399 pid_cpu_cores_ = QString("");
400 pid_protocol_ = QString("");
401 pid_mem_deny_exec_ = QString("disabled");
402 pid_apparmor_ = QString("");
403
404 // caps
405 char *cmd;
406 if (asprintf(&cmd, "firemon --caps %d", pid_) == -1)
407 return;
408 char *str = run_program(cmd);
409 if (str) {
410 char *ptr = strstr(str, "CapBnd:");
411 if (ptr)
412 pid_caps_ = QString(ptr + 7);
539 storage_dns_ += "<table><tr><td width=\"5\"></td><td><b>DNS</b><br/>";
540 storage_dns_ += get_dns(pid_);
541 storage_dns_ += "</td>";
542 }
543 msg += storage_dns_;
544
545 // network interfaces
546 if (storage_network_.isEmpty()) {
547 //printf("network namespace disabled %d, net_none_ %d\n", dbptr->networkDisabled(), net_none_);
548 if (net_none_)
549 storage_network_ = "<td><b>Network Interfaces</b><br/>lo<br/>";
550 else if (dbptr->networkDisabled())
551 storage_network_ = "<td>Using the system network namespace";
552 else {
553
554 storage_network_ = "<td><b>Network Interfaces</b><br/>lo<br/>";
555 QString tmp = get_interfaces_new(pid_);
556 if (tmp.isEmpty())
557 tmp = get_interfaces_old(pid_);
558 storage_network_ += tmp;
559 }
560 storage_network_ += "</td></tr>";
561
562 }
563 msg += storage_network_;
564
565
566
567 // graph type
568 msg += "<tr><td></td>";
569 if (dbptr->networkDisabled() == false && net_none_ == false) {
570 if (graph_type_ == GRAPH_4MIN) {
571 msg += "<td><b>Stats: </b>1min <a href=\"1h\">1h</a> <a href=\"12h\">12h</a></td>";
572 }
573 else if (graph_type_ == GRAPH_1H) {
574 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> 1h <a href=\"12h\">12h</a></td>";
575 }
576 else if (graph_type_ == GRAPH_12H) {
577 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> <a href=\"1h\">1h</a> 12h</td>";
578 }
413579 else
414 pid_caps_ = QString("");
415 }
416 free(cmd);
417
418 // seccomp
419 if (asprintf(&cmd, "firemon --seccomp %d", pid_) == -1)
420 return;
421 str = run_program(cmd);
422 if (str) {
423 char *ptr = strstr(str, "Seccomp");
424 if (ptr) {
425 if (strstr(ptr, "2"))
426 pid_seccomp_ = true;
427 }
428 }
429 free(cmd);
430
431 // cpu cores
432 if (asprintf(&cmd, "firemon --cpu %d", pid_) == -1)
433 return;
434 str = run_program(cmd);
435 if (str) {
436 char *ptr = strstr(str, "Cpus_allowed_list:");
437 if (ptr) {
438 ptr += 18;
439 pid_cpu_cores_ = QString(ptr);
440 }
441 }
442 free(cmd);
443
444 // protocols
445 if (asprintf(&cmd, "firejail --protocol.print=%d", pid_) == -1)
446 return;
447
448 str = run_program(cmd);
449 if (str) {
450 if (strncmp(str, "Cannot", 6) == 0)
451 pid_protocol_ = QString("disabled");
452 else
453 pid_protocol_ = QString(str);
454 }
455 free(cmd);
456
457 // mem deny exec
458 if (asprintf(&cmd, "firejail --ls=%d /run/firejail/mnt", pid_) == -1)
459 return;
460 str = run_program(cmd);
461 if (str) {
462 if (strstr(str, "seccomp.mdwx"))
463 pid_mem_deny_exec_ = "enabled";
464 }
465 free(cmd);
466
467 // apparmor
468 if (asprintf(&cmd, "firejail --apparmor.print=%d", pid_) == -1)
469 return;
470 str = run_program(cmd);
471 if (str) {
472 const char *tofind = "AppArmor: ";
473 char *ptr = strstr(str, tofind);
474 if (ptr)
475 pid_apparmor_ = QString(ptr + strlen(tofind));
476 }
477 free(cmd);
478 }
479
480
481 // find the first child process for the specified pid
482 // return -1 if not found
483 static int find_child(int id) {
484 int i;
485 for (i = 0; i < max_pids; i++) {
486 if (pids[i].level == 2 && pids[i].parent == id)
487 return i;
488 }
489
490 return -1;
491 }
492
493
494
495
496 void StatsDialog::updatePid() {
497 QString msg = "";
498
499 int cycle = Db::instance().getCycle();
500 assert(cycle < DbPid::MAXCYCLE);
501 DbPid *ptr = Db::instance().findPid(pid_);
502 if (!ptr) {
503 mode_ = MODE_TOP;
504 return;
505 }
506
507 const char *cmd = ptr->getCmd();
508 if (!cmd) {
509 mode_ = MODE_TOP;
510 return;
511 }
512
513 // initialize static values
514 if (pid_initialized_ == false) {
515 kernelSecuritySettings();
516 pid_noroot_ = userNamespace(pid_);
517 pid_name_ = getName(pid_);
518 profile_ = getProfile(pid_);
519 pid_x11_ = getX11Display(pid_);
520 pid_initialized_ = true;
521 }
522
523 // get user name
524 DbStorage *st = &ptr->data_4min_[cycle];
525 struct passwd *pw = getpwuid(ptr->getUid());
526 if (!pw)
527 errExit("getpwuid");
528 uid_ = pw->pw_uid;
529
530 msg += header() + "<hr>";
531 msg += "<table>";
532 if (!pid_name_.isEmpty())
533 msg += "<tr><td width=\"5\"></td><td><b>Sandbox name:</b> " + pid_name_ + "</td></tr>";
534 msg += "<tr><td width=\"5\"></td><td><b>Command:</b> " + QString(cmd) + "</td></tr>";
535 if (!profile_.isEmpty())
536 msg += "<tr><td width=\"5\"></td><td><b>Profile:</b> " + profile_ + "</td></tr>";
537 // add
538 msg += "</table><br/>";
539
540 msg += "<table>";
541 msg += QString("<tr><td width=\"5\"></td><td><b>PID:</b> ") + QString::number(pid_) + "</td>";
542 if (ptr->networkDisabled() || no_network_)
543 msg += "<td><b>RX:</b> unknown</td></tr>";
544 else
545 msg += QString("<td><b>RX:</b> ") + QString::number(st->rx_) + " KB/s</td></tr>";
546
547 msg += QString("<tr><td></td><td><b>User:</b> ") + pw->pw_name + "</td>";
548 if (ptr->networkDisabled() || no_network_)
549 msg += "<td><b>TX:</b> unknown</td></tr>";
550 else
551 msg += QString("<td><b>TX:</b> ") + QString::number(st->tx_) + " KB/s</td></tr>";
552
553 msg += QString("<tr><td></td><td><b>CPU:</b> ") + QString::number(st->cpu_) + "%</td>";
554 msg += QString("<td><b>Seccomp:</b> ");
555 if (pid_seccomp_)
556 msg += "<a href=\"seccomp\">enabled</a>";
557 else
558 msg += "disabled";
559 msg += "</td></tr>";
560
561 msg += QString("<tr><td></td><td><b>Memory:</b> ") + QString::number((int) (st->rss_ + st->shared_)) + " KiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
562 msg += QString("<td><b>Capabilities:</b> <a href=\"caps\">") + pid_caps_ + "</a></td></tr>";
563
564 msg += QString("<tr><td></td><td><b>RSS</b> " + QString::number((int) st->rss_) + ", <b>shared</b> " + QString::number((int) st->shared_)) + "</td>";
565
566 // user namespace
567 msg += "<td><b>User Namespace:</b> ";
568
569 if (pid_noroot_)
570 msg += "enabled";
571 else
572 msg += "disabled";
573 msg += "</td></tr>";
574
575 msg += QString("<tr><td></td><td><b>CPU Cores:</b> ") + pid_cpu_cores_ + "</td>";
576 if (pid_seccomp_)
577 msg += QString("<td><b>Protocols:</b> ") + pid_protocol_ + "</td>";
578 else
579 msg += QString("<td><b>Protocols:</b> disabled</td>");
580 msg += "</td></tr>";
581
582 msg += "<tr><td></td>";
583
584 // X11 display
585 if (pid_x11_) {
586 msg += "<td><b>X11 Dispaly:</b> " + QString::number(pid_x11_) + "</td>";
587 }
588 else
589 msg +="<td></td>";
590
591 // memory deny exec
592 msg += "<td><b>Memory deny exec:</b> " + pid_mem_deny_exec_ + "</td></tr>";
593
594
595 // apparmor
596 if (!pid_apparmor_.isEmpty())
597 msg += "<tr><td></td><td></td><td><b>AppArmor: </b>" + pid_apparmor_ + "</td></tr>";
598
599
600 // graph type
601 msg += "<tr></tr>";
602 msg += "<tr><td></td>";
603 if (graph_type_ == GRAPH_4MIN) {
604 msg += "<td><b>Stats: </b>1min <a href=\"1h\">1h</a> <a href=\"12h\">12h</a></td>";
605 }
606 else if (graph_type_ == GRAPH_1H) {
607 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> 1h <a href=\"12h\">12h</a></td>";
608 }
609 else if (graph_type_ == GRAPH_12H) {
610 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> <a href=\"1h\">1h</a> 12h</td>";
611 }
612 else
613 assert(0);
580 assert(0);
581 }
614582
615583 // netfilter
616 if (ptr->networkDisabled() == false && no_network_ == false)
584 if (dbptr->networkDisabled() == false && net_none_ == false)
617585 msg += "<td><b>Firewall</b>: <a href=\"firewall\">enabled</a></td></tr>\n";
618586 else
619587 msg += "<td><b>Firewall</b>: system firewall</td></tr>\n";
620588
621 // graphs
622 msg += "<tr></tr>";
623 msg += "<tr><td></td><td>"+ graph(0, ptr, cycle, graph_type_) + "</td><td>" + graph(1, ptr, cycle, graph_type_) + "</td></tr>";
624 if (ptr->networkDisabled() == false && no_network_ == false)
625 msg += "<tr><td></td><td>"+ graph(2, ptr, cycle, graph_type_) + "</td><td>" + graph(3, ptr, cycle, graph_type_) + "</td></tr>";
589
590 if (dbptr->networkDisabled() == false && net_none_ == false)
591 msg += "<tr><td></td><td>"+ graph(2, dbptr, cycle, graph_type_) + "</td><td>" + graph(3, dbptr, cycle, graph_type_) + "</td></tr>";
626592
627593 msg += QString("</table><br/>");
628594
629595 // bandwidth limits
630 if (ptr->networkDisabled() == false && no_network_ == false) {
596 if (dbptr->networkDisabled() == false && net_none_ == false) {
631597 char *fname;
632598 if (asprintf(&fname, "/run/firejail/bandwidth/%d-bandwidth", pid_) == -1)
633599 errExit("asprintf");
647613 }
648614
649615 procView_->setHtml(msg);
616
617 }
618
619 void StatsDialog::kernelSecuritySettings() {
620 if (arg_debug)
621 printf("Checking security settings for pid %d\n", pid_);
622
623 // reset all
624 pid_seccomp_ = false;
625 pid_caps_ = QString("");
626 pid_cpu_cores_ = QString("");
627 pid_protocol_ = QString("");
628 pid_mem_deny_exec_ = QString("disabled");
629 pid_apparmor_ = QString("");
630
631 // caps
632 char *cmd;
633 if (asprintf(&cmd, "firemon --caps %d", pid_) == -1)
634 return;
635 char *str = run_program(cmd);
636 if (str) {
637 char *ptr = strstr(str, "CapBnd:");
638 if (ptr)
639 pid_caps_ = QString(ptr + 7);
640 else
641 pid_caps_ = QString("");
642 }
643 free(cmd);
644
645 // seccomp
646 if (asprintf(&cmd, "firemon --seccomp %d", pid_) == -1)
647 return;
648 str = run_program(cmd);
649 if (str) {
650 char *ptr = strstr(str, "Seccomp");
651 if (ptr) {
652 if (strstr(ptr, "2"))
653 pid_seccomp_ = true;
654 }
655 }
656 free(cmd);
657
658 // cpu cores
659 if (asprintf(&cmd, "firemon --cpu %d", pid_) == -1)
660 return;
661 str = run_program(cmd);
662 if (str) {
663 char *ptr = strstr(str, "Cpus_allowed_list:");
664 if (ptr) {
665 ptr += 18;
666 pid_cpu_cores_ = QString(ptr);
667 }
668 }
669 free(cmd);
670
671 // protocols
672 if (asprintf(&cmd, "firejail --protocol.print=%d", pid_) == -1)
673 return;
674
675 str = run_program(cmd);
676 if (str) {
677 if (strncmp(str, "Cannot", 6) == 0)
678 pid_protocol_ = QString("disabled");
679 else
680 pid_protocol_ = QString(str);
681 }
682 free(cmd);
683
684 // mem deny exec
685 if (asprintf(&cmd, "firejail --ls=%d /run/firejail/mnt", pid_) == -1)
686 return;
687 str = run_program(cmd);
688 if (str) {
689 if (strstr(str, "seccomp.mdwx"))
690 pid_mem_deny_exec_ = "enabled";
691 }
692 free(cmd);
693
694 // apparmor
695 if (asprintf(&cmd, "firejail --apparmor.print=%d", pid_) == -1)
696 return;
697 str = run_program(cmd);
698 if (str) {
699 const char *tofind = "AppArmor: ";
700 char *ptr = strstr(str, tofind);
701 if (ptr)
702 pid_apparmor_ = QString(ptr + strlen(tofind));
703 }
704 free(cmd);
705 }
706
707 void StatsDialog::updatePid() {
708 QString msg = "";
709
710 int cycle = Db::instance().getCycle();
711 assert(cycle < DbPid::MAXCYCLE);
712 DbPid *ptr = Db::instance().findPid(pid_);
713 if (!ptr) {
714 mode_ = MODE_TOP;
715 return;
716 }
717
718 const char *cmd = ptr->getCmd();
719 if (!cmd) {
720 mode_ = MODE_TOP;
721 return;
722 }
723
724 // initialize static values
725 if (pid_initialized_ == false) {
726 kernelSecuritySettings();
727 pid_noroot_ = userNamespace(pid_);
728 pid_name_ = getName(pid_);
729 profile_ = getProfile(pid_);
730 pid_x11_ = getX11Display(pid_);
731 pid_initialized_ = true;
732
733 // detect --net=none
734 int child = find_child(pid_);
735 char *fname;
736 if (asprintf(&fname, "/proc/%d/net/dev", child) == -1)
737 errExit("asprintf");
738 FILE *fp = fopen(fname, "r");
739 if (fp) {
740 char buf[4096];
741 int cnt = 0;
742 while (fgets(buf, 4096, fp))
743 cnt++;
744 fclose(fp);
745 if (cnt <= 3)
746 net_none_ = true;
747 else
748 net_none_ = false;
749 }
750 free(fname);
751 }
752
753 // get user name
754 DbStorage *st = &ptr->data_4min_[cycle];
755 struct passwd *pw = getpwuid(ptr->getUid());
756 if (!pw)
757 errExit("getpwuid");
758 uid_ = pw->pw_uid;
759
760 // add header
761 msg += header();
762
763 // add intro
764 storage_intro_ = "<table>";
765 if (!pid_name_.isEmpty())
766 storage_intro_ += "<tr><td width=\"5\"></td><td><b>Sandbox name:</b> " + pid_name_ + "</td></tr>";
767 storage_intro_ += "<tr><td width=\"5\"></td><td><b>Command:</b> " + QString(cmd) + "</td></tr>";
768 if (!profile_.isEmpty())
769 storage_intro_ += "<tr><td width=\"5\"></td><td><b>Profile:</b> " + profile_ + "</td></tr>";
770 storage_intro_ += "</table><br/>";
771 msg += storage_intro_;
772
773 msg += "<table>";
774 msg += QString("<tr><td width=\"5\"></td><td><b>PID:</b> ") + QString::number(pid_) + "</td>";
775 if (ptr->networkDisabled() || net_none_)
776 msg += "<td><b>RX:</b> unknown</td></tr>";
777 else
778 msg += QString("<td><b>RX:</b> ") + QString::number(st->rx_) + " KB/s</td></tr>";
779
780 msg += QString("<tr><td></td><td><b>User:</b> ") + pw->pw_name + "</td>";
781 if (ptr->networkDisabled() || net_none_)
782 msg += "<td><b>TX:</b> unknown</td></tr>";
783 else
784 msg += QString("<td><b>TX:</b> ") + QString::number(st->tx_) + " KB/s</td></tr>";
785
786 msg += QString("<tr><td></td><td><b>CPU:</b> ") + QString::number(st->cpu_) + "%</td>";
787 msg += QString("<td><b>Seccomp:</b> ");
788 if (pid_seccomp_)
789 msg += "<a href=\"seccomp\">enabled</a>";
790 else
791 msg += "disabled";
792 msg += "</td></tr>";
793
794 msg += QString("<tr><td></td><td><b>Memory:</b> ") + QString::number((int) (st->rss_ + st->shared_)) + " KiB&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>";
795 msg += QString("<td><b>Capabilities:</b> <a href=\"caps\">") + pid_caps_ + "</a></td></tr>";
796
797 msg += QString("<tr><td></td><td><b>RSS</b> " + QString::number((int) st->rss_) + ", <b>shared</b> " + QString::number((int) st->shared_)) + "</td>";
798
799 // user namespace
800 msg += "<td><b>User Namespace:</b> ";
801
802 if (pid_noroot_)
803 msg += "enabled";
804 else
805 msg += "disabled";
806 msg += "</td></tr>";
807
808 msg += QString("<tr><td></td><td><b>CPU Cores:</b> ") + pid_cpu_cores_ + "</td>";
809 if (pid_seccomp_)
810 msg += QString("<td><b>Protocols:</b> ") + pid_protocol_ + "</td>";
811 else
812 msg += QString("<td><b>Protocols:</b> disabled</td>");
813 msg += "</td></tr>";
814
815 msg += "<tr><td></td>";
816
817 // X11 display
818 if (pid_x11_) {
819 msg += "<td><b>X11 Dispaly:</b> " + QString::number(pid_x11_) + "</td>";
820 }
821 else
822 msg +="<td></td>";
823
824 // memory deny exec
825 msg += "<td><b>Memory deny exec:</b> " + pid_mem_deny_exec_ + "</td></tr>";
826
827
828 // apparmor
829 if (!pid_apparmor_.isEmpty())
830 msg += "<tr><td></td><td></td><td><b>AppArmor: </b>" + pid_apparmor_ + "</td></tr>";
831
832
833 // graph type
834 msg += "<tr></tr>";
835 msg += "<tr><td></td>";
836 if (graph_type_ == GRAPH_4MIN) {
837 msg += "<td><b>Stats: </b>1min <a href=\"1h\">1h</a> <a href=\"12h\">12h</a></td>";
838 }
839 else if (graph_type_ == GRAPH_1H) {
840 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> 1h <a href=\"12h\">12h</a></td>";
841 }
842 else if (graph_type_ == GRAPH_12H) {
843 msg += "<td><b>Stats: </b><a href=\"1min\">1min</a> <a href=\"1h\">1h</a> 12h</td>";
844 }
845 else
846 assert(0);
847
848 // graphs
849 msg += "<tr></tr>";
850 msg += "<tr><td></td><td>"+ graph(0, ptr, cycle, graph_type_) + "</td><td>" + graph(1, ptr, cycle, graph_type_) + "</td></tr>";
851
852 msg += QString("</table><br/>");
853
854 procView_->setHtml(msg);
650855 }
651856
652857 void StatsDialog::cycleReady() {
658863 updateTree();
659864 else if (mode_ == MODE_SECCOMP)
660865 updateSeccomp();
661 else if (mode_ == MODE_DNS)
662 updateDns();
866 else if (mode_ == MODE_NETWORK)
867 updateNetwork();
663868 else if (mode_ == MODE_CAPS)
664869 updateCaps();
665870 else if (mode_ == MODE_FIREWALL)
680885 mode_ = MODE_PID;
681886 else if (mode_ == MODE_SECCOMP)
682887 mode_ = MODE_PID;
683 else if (mode_ == MODE_DNS)
888 else if (mode_ == MODE_NETWORK)
684889 mode_ = MODE_PID;
685890 else if (mode_ == MODE_CAPS)
686891 mode_ = MODE_PID;
709914 else if (linkstr == "1min") {
710915 graph_type_ = GRAPH_4MIN;
711916 }
712 else if (linkstr == "dns") {
713 mode_ = MODE_DNS;
917 else if (linkstr == "network") {
918 mode_ = MODE_NETWORK;
714919 }
715920 else if (linkstr == "firewall") {
716921 mode_ = MODE_FIREWALL;
4343 private:
4444 QString header();
4545 void kernelSecuritySettings();
46 void updateTop();
47 void updatePid();
48 void updateTree();
49 void updateSeccomp();
50 void updateDns();
51 void updateCaps();
46 void updateTop();
47 void updatePid();
48 void updateTree();
49 void updateSeccomp();
50 void updateNetwork();
51 void updateCaps();
5252 void updateFirewall();
5353 void cleanStorage();
5454
5656 QTextBrowser *procView_;
5757
5858 #define MODE_TOP 0
59 #define MODE_PID 1
59 #define MODE_PID 1
6060 #define MODE_TREE 2
6161 #define MODE_SECCOMP 3
62 #define MODE_DNS 4
62 #define MODE_NETWORK 4
6363 #define MODE_CAPS 5
6464 #define MODE_FIREWALL 6
6565 int mode_;
6666 int pid_; // pid value for mode 1
6767 uid_t uid_;
68 bool lts_; // flag to detect LTS version of firejail
6869
6970 // security settings
7071 bool pid_initialized_;
7879 QString pid_apparmor_;
7980 QString profile_;
8081 int pid_x11_;
81
82
8283 bool have_join_;
8384 int caps_cnt_;
8485 GraphType graph_type_;
85 bool no_network_;
86 bool net_none_;
8687
8788 PidThread *thread_;
88
89
8990 // storage for various sandbox settings
9091 QString storage_dns_;
9192 QString storage_caps_;
9293 QString storage_seccomp_;
94 QString storage_intro_;
95 QString storage_network_;
9396 };
9497
9598