diff --git a/Doxyfile b/Doxyfile index 0644aa7..d3f30ad 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,18 +1,17 @@ -# Doxyfile 1.9.1 +# Doxyfile 1.9.3 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = "PF Rule Editor" -PROJECT_NUMBER = 7.0 +PROJECT_NUMBER = 7.1 PROJECT_BRIEF = PROJECT_LOGO = OUTPUT_DIRECTORY = ./docs CREATE_SUBDIRS = NO ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -OUTPUT_TEXT_DIRECTION = None BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ @@ -82,6 +81,7 @@ INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = YES HIDE_COMPOUND_REFERENCE= NO +SHOW_HEADERFILE = YES SHOW_INCLUDE_FILES = YES SHOW_GROUPED_MEMB_INC = NO FORCE_LOCAL_INCLUDES = NO @@ -111,6 +111,7 @@ QUIET = NO WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES +WARN_IF_INCOMPLETE_DOC = YES WARN_NO_PARAMDOC = NO WARN_AS_ERROR = NO WARN_FORMAT = "$file:$line: $text" @@ -176,6 +177,7 @@ HTML_DYNAMIC_SECTIONS = NO HTML_INDEX_NUM_ENTRIES = 100 GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +DOCSET_FEEDURL = DOCSET_BUNDLE_ID = org.doxygen.Project DOCSET_PUBLISHER_ID = org.doxygen.Publisher DOCSET_PUBLISHER_NAME = Publisher @@ -198,14 +200,17 @@ GENERATE_ECLIPSEHELP = NO ECLIPSE_DOC_ID = org.doxygen.Project DISABLE_INDEX = NO GENERATE_TREEVIEW = YES +FULL_SIDEBAR = NO ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 EXT_LINKS_IN_WINDOW = NO +OBFUSCATE_EMAILS = YES HTML_FORMULA_FORMAT = png FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES FORMULA_MACROFILE = USE_MATHJAX = NO +MATHJAX_VERSION = MathJax_2 MATHJAX_FORMAT = HTML-CSS MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest MATHJAX_EXTENSIONS = @@ -236,7 +241,6 @@ PDF_HYPERLINKS = YES USE_PDFLATEX = YES LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO -LATEX_SOURCE_CODE = NO LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO LATEX_EMOJI_DIRECTORY = @@ -249,7 +253,6 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = -RTF_SOURCE_CODE = NO #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- @@ -270,7 +273,6 @@ XML_NS_MEMB_FILE_SCOPE = NO #--------------------------------------------------------------------------- GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- @@ -305,7 +307,6 @@ EXTERNAL_PAGES = YES #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -CLASS_DIAGRAMS = YES DIA_PATH = HIDE_UNDOC_RELATIONS = NO HAVE_DOT = YES @@ -327,6 +328,7 @@ CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES +DIR_GRAPH_MAX_DEPTH = 1 DOT_IMAGE_FORMAT = png INTERACTIVE_SVG = NO DOT_PATH = /usr/local/bin/ diff --git a/README.md b/README.md index e93cb22..257d371 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ PFRE is a packet filter rule editor for OpenBSD/pf. PFRE is expected to be used by beginners and system administrators alike. -The [UTMFW](https://github.com/sonertari/UTMFW) and [PFFW](https://github.com/sonertari/PFFW) projects use PFRE on their web administration interfaces. If you don't want to [install PFRE](https://github.com/sonertari/PFRE#how-to-install) yourself, you can download the installation iso file of [UTMFW](https://github.com/sonertari/UTMFW#download) or [PFFW](https://github.com/sonertari/PFFW#download) to test drive PFRE easily. +The [UTMFW](https://github.com/sonertari/UTMFW) and [PFFW](https://github.com/sonertari/PFFW) projects use PFRE on their web administration interfaces. If you don't want to [install PFRE](https://github.com/sonertari/PFRE#how-to-install) yourself, you can download the installation files of [UTMFW](https://github.com/sonertari/UTMFW#download) or [PFFW](https://github.com/sonertari/PFFW#download) to test drive PFRE easily. ## Features @@ -58,8 +58,8 @@ You can find a couple of screenshots on the [wiki](https://github.com/sonertari/ Here are the basic steps to obtain a working PFRE installation: -- Install OpenBSD 7.0, perhaps on a VM. -- Install PHP 8.0.10, php-pcntl, and php-cgi. +- Install OpenBSD 7.1, perhaps on a VM. +- Install PHP 8.1.4, php-pcntl, and php-cgi. - Copy the files in PFRE src folder to /var/www/htdocs/pfre/. - Configure httpd.conf for PFRE. - Create admin and user users, and set their passwords. @@ -74,9 +74,9 @@ The OpenBSD installation guide is at [faq4](http://www.openbsd.org/faq/faq4.html Here are a couple of guidelines: -- You can download install69.iso available at OpenBSD mirrors. +- You can download install71.iso available at OpenBSD mirrors. - It may be easier to install a PFRE test system on a VM of your choice, e.g. VMware or VirtualBox, rather than bare hardware. -- 512MB RAM and 8GB HD should be more than enough. +- 256MB RAM and 8GB HD should be enough. - If you want to obtain a packet filtering firewall, make sure the VM has at least 2 ethernet interfaces: + The external interface may obtain its IP address over DHCP + The internal interface should have a static IP address @@ -100,18 +100,19 @@ Download the required packages from an OpenBSD mirror and copy them to $PKG\_PAT argon2-20190702.tgz bzip2-1.0.8p0.tgz + capstone-4.0.2.tgz femail-1.0p1.tgz femail-chroot-1.0p3.tgz gettext-runtime-0.21p1.tgz libiconv-1.16p0.tgz libsodium-1.0.18p1.tgz - libxml-2.9.12.tgz + libxml-2.9.13.tgz oniguruma-6.9.7.1.tgz - pcre2-10.36.tgz - php-8.0.10p0.tgz - php-cgi-8.0.10p0.tgz - php-pcntl-8.0.10p0.tgz - xz-5.2.5.tgz + pcre2-10.37.tgz + php-8.1.4p1.tgz + php-cgi-8.1.4.tgz + php-pcntl-8.1.4.tgz + xz-5.2.5p0.tgz Install PHP, php-pcntl, and php-cgi by running the following commands, which should install their dependencies as well: @@ -127,18 +128,19 @@ Here is the expected output of that command: argon2-20190702 C implementation of Argon2 - password hashing function bzip2-1.0.8p0 block-sorting file compressor, unencumbered + capstone-4.0.2 multi-platform, multi-architecture disassembly framework femail-1.0p1 simple SMTP client femail-chroot-1.0p3 simple SMTP client for chrooted web servers gettext-runtime-0.21p1 GNU gettext runtime libraries and programs libiconv-1.16p0 character set conversion library libsodium-1.0.18p1 library for network communications and cryptography - libxml-2.9.12 XML parsing library + libxml-2.9.13 XML parsing library oniguruma-6.9.7.1 regular expressions library - pcre2-10.36 perl-compatible regular expression library, version 2 - php-8.0.10p0 server-side HTML-embedded scripting language - php-cgi-8.0.10p0 php CGI binary - php-pcntl-8.0.10p0 PCNTL extensions for php - xz-5.2.5 LZMA compression and decompression tools + pcre2-10.37 perl-compatible regular expression library, version 2 + php-8.1.4p1 server-side HTML-embedded scripting language + php-cgi-8.1.4 php CGI binary + php-pcntl-8.1.4 PCNTL extensions for php + xz-5.2.5p0 LZMA compression and decompression tools ### Install PFRE @@ -216,9 +218,9 @@ However, you are advised to pick a better password than soner123. Go to /usr/local/bin/ and create a link to php executable: # cd /usr/local/bin - # ln -s php-8.0 php + # ln -s php-8.1 php -Edit the /etc/php-8.0.ini file to write error messages to syslog, otherwise they may disturb pfctl test reports: +Edit the /etc/php-8.1.ini file to write error messages to syslog, otherwise they may disturb pfctl test reports: error_log = syslog @@ -226,9 +228,9 @@ Also, edit the /etc/php-fpm.conf file to write error messages to syslog: error_log = syslog -To enable pcntl, go to /etc/php-8.0/ and create the pcntl.ini file: +To enable pcntl, go to /etc/php-8.1/ and create the pcntl.ini file: - # cd /etc/php-8.0/ + # cd /etc/php-8.1/ # touch pcntl.ini And add the following line to pcntl.ini: @@ -267,9 +269,9 @@ If you want the web server to be started automatically after a reboot, first cop Then add the following lines to it: - if [ -x /usr/local/sbin/php-fpm-8.0 ]; then + if [ -x /usr/local/sbin/php-fpm-8.1 ]; then echo 'PHP CGI server' - /usr/local/sbin/php-fpm-8.0 + /usr/local/sbin/php-fpm-8.1 fi Create the rc.conf.local file under /etc/ @@ -294,7 +296,7 @@ And uncomment the line which enables forwarding of IPv4 packets: Now you can either reboot the system or start the php cgi server and the web server manually using the following commands: - # /usr/local/sbin/php-fpm-8.0 + # /usr/local/sbin/php-fpm-8.1 # /usr/sbin/httpd Finally, if you point your web browser to the IP address of PFRE, you should see the login page. And you should be able to log in by entering admin:soner123 as user and password. diff --git a/src/Controller/ctlr.php b/src/Controller/ctlr.php index ed56a7d..813281f 100755 --- a/src/Controller/ctlr.php +++ b/src/Controller/ctlr.php @@ -1,7 +1,7 @@ #!/usr/bin/env php &1", $encoded, $retval); +exec("/usr/bin/doas $cwd/ctlr.php -n ".escapeshellarg($args)." 2>&1", $encoded, $retval); // There must be only one element in $encoded array, but do not miss the others if any $encoded= implode(' ', $encoded); echo $encoded; diff --git a/src/Model/include.php b/src/Model/include.php index e274b0f..872b510 100644 --- a/src/Model/include.php +++ b/src/Model/include.php @@ -1,6 +1,6 @@ str= preg_replace('/{/', ' { ', $this->str); $this->str= preg_replace('/}/', ' } ', $this->str); - $this->str= preg_replace('/\(/', ' ( ', $this->str); - $this->str= preg_replace('/\)/', ' ) ', $this->str); $this->str= preg_replace('/,/', ' , ', $this->str); $this->str= preg_replace('/=/', ' = ', $this->str); $this->str= preg_replace('/"/', '', $this->str); @@ -92,4 +90,4 @@ function generate() return $this->str; } } -?> \ No newline at end of file +?> diff --git a/src/Model/lib/NatBase.php b/src/Model/lib/NatBase.php index 30e0112..0ea5558 100644 --- a/src/Model/lib/NatBase.php +++ b/src/Model/lib/NatBase.php @@ -1,6 +1,6 @@
- (C) 2021 Soner Tari. + (C) 2022 Soner Tari.
diff --git a/src/View/header.php b/src/View/header.php index 5fda81b..f45f095 100644 --- a/src/View/header.php +++ b/src/View/header.php @@ -1,6 +1,6 @@ engine === CRYPT_ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->enchanged) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); $this->enchanged = false; } @@ -861,15 +862,15 @@ function encrypt($plaintext) if ($len >= $block_size) { if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) { if ($this->enbuffer['enmcrypt_init'] === true) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $iv); + mcrypt_generic_init($this->enmcrypt, $this->key, $iv); $this->enbuffer['enmcrypt_init'] = false; } - $ciphertext.= @mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); + $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size)); $iv = substr($ciphertext, -$block_size); $len%= $block_size; } else { while ($len >= $block_size) { - $iv = @mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); + $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size); $ciphertext.= $iv; $len-= $block_size; $i+= $block_size; @@ -878,22 +879,26 @@ function encrypt($plaintext) } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $block = $iv ^ substr($plaintext, -$len); $iv = substr_replace($iv, $block, 0, $len); $ciphertext.= $block; $pos = $len; } + restore_error_handler(); + return $ciphertext; } - $ciphertext = @mcrypt_generic($this->enmcrypt, $plaintext); + $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); + mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV); } + restore_error_handler(); + return $ciphertext; } @@ -1134,13 +1139,14 @@ function decrypt($ciphertext) } if ($this->engine === CRYPT_ENGINE_MCRYPT) { + set_error_handler(array($this, 'do_nothing')); $block_size = $this->block_size; if ($this->changed) { $this->_setupMcrypt(); $this->changed = false; } if ($this->dechanged) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); $this->dechanged = false; } @@ -1168,26 +1174,30 @@ function decrypt($ciphertext) } if ($len >= $block_size) { $cb = substr($ciphertext, $i, $len - $len % $block_size); - $plaintext.= @mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; + $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb; $iv = substr($cb, -$block_size); $len%= $block_size; } if ($len) { - $iv = @mcrypt_generic($this->ecb, $iv); + $iv = mcrypt_generic($this->ecb, $iv); $plaintext.= $iv ^ substr($ciphertext, -$len); $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len); $pos = $len; } + restore_error_handler(); + return $plaintext; } - $plaintext = @mdecrypt_generic($this->demcrypt, $ciphertext); + $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext); if (!$this->continuousBuffer) { - @mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); + mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV); } + restore_error_handler(); + return $this->paddable ? $this->_unpad($plaintext) : $plaintext; } @@ -1645,9 +1655,12 @@ function isValidEngine($engine) } return false; case CRYPT_ENGINE_MCRYPT: - return $this->cipher_name_mcrypt && + set_error_handler(array($this, 'do_nothing')); + $result = $this->cipher_name_mcrypt && extension_loaded('mcrypt') && - in_array($this->cipher_name_mcrypt, @mcrypt_list_algorithms()); + in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms()); + restore_error_handler(); + return $result; case CRYPT_ENGINE_INTERNAL: return true; } @@ -1724,17 +1737,19 @@ function _setEngine() } if ($this->engine != CRYPT_ENGINE_MCRYPT && $this->enmcrypt) { + set_error_handler(array($this, 'do_nothing')); // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed, // (re)open them with the module named in $this->cipher_name_mcrypt - @mcrypt_module_close($this->enmcrypt); - @mcrypt_module_close($this->demcrypt); + mcrypt_module_close($this->enmcrypt); + mcrypt_module_close($this->demcrypt); $this->enmcrypt = null; $this->demcrypt = null; if ($this->ecb) { - @mcrypt_module_close($this->ecb); + mcrypt_module_close($this->ecb); $this->ecb = null; } + restore_error_handler(); } $this->changed = true; @@ -1852,19 +1867,19 @@ function _setupMcrypt() CRYPT_MODE_STREAM => MCRYPT_MODE_STREAM, ); - $this->demcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); - $this->enmcrypt = @mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); + $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], ''); // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer() // to workaround mcrypt's broken ncfb implementation in buffered mode // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps} if ($this->mode == CRYPT_MODE_CFB) { - $this->ecb = @mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); + $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, ''); } } // else should mcrypt_generic_deinit be called? if ($this->mode == CRYPT_MODE_CFB) { - @mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); + mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size)); } } @@ -2586,7 +2601,7 @@ function &_getLambdaFunctions() * * @see self::_setupInlineCrypt() * @access private - * @param $bytes + * @param string $bytes * @return string */ function _hashInlineCryptFunction($bytes) @@ -2659,4 +2674,13 @@ function safe_intval_inline() return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))'; } } + + /** + * Dummy error handler to suppress mcrypt errors + * + * @access private + */ + function do_nothing() + { + } } diff --git a/src/View/lib/phpseclib/Crypt/Hash.php b/src/View/lib/phpseclib/Crypt/Hash.php index ef514e9..a665801 100644 --- a/src/View/lib/phpseclib/Crypt/Hash.php +++ b/src/View/lib/phpseclib/Crypt/Hash.php @@ -191,7 +191,7 @@ function __construct($hash = 'sha1') * PHP4 compatible Default Constructor. * * @see self::__construct() - * @param int $mode + * @param string $hash * @access public */ function Crypt_Hash($hash = 'sha1') @@ -881,7 +881,6 @@ function _not($int) * _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the * possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster. * - * @param int $... * @return int * @see self::_sha256() * @access private diff --git a/src/View/lib/phpseclib/Crypt/RSA.php b/src/View/lib/phpseclib/Crypt/RSA.php index d57617b..b156914 100644 --- a/src/View/lib/phpseclib/Crypt/RSA.php +++ b/src/View/lib/phpseclib/Crypt/RSA.php @@ -517,7 +517,7 @@ function __construct() case !function_exists('openssl_pkey_get_details'): define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL); break; - case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): + case function_exists('phpinfo') && extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>=') && file_exists($this->configFile): // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work ob_start(); @phpinfo(); @@ -591,7 +591,7 @@ function Crypt_RSA() * @access public * @param int $bits * @param int $timeout - * @param Math_BigInteger $p + * @param array $partial */ function createKey($bits = 1024, $timeout = false, $partial = array()) { @@ -770,7 +770,12 @@ function createKey($bits = 1024, $timeout = false, $partial = array()) * * @access private * @see self::setPrivateKeyFormat() - * @param string $RSAPrivateKey + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @param Math_BigInteger $d + * @param array $primes + * @param array $exponents + * @param array $coefficients * @return string */ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) @@ -935,9 +940,9 @@ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) ); $key = "openssh-key-v1\0$key"; - return "-----BEGIN OPENSSH PRIVATE KEY-----\r\n" . - chunk_split(base64_encode($key), 70) . - "-----END OPENSSH PRIVATE KEY-----"; + return "-----BEGIN OPENSSH PRIVATE KEY-----\n" . + chunk_split(base64_encode($key), 70, "\n") . + "-----END OPENSSH PRIVATE KEY-----\n"; default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1 $components = array(); foreach ($raw as $name => $value) { @@ -1065,8 +1070,9 @@ function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients) * * @access private * @see self::setPublicKeyFormat() - * @param string $RSAPrivateKey - * @return string + * @param Math_BigInteger $n + * @param Math_BigInteger $e + * @return string|array */ function _convertPublicKey($n, $e) { @@ -1298,6 +1304,7 @@ function. As is, the definitive authority on this encoding scheme isn't the IET $length = $this->_decodeLength($temp); switch ($this->_string_shift($temp, $length)) { case "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01": // rsaEncryption + case "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0A": // rsaPSS break; case "\x2a\x86\x48\x86\xf7\x0d\x01\x05\x03": // pbeWithMD5AndDES-CBC /* @@ -1630,6 +1637,8 @@ function. As is, the definitive authority on this encoding scheme isn't the IET return $components; } + + return false; } /** @@ -1968,7 +1977,6 @@ function setPrivateKey($key = false, $type = false) * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional */ function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS8) @@ -2026,7 +2034,6 @@ function getPublicKeyFingerprint($algorithm = 'md5') * * @see self::getPublicKey() * @access public - * @param string $key * @param int $type optional * @return mixed */ @@ -2051,8 +2058,7 @@ function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1) * * @see self::getPrivateKey() * @access private - * @param string $key - * @param int $type optional + * @param int $mode optional */ function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS8) { @@ -2269,7 +2275,7 @@ function setMGFHash($hash) * of the hash function Hash) and 0. * * @access public - * @param int $format + * @param int $sLen */ function setSaltLength($sLen) { @@ -2302,7 +2308,7 @@ function _i2osp($x, $xLen) * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}. * * @access private - * @param string $x + * @param int|string|resource $x * @return Math_BigInteger */ function _os2ip($x) @@ -2529,7 +2535,7 @@ function _rsavp1($s) * * @access private * @param string $mgfSeed - * @param int $mgfLen + * @param int $maskLen * @return string */ function _mgf1($mgfSeed, $maskLen) @@ -2664,9 +2670,9 @@ function _rsaes_oaep_decrypt($c, $l = '') $offset+= $patternMatch ? 0 : 1; } - // we do & instead of && to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation + // we do | instead of || to avoid https://en.wikipedia.org/wiki/Short-circuit_evaluation // to protect against timing attacks - if (!$hashesMatch & !$patternMatch) { + if (!$hashesMatch | !$patternMatch) { user_error('Decryption error'); return false; } @@ -3001,6 +3007,59 @@ function _emsa_pkcs1_v1_5_encode($m, $emLen) return $em; } + /** + * EMSA-PKCS1-V1_5-ENCODE (without NULL) + * + * Quoting https://tools.ietf.org/html/rfc8017#page-65, + * + * "The parameters field associated with id-sha1, id-sha224, id-sha256, + * id-sha384, id-sha512, id-sha512/224, and id-sha512/256 should + * generally be omitted, but if present, it shall have a value of type + * NULL" + * + * @access private + * @param string $m + * @param int $emLen + * @return string + */ + function _emsa_pkcs1_v1_5_encode_without_null($m, $emLen) + { + $h = $this->hash->hash($m); + if ($h === false) { + return false; + } + + switch ($this->hashName) { + case 'sha1': + $t = pack('H*', '301f300706052b0e03021a0414'); + break; + case 'sha256': + $t = pack('H*', '302f300b06096086480165030402010420'); + break; + case 'sha384': + $t = pack('H*', '303f300b06096086480165030402020430'); + break; + case 'sha512': + $t = pack('H*', '304f300b06096086480165030402030440'); + break; + default: + return false; + } + $t.= $h; + $tLen = strlen($t); + + if ($emLen < $tLen + 11) { + user_error('Intended encoded message length too short'); + return false; + } + + $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3); + + $em = "\0\1$ps\0$t"; + + return $em; + } + /** * RSASSA-PKCS1-V1_5-SIGN * @@ -3038,6 +3097,7 @@ function _rsassa_pkcs1_v1_5_sign($m) * * @access private * @param string $m + * @param string $s * @return string */ function _rsassa_pkcs1_v1_5_verify($m, $s) @@ -3066,13 +3126,17 @@ function _rsassa_pkcs1_v1_5_verify($m, $s) // EMSA-PKCS1-v1_5 encoding $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k); - if ($em2 === false) { + $em3 = $this->_emsa_pkcs1_v1_5_encode_without_null($m, $this->k); + + if ($em2 === false && $em3 === false) { user_error('RSA modulus too short'); return false; } // Compare - return $this->_equals($em, $em2); + + return ($em2 !== false && $this->_equals($em, $em2)) || + ($em3 !== false && $this->_equals($em, $em3)); } /** @@ -3178,7 +3242,7 @@ function encrypt($plaintext) * * @see self::encrypt() * @access public - * @param string $plaintext + * @param string $ciphertext * @return string */ function decrypt($ciphertext) diff --git a/src/View/lib/phpseclib/File/ANSI.php b/src/View/lib/phpseclib/File/ANSI.php index ac030df..153bd44 100644 --- a/src/View/lib/phpseclib/File/ANSI.php +++ b/src/View/lib/phpseclib/File/ANSI.php @@ -230,8 +230,7 @@ function setDimensions($x, $y) /** * Set the number of lines that should be logged past the terminal height * - * @param int $x - * @param int $y + * @param int $history * @access public */ function setHistory($history) @@ -343,19 +342,20 @@ function appendString($source) $mods = explode(';', $match[1]); foreach ($mods as $mod) { switch ($mod) { - case 0: // Turn off character attributes + case '': + case '0': // Turn off character attributes $attr_cell = clone($this->base_attr_cell); break; - case 1: // Turn bold mode on + case '1': // Turn bold mode on $attr_cell->bold = true; break; - case 4: // Turn underline mode on + case '4': // Turn underline mode on $attr_cell->underline = true; break; - case 5: // Turn blinking mode on + case '5': // Turn blinking mode on $attr_cell->blink = true; break; - case 7: // Turn reverse video on + case '7': // Turn reverse video on $attr_cell->reverse = !$attr_cell->reverse; $temp = $attr_cell->background; $attr_cell->background = $attr_cell->foreground; @@ -368,23 +368,23 @@ function appendString($source) $back = &$attr_cell->{ $attr_cell->reverse ? 'foreground' : 'background' }; switch ($mod) { // @codingStandardsIgnoreStart - case 30: $front = 'black'; break; - case 31: $front = 'red'; break; - case 32: $front = 'green'; break; - case 33: $front = 'yellow'; break; - case 34: $front = 'blue'; break; - case 35: $front = 'magenta'; break; - case 36: $front = 'cyan'; break; - case 37: $front = 'white'; break; - - case 40: $back = 'black'; break; - case 41: $back = 'red'; break; - case 42: $back = 'green'; break; - case 43: $back = 'yellow'; break; - case 44: $back = 'blue'; break; - case 45: $back = 'magenta'; break; - case 46: $back = 'cyan'; break; - case 47: $back = 'white'; break; + case '30': $front = 'black'; break; + case '31': $front = 'red'; break; + case '32': $front = 'green'; break; + case '33': $front = 'yellow'; break; + case '34': $front = 'blue'; break; + case '35': $front = 'magenta'; break; + case '36': $front = 'cyan'; break; + case '37': $front = 'white'; break; + + case '40': $back = 'black'; break; + case '41': $back = 'red'; break; + case '42': $back = 'green'; break; + case '43': $back = 'yellow'; break; + case '44': $back = 'blue'; break; + case '45': $back = 'magenta'; break; + case '46': $back = 'cyan'; break; + case '47': $back = 'white'; break; // @codingStandardsIgnoreEnd default: diff --git a/src/View/lib/phpseclib/File/ASN1.php b/src/View/lib/phpseclib/File/ASN1.php index 8399b32..9dfc687 100644 --- a/src/View/lib/phpseclib/File/ASN1.php +++ b/src/View/lib/phpseclib/File/ASN1.php @@ -140,7 +140,7 @@ function __construct($encoded) * PHP4 compatible Default Constructor. * * @see self::__construct() - * @param int $mode + * @param string $encoded * @access public */ function File_ASN1_Element($encoded) @@ -318,8 +318,11 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) { $current = array('start' => $start); + if (!isset($encoded[$encoded_pos])) { + return false; + } $type = ord($encoded[$encoded_pos++]); - $start++; + $startOffset = 1; $constructed = ($type >> 5) & 1; @@ -328,15 +331,28 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) $tag = 0; // process septets (since the eighth bit is ignored, it's not an octet) do { + if (!isset($encoded[$encoded_pos])) { + return false; + } $temp = ord($encoded[$encoded_pos++]); + $startOffset++; $loop = $temp >> 7; $tag <<= 7; - $tag |= $temp & 0x7F; - $start++; + $temp &= 0x7F; + // "bits 7 to 1 of the first subsequent octet shall not all be zero" + if ($startOffset == 2 && $temp == 0) { + return false; + } + $tag |= $temp; } while ($loop); } + $start+= $startOffset; + // Length, as discussed in paragraph 8.1.3 of X.690-0207.pdf#page=13 + if (!isset($encoded[$encoded_pos])) { + return false; + } $length = ord($encoded[$encoded_pos++]); $start++; if ($length == 0x80) { // indefinite length @@ -428,13 +444,16 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) switch ($tag) { case FILE_ASN1_TYPE_BOOLEAN: // "The contents octets shall consist of a single octet." -- paragraph 8.2.1 - //if (strlen($content) != 1) { - // return false; - //} + if ($constructed || strlen($content) != 1) { + return false; + } $current['content'] = (bool) ord($content[$content_pos]); break; case FILE_ASN1_TYPE_INTEGER: case FILE_ASN1_TYPE_ENUMERATED: + if ($constructed) { + return false; + } $current['content'] = new Math_BigInteger(substr($content, $content_pos), -256); break; case FILE_ASN1_TYPE_REAL: // not currently supported @@ -454,15 +473,15 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) $last = count($temp) - 1; for ($i = 0; $i < $last; $i++) { // all subtags should be bit strings - //if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$i]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + return false; + } $current['content'].= substr($temp[$i]['content'], 1); } // all subtags should be bit strings - //if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) { - // return false; - //} + if ($temp[$last]['type'] != FILE_ASN1_TYPE_BIT_STRING) { + return false; + } $current['content'] = $temp[$last]['content'][0] . $current['content'] . substr($temp[$i]['content'], 1); } break; @@ -479,9 +498,9 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) } $content_pos += $temp['length']; // all subtags should be octet strings - //if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) { - // return false; - //} + if ($temp['type'] != FILE_ASN1_TYPE_OCTET_STRING) { + return false; + } $current['content'].= $temp['content']; $length+= $temp['length']; } @@ -492,12 +511,15 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) break; case FILE_ASN1_TYPE_NULL: // "The contents octets shall not contain any octets." -- paragraph 8.8.2 - //if (strlen($content)) { - // return false; - //} + if ($constructed || strlen($content)) { + return false; + } break; case FILE_ASN1_TYPE_SEQUENCE: case FILE_ASN1_TYPE_SET: + if (!$constructed) { + return false; + } $offset = 0; $current['content'] = array(); $content_len = strlen($content); @@ -518,7 +540,13 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) } break; case FILE_ASN1_TYPE_OBJECT_IDENTIFIER: + if ($constructed) { + return false; + } $current['content'] = $this->_decodeOID(substr($content, $content_pos)); + if ($current['content'] === false) { + return false; + } break; /* Each character string type shall be encoded as if it had been declared: [UNIVERSAL x] IMPLICIT OCTET STRING @@ -548,14 +576,22 @@ function _decode_ber($encoded, $start = 0, $encoded_pos = 0) case FILE_ASN1_TYPE_UTF8_STRING: // ???? case FILE_ASN1_TYPE_BMP_STRING: + if ($constructed) { + return false; + } $current['content'] = substr($content, $content_pos); break; case FILE_ASN1_TYPE_UTC_TIME: case FILE_ASN1_TYPE_GENERALIZED_TIME: + if ($constructed) { + return false; + } $current['content'] = class_exists('DateTime') ? $this->_decodeDateTime(substr($content, $content_pos), $tag) : $this->_decodeUnixTime(substr($content, $content_pos), $tag); + break; default: + return false; } $start+= $length; @@ -889,7 +925,7 @@ function asn1map($decoded, $mapping, $special = array()) * * @param string $source * @param string $mapping - * @param int $idx + * @param array $special * @return string * @access public */ @@ -905,6 +941,7 @@ function encodeDER($source, $mapping, $special = array()) * @param string $source * @param string $mapping * @param int $idx + * @param array $special * @return string * @access private */ @@ -1067,7 +1104,10 @@ function _encode_der($source, $mapping, $idx = null, $special = array()) if (!class_exists('DateTime')) { $value = @gmdate($format, strtotime($source)) . 'Z'; } else { + // if $source does _not_ include timezone information within it then assume that the timezone is GMT $date = new DateTime($source, new DateTimeZone('GMT')); + // if $source _does_ include timezone information within it then convert the time to GMT + $date->setTimezone(new DateTimeZone('GMT')); $value = $date->format($format) . 'Z'; } break; @@ -1229,6 +1269,11 @@ function _decodeOID($content) $oid = array(); $pos = 0; $len = strlen($content); + + if (ord($content[$len - 1]) & 0x80) { + return false; + } + $n = new Math_BigInteger(); while ($pos < $len) { $temp = ord($content[$pos++]); @@ -1264,7 +1309,7 @@ function _decodeOID($content) * Called by _encode_der() * * @access private - * @param string $content + * @param string $source * @return string */ function _encodeOID($source) diff --git a/src/View/lib/phpseclib/File/X509.php b/src/View/lib/phpseclib/File/X509.php index 2648233..608ce0a 100644 --- a/src/View/lib/phpseclib/File/X509.php +++ b/src/View/lib/phpseclib/File/X509.php @@ -1640,7 +1640,7 @@ function saveX509($cert, $format = FILE_X509_FORMAT_PEM) * Map extension values from octet string to extension-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1654,7 +1654,6 @@ function _mapInExtensions(&$root, $path, $asn1) $id = $extensions[$i]['extnId']; $value = &$extensions[$i]['extnValue']; $value = base64_decode($value); - $decoded = $asn1->decodeBER($value); /* [extnValue] contains the DER encoding of an ASN.1 value corresponding to the extension type identified by extnID */ $map = $this->_getMapping($id); @@ -1662,6 +1661,7 @@ function _mapInExtensions(&$root, $path, $asn1) $decoder = $id == 'id-ce-nameConstraints' ? array($this, '_decodeNameConstraintIP') : array($this, '_decodeIP'); + $decoded = $asn1->decodeBER($value); $mapped = $asn1->asn1map($decoded[0], $map, array('iPAddress' => $decoder)); $value = $mapped === false ? $decoded[0] : $mapped; @@ -1693,7 +1693,7 @@ function _mapInExtensions(&$root, $path, $asn1) * Map extension values from extension-specific internal format to * octet string. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1759,7 +1759,7 @@ function _mapOutExtensions(&$root, $path, $asn1) * Map attribute values from ANY type to attribute-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1800,7 +1800,7 @@ function _mapInAttributes(&$root, $path, $asn1) * Map attribute values from attribute-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1843,7 +1843,7 @@ function _mapOutAttributes(&$root, $path, $asn1) * Map DN values from ANY type to DN-specific internal * format. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -1873,7 +1873,7 @@ function _mapInDNs(&$root, $path, $asn1) * Map DN values from DN-specific internal format to * ANY type. * - * @param array ref $root + * @param array $root (by reference) * @param string $path * @param object $asn1 * @access private @@ -3251,7 +3251,8 @@ function getPublicKey() /** * Load a Certificate Signing Request * - * @param string $csr + * @param string|array $csr + * @param int $mode * @access public * @return mixed */ @@ -3393,7 +3394,7 @@ function saveCSR($csr, $format = FILE_X509_FORMAT_PEM) * * https://developer.mozilla.org/en-US/docs/HTML/Element/keygen * - * @param string $csr + * @param string|array $spkac * @access public * @return mixed */ @@ -3469,7 +3470,7 @@ function loadSPKAC($spkac) /** * Save a SPKAC CSR request * - * @param array $csr + * @param string|array $spkac * @param int $format optional * @access public * @return string @@ -3513,6 +3514,7 @@ function saveSPKAC($spkac, $format = FILE_X509_FORMAT_PEM) * Load a Certificate Revocation List * * @param string $crl + * @param int $mode * @access public * @return mixed */ @@ -4128,7 +4130,6 @@ function signCRL($issuer, $crl, $signatureAlgorithm = 'sha1WithRSAEncryption') * X.509 certificate signing helper function. * * @param object $key - * @param File_X509 $subject * @param string $signatureAlgorithm * @access public * @return mixed @@ -4206,7 +4207,7 @@ function setEndDate($date) * Set Serial Number * * @param string $serial - * @param $base optional + * @param int $base optional * @access public */ function setSerialNumber($serial, $base = -256) @@ -4882,7 +4883,6 @@ function setDomain() * Set the IP Addresses's which the cert is to be valid for * * @access public - * @param string $ipAddress optional */ function setIPAddress() { @@ -5160,11 +5160,16 @@ function _extractBER($str) * subject=/O=organization/OU=org unit/CN=common name * issuer=/O=organization/CN=common name */ - $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); - // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff - $temp = preg_replace('#-+[^-]+-+#', '', $temp); + if (strlen($str) > ini_get('pcre.backtrack_limit')) { + $temp = $str; + } else { + $temp = preg_replace('#.*?^-+[^-]+-+[\r\n ]*$#ms', '', $str, 1); + $temp = preg_replace('#-+END.*[\r\n ]*.*#ms', '', $temp, 1); + } // remove new lines $temp = str_replace(array("\r", "\n", ' '), '', $temp); + // remove the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- stuff + $temp = preg_replace('#^-+[^-]+-+|-+[^-]+-+$#', '', $temp); $temp = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $temp) ? base64_decode($temp) : false; return $temp != false ? $temp : $str; } diff --git a/src/View/lib/phpseclib/Math/BigInteger.php b/src/View/lib/phpseclib/Math/BigInteger.php index 35df7ad..961e6ca 100644 --- a/src/View/lib/phpseclib/Math/BigInteger.php +++ b/src/View/lib/phpseclib/Math/BigInteger.php @@ -237,7 +237,7 @@ class Math_BigInteger * ?> * * - * @param $x base-10 number or base-$base number if $base set. + * @param int|string|resource $x base-10 number or base-$base number if $base set. * @param int $base * @return Math_BigInteger * @access public @@ -257,7 +257,7 @@ function __construct($x = 0, $base = 10) } } - if (extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { + if (function_exists('phpinfo') && extension_loaded('openssl') && !defined('MATH_BIGINTEGER_OPENSSL_DISABLE') && !defined('MATH_BIGINTEGER_OPENSSL_ENABLED')) { // some versions of XAMPP have mismatched versions of OpenSSL which causes it not to work ob_start(); @phpinfo(); @@ -673,11 +673,11 @@ function toBits($twos_compliment = false) { $hex = $this->toHex($twos_compliment); $bits = ''; - for ($i = strlen($hex) - 8, $start = strlen($hex) & 7; $i >= $start; $i-=8) { - $bits = str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT) . $bits; + for ($i = strlen($hex) - 6, $start = strlen($hex) % 6; $i >= $start; $i-=6) { + $bits = str_pad(decbin(hexdec(substr($hex, $i, 6))), 24, '0', STR_PAD_LEFT) . $bits; } if ($start) { // hexdec('') == 0 - $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8, '0', STR_PAD_LEFT) . $bits; + $bits = str_pad(decbin(hexdec(substr($hex, 0, $start))), 8 * $start, '0', STR_PAD_LEFT) . $bits; } $result = $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0'); @@ -2021,7 +2021,7 @@ function _squareReduce($x, $n, $mode) * * @see self::_slidingWindow() * @access private - * @param Math_BigInteger + * @param Math_BigInteger $n * @return Math_BigInteger */ function _mod2($n) @@ -3136,7 +3136,7 @@ function setRandomGenerator($generator) * * Byte length is equal to $length. Uses crypt_random if it's loaded and mt_rand if it's not. * - * @param int $length + * @param int $size * @return Math_BigInteger * @access private */ @@ -3603,7 +3603,7 @@ function _rshift($shift) * * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision * - * @param Math_BigInteger + * @param Math_BigInteger $result * @return Math_BigInteger * @see self::_trim() * @access private @@ -3680,8 +3680,8 @@ function _trim($value) /** * Array Repeat * - * @param $input Array - * @param $multiplier mixed + * @param array $input + * @param mixed $multiplier * @return array * @access private */ @@ -3695,8 +3695,8 @@ function _array_repeat($input, $multiplier) * * Shifts binary strings $shift bits, essentially multiplying by 2**$shift. * - * @param $x String - * @param $shift Integer + * @param string $x (by reference) + * @param int $shift * @return string * @access private */ @@ -3724,8 +3724,8 @@ function _base256_lshift(&$x, $shift) * * Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder. * - * @param $x String - * @param $shift Integer + * @param string $x (by referenc) + * @param int $shift * @return string * @access private */ diff --git a/src/View/lib/phpseclib/Net/SSH2.php b/src/View/lib/phpseclib/Net/SSH2.php index 494dd89..2f80bd0 100644 --- a/src/View/lib/phpseclib/Net/SSH2.php +++ b/src/View/lib/phpseclib/Net/SSH2.php @@ -150,6 +150,23 @@ define('NET_SSH2_READ_NEXT', 3); /**#@-*/ +/**#@+ + * @access private + */ +/** + * No compression + */ +define('NET_SSH2_COMPRESSION_NONE', 1); +/** + * zlib compression + */ +define('NET_SSH2_COMPRESSION_ZLIB', 2); +/** + * zlib@openssh.com + */ +define('NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH', 3); +/**#@-*/ + /** * Pure-PHP implementation of SSHv2. * @@ -692,6 +709,14 @@ class Net_SSH2 */ var $curTimeout; + /** + * Keep Alive Interval + * + * @see self::setKeepAlive() + * @access private + */ + var $keepAlive; + /** * Real-time log file pointer * @@ -967,6 +992,71 @@ class Net_SSH2 */ var $auth = array(); + /** + * The authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @var array|null + * @access private + */ + var $auth_methods_to_continue = null; + + /** + * Compression method + * + * @var int + * @access private + */ + var $compress = NET_SSH2_COMPRESSION_NONE; + + /** + * Decompression method + * + * @var resource|object + * @access private + */ + var $decompress = NET_SSH2_COMPRESSION_NONE; + + /** + * Compression context + * + * @var int + * @access private + */ + var $compress_context; + + /** + * Decompression context + * + * @var resource|object + * @access private + */ + var $decompress_context; + + /** + * Regenerate Compression Context + * + * @var bool + * @access private + */ + var $regenerate_compression_context = false; + + /** + * Regenerate Decompression Context + * + * @var bool + * @access private + */ + var $regenerate_decompression_context = false; + + /** + * Smart multi-factor authentication flag + * + * @var bool + * @access private + */ + var $smartMFA = true; + /** * Default Constructor. * @@ -1354,6 +1444,7 @@ function _generate_identifier() function _key_exchange($kexinit_payload_server = false) { $preferred = $this->preferred; + $send_kex = true; $kex_algorithms = isset($preferred['kex']) ? $preferred['kex'] : @@ -1437,7 +1528,7 @@ function _key_exchange($kexinit_payload_server = false) 0 ); - if ($this->send_kex_first) { + if ($kexinit_payload_server === false) { if (!$this->_send_binary_packet($kexinit_payload_client)) { return false; } @@ -1453,6 +1544,8 @@ function _key_exchange($kexinit_payload_server = false) user_error('Expected SSH_MSG_KEXINIT'); return false; } + + $send_kex = false; } $response = $kexinit_payload_server; @@ -1525,7 +1618,7 @@ function _key_exchange($kexinit_payload_server = false) extract(unpack('Cfirst_kex_packet_follows', $this->_string_shift($response, 1))); $first_kex_packet_follows = $first_kex_packet_follows != 0; - if (!$this->send_kex_first && !$this->_send_binary_packet($kexinit_payload_client)) { + if ($send_kex && !$this->_send_binary_packet($kexinit_payload_client)) { return false; } @@ -1554,6 +1647,45 @@ function _key_exchange($kexinit_payload_server = false) user_error('No compatible key exchange algorithms found'); return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); } + + $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); + if ($server_host_key_algorithm === false) { + user_error('No compatible server host key algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $mac_algorithm_out = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); + if ($mac_algorithm_out === false) { + user_error('No compatible client to server message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $mac_algorithm_in = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); + if ($mac_algorithm_in === false) { + user_error('No compatible server to client message authentication algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + + $compression_map = array( + 'none' => NET_SSH2_COMPRESSION_NONE, + 'zlib' => NET_SSH2_COMPRESSION_ZLIB, + 'zlib@openssh.com' => NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH + ); + + $compression_algorithm_out = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); + if ($compression_algorithm_out === false) { + user_error('No compatible client to server compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->compress = $compression_map[$compression_algorithm_out]; + + $compression_algorithm_in = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); + if ($compression_algorithm_in === false) { + user_error('No compatible server to client compression algorithms found'); + return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); + } + $this->decompress = $compression_map[$compression_algorithm_in]; + if (strpos($kex_algorithm, 'diffie-hellman-group-exchange') === 0) { $dh_group_sizes_packed = pack( 'NNN', @@ -1761,12 +1893,6 @@ function _key_exchange($kexinit_payload_server = false) $this->session_id = $this->exchange_hash; } - $server_host_key_algorithm = $this->_array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); - if ($server_host_key_algorithm === false) { - user_error('No compatible server host key algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - switch ($server_host_key_algorithm) { case 'ssh-dss': $expected_key_format = 'ssh-dss'; @@ -1886,14 +2012,8 @@ function _key_exchange($kexinit_payload_server = false) $this->decrypt->decrypt(str_repeat("\0", 1536)); } - $mac_algorithm = $this->_array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); - if ($mac_algorithm === false) { - user_error('No compatible client to server message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - $createKeyLength = 0; // ie. $mac_algorithm == 'none' - switch ($mac_algorithm) { + switch ($mac_algorithm_out) { case 'hmac-sha2-256': $this->hmac_create = new Crypt_Hash('sha256'); $createKeyLength = 32; @@ -1914,17 +2034,11 @@ function _key_exchange($kexinit_payload_server = false) $this->hmac_create = new Crypt_Hash('md5-96'); $createKeyLength = 16; } - $this->hmac_create->name = $mac_algorithm; - - $mac_algorithm = $this->_array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); - if ($mac_algorithm === false) { - user_error('No compatible server to client message authentication algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } + $this->hmac_create->name = $mac_algorithm_out; $checkKeyLength = 0; $this->hmac_size = 0; - switch ($mac_algorithm) { + switch ($mac_algorithm_in) { case 'hmac-sha2-256': $this->hmac_check = new Crypt_Hash('sha256'); $checkKeyLength = 32; @@ -1950,7 +2064,7 @@ function _key_exchange($kexinit_payload_server = false) $checkKeyLength = 16; $this->hmac_size = 12; } - $this->hmac_check->name = $mac_algorithm; + $this->hmac_check->name = $mac_algorithm_in; $key = $kexHash->hash($keyBytes . $this->exchange_hash . 'E' . $this->session_id); while ($createKeyLength > strlen($key)) { @@ -1964,19 +2078,7 @@ function _key_exchange($kexinit_payload_server = false) } $this->hmac_check->setKey(substr($key, 0, $checkKeyLength)); - $compression_algorithm = $this->_array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - user_error('No compatible client to server compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - //$this->decompress = $compression_algorithm == 'zlib'; - - $compression_algorithm = $this->_array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_client_to_server); - if ($compression_algorithm === false) { - user_error('No compatible server to client compression algorithms found'); - return $this->_disconnect(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); - } - //$this->compress = $compression_algorithm == 'zlib'; + $this->regenerate_compression_context = $this->regenerate_decompression_context = true; return true; } @@ -2126,8 +2228,6 @@ function _bad_algorithm_candidate($algorithm) * The $password parameter can be a plaintext password, a Crypt_RSA object or an array * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login() * @access public @@ -2139,11 +2239,13 @@ function login($username) // try logging with 'none' as an authentication method first since that's what // PuTTY does - if ($this->_login($username)) { - return true; - } - if (count($args) == 1) { - return false; + if (substr($this->server_identifier, 0, 15) != 'SSH-2.0-CoreFTP' && $this->auth_methods_to_continue === null) { + if ($this->_login($username)) { + return true; + } + if (count($args) == 1) { + return false; + } } return call_user_func_array(array(&$this, '_login'), $args); } @@ -2152,8 +2254,6 @@ function login($username) * Login Helper * * @param string $username - * @param mixed $password - * @param mixed $... * @return bool * @see self::_login_helper() * @access private @@ -2171,9 +2271,61 @@ function _login($username) return $this->_login_helper($username); } - foreach ($args as $arg) { - if ($this->_login_helper($username, $arg)) { - return true; + while (count($args)) { + if (!$this->auth_methods_to_continue || !$this->smartMFA) { + $newargs = $args; + $args = array(); + } else { + $newargs = array(); + foreach ($this->auth_methods_to_continue as $method) { + switch ($method) { + case 'publickey': + foreach ($args as $key => $arg) { + if (is_object($arg)) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + break; + case 'keyboard-interactive': + $hasArray = $hasString = false; + foreach ($args as $arg) { + if ($hasArray || is_array($arg)) { + $hasArray = true; + break; + } + if ($hasString || is_string($arg)) { + $hasString = true; + break; + } + } + if ($hasArray && $hasString) { + foreach ($args as $key => $arg) { + if (is_array($arg)) { + $newargs[] = $arg; + break 2; + } + } + } + case 'password': + foreach ($args as $key => $arg) { + $newargs[] = $arg; + unset($args[$key]); + break; + } + } + } + } + + if (!count($newargs)) { + return false; + } + + foreach ($newargs as $arg) { + if ($this->_login_helper($username, $arg)) { + return true; + } } } return false; @@ -2287,7 +2439,9 @@ function _login_helper($username, $password = null) case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= NET_SSH2_MASK_LOGIN; return true; - //case NET_SSH2_MSG_USERAUTH_FAILURE: + case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); default: return false; } @@ -2359,6 +2513,7 @@ function _login_helper($username, $password = null) } extract(unpack('Nlength', $this->_string_shift($response, 4))); $auth_methods = explode(',', $this->_string_shift($response, $length)); + $this->auth_methods_to_continue = $auth_methods; if (!strlen($response)) { return false; } @@ -2418,7 +2573,6 @@ function _keyboard_interactive_login($username, $password) /** * Handle the keyboard-interactive requests / responses. * - * @param string $responses... * @return bool * @access private */ @@ -2532,6 +2686,8 @@ function _keyboard_interactive_process() case NET_SSH2_MSG_USERAUTH_SUCCESS: return true; case NET_SSH2_MSG_USERAUTH_FAILURE: + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; } @@ -2563,7 +2719,7 @@ function _ssh_agent_login($username, $agent) * Login with an RSA private key * * @param string $username - * @param Crypt_RSA $password + * @param Crypt_RSA $privatekey * @return bool * @access private * @internal It might be worthwhile, at some point, to protect against {@link http://tools.ietf.org/html/rfc4251#section-9.3.9 traffic analysis} @@ -2640,13 +2796,21 @@ function _privatekey_login($username, $privatekey) if (strlen($response) < 4) { return false; } - extract(unpack('Nlength', $this->_string_shift($response, 4))); - $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE: ' . $this->_string_shift($response, $length); + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); + $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; return false; case NET_SSH2_MSG_USERAUTH_PK_OK: // we'll just take it on faith that the public key blob and the public key algorithm name are as // they should be $this->_updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); + break; + case NET_SSH2_MSG_USERAUTH_SUCCESS: + $this->bitmap |= NET_SSH2_MASK_LOGIN; + return true; + default: + user_error('Unexpected response to publickey authentication pt 1'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = $part1 . chr(1) . $part2; @@ -2675,13 +2839,16 @@ function _privatekey_login($username, $privatekey) switch ($type) { case NET_SSH2_MSG_USERAUTH_FAILURE: // either the login is bad or the server employs multi-factor authentication + extract(unpack('Nmethodlistlen', $this->_string_shift($response, 4))); + $this->auth_methods_to_continue = explode(',', $this->_string_shift($response, $methodlistlen)); return false; case NET_SSH2_MSG_USERAUTH_SUCCESS: $this->bitmap |= NET_SSH2_MASK_LOGIN; return true; } - return false; + user_error('Unexpected response to publickey authentication pt 2'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } /** @@ -2698,6 +2865,19 @@ function setTimeout($timeout) $this->timeout = $this->curTimeout = $timeout; } + /** + * Set Keep Alive + * + * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. + * + * @param int $interval + * @access public + */ + function setKeepAlive($interval) + { + $this->keepAlive = $interval; + } + /** * Get the output from stdError * @@ -2787,26 +2967,13 @@ function exec($command, $callback = null) return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } + $this->channel_status[NET_SSH2_CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; - if (!strlen($response)) { - return false; + if (!$this->_get_channel_packet(NET_SSH2_CHANNEL_EXEC)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - break; - case NET_SSH2_MSG_CHANNEL_FAILURE: - default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); - } $this->in_request_pty_exec = true; } @@ -2928,26 +3095,11 @@ function _initShell() return false; } - $response = $this->_get_binary_packet(); - if ($response === false) { - $this->bitmap = 0; - user_error('Connection closed by server'); - return false; - } - - if (!strlen($response)) { - return false; - } - list(, $type) = unpack('C', $this->_string_shift($response, 1)); + $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - switch ($type) { - case NET_SSH2_MSG_CHANNEL_SUCCESS: - // if a pty can't be opened maybe commands can still be executed - case NET_SSH2_MSG_CHANNEL_FAILURE: - break; - default: - user_error('Unable to request pseudo-terminal'); - return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + if (!$this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL)) { + user_error('Unable to request pseudo-terminal'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } $packet = pack( @@ -2962,8 +3114,6 @@ function _initShell() return false; } - $this->channel_status[NET_SSH2_CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; - $response = $this->_get_channel_packet(NET_SSH2_CHANNEL_SHELL); if ($response === false) { return false; @@ -3337,9 +3487,57 @@ function _reset_connection($reason) */ function _get_binary_packet($skip_channel_filter = false) { + if ($skip_channel_filter) { + $read = array($this->fsock); + $write = $except = null; + + if (!$this->curTimeout) { + if ($this->keepAlive <= 0) { + @stream_select($read, $write, $except, null); + } else { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + return $this->_get_binary_packet(true); + } + } + } else { + if ($this->curTimeout < 0) { + $this->is_timeout = true; + return true; + } + + $read = array($this->fsock); + $write = $except = null; + + $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 + + if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { + if (!@stream_select($read, $write, $except, $this->keepAlive) && !count($read)) { + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + return $this->_get_binary_packet(true); + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + + $sec = floor($this->curTimeout); + $usec = 1000000 * ($this->curTimeout - $sec); + + // on windows this returns a "Warning: Invalid CRT parameters detected" error + if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { + $this->is_timeout = true; + return true; + } + $elapsed = strtok(microtime(), ' ') + strtok('') - $start; + $this->curTimeout-= $elapsed; + } + } + if (!is_resource($this->fsock) || feof($this->fsock)) { $this->bitmap = 0; - user_error('Connection closed prematurely'); + user_error('Connection closed (by server) prematurely ' . $elapsed . 's'); return false; } @@ -3347,7 +3545,8 @@ function _get_binary_packet($skip_channel_filter = false) $raw = fread($this->fsock, $this->decrypt_block_size); if (!strlen($raw)) { - return ''; + user_error('No data received from server'); + return false; } if ($this->decrypt !== false) { @@ -3410,9 +3609,41 @@ function _get_binary_packet($skip_channel_filter = false) } } - //if ($this->decompress) { - // $payload = gzinflate(substr($payload, 2)); - //} + switch ($this->decompress) { + case NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case NET_SSH2_COMPRESSION_ZLIB: + if ($this->regenerate_decompression_context) { + $this->regenerate_decompression_context = false; + + $cmf = ord($payload[0]); + $cm = $cmf & 0x0F; + if ($cm != 8) { // deflate + user_error("Only CM = 8 ('deflate') is supported ($cm)"); + } + $cinfo = ($cmf & 0xF0) >> 4; + if ($cinfo > 7) { + user_error("CINFO above 7 is not allowed ($cinfo)"); + } + $windowSize = 1 << ($cinfo + 8); + + $flg = ord($payload[1]); + //$fcheck = $flg && 0x0F; + if ((($cmf << 8) | $flg) % 31) { + user_error('fcheck failed'); + } + $fdict = boolval($flg & 0x20); + $flevel = ($flg & 0xC0) >> 6; + + $this->decompress_context = inflate_init(ZLIB_ENCODING_RAW, array('window' => $cinfo + 8)); + $payload = substr($payload, 2); + } + if ($this->decompress_context) { + $payload = inflate_add($this->decompress_context, $payload, ZLIB_PARTIAL_FLUSH); + } + } $this->get_seq_no++; @@ -3487,10 +3718,24 @@ function _filter($payload, $skip_channel_filter) // only called when we've already logged in if (($this->bitmap & NET_SSH2_MASK_CONNECTED) && $this->isAuthenticated()) { + if (is_bool($payload)) { + return $payload; + } + switch (ord($payload[0])) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + if (strlen($payload) == 31) { + extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); + if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { + if (ord(substr($payload, 9 + $length))) { // want reply + $this->_send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); + } + $payload = $this->_get_binary_packet($skip_channel_filter); + } + } + break; case NET_SSH2_MSG_CHANNEL_DATA: case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: - case NET_SSH2_MSG_CHANNEL_REQUEST: case NET_SSH2_MSG_CHANNEL_CLOSE: case NET_SSH2_MSG_CHANNEL_EOF: if (!$skip_channel_filter && !empty($this->server_channels)) { @@ -3672,14 +3917,28 @@ function isPTYEnabled() * * Returns the data as a string if it's available and false if not. * - * @param $client_channel - * @return mixed + * @param int $client_channel + * @param bool $skip_extended + * @return mixed|bool * @access private */ function _get_channel_packet($client_channel, $skip_extended = false) { if (!empty($this->channel_buffers[$client_channel])) { - return array_shift($this->channel_buffers[$client_channel]); + switch ($this->channel_status[$client_channel]) { + case NET_SSH2_MSG_CHANNEL_REQUEST: + foreach ($this->channel_buffers[$client_channel] as $i => $packet) { + switch (ord($packet[0])) { + case NET_SSH2_MSG_CHANNEL_SUCCESS: + case NET_SSH2_MSG_CHANNEL_FAILURE: + unset($this->channel_buffers[$client_channel][$i]); + return substr($packet, 1); + } + } + break; + default: + return substr(array_shift($this->channel_buffers[$client_channel]), 1); + } } while (true) { @@ -3687,36 +3946,13 @@ function _get_channel_packet($client_channel, $skip_extended = false) $response = $this->binary_packet_buffer; $this->binary_packet_buffer = false; } else { - $read = array($this->fsock); - $write = $except = null; - - if (!$this->curTimeout) { - @stream_select($read, $write, $except, null); - } else { - if ($this->curTimeout < 0) { - $this->is_timeout = true; - return true; - } - - $read = array($this->fsock); - $write = $except = null; - - $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $sec = floor($this->curTimeout); - $usec = 1000000 * ($this->curTimeout - $sec); - // on windows this returns a "Warning: Invalid CRT parameters detected" error - if (!@stream_select($read, $write, $except, $sec, $usec) && !count($read)) { - $this->is_timeout = true; - if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { - $this->_close_channel($client_channel); - } - return true; + $response = $this->_get_binary_packet(true); + if ($response === true && $this->is_timeout) { + if ($client_channel == NET_SSH2_CHANNEL_EXEC && !$this->request_pty) { + $this->_close_channel($client_channel); } - $elapsed = strtok(microtime(), ' ') + strtok('') - $start; - $this->curTimeout-= $elapsed; + return true; } - - $response = $this->_get_binary_packet(true); if ($response === false) { $this->bitmap = 0; user_error('Connection closed by server'); @@ -3776,10 +4012,7 @@ function _get_channel_packet($client_channel, $skip_extended = false) if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { return $data; } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; + $this->channel_buffers[$channel][] = chr($type) . $data; continue 2; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3858,10 +4091,15 @@ function _get_channel_packet($client_channel, $skip_extended = false) $result = $client_channel == $channel ? true : $this->_get_channel_packet($client_channel, $skip_extended); $this->_on_channel_open(); return $result; - //case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: - default: + case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: user_error('Unable to open channel'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + default: + if ($client_channel == $channel) { + user_error('Unexpected response to open request'); + return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); + } + return $this->_get_channel_packet($client_channel, $skip_extended); } break; case NET_SSH2_MSG_CHANNEL_REQUEST: @@ -3870,6 +4108,14 @@ function _get_channel_packet($client_channel, $skip_extended = false) return true; case NET_SSH2_MSG_CHANNEL_FAILURE: return false; + case NET_SSH2_MSG_CHANNEL_DATA: + if (strlen($response) < 4) { + return false; + } + extract(unpack('Nlength', $this->_string_shift($response, 4))); + $data = $this->_string_shift($response, $length); + $this->channel_buffers[$channel][] = chr($type) . $data; + return $this->_get_channel_packet($client_channel, $skip_extended); default: user_error('Unable to fulfill channel request'); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); @@ -3909,10 +4155,7 @@ function _get_channel_packet($client_channel, $skip_extended = false) if ($client_channel == $channel) { return $data; } - if (!isset($this->channel_buffers[$channel])) { - $this->channel_buffers[$channel] = array(); - } - $this->channel_buffers[$channel][] = $data; + $this->channel_buffers[$channel][] = chr($type) . $data; break; case NET_SSH2_MSG_CHANNEL_CLOSE: $this->curTimeout = 5; @@ -3931,7 +4174,7 @@ function _get_channel_packet($client_channel, $skip_extended = false) case NET_SSH2_MSG_CHANNEL_EOF: break; default: - user_error('Error reading channel data'); + user_error("Error reading channel data ($type)"); return $this->_disconnect(NET_SSH2_DISCONNECT_BY_APPLICATION); } } @@ -3956,11 +4199,27 @@ function _send_binary_packet($data, $logged = null) return false; } - //if ($this->compress) { - // // the -4 removes the checksum: - // // http://php.net/function.gzcompress#57710 - // $data = substr(gzcompress($data), 0, -4); - //} + if (!isset($logged)) { + $logged = $data; + } + + switch ($this->compress) { + case NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH: + if (!$this->isAuthenticated()) { + break; + } + case NET_SSH2_COMPRESSION_ZLIB: + if (!$this->regenerate_compression_context) { + $header = ''; + } else { + $this->regenerate_compression_context = false; + $this->compress_context = deflate_init(ZLIB_ENCODING_RAW, ['window' => 15]); + $header = "\x78\x9C"; + } + if ($this->compress_context) { + $data = $header . deflate_add($this->compress_context, $data, ZLIB_PARTIAL_FLUSH); + } + } // 4 (packet length) + 1 (padding length) + 4 (minimal padding amount) == 9 $packet_length = strlen($data) + 9; @@ -3983,15 +4242,15 @@ function _send_binary_packet($data, $logged = null) $packet.= $hmac; $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838 - $result = strlen($packet) == fputs($this->fsock, $packet); + $result = strlen($packet) == @fputs($this->fsock, $packet); $stop = strtok(microtime(), ' ') + strtok(''); if (defined('NET_SSH2_LOGGING')) { $current = strtok(microtime(), ' ') + strtok(''); - $message_number = isset($this->message_numbers[ord($data[0])]) ? $this->message_numbers[ord($data[0])] : 'UNKNOWN (' . ord($data[0]) . ')'; + $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; $message_number = '-> ' . $message_number . ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; - $this->_append_log($message_number, isset($logged) ? $logged : $data); + $this->_append_log($message_number, $logged); $this->last_packet = $current; } @@ -4003,7 +4262,8 @@ function _send_binary_packet($data, $logged = null) * * Makes sure that only the last 1MB worth of packets will be logged * - * @param string $data + * @param string $message_number + * @param string $message * @access private */ function _append_log($message_number, $message) @@ -4208,7 +4468,6 @@ function _string_shift(&$string, $index = 1) * named constants from it, using the value as the name of the constant and the index as the value of the constant. * If any of the constants that would be defined already exists, none of the constants will be defined. * - * @param array $array * @access private */ function _define_array() @@ -4612,11 +4871,15 @@ function getSupportedEncryptionAlgorithms() //'none' // OPTIONAL no encryption; NOT RECOMMENDED ); - $engines = array( - CRYPT_ENGINE_OPENSSL, - CRYPT_ENGINE_MCRYPT, - CRYPT_ENGINE_INTERNAL - ); + if ($this->crypto_engine) { + $engines = array($this->crypto_engine); + } else { + $engines = array( + CRYPT_ENGINE_OPENSSL, + CRYPT_ENGINE_MCRYPT, + CRYPT_ENGINE_INTERNAL + ); + } $ciphers = array(); foreach ($engines as $engine) { @@ -4670,10 +4933,12 @@ function getSupportedMACAlgorithms() */ function getSupportedCompressionAlgorithms() { - return array( - 'none' // REQUIRED no compression - //'zlib' // OPTIONAL ZLIB (LZ77) compression - ); + $algos = array('none'); // REQUIRED no compression + if (function_exists('deflate_init')) { + $algos[] = 'zlib@openssh.com'; // https://datatracker.ietf.org/doc/html/draft-miller-secsh-compression-delayed + $algos[] = 'zlib'; + } + return $algos; } /** @@ -4688,18 +4953,24 @@ function getAlgorithmsNegotiated() { $this->_connect(); + $compression_map = array( + NET_SSH2_COMPRESSION_NONE => 'none', + NET_SSH2_COMPRESSION_ZLIB => 'zlib', + NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH => 'zlib@openssh.com' + ); + return array( 'kex' => $this->kex_algorithm, 'hostkey' => $this->signature_format, 'client_to_server' => array( 'crypt' => $this->encrypt->name, 'mac' => $this->hmac_create->name, - 'comp' => 'none', + 'comp' => $compression_map[$this->compress], ), 'server_to_client' => array( 'crypt' => $this->decrypt->name, 'mac' => $this->hmac_check->name, - 'comp' => 'none', + 'comp' => $compression_map[$this->decompress], ) ); } @@ -5106,4 +5377,31 @@ function _updateLogHistory($old, $new) ); } } + + /** + * Return the list of authentication methods that may productively continue authentication. + * + * @see https://tools.ietf.org/html/rfc4252#section-5.1 + * @return array|null + */ + function getAuthMethodsToContinue() + { + return $this->auth_methods_to_continue; + } + + /** + * Enables "smart" multi-factor authentication (MFA) + */ + function enableSmartMFA() + { + $this->smartMFA = true; + } + + /** + * Disables "smart" multi-factor authentication (MFA) + */ + function disableSmartMFA() + { + $this->smartMFA = false; + } } diff --git a/src/View/lib/setup.php b/src/View/lib/setup.php index 1c50290..b8aa4d4 100644 --- a/src/View/lib/setup.php +++ b/src/View/lib/setup.php @@ -1,6 +1,6 @@ , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre_CONTROL.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre_CONTROL.po index 2a9b186..8da412c 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre_CONTROL.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre_CONTROL.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre_HELPBOX.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre_HELPBOX.po index 210df6b..3b01e5d 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre_HELPBOX.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre_HELPBOX.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre_MENU.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre_MENU.po index 131e3c3..64f3753 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre_MENU.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre_MENU.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre_NOTICE.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre_NOTICE.po index 42ae23c..7daad25 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre_NOTICE.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre_NOTICE.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre_TITLE.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre_TITLE.po index 7a785f9..6cb60aa 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre_TITLE.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre_TITLE.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/locale/tr_TR/LC_MESSAGES/pfre__.po b/src/View/locale/tr_TR/LC_MESSAGES/pfre__.po index 6f15792..2cdb174 100644 --- a/src/View/locale/tr_TR/LC_MESSAGES/pfre__.po +++ b/src/View/locale/tr_TR/LC_MESSAGES/pfre__.po @@ -1,10 +1,10 @@ -# Copyright (C) 2021 Soner Tari, The PFRE project +# Copyright (C) 2016-2022 Soner Tari, The PFRE project # This file is distributed under the same license as PFRE. -# Soner Tari , 2020. +# Soner Tari # msgid "" msgstr "" -"Project-Id-Version: PFRE 6.9\n" +"Project-Id-Version: PFRE 7.1\n" "Last-Translator: Soner Tari \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/src/View/login.php b/src/View/login.php index fd68acd..a26e5b4 100644 --- a/src/View/login.php +++ b/src/View/login.php @@ -1,6 +1,6 @@ divert-to host port port Used to redirect packets to a local socket bound to host and - port. The packets will not be modified, so getsockname(2) on the - socket will return the original destination address of the - packet. + port. The packets will not be modified, preserving the original + destination address for the application to access. SOCK_STREAM + connections can access the original destination address using + getsockname(2). SOCK_DGRAM sockets can be configured with the + ip(4) IP_RECVDSTADDR and IP_RECVDSTPORT socket options when + receiving IPv4 packets, or the ip6(4) IPV6_RECVPKTINFO and + IPV6_RECVDSTPORT socket options when receiving IPv6 packets. flags a/b | any This rule only applies to TCP packets that have the flags a set @@ -631,7 +635,7 @@ It is possible to embed a complete IPv4 address into an IPv6 address using a network prefix of /96 or smaller. - When a destination address is not specified it is assumed that + When a destination address is not specified, it is assumed that the host part is 32-bit long. For IPv6 to IPv4 translation this would mean using only the lower 32 bits of the original IPv6 destination address. For IPv4 to IPv6 translation the @@ -806,12 +810,12 @@ reply-to The reply-to option is similar to route-to, but routes packets that pass in the opposite direction (replies) to the specified - interface. Opposite direction is only defined in the context - of a state entry, and reply-to is useful only in rules that - create state. It can be used on systems with multiple external - connections to route all outgoing packets of a connection - through the interface the incoming connection arrived through - (symmetric routing enforcement). + address. Opposite direction is only defined in the context of + a state entry, and reply-to is useful only in rules that create + state. It can be used on systems with multiple paths to the + internet to ensure that replies to an incoming network + connection to a particular address are sent using the path + associated with that address (symmetric routing enforcement). route-to The route-to option routes the packet to the specified @@ -1014,10 +1018,8 @@ Packets passing in or out on such interfaces are passed as if pf was disabled, i.e. pf does not process them in any way. This can be useful on loopback and other virtual interfaces, when packet - filtering is not desired and can have unexpected effects. ifspec - is only evaluated when the ruleset is loaded; interfaces created - later will not be skipped. PF filters traffic on all interfaces - by default. + filtering is not desired and can have unexpected effects. PF + filters traffic on all interfaces by default. set state-defaults state-option, ... @@ -1160,7 +1162,7 @@ set timeout { adaptive.start 60000, adaptive.end 120000 } set limit states 100000 - With 9000 state table entries, the timeout values are scaled to + With 90000 state table entries, the timeout values are scaled to 50% (tcp.first 60, tcp.established 43200). "pfctl -F Reset" restores default values for the following options: @@ -1414,7 +1416,7 @@ An anchor rule can also contain a filter ruleset in a brace-delimited block. In that case, no separate loading of rules into the anchor is required. Brace delimited blocks may contain rules or other brace- - delimited blocks. When an anchor is populated this way the anchor name + delimited blocks. When an anchor is populated this way, the anchor name becomes optional. Since the parser specification for anchor names is a string, double quote characters (`"') should be placed around the anchor name. @@ -1919,10 +1921,10 @@ currently established connection. Caveat: operating system fingerprints are occasionally wrong. There are - three problems: an attacker can trivially craft his packets to appear as - any operating system he chooses; an operating system patch could change - the stack behavior and no fingerprints will match it until the database - is updated; and multiple operating systems may have the same fingerprint. + three problems: an attacker can trivially craft packets to appear as any + operating system; an operating system patch could change the stack + behavior and no fingerprints will match it until the database is updated; + and multiple operating systems may have the same fingerprint. EXAMPLES In this example, the external interface is kue0. We use a macro for the @@ -2284,7 +2286,7 @@ HISTORY The pf.conf file format first appeared in OpenBSD 3.0. -OpenBSD 7.0 July 19, 2021 OpenBSD 7.0 +OpenBSD 7.1 March 31, 2022 OpenBSD 7.1 diff --git a/src/View/pf/pf.php b/src/View/pf/pf.php index 5a3abee..fee8ba1 100644 --- a/src/View/pf/pf.php +++ b/src/View/pf/pf.php @@ -1,6 +1,6 @@