[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: RFC: php-cas



(As suggested, I'm moving the discussion to debian-lts@lists.debian.org, CC'ing 
the security team)

> On 19/06/2023 18:17, Tobias Frost wrote:
> > Hey,
> > 
> > As I am currently preparing a fix for php-cas to tackle CVE-2022-39369 [1], and
> > as the changes required are breaking changes, I'd like to discuss whether the
> > vulnerability justifies a breaking change, or if the issue should be ignored instead.
> > (Maybe feedback from interested customers can be collected, so that they can assess
> > the impact on their side already.)
> > 
> > I've packaged my backport of the patch and uploaded it to [3] as an (untested) preview.
> > 
> > The breaking change: users of php-cas needs to perform additional steps when
> > using the php module, as described in docs/updating of the upstream pull
> > request fixing the issue: [2]
> > 
> >      phpCAS now requires an additional service base URL argument when
> >      constructing the client class, similar to other CAS client's serverName config.
> > 
> > Upstream asses the situation as [4]
> > 
> >      This vulnerability may allow an attacker to gain access to a victim's account
> >      on a vulnerable CASified service without victim's knowledge, when the victim
> >      visits attacker's website while being logged in to the same CAS server.
> > 


The patch applied to the package is this commit:
https://salsa.debian.org/lts-team/packages/php-cas/-/commit/2c2b5f73da55f5c6d9f69e1ac11b3a1ee565d435
(also debdiff attached.)

-- 
Cheers, 
tobi


diff -Nru php-cas-1.3.6/debian/changelog php-cas-1.3.6/debian/changelog
--- php-cas-1.3.6/debian/changelog	2019-02-10 09:29:07.000000000 +0100
+++ php-cas-1.3.6/debian/changelog	2023-06-19 17:12:56.000000000 +0200
@@ -1,3 +1,10 @@
+php-cas (1.3.6-1+deb10u1) buster-security-UNRELEASED; urgency=medium
+
+  * Non-maintainer upload by the LTS Security Team.
+  * Backport patch for CVE-2022-39369.
+
+ -- Tobias Frost <tobi@debian.org>  Mon, 19 Jun 2023 17:12:56 +0200
+
 php-cas (1.3.6-1) unstable; urgency=medium
 
   * Update debian/watch
diff -Nru php-cas-1.3.6/debian/patches/CVE-2022-39369.patch php-cas-1.3.6/debian/patches/CVE-2022-39369.patch
--- php-cas-1.3.6/debian/patches/CVE-2022-39369.patch	1970-01-01 01:00:00.000000000 +0100
+++ php-cas-1.3.6/debian/patches/CVE-2022-39369.patch	2023-06-19 17:12:56.000000000 +0200
@@ -0,0 +1,954 @@
+From b759361d904a2cb2a3bcee9411fc348cfde5d163 Mon Sep 17 00:00:00 2001
+From: Phy <git@phy25.com>
+Date: Mon, 31 Oct 2022 16:34:25 -0400
+Subject: [PATCH] Merge pull request from GHSA-8q72-6qq8-xv64
+
+* Add ServerName classes and required service_name constructor argument
+
+This includes a refactoring of moving Client->_getClientUrl() method to a new class.
+
+Unit tests are also added and updated for the new constructor argument.
+
+* Add service_name argument to the static helper class and examples
+
+* Update docs for 1.6.0 release
+
+* Update versions for the 1.6.0 release
+
+* Rename ServerName class to ServiceBaseUrl and add protocol in allowedlist check
+
+* Update docs for the ServiceBaseUrl class and argument change
+
+* Minor typo fixes
+---
+ docs/ChangeLog                                |  31 ++-
+ docs/Upgrading                                |  34 +++
+ docs/examples/config.example.php              |   3 +
+ docs/examples/create_pgt_storage_db_table.php |   2 +-
+ docs/examples/example_advanced_saml11.php     |   2 +-
+ docs/examples/example_custom_urls.php         |   2 +-
+ docs/examples/example_gateway.php             |   2 +-
+ docs/examples/example_hardening.php           |   2 +-
+ docs/examples/example_html.php                |   2 +-
+ docs/examples/example_lang.php                |   2 +-
+ docs/examples/example_logout.php              |   2 +-
+ .../examples/example_no_ssl_cn_validation.php |   2 +-
+ docs/examples/example_pgt_storage_db.php      |   2 +-
+ docs/examples/example_pgt_storage_file.php    |   2 +-
+ docs/examples/example_proxy_GET.php           |   2 +-
+ docs/examples/example_proxy_POST.php          |   2 +-
+ docs/examples/example_proxy_rebroadcast.php   |   2 +-
+ docs/examples/example_proxy_serviceWeb.php    |   2 +-
+ .../example_proxy_serviceWeb_chaining.php     |   2 +-
+ docs/examples/example_renew.php               |   2 +-
+ docs/examples/example_service.php             |   2 +-
+ docs/examples/example_service_POST.php        |   2 +-
+ .../examples/example_service_that_proxies.php |   2 +-
+ docs/examples/example_simple.php              |   2 +-
+ source/CAS.php                                |  26 +-
+ source/CAS/Client.php                         | 109 ++++----
+ .../ServiceBaseUrl/AllowedListDiscovery.php   | 152 +++++++++++
+ source/CAS/ServiceBaseUrl/Base.php            |  98 +++++++
+ source/CAS/ServiceBaseUrl/Interface.php       |  61 +++++
+ source/CAS/ServiceBaseUrl/Static.php          |  69 +++++
+ test/CAS/Tests/AuthenticationTest.php         |   1 +
+ test/CAS/Tests/CallbackTest.php               |   1 +
+ test/CAS/Tests/Cas20AttributesTest.php        |   1 +
+ test/CAS/Tests/LogTest.php                    |   2 +
+ test/CAS/Tests/ProxyTicketValidationTest.php  |   1 +
+ test/CAS/Tests/ServiceBaseUrlTest.php         | 244 ++++++++++++++++++
+ test/CAS/Tests/ServiceMailTest.php            |   1 +
+ .../CAS/Tests/ServiceTicketValidationTest.php |   1 +
+ test/CAS/Tests/ServiceWebTest.php             |   1 +
+ utils/version.properties                      |   2 +-
+ 40 files changed, 789 insertions(+), 91 deletions(-)
+ create mode 100644 source/CAS/ServiceBaseUrl/AllowedListDiscovery.php
+ create mode 100644 source/CAS/ServiceBaseUrl/Base.php
+ create mode 100644 source/CAS/ServiceBaseUrl/Interface.php
+ create mode 100644 source/CAS/ServiceBaseUrl/Static.php
+ create mode 100644 test/CAS/Tests/ServiceBaseUrlTest.php
+
+--- a/source/CAS/Client.php
++++ b/source/CAS/Client.php
+@@ -896,6 +896,14 @@
+      * @param bool   $changeSessionID Allow phpCAS to change the session_id
+      *                                (Single Sign Out/handleLogoutRequests
+      *                                is based on that change)
++     * @param string|string[]|CAS_ServiceBaseUrl_Interface
++     *                                 $service_base_url the base URL (protocol, host and the
++     *                                                  optional port) of the CAS client; pass
++     *                                                  in an array to use auto discovery with
++     *                                                  an allowlist; pass in
++     *                                                  CAS_ServiceBaseUrl_Interface for custom
++     *                                                  behavior. Added in 1.6.0. Similar to
++     *                                                  serverName config in other CAS clients.
+      *
+      * @return a newly created CAS_Client object
+      */
+@@ -905,6 +913,7 @@
+         $server_hostname,
+         $server_port,
+         $server_uri,
++        $service_base_url,
+         $changeSessionID = true
+     ) {
+ 		// Argument validation
+@@ -921,6 +930,8 @@
+         if (gettype($changeSessionID) != 'boolean')
+         	throw new CAS_TypeMismatchException($changeSessionID, '$changeSessionID', 'boolean');
+ 
++    	$this->_setServiceBaseUrl($service_base_url);
++        	
+         phpCAS::traceBegin();
+         // true : allow to change the session_id(), false session_id won't be
+         // change and logout won't be handle because of that
+@@ -1007,7 +1018,7 @@
+ 
+         if ( $this->_isCallbackMode() ) {
+             //callback mode: check that phpCAS is secured
+-            if ( !$this->_isHttps() ) {
++            if ( !$this->getServiceBaseUrl()->isHttps() ) {
+                 phpCAS::error(
+                     'CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server'
+                 );
+@@ -2363,8 +2374,7 @@
+         if ( empty($this->_callback_url) ) {
+             $final_uri = '';
+             // remove the ticket if present in the URL
+-            $final_uri = 'https://';
+-            $final_uri .= $this->_getClientUrl();
++            $final_uri = $this->getServiceBaseUrl()->get();
+             $request_uri = $_SERVER['REQUEST_URI'];
+             $request_uri = preg_replace('/\?.*$/', '', $request_uri);
+             $final_uri .= $request_uri;
+@@ -3528,10 +3538,7 @@
+         if ( empty($this->_url) ) {
+             $final_uri = '';
+             // remove the ticket if present in the URL
+-            $final_uri = ($this->_isHttps()) ? 'https' : 'http';
+-            $final_uri .= '://';
+-
+-            $final_uri .= $this->_getClientUrl();
++            $final_uri = $this->getServiceBaseUrl()->get();
+             $request_uri	= explode('?', $_SERVER['REQUEST_URI'], 2);
+             $final_uri		.= $request_uri[0];
+ 
+@@ -3568,66 +3575,62 @@
+         return $this->_server['base_url'] = $url;
+     }
+ 
+-
+     /**
+-     * Try to figure out the phpCas client URL with possible Proxys / Ports etc.
++     * The ServiceBaseUrl object that provides base URL during service URL
++     * discovery process.
++     *
++     * @var CAS_ServiceBaseUrl_Interface
++     *
++     * @hideinitializer
++     */
++    private $_serviceBaseUrl = null;
++    
++    
++    /**
++     * Answer the CAS_ServiceBaseUrl_Interface object for this client.
+      *
+-     * @return string Server URL with domain:port
++     * @return CAS_ServiceBaseUrl_Interface
+      */
+-    private function _getClientUrl()
++    public function getServiceBaseUrl()
+     {
+-        $server_url = '';
+-        if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
+-            // explode the host list separated by comma and use the first host
+-            $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
+-            // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default
+-            return $hosts[0];
+-        } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
+-            $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
+-        } else {
+-            if (empty($_SERVER['SERVER_NAME'])) {
+-                $server_url = $_SERVER['HTTP_HOST'];
+-            } else {
+-                $server_url = $_SERVER['SERVER_NAME'];
+-            }
++        if (empty($this->_serviceBaseUrl)) {
++            phpCAS::error("ServiceBaseUrl object is not initialized");
+         }
+-        if (!strpos($server_url, ':')) {
+-            if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
+-                $server_port = $_SERVER['SERVER_PORT'];
+-            } else {
+-                $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']);
+-                $server_port = $ports[0];
+-            }
+-
+-            if ( ($this->_isHttps() && $server_port!=443)
+-                || (!$this->_isHttps() && $server_port!=80)
+-            ) {
+-                $server_url .= ':';
+-                $server_url .= $server_port;
+-            }
+-        }
+-        return $server_url;
++        return $this->_serviceBaseUrl;
+     }
+ 
+     /**
+-     * This method checks to see if the request is secured via HTTPS
++     * This method sets the service base URL used during service URL discovery process.
++     *
++     * This is required since phpCAS 1.6.0 to protect the integrity of the authentication.
++     *
++     * @since phpCAS 1.6.0
++     *
++     * @param $name can be any of the following:
++     *   - A base URL string. The service URL discovery will always use this (protocol,
++     *     hostname and optional port number) without using any external host names.
++     *   - An array of base URL strings. The service URL discovery will check against
++     *     this list before using the auto discovered base URL. If there is no match,
++     *     the first base URL in the array will be used as the default. This option is
++     *     helpful if your PHP website is accessible through multiple domains without a
++     *     canonical name, or through both HTTP and HTTPS.
++     *   - A class that implements CAS_ServiceBaseUrl_Interface. If you need to customize
++     *     the base URL discovery behavior, you can pass in a class that implements the
++     *     interface.
+      *
+-     * @return bool true if https, false otherwise
++     * @return void
+      */
+-    private function _isHttps()
++    private function _setServiceBaseUrl($name)
+     {
+-        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
+-            return ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
+-        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
+-            return ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
+-        } elseif ( isset($_SERVER['HTTPS'])
+-            && !empty($_SERVER['HTTPS'])
+-            && strcasecmp($_SERVER['HTTPS'], 'off') !== 0
+-        ) {
+-            return true;
++        if (is_array($name)) {
++            $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_AllowedListDiscovery($name);
++        } else if (is_string($name)) {
++            $this->_serviceBaseUrl = new CAS_ServiceBaseUrl_Static($name);
++        } else if ($name instanceof CAS_ServiceBaseUrl_Interface) {
++            $this->_serviceBaseUrl = $name;
++        } else {
++            throw new CAS_TypeMismatchException($name, '$name', 'array, string, or CAS_ServiceBaseUrl_Interface object');
+         }
+-        return false;
+-
+     }
+ 
+     /**
+--- /dev/null
++++ b/source/CAS/ServiceBaseUrl/AllowedListDiscovery.php
+@@ -0,0 +1,152 @@
++<?php
++
++/**
++ * Licensed to Jasig under one or more contributor license
++ * agreements. See the NOTICE file distributed with this work for
++ * additional information regarding copyright ownership.
++ *
++ * Jasig licenses this file to you under the Apache License,
++ * Version 2.0 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License at:
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ * PHP Version 7
++ *
++ * @file     CAS/ServiceBaseUrl/AllowedListDiscovery.php
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++
++/**
++ * Class that gets the service base URL of the PHP server by HTTP header
++ * discovery and allowlist check. This is used to generate service URL
++ * and PGT callback URL.
++ *
++ * @class    CAS_ServiceBaseUrl_AllowedListDiscovery
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++class CAS_ServiceBaseUrl_AllowedListDiscovery
++extends CAS_ServiceBaseUrl_Base
++{
++    private $_list = array();
++
++    public function __construct($list) {
++        if (is_array($list)) {
++            if (count($list) === 0) {
++                throw new CAS_InvalidArgumentException('$list should not be empty');
++            }
++            foreach ($list as $value) {
++                $this->allow($value);
++            }
++        } else {
++            throw new CAS_TypeMismatchException($list, '$list', 'array');
++        }
++    }
++
++    /**
++     * Add a base URL to the allowed list.
++     *
++     * @param $url protocol, host name and port to add to the allowed list
++     *
++     * @return void
++     */
++    public function allow($url)
++    {
++        $this->_list[] = $this->removeStandardPort($url);
++    }
++
++    /**
++     * Check if the server name is allowed by configuration.
++     *
++     * @param $name server name to check
++     *
++     * @return bool whether the allowed list contains the server name
++     */
++    protected function isAllowed($name)
++    {
++        return in_array($name, $this->_list);
++    }
++
++    /**
++     * Discover the server name through HTTP headers.
++     *
++     * We read:
++     * - HTTP header X-Forwarded-Host
++     * - HTTP header X-Forwarded-Server and X-Forwarded-Port
++     * - HTTP header Host and SERVER_PORT
++     * - PHP SERVER_NAME (which can change based on the HTTP server used)
++     *
++     * The standard port will be omitted (80 for HTTP, 443 for HTTPS).
++     *
++     * @return string the discovered, unsanitized server protocol, hostname and port
++     */
++    protected function discover()
++    {
++        $isHttps = $this->isHttps();
++        $protocol = $isHttps ? 'https' : 'http';
++        $protocol .= '://';
++        if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
++            // explode the host list separated by comma and use the first host
++            $hosts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
++            // see rfc7239#5.3 and rfc7230#2.7.1: port is in HTTP_X_FORWARDED_HOST if non default
++            return $protocol . $hosts[0];
++        } else if (!empty($_SERVER['HTTP_X_FORWARDED_SERVER'])) {
++            $server_url = $_SERVER['HTTP_X_FORWARDED_SERVER'];
++        } else {
++            if (empty($_SERVER['SERVER_NAME'])) {
++                $server_url = $_SERVER['HTTP_HOST'];
++            } else {
++                $server_url = $_SERVER['SERVER_NAME'];
++            }
++        }
++        if (!strpos($server_url, ':')) {
++            if (empty($_SERVER['HTTP_X_FORWARDED_PORT'])) {
++                $server_port = $_SERVER['SERVER_PORT'];
++            } else {
++                $ports = explode(',', $_SERVER['HTTP_X_FORWARDED_PORT']);
++                $server_port = $ports[0];
++            }
++
++            $server_url .= ':';
++            $server_url .= $server_port;
++        }
++        return $protocol . $server_url;
++    }
++
++    /**
++     * Get PHP server base URL.
++     *
++     * @return string the server protocol, hostname and port
++     */
++    public function get()
++    {
++        phpCAS::traceBegin();
++        $result = $this->removeStandardPort($this->discover());
++        phpCAS::trace("Discovered server base URL: " . $result);
++        if ($this->isAllowed($result)) {
++            phpCAS::trace("Server base URL is allowed");
++            phpCAS::traceEnd(true);
++        } else {
++            $result = $this->_list[0];
++            phpCAS::trace("Server base URL is not allowed, using default: " . $result);
++            phpCAS::traceEnd(false);
++        }
++        return $result;
++    }
++}
+--- /dev/null
++++ b/source/CAS/ServiceBaseUrl/Base.php
+@@ -0,0 +1,98 @@
++<?php
++
++/**
++ * Licensed to Jasig under one or more contributor license
++ * agreements. See the NOTICE file distributed with this work for
++ * additional information regarding copyright ownership.
++ *
++ * Jasig licenses this file to you under the Apache License,
++ * Version 2.0 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License at:
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ * PHP Version 7
++ *
++ * @file     CAS/ServiceBaseUrl/Base.php
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++/**
++ * Base class of CAS/ServiceBaseUrl that implements isHTTPS method.
++ *
++ * @class    CAS_ServiceBaseUrl_Base
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++abstract class CAS_ServiceBaseUrl_Base
++implements CAS_ServiceBaseUrl_Interface
++{
++
++    /**
++     * Get PHP server name.
++     *
++     * @return string the server hostname and port of the server
++     */
++    abstract public function get();
++
++    /**
++     * Check whether HTTPS is used.
++     *
++     * This is used to construct the protocol in the URL.
++     *
++     * @return bool true if HTTPS is used
++     */
++    public function isHttps() {
++        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
++            return ($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
++        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_PROTOCOL'])) {
++            return ($_SERVER['HTTP_X_FORWARDED_PROTOCOL'] === 'https');
++        } elseif ( isset($_SERVER['HTTPS'])
++            && !empty($_SERVER['HTTPS'])
++            && strcasecmp($_SERVER['HTTPS'], 'off') !== 0
++        ) {
++            return true;
++        }
++        return false;
++    }
++
++    /**
++     * Remove standard HTTP and HTTPS port for discovery and allowlist input.
++     *
++     * @param $url URL as https://domain:port without trailing slash
++     * @return standardized URL, or the original URL
++     * @throws CAS_InvalidArgumentException if the URL does not include the protocol
++     */
++    protected function removeStandardPort($url) {
++        if (strpos($url, "://") === false) {
++            throw new CAS_InvalidArgumentException(
++                "Configured base URL should include the protocol string: " . $url);
++        }
++
++        $url = rtrim($url, '/');
++
++        if (strpos($url, "https://";) === 0 && substr_compare($url, ':443', -4) === 0) {
++            return substr($url, 0, -4);
++        }
++
++        if (strpos($url, "http://";) === 0 && substr_compare($url, ':80', -3) === 0) {
++            return substr($url, 0, -3);
++        }
++
++        return $url;
++    }
++
++}
+--- /dev/null
++++ b/source/CAS/ServiceBaseUrl/Interface.php
+@@ -0,0 +1,61 @@
++<?php
++
++/**
++ * Licensed to Jasig under one or more contributor license
++ * agreements. See the NOTICE file distributed with this work for
++ * additional information regarding copyright ownership.
++ *
++ * Jasig licenses this file to you under the Apache License,
++ * Version 2.0 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License at:
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ * PHP Version 7
++ *
++ * @file     CAS/ServerHostname/Interface.php
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++/**
++ * An interface for classes that gets the server name of the PHP server.
++ * This is used to generate service URL and PGT callback URL.
++ *
++ * @class    CAS_ServiceBaseUrl_Interface
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++interface CAS_ServiceBaseUrl_Interface
++{
++
++    /**
++     * Get PHP HTTP protocol and server name.
++     *
++     * @return string protocol, server hostname, and optionally port,
++     *                without trailing slash (https://localhost:8443)
++     */
++    public function get();
++
++    /**
++     * Check whether HTTPS is used.
++     *
++     * This is used to construct the protocol in the URL.
++     *
++     * @return bool true if HTTPS is used
++     */
++    public function isHttps();
++
++}
+--- /dev/null
++++ b/source/CAS/ServiceBaseUrl/Static.php
+@@ -0,0 +1,69 @@
++<?php
++
++/**
++ * Licensed to Jasig under one or more contributor license
++ * agreements. See the NOTICE file distributed with this work for
++ * additional information regarding copyright ownership.
++ *
++ * Jasig licenses this file to you under the Apache License,
++ * Version 2.0 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License at:
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ * PHP Version 7
++ *
++ * @file     CAS/ServiceBaseUrl/Static.php
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++
++/**
++ * Class that gets the server name of the PHP server by statically set
++ * hostname and port. This is used to generate service URL and PGT
++ * callback URL.
++ *
++ * @class    CAS_ServiceBaseUrl_Static
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++class CAS_ServiceBaseUrl_Static
++extends CAS_ServiceBaseUrl_Base
++{
++    private $_name = null;
++
++    public function __construct($name) {
++        if (is_string($name)) {
++            $this->_name = $this->removeStandardPort($name);
++        } else {
++            throw new CAS_TypeMismatchException($name, '$name', 'string');
++        }
++    }
++
++    /**
++     * Get the server name through static config.
++     *
++     * @return string the server hostname and port of the server configured
++     */
++    public function get()
++    {
++        phpCAS::traceBegin();
++        phpCAS::trace("Returning static server name: " . $this->_name);
++        phpCAS::traceEnd(true);
++        return $this->_name;
++    }
++}
+\ No newline at end of file
+--- /dev/null
++++ b/test/CAS/Tests/ServiceBaseUrlTest.php
+@@ -0,0 +1,244 @@
++<?php
++
++/**
++ * Licensed to Jasig under one or more contributor license
++ * agreements. See the NOTICE file distributed with this work for
++ * additional information regarding copyright ownership.
++ *
++ * Jasig licenses this file to you under the Apache License,
++ * Version 2.0 (the "License"); you may not use this file except in
++ * compliance with the License. You may obtain a copy of the License at:
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ *
++ * PHP Version 7
++ *
++ * @file     CAS/Tests/ServiceBaseUrlTest.php
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++
++namespace PhpCas\Tests;
++
++use PHPUnit\Framework\TestCase;
++
++/**
++ * Test class for verifying the operation of the ServiceBaseUrl classes.
++ *
++ * @class    ServiceBaseUrlTest
++ * @category Authentication
++ * @package  PhpCAS
++ * @author   Henry Pan <git@phy25.com>
++ * @license  http://www.apache.org/licenses/LICENSE-2.0  Apache License 2.0
++ * @link     https://wiki.jasig.org/display/CASC/phpCAS
++ */
++class ServiceBaseUrlTest extends TestCase
++{
++    /**
++     * @var CAS_Client
++     */
++    protected $object;
++
++    const DEFAULT_NAME = 'https://default.domain';
++
++    const DOMAIN_1 = 'http://domain1';
++
++    /**
++     * Sets up the fixture, for example, opens a network connection.
++     * This method is called before a test is executed.
++     */
++    protected function setUp(): void
++    {
++        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array(self::DEFAULT_NAME, self::DOMAIN_1));
++    }
++
++    /**
++     * Tears down the fixture, for example, closes a network connection.
++     * This method is called after a test is executed.
++     */
++    protected function tearDown(): void
++    {
++
++    }
++
++    /*********************************************************
++     * Tests of public (interface) methods
++     *********************************************************/
++
++    /**
++     * Verify that non allowlisted SERVER_NAME will return default name.
++     *
++     * @return void
++     */
++    public function testNonAllowlistedServerName()
++    {
++        $_SERVER['SERVER_NAME'] = 'domain1:8080';
++
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that non allowlisted HTTP_HOST will return default name.
++     *
++     * @return void
++     */
++    public function testNonAllowlistedHttpHost()
++    {
++        $_SERVER['HTTP_HOST'] = 'domain1:8080';
++        $_SERVER['SERVER_NAME'] = '';
++
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that non allowlisted HTTP_X_FORWARDED_SERVER will return default name.
++     *
++     * @return void
++     */
++    public function testNonAllowlistedXForwardedServer()
++    {
++        $_SERVER['HTTP_X_FORWARDED_SERVER'] = 'domain1';
++        $_SERVER['HTTP_X_FORWARDED_PORT'] = '8080';
++
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that non allowlisted HTTP_X_FORWARDED_SERVER will return.
++     *
++     * @return void
++     */
++    public function testNonAllowlistedXForwardedHost()
++    {
++        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1:8080';
++
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted SERVER_NAME will return in standarized form.
++     *
++     * @return void
++     */
++    public function testAllowlistedServerName()
++    {
++        $_SERVER['SERVER_NAME'] = 'domain1';
++        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
++
++        $this->assertSame(self::DOMAIN_1, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_HOST will return.
++     *
++     * @return void
++     */
++    public function testAllowlistedHttpHost()
++    {
++        $_SERVER['HTTP_HOST'] = 'domain1';
++        $_SERVER['SERVER_NAME'] = '';
++        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
++
++        $this->assertSame(self::DOMAIN_1, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_X_FORWARDED_SERVER will return.
++     *
++     * @return void
++     */
++    public function testAllowlistedXForwardedServer()
++    {
++        $_SERVER['HTTP_X_FORWARDED_SERVER'] = 'domain1';
++        $_SERVER['HTTP_X_FORWARDED_PORT'] = '80';
++
++        $this->assertSame(self::DOMAIN_1, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return.
++     *
++     * @return void
++     */
++    public function testAllowlistedXForwardedHost()
++    {
++        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1';
++
++        $this->assertSame(self::DOMAIN_1, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
++     * that needs to be standardized.
++     *
++     * @return void
++     */
++    public function testAllowlistedXForwardedHostHttpStandardized()
++    {
++        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'domain1';
++
++        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array(self::DEFAULT_NAME, "http://domain1:80/";));
++        $this->assertSame(self::DOMAIN_1, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
++     * that needs to be standardized.
++     *
++     * @return void
++     */
++    public function testAllowlistedXForwardedHostWithSslHttpsStandardized()
++    {
++        $_SERVER['HTTPS'] = 'on';
++        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'default.domain:443';
++
++        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array("https://default.domain:443/";, self::DOMAIN_1));
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that allowlisted HTTP_X_FORWARDED_HOST will return with a HTTP allowlist
++     * that needs to be standardized.
++     *
++     * @return void
++     */
++    public function testAllowlistedXForwardedHostHttpsStandardized()
++    {
++        $_SERVER['HTTP_X_FORWARDED_HOST'] = 'default.domain:443';
++
++        $this->object = new \CAS_ServiceBaseUrl_AllowedListDiscovery(array("https://default.domain:443/";, "http://default.domain:443/";));
++        $this->assertSame("http://default.domain:443";, $this->object->get());
++    }
++
++    /**
++     * Verify that static configuration always return the standardized base URL.
++     *
++     * @return void
++     */
++    public function testStaticHappyPath()
++    {
++        $this->object = new \CAS_ServiceBaseUrl_Static("https://default.domain:443/";);
++        $this->assertSame(self::DEFAULT_NAME, $this->object->get());
++    }
++
++    /**
++     * Verify that static configuration always return the standardized base URL.
++     *
++     * @return void
++     */
++    public function testStaticNoProtocol()
++    {
++        $this->expectException(\CAS_InvalidArgumentException::class);
++        $this->object = new \CAS_ServiceBaseUrl_Static("default.domain/");
++    }
++
++}
+--- a/source/CAS.php
++++ b/source/CAS.php
+@@ -327,6 +327,14 @@
+      * @param string $server_hostname the hostname of the CAS server
+      * @param string $server_port     the port the CAS server is running on
+      * @param string $server_uri      the URI the CAS server is responding on
++     * @param string|string[]|CAS_ServiceBaseUrl_Interface
++     *                                 $service_base_url the base URL (protocol, host and the
++     *                                                  optional port) of the CAS client; pass
++     *                                                  in an array to use auto discovery with
++     *                                                  an allowlist; pass in
++     *                                                  CAS_ServiceBaseUrl_Interface for custom
++     *                                                  behavior. Added in 1.6.0. Similar to
++     *                                                  serverName config in other CAS clients.
+      * @param bool   $changeSessionID Allow phpCAS to change the session_id (Single
+      * Sign Out/handleLogoutRequests is based on that change)
+      *
+@@ -336,7 +344,7 @@
+      * and phpCAS::setDebug()).
+      */
+     public static function client($server_version, $server_hostname,
+-        $server_port, $server_uri, $changeSessionID = true
++        $server_port, $server_uri, $service_base_url, $changeSessionID = true
+     ) {
+         phpCAS :: traceBegin();
+         if (is_object(self::$_PHPCAS_CLIENT)) {
+@@ -355,7 +363,7 @@
+         // initialize the object $_PHPCAS_CLIENT
+         try {
+             self::$_PHPCAS_CLIENT = new CAS_Client(
+-                $server_version, false, $server_hostname, $server_port, $server_uri,
++                $server_version, false, $server_hostname, $server_port, $server_uri, $service_base_url,
+                 $changeSessionID
+             );
+         } catch (Exception $e) {
+@@ -371,6 +379,14 @@
+      * @param string $server_hostname the hostname of the CAS server
+      * @param string $server_port     the port the CAS server is running on
+      * @param string $server_uri      the URI the CAS server is responding on
++     * @param string|string[]|CAS_ServiceBaseUrl_Interface
++     *                                 $service_base_url the base URL (protocol, host and the
++     *                                                  optional port) of the CAS client; pass
++     *                                                  in an array to use auto discovery with
++     *                                                  an allowlist; pass in
++     *                                                  CAS_ServiceBaseUrl_Interface for custom
++     *                                                  behavior. Added in 1.6.0. Similar to
++     *                                                  serverName config in other CAS clients.
+      * @param bool   $changeSessionID Allow phpCAS to change the session_id (Single
+      * Sign Out/handleLogoutRequests is based on that change)
+      *
+@@ -380,7 +396,7 @@
+      * and phpCAS::setDebug()).
+      */
+     public static function proxy($server_version, $server_hostname,
+-        $server_port, $server_uri, $changeSessionID = true
++        $server_port, $server_uri, $service_base_url, $changeSessionID = true
+     ) {
+         phpCAS :: traceBegin();
+         if (is_object(self::$_PHPCAS_CLIENT)) {
+@@ -399,7 +415,7 @@
+         // initialize the object $_PHPCAS_CLIENT
+         try {
+             self::$_PHPCAS_CLIENT = new CAS_Client(
+-                $server_version, true, $server_hostname, $server_port, $server_uri,
++                $server_version, true, $server_hostname, $server_port, $server_uri, $service_base_url,
+                 $changeSessionID
+             );
+         } catch (Exception $e) {
diff -Nru php-cas-1.3.6/debian/patches/series php-cas-1.3.6/debian/patches/series
--- php-cas-1.3.6/debian/patches/series	1970-01-01 01:00:00.000000000 +0100
+++ php-cas-1.3.6/debian/patches/series	2023-06-19 17:12:56.000000000 +0200
@@ -0,0 +1 @@
+CVE-2022-39369.patch

Attachment: signature.asc
Description: PGP signature


Reply to: