Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pfSense-pkg-tftpd package improvements #262

Merged
merged 18 commits into from
Jan 23, 2017
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion ftp/pfSense-pkg-tftpd/Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# $FreeBSD$

PORTNAME= pfSense-pkg-tftpd
PORTVERSION= 0.1.2
PORTVERSION= 0.1.3
CATEGORIES= ftp
MASTER_SITES= # empty
DISTFILES= # empty
Expand All @@ -25,12 +25,15 @@ do-extract:

do-install:
${MKDIR} ${STAGEDIR}${PREFIX}/pkg
${MKDIR} ${STAGEDIR}${PREFIX}/www
${MKDIR} ${STAGEDIR}/etc/inc/priv
${MKDIR} ${STAGEDIR}${DATADIR}
${INSTALL_DATA} -m 0644 ${FILESDIR}${PREFIX}/pkg/tftpd.xml \
${STAGEDIR}${PREFIX}/pkg
${INSTALL_DATA} ${FILESDIR}${PREFIX}/pkg/tftpd.inc \
${STAGEDIR}${PREFIX}/pkg
${INSTALL_DATA} -m 0755 ${FILESDIR}${PREFIX}/www/tftp_files.php \
${STAGEDIR}${PREFIX}/www
${INSTALL_DATA} ${FILESDIR}/etc/inc/priv/tftpd.priv.inc \
${STAGEDIR}/etc/inc/priv
${INSTALL_DATA} ${FILESDIR}${DATADIR}/info.xml \
Expand Down
12 changes: 7 additions & 5 deletions ftp/pfSense-pkg-tftpd/files/etc/inc/priv/tftpd.priv.inc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* tftpd.priv.inc
*
* part of pfSense (https://www.pfsense.org)
* Copyright (c) 2015-2017 Rubicon Communications, LLC (Netgate)
* Copyright (c) 2016 Stefan Seidel
* All rights reserved.
*
Expand All @@ -21,11 +22,12 @@

global $priv_list;

$priv_list['page-system-tftpd'] = array();
$priv_list['page-system-tftpd']['name'] = "WebCfg - System: tftpd package";
$priv_list['page-system-tftpd']['descr'] = "Allow access to tftpd package GUI";
$priv_list['page-system-tftpd']['match'] = array();
$priv_list['page-services-tftpd'] = array();
$priv_list['page-services-tftpd']['name'] = "WebCfg - Services: tftpd package";
$priv_list['page-services-tftpd']['descr'] = "Allow access to tftpd package GUI";
$priv_list['page-services-tftpd']['match'] = array();

$priv_list['page-system-tftpd']['match'][] = "pkg_edit.php?xml=tftpd.xml*";
$priv_list['page-services-tftpd']['match'][] = "pkg_edit.php?xml=tftpd.xml*";
$priv_list['page-services-tftpd']['match'][] = "tftp_files.php*";

?>
105 changes: 89 additions & 16 deletions ftp/pfSense-pkg-tftpd/files/usr/local/pkg/tftpd.inc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* tftpd.inc
*
* part of pfSense (https://www.pfsense.org)
* Copyright (c) 2015-2017 Rubicon Communications, LLC (Netgate)
* Copyright (c) 2016 Stefan Seidel
* All rights reserved.
*
Expand All @@ -19,25 +20,60 @@
* limitations under the License.
*/

if (!function_exists("filter_configure")) {
require_once("filter.inc");
}
require_once("globals.inc");
require_once("interfaces.inc");
require_once("pfsense-utils.inc");
require_once("service-utils.inc");
require_once("util.inc");

/* Helper function for files listing */
function tftp_byte_convert($bytes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have format_bytes() in util.inc, is this different in some way that's required by TFTP?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, no clue that it existed.

if ($bytes <= 0) {
return '0 Byte';
}
$convention = 1000;
$s = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB');
$e = floor(log($bytes, $convention));
return round($bytes/pow($convention, $e), 2) . ' ' . $s[$e];
}

/* Create backup of the TFTP server directory */
function tftp_create_backup($trigger_download = false) {
global $backup_dir, $backup_path, $files_dir;

conf_mount_rw();
safe_mkdir("{$backup_dir}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to see a test that $backup_dir contains something sane before using it in this way. Better as a function parameter than as a global. (Same with $files_dir)

Copy link
Contributor Author

@doktornotor doktornotor Jan 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backup_dir is hardcoded to /root/backup in tftp_files.php

if (mwexec("/usr/bin/tar -czC / -f {$backup_path} {$files_dir}") || !file_exists("{$backup_path}")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable insertions here need to be changed out so they use escapeshellarg() instead of using them directly. For example:
if (mwexec("/usr/bin/tar -czC / -f " . escapeshellarg($backup_path) . " " . escapeshellarg($files_dir)) || !file_exists("{$backup_path}")) {

header("Location: tftp_files.php?savemsg=Backup+failed.&result=alert-warning");
} elseif ($trigger_download == false) {
header("Location: tftp_files.php?savemsg=Backup+has+been+created");
}
conf_mount_ro();
}

function install_package_tftpd() {
safe_mkdir("/tftpboot");
if (is_array($config['installedpackages']['tftpd'])) {
$tftpd_conf = &$config['installedpackages']['tftpd']['config'][0];
} else {
$tftpd_conf = array();
}
$datadir = ($tftpd_conf['datadir'] ?: '/tftpboot');
safe_mkdir("{$datadir}");
unlink_if_exists("/usr/local/etc/rc.d/tftpd");
}

function deinstall_package_tftpd() {
@rmdir("/tftpboot");
if (is_array($config['installedpackages']['tftpd'])) {
$tftpd_conf = &$config['installedpackages']['tftpd']['config'][0];
} else {
$tftpd_conf = array();
}
$datadir = ($tftpd_conf['datadir'] ?: '/tftpboot');
// Will only get removed when empty
@rmdir("{$datadir}");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely needs a sanity check to make sure the user didn't do something dumb like set it to /usr/local/www/ or /etc/. I see input validation prevents / but even so this may be too dangerous to rmdir. It's probably better to just leave the data in place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As noted there, the only thing it removed are empty dirs. http://php.net/manual/en/function.rmdir.php

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True but if it's empty, why bother?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well to not leave lingering empty dir there (some people like to not leave cruft behind). Can remove it altogether of course, I have no preference here.

}

function sync_package_tftpd() {
global $config;
global $g, $config;

conf_mount_rw();

Expand All @@ -56,17 +92,35 @@ function sync_package_tftpd() {
unlink_if_exists('/usr/local/etc/rc.d/tftpd.sh');
return;
}


// Root directory
$datadir = $tftpd_conf['datadir'];

if (!is_dir($datadir)) {
log_error("TFTP files directory {$datadir} does not exist.");
return;
}

// TFTP Server Bind IP
if (!empty($tftpd_conf['tftpd_ip'])) {
$address = $tftpd_conf['tftpd_ip'];
if (is_ipaddrv6($address)) {
$address = "-a [{$address}]";
} else {
$address = "-a {$address}";
}
}

$pidfile = "{$g['varrun_path']}/tftpd-hpa.pid";

// IPv4 Only?
if ($tftpd_conf['tftpd_ipv4only'] == "on") {
$options = "-4";
}

// Max Block Size
if (!empty($tftpd_conf['tftpd_blocksize'])) {
$options .= " -B {$tftpd_conf['tftpd_blocksize']}";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, re: escapeshellarg()

}

write_rcfile(array(
"file" => "tftpd.sh",
"start" => "/usr/local/libexec/in.tftpd -l -s {$datadir}",
"start" => "/usr/local/libexec/in.tftpd -l -s {$datadir} {$address} -P {$pidfile} {$options}",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above, re: escapeshellarg(), except for $options which should be OK already

"stop" => "/usr/bin/killall in.tftpd"
)
);
Expand All @@ -82,14 +136,33 @@ function sync_package_tftpd() {
}

conf_mount_ro();

filter_configure();
}

function validate_form_tftpd($post, &$input_errors) {
if ($post['datadir'] && !is_dir($post['datadir'])) {
$input_errors[] = 'Directory for files does not exist!';
}

if ($post['datadir'] == '/') {
$input_errors[] = 'Setting "/" as directory for files is not allowed!';
}

if ($post['tftpd_ip']) {
if ($post['tftpd_ipv4only'] && !is_ipaddrv4($post['tftpd_ip'])) {
$input_errors[] = 'TFTP Server Bind IP must be a valid IPv4 address!';
} elseif (!is_ipaddr($post['tftpd_ip'])) {
$input_errors[] = 'TFTP Server Bind IP must be a valid IP address!';
}
if (!is_ipaddr_configured($post['tftpd_ip'])) {
$input_errors[] = "{$post['tftpd_ip']} TFTP Server Bind IP must be a valid, locally configured IP address!";
}
}

if ($post['tftpd_blocksize']) {
if (!is_numericint($post['tftpd_blocksize']) || ($post['tftpd_blocksize'] < 512) || ($post['tftpd_blocksize'] > 65464)) {
$input_errors[] = 'Max Block Size must be an integer with a permitted range from 512 to 65464!';
}
}
}

?>
64 changes: 57 additions & 7 deletions ftp/pfSense-pkg-tftpd/files/usr/local/pkg/tftpd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* tftpd.xml
*
* part of pfSense (https://www.pfsense.org)
* Copyright (c) 2015-2017 Rubicon Communications, LLC (Netgate)
* Copyright (c) 2016 Stefan Seidel
* All rights reserved.
*
Expand All @@ -30,7 +31,7 @@
<include_file>/usr/local/pkg/tftpd.inc</include_file>
<aftersaveredirect>/pkg_edit.php?xml=tftpd.xml</aftersaveredirect>
<menu>
<name>tftpd</name>
<name>TFTP Server</name>
<section>Services</section>
<url>/pkg_edit.php?xml=tftpd.xml</url>
</menu>
Expand All @@ -40,19 +41,71 @@
<executable>in.tftpd</executable>
<description>TFTP daemon</description>
</service>
<tabs>
<tab>
<text>Settings</text>
<url>/pkg_edit.php?xml=tftpd.xml</url>
<active/>
</tab>
<tab>
<text>Files</text>
<url>/tftp_files.php</url>
</tab>
</tabs>
<fields>
<field>
<fielddescr>Enable TFTP service</fielddescr>
<fieldname>tftpd_enable</fieldname>
<description>Enable the TFTP service?</description>
<description>Check to enable the TFTP service.</description>
<type>checkbox</type>
</field>
<field>
<fielddescr>Directory for files</fielddescr>
<fielddescr>Directory for Files</fielddescr>
<fieldname>datadir</fieldname>
<description>Enter the directory path to the files that the TFTP server should serve. The directory must exist. Default: /tftpboot</description>
<description>
<![CDATA[
Enter the directory path to the files that the TFTP server should serve. The directory must exist.
<span class="text-info">Default: /tftpboot</span>
]]>
</description>
<type>input</type>
<default_value>/tftpboot</default_value>
<required/>
</field>
<field>
<fielddescr>TFTP Server Bind IP</fielddescr>
<fieldname>tftpd_ip</fieldname>
<description>
<![CDATA[
By default, TFTP server will listen on all local addresses. If this is not desired,
you can restrict this to a specific local address.<br/>
<span class="text-danger">This must be a valid, locally configured IP address.</span>
]]>
</description>
<type>input</type>
</field>
<field>
<fielddescr>IPv4 Only</fielddescr>
<fieldname>tftpd_ipv4only</fieldname>
<description>Check to allow clients to connect with IPv4 only.</description>
<type>checkbox</type>
</field>
<field>
<fielddescr>Max Block Size</fielddescr>
<fieldname>tftpd_blocksize</fieldname>
<description>
<![CDATA[
Specifies the maximum permitted block size. <span class="text-info">The permitted range is from 512 to 65464.</span>
<div class="infoblock">
Some embedded clients request large block sizes and yet do not handle fragmented packets
correctly; for these clients, it is recommended to set this value to the smallest MTU
on your network minus 32 bytes (20 bytes for IP, 8 for UDP, and 4 for TFTP; less if you
use IP options on your network.)<br/>
For example, on a standard Ethernet (MTU 1500) a value of 1468 is reasonable.
</div>
]]>
</description>
<type>input</type>
</field>
</fields>
<custom_php_install_command>
Expand All @@ -61,9 +114,6 @@
<custom_php_deinstall_command>
deinstall_package_tftpd();
</custom_php_deinstall_command>
<custom_add_php_command>
sync_package_tftpd();
</custom_add_php_command>
<custom_php_resync_config_command>
sync_package_tftpd();
</custom_php_resync_config_command>
Expand Down
Loading