aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/AdminPanel/Module/Firewall.pm445
1 files changed, 398 insertions, 47 deletions
diff --git a/lib/AdminPanel/Module/Firewall.pm b/lib/AdminPanel/Module/Firewall.pm
index 9226667c..d9c8ff54 100644
--- a/lib/AdminPanel/Module/Firewall.pm
+++ b/lib/AdminPanel/Module/Firewall.pm
@@ -23,6 +23,7 @@ package AdminPanel::Module::Firewall;
use Modern::Perl '2011';
use autodie;
use Moose;
+use Moose::Autobox;
use utf8;
use yui;
@@ -33,7 +34,9 @@ use AdminPanel::Shared::Firewall;
use List::Util qw(any);
use List::MoreUtils qw(uniq);
-use MDK::Common::Func qw(if_);
+use MDK::Common::Func qw(if_ partition);
+use MDK::Common::System qw(getVarsFromSh);
+use MDK::Common::Various qw(text2bool);
extends qw( AdminPanel::Module );
@@ -77,6 +80,26 @@ has 'all_servers' => (
isa => 'ArrayRef',
);
+has 'ifw_rules' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+);
+
+has 'wdg_ifw' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+ default => sub { [] },
+);
+
+has 'wdg_servers' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+ default => sub { [] },
+);
+
has 'net' => (
is => 'rw',
init_arg => undef,
@@ -84,6 +107,25 @@ has 'net' => (
builder => '_initNet',
);
+has 'aboutDialog' => (
+ is => 'ro',
+ init_arg => undef,
+ isa => 'HashRef',
+ builder => '_setupAboutDialog',
+);
+
+sub _setupAboutDialog {
+ my $self = shift();
+ return {
+ name => "",
+ version => $VERSION,
+ credits => "Copyright (c) 2013-2015 by Matteo Pasotti",
+ license => "GPLv2",
+ description => "",
+ authors => "Matteo Pasotti <matteo.pasotti\@gmail.com>"
+ };
+}
+
sub _localeInitialize {
my $self = shift();
@@ -102,48 +144,57 @@ sub _initAllServers {
my $self = shift();
my @all_servers = (
{
+ id => 'www',
name => $self->loc->N("Web Server"),
pkg => 'apache apache-mod_perl boa lighttpd thttpd',
ports => '80/tcp 443/tcp',
},
{
+ id => 'dns',
name => $self->loc->N("Domain Name Server"),
pkg => 'bind dnsmasq mydsn',
ports => '53/tcp 53/udp',
},
{
+ id => 'ssh',
name => $self->loc->N("SSH server"),
pkg => 'openssh-server',
ports => '22/tcp',
},
{
+ id => 'ftp',
name => $self->loc->N("FTP server"),
pkg => 'ftp-server-krb5 wu-ftpd proftpd pure-ftpd',
ports => '20/tcp 21/tcp',
},
{
+ id => 'dhcp',
name => $self->loc->N("DHCP Server"),
pkg => 'dhcp-server udhcpd',
ports => '67/udp 68/udp',
hide => 1,
},
{
+ id => 'mail',
name => $self->loc->N("Mail Server"),
pkg => 'sendmail postfix qmail exim',
ports => '25/tcp 465/tcp 587/tcp',
},
{
+ id => 'popimap',
name => $self->loc->N("POP and IMAP Server"),
pkg => 'imap courier-imap-pop',
ports => '109/tcp 110/tcp 143/tcp 993/tcp 995/tcp',
},
{
+ id => 'telnet',
name => $self->loc->N("Telnet server"),
pkg => 'telnet-server-krb5',
ports => '23/tcp',
hide => 1,
},
{
+ id => 'nfs',
name => $self->loc->N("NFS Server"),
pkg => 'nfs-utils nfs-utils-clients',
ports => '111/tcp 111/udp 2049/tcp 2049/udp ' . network::nfs::list_nfs_ports(),
@@ -152,58 +203,68 @@ sub _initAllServers {
restart => 'nfs-common nfs-server',
},
{
+ id => 'smb',
name => $self->loc->N("Windows Files Sharing (SMB)"),
pkg => 'samba-server',
ports => '137/tcp 137/udp 138/tcp 138/udp 139/tcp 139/udp 445/tcp 445/udp 1024:1100/tcp 1024:1100/udp',
hide => 1,
},
{
+ id => 'bacula',
name => $self->loc->N("Bacula backup"),
pkg => 'bacula-fd bacula-sd bacula-dir-common',
ports => '9101:9103/tcp',
hide => 1,
},
{
+ id => 'syslog',
name => $self->loc->N("Syslog network logging"),
pkg => 'rsyslog syslog-ng',
ports => '514/udp',
hide => 1,
},
{
+ id => 'cups',
name => $self->loc->N("CUPS server"),
pkg => 'cups',
ports => '631/tcp 631/udp',
hide => 1,
},
{
+ id => 'mysql',
name => $self->loc->N("MySQL server"),
pkg => 'mysql',
ports => '3306/tcp 3306/udp',
hide => 1,
},
{
+ id => 'postgresql',
name => $self->loc->N("PostgreSQL server"),
pkg => 'postgresql8.2 postgresql8.3',
ports => '5432/tcp 5432/udp',
hide => 1,
},
{
+ id => 'echo',
name => $self->loc->N("Echo request (ping)"),
ports => '8/icmp',
force_default_selection => 0,
},
{
+ id => 'zeroconf',
name => $self->loc->N("Network services autodiscovery (zeroconf and slp)"),
ports => '5353/udp 427/udp',
pkg => 'avahi cups openslp',
},
{
+ id => 'bittorrent',
name => $self->loc->N("BitTorrent"),
ports => '6881:6999/tcp 6881:6999/udp',
hide => 1,
pkg => 'bittorrent deluge ktorrent transmission vuze rtorrent ctorrent',
},
{
+ id => 'wmds',
name => $self->loc->N("Windows Mobile device synchronization"),
pkg => 'synce-hal',
ports => '990/tcp 999/tcp 5678/tcp 5679/udp 26675/tcp',
@@ -213,6 +274,18 @@ sub _initAllServers {
return \@all_servers;
}
+sub _initIFW {
+ my $self = shift();
+ my @ifw_rules = (
+ {
+ id => 'psd',
+ name => $self->loc->N("Port scan detection"),
+ ifw_rule => 'psd',
+ },
+ );
+ return \@ifw_rules;
+}
+
sub _initNet {
my $self = shift();
my $net = {};
@@ -253,6 +326,29 @@ sub port2server {
#=============================================================
+=head2 to_ports
+
+=head3 INPUT
+
+ $self: this object
+
+ $unlisted: unlisted services
+
+=head3 DESCRIPTION
+
+ This method converts from server definitions to port definitions
+
+=cut
+
+#=============================================================
+
+sub to_ports {
+ my ($servers, $unlisted) = @_;
+ join(' ', (map { $_->{ports} } @$servers), if_($unlisted, $unlisted));
+}
+
+#=============================================================
+
=head2 from_ports
=head3 INPUT
@@ -339,6 +435,172 @@ drakconnect before going any further."),
#=============================================================
+=head2 choose_watched_services
+
+=head3 INPUT
+
+ $self: this object
+
+ $disabled: boolean
+
+ $servers: array of hashes representing servers
+
+ $unlisted: array of hashes with the port not listed (???)
+
+ $log_net_drop: network::shorewall log_net_drop attribute
+
+=head3 DESCRIPTION
+
+ This method shows the main dialog to let users choose the allowed services
+
+=cut
+
+#=============================================================
+
+sub choose_watched_services {
+ my ($self, $servers, $unlisted) = @_;
+
+ my @l = (@{$self->ifw_rules()}, @$servers, map { { ports => $_ } } split(' ', $unlisted));
+
+ my $enabled = 1;
+ $_->{ifw} = 1 foreach @l;
+
+ $self->ask_WatchedServices({
+ title => $self->loc->N("Interactive Firewall"),
+ icon => $network::shorewall::firewall_icon,
+ # if_(!$::isEmbedded, banner_title => N("Interactive Firewall")),
+ messages =>
+ $self->loc->N("You can be warned when someone accesses to a service or tries to intrude into your computer.
+Please select which network activities should be watched."),
+ },
+ [
+ {
+ text => $self->loc->N("Use Interactive Firewall"), val => \$enabled, type => 'bool' },
+ map {
+ {
+ text => (exists $_->{name} ? $_->{name} : $_->{ports}),
+ val => \$_->{ifw},
+ type => 'bool', disabled => sub { !$enabled },
+ },
+ } @l,
+ ]);
+ my ($rules, $ports) = partition { exists $_->{ifw_rule} } grep { $_->{ifw} } @l;
+ # set_ifw($in->do_pkgs, $enabled, [ map { $_->{ifw_rule} } @$rules ], to_ports($ports));
+
+ # return something to say that we are done ok
+ return ($rules, $ports);
+}
+
+#=============================================================
+
+sub ask_WatchedServices {
+ my $self = shift;
+
+ my ($dlg_data,
+ $items) = @_;
+
+ my $old_title = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($dlg_data->{title});
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $dlg_data->{icon});
+ my $labelAppDescription = $factory->createLabel($headRight,$dlg_data->{messages});
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ my $hbox_content = $factory->createHBox($layout);
+
+ my $widgetContainer = $factory->createVBox($hbox_content);
+
+
+ foreach my $item(@{$items})
+ {
+ if(defined($item->{label}))
+ {
+ $factory->createLabel($factory->createLeft($factory->createHBox($widgetContainer)), $item->{label});
+ }
+ elsif(defined($item->{text}))
+ {
+ my $ckbox = $factory->createCheckBox(
+ $factory->createLeft($factory->createHBox($widgetContainer)),
+ $item->{text},
+ ${$item->{val}}
+ );
+ $ckbox->setNotify(1);
+ push @{$self->wdg_ifw()}, {
+ id => $item->{id},
+ widget => \$ckbox,
+ value => $item->{val},
+ };
+ $ckbox->DISOWN();
+ }
+ }
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("OK"));
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### Buttons and widgets ###
+ my $widget = $event->widget();
+
+ # loop on every checkbox representing servers
+ foreach my $server(@{$self->wdg_servers()})
+ {
+ if($widget == ${$server->{widget}})
+ {
+ ${$server->{value}} = !${$server->{value}};
+ }
+ }
+
+ if ($widget == $cancelButton) {
+ last;
+ }elsif ($widget == $aboutButton) {
+ my $abtdlg = $self->aboutDialog();
+ $abtdlg->{name} = $dlg_data->{title};
+ $abtdlg->{description} = $self->loc->N("Graphical manager for interactive firewall rules");
+ $self->sh_gui->AboutDialog($abtdlg
+ );
+ }elsif ($widget == $okButton) {
+ last;
+ }
+ }
+ }
+
+ $self->dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($old_title);
+
+ return 1;
+}
+
+
+#=============================================================
+
=head2 choose_allowed_services
=head3 INPUT
@@ -394,47 +656,33 @@ Have a look at /etc/services for information."),
{ label => $self->loc->N("Which services would you like to allow the Internet to connect to?"), title => 1 },
if_($self->net()->{PROFILE} && network::network::netprofile_count() > 0, { label => $self->loc->N("Those settings will be saved for the network profile <b>%s</b>", $self->net()->{PROFILE}) }),
{ text => $self->loc->N("Everything (no firewall)"), val => \$disabled, type => 'bool' },
- (map { { text => $_->{name}, val => \$_->{on}, type => 'bool', disabled => sub { $disabled } } } @l),
+ (map { { text => $_->{name}, val => \$_->{on}, type => 'bool', disabled => sub { $disabled }, id => $_->{id} } } @l),
{ label => $self->loc->N("Other ports"), val => \$unlisted, advanced => 1, disabled => sub { $disabled } },
{ text => $self->loc->N("Log firewall messages in system logs"), val => \$log_net_drop, type => 'bool', advanced => 1, disabled => sub { $disabled } },
];
- $self->ask_AllowedServices($dialog_data, $items) or return;
+ $self->ask_AllowedServices($dialog_data, $items);
+ for my $server(@{$self->wdg_servers()})
+ {
+ for my $k(keys @l)
+ {
+ if(defined($l[$k]->{id}) && defined($server->{id}))
+ {
+ if($l[$k]->{id} eq $server->{id})
+ {
+ $l[$k]->{on} = ${$server->{value}};
+ last;
+ }
+ }
+ }
+ }
+
return ($disabled, [ grep { $_->{on} } @l ], $unlisted, $log_net_drop);
}
#=============================================================
-=head2 start
-
-=head3 INPUT
-
- $self: this object
-
-=head3 DESCRIPTION
-
- This method extends Module::start and is invoked to
- start host manager
-
-=cut
-
-#=============================================================
-
-sub start {
- my $self = shift;
-
- $self->all_servers($self->_initAllServers());
-
-
-
- my ($disabled, $servers, $unlisted, $log_net_drop) = $self->get_conf(undef) or return;
- ($disabled, $servers, $unlisted, $log_net_drop) = $self->choose_allowed_services($disabled, $servers, $unlisted, $log_net_drop) or return;
-
-};
-
-#=============================================================
-
sub ask_AllowedServices {
my $self = shift;
@@ -469,14 +717,25 @@ sub ask_AllowedServices {
{
if(defined($item->{label}))
{
- $factory->createLabel($widgetContainer, $item->{label});
+ $factory->createLabel($factory->createLeft($factory->createHBox($widgetContainer)), $item->{label});
}
elsif(defined($item->{text}))
{
- $factory->createLabel($widgetContainer, $item->{text} . " - ". $item->{val} . " - " . $item->{type});
+ my $ckbox = $factory->createCheckBox(
+ $factory->createLeft($factory->createHBox($widgetContainer)),
+ $item->{text},
+ ${$item->{val}}
+ );
+ $ckbox->setNotify(1);
+ push @{$self->wdg_servers()}, {
+ id => $item->{id},
+ widget => \$ckbox,
+ value => $item->{val},
+ };
+ $ckbox->DISOWN();
}
}
-
+
my $hbox_foot = $factory->createHBox($layout);
my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
@@ -494,32 +753,124 @@ sub ask_AllowedServices {
last;
}
elsif ($eventType == $yui::YEvent::WidgetEvent) {
-### Buttons and widgets ###
+ ### Buttons and widgets ###
my $widget = $event->widget();
+
+ # loop on every checkbox representing servers
+ foreach my $server(@{$self->wdg_servers()})
+ {
+ if($widget == ${$server->{widget}})
+ {
+ ${$server->{value}} = !${$server->{value}};
+ }
+ }
+
if ($widget == $cancelButton) {
last;
}elsif ($widget == $aboutButton) {
- $self->sh_gui->AboutDialog({
- name => $dlg_data->{title},
- version => $VERSION,
- credits => "Copyright (c) 2013-2015 by Matteo Pasotti",
- license => "GPLv2",
- description => $self->loc->N("Graphical manager for firewall rules"),
- authors => "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;"
- }
+ my $abtdlg = $self->aboutDialog();
+ $abtdlg->{name} = $dlg_data->{title};
+ $abtdlg->{description} = $self->loc->N("Graphical manager for firewall rules");
+ $self->sh_gui->AboutDialog($abtdlg
);
}elsif ($widget == $okButton) {
- # write changes
- return
last;
}
}
}
- $self->dialog->destroy() ;
+ $self->dialog->destroy();
#restore old application title
yui::YUI::app()->setApplicationTitle($old_title);
+
+ return 1;
+}
+
+#=============================================================
+
+=head2 set_ports
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start host manager
+
+=cut
+
+#=============================================================
+
+sub set_ports {
+ my ($disabled, $ports, $log_net_drop) = @_;
+
+ if (!$disabled || -x "$::prefix/sbin/shorewall") {
+ # $do_pkgs->ensure_files_are_installed([ [ qw(shorewall shorewall) ], [ qw(shorewall-ipv6 shorewall6) ] ], $::isInstall) or return;
+ my $shorewall = network::shorewall::read(!$disabled);
+ if (!$shorewall) {
+ print ("unable to read shorewall configuration, skipping installation");
+ return;
+ }
+
+ $shorewall->{disabled} = $disabled;
+ $shorewall->{ports} = $ports;
+ $shorewall->{log_net_drop} = $log_net_drop;
+ print ($disabled ? "disabling shorewall" : "configuring shorewall to allow ports: $ports");
+ network::shorewall::write($shorewall, undef);
+ }
}
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start host manager
+
+=cut
+
+#=============================================================
+
+sub start {
+ my $self = shift;
+
+ my @server = ();
+ $self->wdg_servers(@server);
+
+ # init servers definitions
+ $self->all_servers($self->_initAllServers());
+
+ # initialize ifw_rules here
+ $self->ifw_rules($self->_initIFW());
+
+ my ($disabled, $servers, $unlisted, $log_net_drop) = $self->get_conf(undef) or return;
+ ($disabled, $servers, $unlisted, $log_net_drop) = $self->choose_allowed_services($disabled, $servers, $unlisted, $log_net_drop) or return;
+
+ my $system_file = '/etc/sysconfig/drakx-net';
+ my %global_settings = getVarsFromSh($system_file);
+
+ if (!$disabled && (!defined($global_settings{IFW}) || text2bool($global_settings{IFW}))) {
+ $self->choose_watched_services($servers, $unlisted) or return;
+ }
+
+ # preparing services when required ( look at $self->all_servers() )
+ foreach (@$servers) {
+ exists $_->{prepare} and $_->{prepare}();
+ }
+
+ my $ports = $self->to_ports($servers, $unlisted);
+
+ $self->set_ports($disabled, $ports, $log_net_drop) or return;
+
+};
+
1;
ass="hl com">* For full copyright and license information, please see * the docs/CREDITS.txt file. * */ /** * @ignore */ if (!defined('IN_PHPBB')) { exit; } /** * Responsible for holding all file relevant information, as well as doing file-specific operations. * The {@link fileupload fileupload class} can be used to upload several files, each of them being this object to operate further on. */ class filespec { var $filename = ''; var $realname = ''; var $uploadname = ''; var $mimetype = ''; var $extension = ''; var $filesize = 0; var $width = 0; var $height = 0; var $image_info = array(); var $destination_file = ''; var $destination_path = ''; var $file_moved = false; var $init_error = false; var $local = false; var $error = array(); var $upload = ''; /** * The plupload object * @var \phpbb\plupload\plupload */ protected $plupload; /** * phpBB Mimetype guesser * @var \phpbb\mimetype\guesser */ protected $mimetype_guesser; /** * File Class * @access private */ function filespec($upload_ary, $upload_namespace, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) { if (!isset($upload_ary)) { $this->init_error = true; return; } $this->filename = $upload_ary['tmp_name']; $this->filesize = $upload_ary['size']; $name = (STRIP) ? stripslashes($upload_ary['name']) : $upload_ary['name']; $name = trim(utf8_basename($name)); $this->realname = $this->uploadname = $name; $this->mimetype = $upload_ary['type']; // Opera adds the name to the mime type $this->mimetype = (strpos($this->mimetype, '; name') !== false) ? str_replace(strstr($this->mimetype, '; name'), '', $this->mimetype) : $this->mimetype; if (!$this->mimetype) { $this->mimetype = 'application/octet-stream'; } $this->extension = strtolower(self::get_extension($this->realname)); // Try to get real filesize from temporary folder (not always working) ;) $this->filesize = (@filesize($this->filename)) ? @filesize($this->filename) : $this->filesize; $this->width = $this->height = 0; $this->file_moved = false; $this->local = (isset($upload_ary['local_mode'])) ? true : false; $this->upload = $upload_namespace; $this->plupload = $plupload; $this->mimetype_guesser = $mimetype_guesser; } /** * Cleans destination filename * * @param real|unique|unique_ext $mode real creates a realname, filtering some characters, lowering every character. Unique creates an unique filename * @param string $prefix Prefix applied to filename * @param string $user_id The user_id is only needed for when cleaning a user's avatar * @access public */ function clean_filename($mode = 'unique', $prefix = '', $user_id = '') { if ($this->init_error) { return; } switch ($mode) { case 'real': // Remove every extension from filename (to not let the mime bug being exposed) if (strpos($this->realname, '.') !== false) { $this->realname = substr($this->realname, 0, strpos($this->realname, '.')); } // Replace any chars which may cause us problems with _ $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|'); $this->realname = rawurlencode(str_replace($bad_chars, '_', strtolower($this->realname))); $this->realname = preg_replace("/%(\w{2})/", '_', $this->realname); $this->realname = $prefix . $this->realname . '.' . $this->extension; break; case 'unique': $this->realname = $prefix . md5(unique_id()); break; case 'avatar': $this->extension = strtolower($this->extension); $this->realname = $prefix . $user_id . '.' . $this->extension; break; case 'unique_ext': default: $this->realname = $prefix . md5(unique_id()) . '.' . $this->extension; break; } } /** * Get property from file object */ function get($property) { if ($this->init_error || !isset($this->$property)) { return false; } return $this->$property; } /** * Check if file is an image (mimetype) * * @return true if it is an image, false if not */ function is_image() { return (strpos($this->mimetype, 'image/') === 0); } /** * Check if the file got correctly uploaded * * @return true if it is a valid upload, false if not */ function is_uploaded() { $is_plupload = $this->plupload && $this->plupload->is_active(); if (!$this->local && !$is_plupload && !is_uploaded_file($this->filename)) { return false; } if (($this->local || $is_plupload) && !file_exists($this->filename)) { return false; } return true; } /** * Remove file */ function remove() { if ($this->file_moved) { @unlink($this->destination_file); } } /** * Get file extension * * @param string Filename that needs to be checked * @return string Extension of the supplied filename */ static public function get_extension($filename) { if (strpos($filename, '.') === false) { return ''; } $filename = explode('.', $filename); return array_pop($filename); } /** * Get mimetype * * @param string $filename Filename that needs to be checked * @return string Mimetype of supplied filename */ function get_mimetype($filename) { if ($this->mimetype_guesser !== null) { $mimetype = $this->mimetype_guesser->guess($filename, $this->uploadname); if ($mimetype !== 'application/octet-stream') { $this->mimetype = $mimetype; } } return $this->mimetype; } /** * Get filesize */ function get_filesize($filename) { return @filesize($filename); } /** * Check the first 256 bytes for forbidden content */ function check_content($disallowed_content) { if (empty($disallowed_content)) { return true; } $fp = @fopen($this->filename, 'rb'); if ($fp !== false) { $ie_mime_relevant = fread($fp, 256); fclose($fp); foreach ($disallowed_content as $forbidden) { if (stripos($ie_mime_relevant, '<' . $forbidden) !== false) { return false; } } } return true; } /** * Move file to destination folder * The phpbb_root_path variable will be applied to the destination path * * @param string $destination Destination path, for example $config['avatar_path'] * @param bool $overwrite If set to true, an already existing file will be overwritten * @param bool $skip_image_check If set to true, the check for the file to be a valid image is skipped * @param string $chmod Permission mask for chmodding the file after a successful move. The mode entered here reflects the mode defined by {@link phpbb_chmod()} * * @access public */ function move_file($destination, $overwrite = false, $skip_image_check = false, $chmod = false) { global $user, $phpbb_root_path; if (sizeof($this->error)) { return false; } $chmod = ($chmod === false) ? CHMOD_READ | CHMOD_WRITE : $chmod; // We need to trust the admin in specifying valid upload directories and an attacker not being able to overwrite it... $this->destination_path = $phpbb_root_path . $destination; // Check if the destination path exist... if (!file_exists($this->destination_path)) { @unlink($this->filename); return false; } $upload_mode = (@ini_get('open_basedir') || @ini_get('safe_mode') || strtolower(@ini_get('safe_mode')) == 'on') ? 'move' : 'copy'; $upload_mode = ($this->local) ? 'local' : $upload_mode; $this->destination_file = $this->destination_path . '/' . utf8_basename($this->realname); // Check if the file already exist, else there is something wrong... if (file_exists($this->destination_file) && !$overwrite) { @unlink($this->filename); $this->error[] = $user->lang($this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR', $this->destination_file); $this->file_moved = false; return false; } else { if (file_exists($this->destination_file)) { @unlink($this->destination_file); } switch ($upload_mode) { case 'copy': if (!@copy($this->filename, $this->destination_file)) { if (!@move_uploaded_file($this->filename, $this->destination_file)) { $this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); } } break; case 'move': if (!@move_uploaded_file($this->filename, $this->destination_file)) { if (!@copy($this->filename, $this->destination_file)) { $this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); } } break; case 'local': if (!@copy($this->filename, $this->destination_file)) { $this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'GENERAL_UPLOAD_ERROR'], $this->destination_file); } break; } // Remove temporary filename @unlink($this->filename); if (sizeof($this->error)) { return false; } phpbb_chmod($this->destination_file, $chmod); } // Try to get real filesize from destination folder $this->filesize = (@filesize($this->destination_file)) ? @filesize($this->destination_file) : $this->filesize; // Get mimetype of supplied file $this->mimetype = $this->get_mimetype($this->destination_file); if ($this->is_image() && !$skip_image_check) { $this->width = $this->height = 0; if (($this->image_info = @getimagesize($this->destination_file)) !== false) { $this->width = $this->image_info[0]; $this->height = $this->image_info[1]; if (!empty($this->image_info['mime'])) { $this->mimetype = $this->image_info['mime']; } // Check image type $types = fileupload::image_types(); if (!isset($types[$this->image_info[2]]) || !in_array($this->extension, $types[$this->image_info[2]])) { if (!isset($types[$this->image_info[2]])) { $this->error[] = sprintf($user->lang['IMAGE_FILETYPE_INVALID'], $this->image_info[2], $this->mimetype); } else { $this->error[] = sprintf($user->lang['IMAGE_FILETYPE_MISMATCH'], $types[$this->image_info[2]][0], $this->extension); } } // Make sure the dimensions match a valid image if (empty($this->width) || empty($this->height)) { $this->error[] = $user->lang['ATTACHED_IMAGE_NOT_IMAGE']; } } else { $this->error[] = $user->lang['UNABLE_GET_IMAGE_SIZE']; } } $this->file_moved = true; $this->additional_checks(); unset($this->upload); return true; } /** * Performing additional checks */ function additional_checks() { global $user; if (!$this->file_moved) { return false; } // Filesize is too big or it's 0 if it was larger than the maxsize in the upload form if ($this->upload->max_filesize && ($this->get('filesize') > $this->upload->max_filesize || $this->filesize == 0)) { $max_filesize = get_formatted_filesize($this->upload->max_filesize, false); $this->error[] = sprintf($user->lang[$this->upload->error_prefix . 'WRONG_FILESIZE'], $max_filesize['value'], $max_filesize['unit']); return false; } if (!$this->upload->valid_dimensions($this)) { $this->error[] = $user->lang($this->upload->error_prefix . 'WRONG_SIZE', $user->lang('PIXELS', (int) $this->upload->min_width), $user->lang('PIXELS', (int) $this->upload->min_height), $user->lang('PIXELS', (int) $this->upload->max_width), $user->lang('PIXELS', (int) $this->upload->max_height), $user->lang('PIXELS', (int) $this->width), $user->lang('PIXELS', (int) $this->height)); return false; } return true; } } /** * Class for assigning error messages before a real filespec class can be assigned */ class fileerror extends filespec { function fileerror($error_msg) { $this->error[] = $error_msg; } } /** * File upload class * Init class (all parameters optional and able to be set/overwritten separately) - scope is global and valid for all uploads */ class fileupload { var $allowed_extensions = array(); var $disallowed_content = array('body', 'head', 'html', 'img', 'plaintext', 'a href', 'pre', 'script', 'table', 'title'); var $max_filesize = 0; var $min_width = 0; var $min_height = 0; var $max_width = 0; var $max_height = 0; var $error_prefix = ''; /** @var int Timeout for remote upload */ var $upload_timeout = 6; /** * Init file upload class. * * @param string $error_prefix Used error messages will get prefixed by this string * @param array $allowed_extensions Array of allowed extensions, for example array('jpg', 'jpeg', 'gif', 'png') * @param int $max_filesize Maximum filesize * @param int $min_width Minimum image width (only checked for images) * @param int $min_height Minimum image height (only checked for images) * @param int $max_width Maximum image width (only checked for images) * @param int $max_height Maximum image height (only checked for images) * @param bool|array $disallowed_content If enabled, the first 256 bytes of the file must not * contain any of its values. Defaults to false. * */ function fileupload($error_prefix = '', $allowed_extensions = false, $max_filesize = false, $min_width = false, $min_height = false, $max_width = false, $max_height = false, $disallowed_content = false) { $this->set_allowed_extensions($allowed_extensions); $this->set_max_filesize($max_filesize); $this->set_allowed_dimensions($min_width, $min_height, $max_width, $max_height); $this->set_error_prefix($error_prefix); $this->set_disallowed_content($disallowed_content); } /** * Reset vars */ function reset_vars() { $this->max_filesize = 0; $this->min_width = $this->min_height = $this->max_width = $this->max_height = 0; $this->error_prefix = ''; $this->allowed_extensions = array(); $this->disallowed_content = array(); } /** * Set allowed extensions */ function set_allowed_extensions($allowed_extensions) { if ($allowed_extensions !== false && is_array($allowed_extensions)) { $this->allowed_extensions = $allowed_extensions; } } /** * Set allowed dimensions */ function set_allowed_dimensions($min_width, $min_height, $max_width, $max_height) { $this->min_width = (int) $min_width; $this->min_height = (int) $min_height; $this->max_width = (int) $max_width; $this->max_height = (int) $max_height; } /** * Set maximum allowed filesize */ function set_max_filesize($max_filesize) { if ($max_filesize !== false && (int) $max_filesize) { $this->max_filesize = (int) $max_filesize; } } /** * Set disallowed strings */ function set_disallowed_content($disallowed_content) { if ($disallowed_content !== false && is_array($disallowed_content)) { $this->disallowed_content = array_diff($disallowed_content, array('')); } } /** * Set error prefix */ function set_error_prefix($error_prefix) { $this->error_prefix = $error_prefix; } /** * Form upload method * Upload file from users harddisk * * @param string $form_name Form name assigned to the file input field (if it is an array, the key has to be specified) * @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser * @param \phpbb\plupload\plupload $plupload The plupload object * * @return object $file Object "filespec" is returned, all further operations can be done with this object * @access public */ function form_upload($form_name, \phpbb\mimetype\guesser $mimetype_guesser = null, \phpbb\plupload\plupload $plupload = null) { global $user, $request; $upload = $request->file($form_name); unset($upload['local_mode']); if ($plupload) { $result = $plupload->handle_upload($form_name); if (is_array($result)) { $upload = array_merge($upload, $result); } } $file = new filespec($upload, $this, $mimetype_guesser, $plupload); if ($file->init_error) { $file->error[] = ''; return $file; } // Error array filled? if (isset($upload['error'])) { $error = $this->assign_internal_error($upload['error']); if ($error !== false) { $file->error[] = $error; return $file; } } // Check if empty file got uploaded (not catched by is_uploaded_file) if (isset($upload['size']) && $upload['size'] == 0) { $file->error[] = $user->lang[$this->error_prefix . 'EMPTY_FILEUPLOAD']; return $file; } // PHP Upload filesize exceeded if ($file->get('filename') == 'none') { $max_filesize = @ini_get('upload_max_filesize'); $unit = 'MB'; if (!empty($max_filesize)) { $unit = strtolower(substr($max_filesize, -1, 1)); $max_filesize = (int) $max_filesize; $unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); } $file->error[] = (empty($max_filesize)) ? $user->lang[$this->error_prefix . 'PHP_SIZE_NA'] : sprintf($user->lang[$this->error_prefix . 'PHP_SIZE_OVERRUN'], $max_filesize, $user->lang[$unit]); return $file; } // Not correctly uploaded if (!$file->is_uploaded()) { $file->error[] = $user->lang[$this->error_prefix . 'NOT_UPLOADED']; return $file; } $this->common_checks($file); return $file; } /** * Move file from another location to phpBB */ function local_upload($source_file, $filedata = false, \phpbb\mimetype\guesser $mimetype_guesser = null) { global $user, $request; $upload = array(); $upload['local_mode'] = true; $upload['tmp_name'] = $source_file; if ($filedata === false) { $upload['name'] = utf8_basename($source_file); $upload['size'] = 0; } else { $upload['name'] = $filedata['realname']; $upload['size'] = $filedata['size']; $upload['type'] = $filedata['type']; } $file = new filespec($upload, $this, $mimetype_guesser); if ($file->init_error) { $file->error[] = ''; return $file; } if (isset($upload['error'])) { $error = $this->assign_internal_error($upload['error']); if ($error !== false) { $file->error[] = $error; return $file; } } // PHP Upload filesize exceeded if ($file->get('filename') == 'none') { $max_filesize = @ini_get('upload_max_filesize'); $unit = 'MB'; if (!empty($max_filesize)) { $unit = strtolower(substr($max_filesize, -1, 1)); $max_filesize = (int) $max_filesize; $unit = ($unit == 'k') ? 'KB' : (($unit == 'g') ? 'GB' : 'MB'); } $file->error[] = (empty($max_filesize)) ? $user->lang[$this->error_prefix . 'PHP_SIZE_NA'] : sprintf($user->lang[$this->error_prefix . 'PHP_SIZE_OVERRUN'], $max_filesize, $user->lang[$unit]); return $file; } // Not correctly uploaded if (!$file->is_uploaded()) { $file->error[] = $user->lang[$this->error_prefix . 'NOT_UPLOADED']; return $file; } $this->common_checks($file); $request->overwrite('local', $upload, \phpbb\request\request_interface::FILES); return $file; } /** * Remote upload method * Uploads file from given url * * @param string $upload_url URL pointing to file to upload, for example http://www.foobar.com/example.gif * @param \phpbb\mimetype\guesser $mimetype_guesser Mimetype guesser * @return object $file Object "filespec" is returned, all further operations can be done with this object * @access public */ function remote_upload($upload_url, \phpbb\mimetype\guesser $mimetype_guesser = null) { global $user, $phpbb_root_path; $upload_ary = array(); $upload_ary['local_mode'] = true; if (!preg_match('#^(https?://).*?\.(' . implode('|', $this->allowed_extensions) . ')$#i', $upload_url, $match)) { $file = new fileerror($user->lang[$this->error_prefix . 'URL_INVALID']); return $file; } if (empty($match[2])) { $file = new fileerror($user->lang[$this->error_prefix . 'URL_INVALID']); return $file; } $url = parse_url($upload_url); $host = $url['host']; $path = $url['path']; $port = (!empty($url['port'])) ? (int) $url['port'] : 80; $upload_ary['type'] = 'application/octet-stream'; $url['path'] = explode('.', $url['path']); $ext = array_pop($url['path']); $url['path'] = implode('', $url['path']); $upload_ary['name'] = utf8_basename($url['path']) . (($ext) ? '.' . $ext : ''); $filename = $url['path']; $filesize = 0; $remote_max_filesize = $this->max_filesize; if (!$remote_max_filesize) { $max_filesize = @ini_get('upload_max_filesize'); if (!empty($max_filesize)) { $unit = strtolower(substr($max_filesize, -1, 1)); $remote_max_filesize = (int) $max_filesize; switch ($unit) { case 'g': $remote_max_filesize *= 1024; // no break case 'm': $remote_max_filesize *= 1024; // no break case 'k': $remote_max_filesize *= 1024; // no break } } } $errno = 0; $errstr = ''; if (!($fsock = @fsockopen($host, $port, $errno, $errstr))) { $file = new fileerror($user->lang[$this->error_prefix . 'NOT_UPLOADED']); return $file; } // Make sure $path not beginning with / if (strpos($path, '/') === 0) { $path = substr($path, 1); } fputs($fsock, 'GET /' . $path . " HTTP/1.1\r\n"); fputs($fsock, "HOST: " . $host . "\r\n"); fputs($fsock, "Connection: close\r\n\r\n"); // Set a proper timeout for the socket socket_set_timeout($fsock, $this->upload_timeout); $get_info = false; $data = ''; $length = false; $timer_stop = time() + $this->upload_timeout; while ((!$length || $filesize < $length) && !@feof($fsock)) { if ($get_info) { if ($length) {