VYPR
High severity8.1CISA KEVNVD Advisory· Published Oct 4, 2017· Updated Apr 21, 2026

CVE-2017-12617

CVE-2017-12617

Description

When running Apache Tomcat versions 9.0.0.M1 to 9.0.0, 8.5.0 to 8.5.22, 8.0.0.RC1 to 8.0.46 and 7.0.0 to 7.0.81 with HTTP PUTs enabled (e.g. via setting the readonly initialisation parameter of the Default servlet to false) it was possible to upload a JSP file to the server via a specially crafted request. This JSP could then be requested and any code it contained would be executed by the server.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.tomcat:tomcat-catalinaMaven
>= 9.0.0.M1, < 9.0.19.0.1
org.apache.tomcat:tomcat-catalinaMaven
>= 8.5.0, < 8.5.238.5.23
org.apache.tomcat:tomcat-catalinaMaven
>= 8.0.0-RC1, < 8.0.478.0.47
org.apache.tomcat:tomcat-catalinaMaven
>= 7.0.0, < 7.0.827.0.82
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 9.0.0.M1, < 9.0.19.0.1
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 8.5.0, < 8.5.238.5.23
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 8.0.0-RC1, < 8.0.478.0.47
org.apache.tomcat.embed:tomcat-embed-coreMaven
>= 7.0.0, < 7.0.827.0.82

Affected products

151
  • cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*
    Range: >=7.0.0,<7.0.82
  • cpe:2.3:a:netapp:active_iq_unified_manager:*:*:*:*:*:vmware_vsphere:*:*+ 1 more
    • cpe:2.3:a:netapp:active_iq_unified_manager:*:*:*:*:*:vmware_vsphere:*:*range: >=9.5
    • cpe:2.3:a:netapp:active_iq_unified_manager:*:*:*:*:*:windows:*:*range: >=7.3
  • cpe:2.3:a:netapp:oncommand_balance:-:*:*:*:*:*:*:*
  • cpe:2.3:a:netapp:oncommand_insight:-:*:*:*:*:*:*:*
  • cpe:2.3:a:netapp:oncommand_shift:-:*:*:*:*:*:*:*
  • cpe:2.3:a:netapp:oncommand_workflow_automation:-:*:*:*:*:*:*:*
  • cpe:2.3:a:netapp:snapcenter:-:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:agile_plm:9.3.3:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:agile_plm:9.3.3:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:agile_plm:9.3.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:agile_plm:9.3.5:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:agile_plm:9.3.6:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:communications_instant_messaging_server:10.0.1:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:endeca_information_discovery_integrator:3.1.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:endeca_information_discovery_integrator:3.1.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:endeca_information_discovery_integrator:3.2.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:enterprise_manager_for_mysql_database:12.1.0.4.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:financial_services_analytical_applications_infrastructure:*:*:*:*:*:*:*:*
    Range: >=7.3.3.0.0,<=7.3.5.3.0
  • cpe:2.3:a:oracle:fmw_platform:12.2.1.2.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:fmw_platform:12.2.1.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:fmw_platform:12.2.1.3.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:health_sciences_empirica_inspections:1.0.1.1:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:hospitality_guest_access:4.2.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:hospitality_guest_access:4.2.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:hospitality_guest_access:4.2.1:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:instantis_enterprisetrack:17.1:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:instantis_enterprisetrack:17.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:instantis_enterprisetrack:17.2:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:management_pack:11.2.1.0.13:*:*:*:*:goldengate:*:*
  • cpe:2.3:a:oracle:micros_lucas:2.9.5:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.0.1:*:*:*:*:*:*:*+ 5 more
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.5.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.6.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.7.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.8.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:micros_retail_xbri_loss_prevention:10.8.1:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:mysql_enterprise_monitor:*:*:*:*:*:*:*:*
    Range: <=3.3.6.3293
  • cpe:2.3:a:oracle:retail_advanced_inventory_planning:13.2:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:retail_advanced_inventory_planning:13.2:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_advanced_inventory_planning:13.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_advanced_inventory_planning:14.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_advanced_inventory_planning:15.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_back_office:14.0.4:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:retail_back_office:14.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_back_office:14.1.3:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_central_office:14.0.4:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:retail_central_office:14.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_central_office:14.1.3:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_convenience_and_fuel_pos_software:2.1.132:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_eftlink:1.1.124:*:*:*:*:*:*:*+ 2 more
    • cpe:2.3:a:oracle:retail_eftlink:1.1.124:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_eftlink:15.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_eftlink:16.0.2:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_insights:14.0:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:retail_insights:14.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_insights:14.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_insights:15.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_insights:16.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_invoice_matching:12.0:*:*:*:*:*:*:*+ 7 more
    • cpe:2.3:a:oracle:retail_invoice_matching:12.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:13.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:13.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:13.2:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:14.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:14.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:15.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_invoice_matching:16.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_order_broker:15.0:*:*:*:*:*:*:*+ 4 more
    • cpe:2.3:a:oracle:retail_order_broker:15.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_broker:16.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_broker:5.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_broker:5.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_broker:5.2:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_order_management_system:4.0:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:retail_order_management_system:4.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_management_system:4.5:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_management_system:4.7:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_order_management_system:5.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_point-of-service:14.0.4:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:oracle:retail_point-of-service:14.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_point-of-service:14.1.3:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_price_management:12.0:*:*:*:*:*:*:*+ 7 more
    • cpe:2.3:a:oracle:retail_price_management:12.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:13.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:13.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:13.2:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:14.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:14.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:15.0:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_price_management:16.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_returns_management:14.0.4:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:retail_returns_management:14.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_returns_management:14.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_returns_management:2.3.8:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_returns_management:2.4.9:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_store_inventory_management:12.0.12:*:*:*:*:*:*:*+ 7 more
    • cpe:2.3:a:oracle:retail_store_inventory_management:12.0.12:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:13.0.7:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:13.1.9:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:13.2.9:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:14.0.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:14.1.3:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:15.0.2:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_store_inventory_management:16.0.1:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:retail_xstore_point_of_service:15.0.1:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:a:oracle:retail_xstore_point_of_service:15.0.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_xstore_point_of_service:6.0.11:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_xstore_point_of_service:7.0.6:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:retail_xstore_point_of_service:7.1.6:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:transportation_management:6.3.1:*:*:*:*:*:*:*+ 6 more
    • cpe:2.3:a:oracle:transportation_management:6.3.1:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.2:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.3:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.4:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.5:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.6:*:*:*:*:*:*:*
    • cpe:2.3:a:oracle:transportation_management:6.3.7:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:tuxedo_system_and_applications_monitor:12.1.3.0.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:webcenter_sites:11.1.1.8.0:*:*:*:*:*:*:*
  • cpe:2.3:a:oracle:workload_manager:12.2.0.1:*:*:*:*:*:*:*
  • cpe:2.3:a:redhat:fuse:1.0:*:*:*:*:*:*:*
  • cpe:2.3:a:redhat:jboss_enterprise_application_platform:6.0.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:redhat:jboss_enterprise_application_platform:6.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:redhat:jboss_enterprise_application_platform:6.4.0:*:*:*:*:*:*:*
  • cpe:2.3:a:redhat:jboss_enterprise_web_server:2.0.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:a:redhat:jboss_enterprise_web_server:2.0.0:*:*:*:*:*:*:*
    • cpe:2.3:a:redhat:jboss_enterprise_web_server:3.0.0:*:*:*:*:*:*:*
  • cpe:2.3:a:redhat:jboss_enterprise_web_server_text-only_advisories:-:*:*:*:*:*:*:*
  • cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:esm:*:*:*+ 3 more
    • cpe:2.3:o:canonical:ubuntu_linux:12.04:*:*:*:esm:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:16.04:*:*:*:esm:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:17.10:*:*:*:*:*:*:*
    • cpe:2.3:o:canonical:ubuntu_linux:18.04:*:*:*:esm:*:*:*
  • cpe:2.3:o:debian:debian_linux:7.0:*:*:*:*:*:*:*
  • cpe:2.3:o:netapp:element:-:*:*:*:*:vcenter_server:*:*
  • cpe:2.3:o:redhat:enterprise_linux_desktop:6.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:redhat:enterprise_linux_desktop:6.0:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_desktop:7.0:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_eus:7.4:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:o:redhat:enterprise_linux_eus:7.4:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus:7.5:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus:7.6:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus:7.7:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_eus_compute_node:7.4:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:o:redhat:enterprise_linux_eus_compute_node:7.4:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus_compute_node:7.5:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus_compute_node:7.6:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_eus_compute_node:7.7:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems:6.0_s390x:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems:6.0_s390x:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems:7.0_s390x:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems_eus:7.4_s390x:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems_eus:7.4_s390x:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems_eus:7.5_s390x:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems_eus:7.6_s390x:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_ibm_z_systems_eus:7.7_s390x:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian:6.0_ppc64:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian:6.0_ppc64:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian:7.0_ppc64:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian_eus:7.4_ppc64:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian_eus:7.4_ppc64:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian_eus:7.5_ppc64:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian_eus:7.6_ppc64:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_big_endian_eus:7.7_ppc64:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian:7.0:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian_eus:7.4_ppc64le:*:*:*:*:*:*:*+ 3 more
    • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian_eus:7.4_ppc64le:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian_eus:7.5_ppc64le:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian_eus:7.6_ppc64le:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_for_power_little_endian_eus:7.7_ppc64le:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_server:6.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:redhat:enterprise_linux_server:6.0:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_server:7.0:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_server_aus:7.4:*:*:*:*:*:*:*+ 2 more
    • cpe:2.3:o:redhat:enterprise_linux_server_aus:7.4:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_server_aus:7.6:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_server_aus:7.7:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_server_tus:7.4:*:*:*:*:*:*:*+ 2 more
    • cpe:2.3:o:redhat:enterprise_linux_server_tus:7.4:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_server_tus:7.6:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_server_tus:7.7:*:*:*:*:*:*:*
  • cpe:2.3:o:redhat:enterprise_linux_workstation:6.0:*:*:*:*:*:*:*+ 1 more
    • cpe:2.3:o:redhat:enterprise_linux_workstation:6.0:*:*:*:*:*:*:*
    • cpe:2.3:o:redhat:enterprise_linux_workstation:7.0:*:*:*:*:*:*:*

Patches

21
f1b85da754c4

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62498

https://github.com/apache/tomcatMark ThomasJun 30, 2018via ghsa
3 files changed · +111 1
  • java/org/apache/naming/resources/VirtualDirContext.java+2 1 modified
    @@ -212,7 +212,8 @@ protected File file(String name, boolean mustExist) {
                         }
                     }
                 }
    -            if (name.startsWith(path + "/")) {
    +            path += "/";
    +            if (name.startsWith(path)) {
                     String res = name.substring(path.length());
                     for (String resourcesDir : dirList) {
                         file = new File(resourcesDir, res);
    
  • test/org/apache/naming/resources/TestVirtualDirContext.java+102 0 added
    @@ -0,0 +1,102 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.
    + */
    +package org.apache.naming.resources;
    +
    +import java.io.File;
    +
    +import javax.naming.NamingException;
    +
    +import org.junit.Assert;
    +import org.junit.Test;
    +
    +import org.apache.catalina.startup.LoggingBaseTest;
    +
    +public class TestVirtualDirContext {
    +
    +    @Test
    +    public void testBug62498() throws NamingException {
    +        VirtualDirContext vdc = new VirtualDirContext();
    +        // No docBase
    +        vdc.setExtraResourcePaths("/=" + LoggingBaseTest.getBuildDirectory().getAbsolutePath());
    +
    +        vdc.allocate();
    +
    +        File f1 = vdc.file("");
    +        Assert.assertNotNull(f1);
    +        File f2 = vdc.file("/");
    +        Assert.assertNotNull(f2);
    +        Assert.assertEquals(f1.getAbsolutePath(), f2.getAbsolutePath());
    +
    +        Object obj1 = vdc.lookup("");
    +        Assert.assertTrue(obj1 instanceof FileDirContext);
    +        Object obj2 = vdc.lookup("/");
    +        Assert.assertTrue(obj2 instanceof FileDirContext);
    +        Assert.assertEquals(((FileDirContext) obj1).absoluteBase, ((FileDirContext) obj2).absoluteBase);
    +    }
    +
    +
    +    @Test
    +    public void testBug62498a() {
    +        VirtualDirContext vdc = new VirtualDirContext();
    +        // No docBase
    +        vdc.setExtraResourcePaths("/=" + LoggingBaseTest.getBuildDirectory().getAbsolutePath());
    +
    +        vdc.allocate();
    +
    +        File f1 = vdc.file("");
    +        Assert.assertNotNull(f1);
    +    }
    +
    +
    +    @Test
    +    public void testBug62498b() {
    +        VirtualDirContext vdc = new VirtualDirContext();
    +        // No docBase
    +        vdc.setExtraResourcePaths("/=" + LoggingBaseTest.getBuildDirectory().getAbsolutePath());
    +
    +        vdc.allocate();
    +
    +        File f2 = vdc.file("/");
    +        Assert.assertNotNull(f2);
    +    }
    +
    +
    +    @Test
    +    public void testBug62498c() throws NamingException {
    +        VirtualDirContext vdc = new VirtualDirContext();
    +        // No docBase
    +        vdc.setExtraResourcePaths("/=" + LoggingBaseTest.getBuildDirectory().getAbsolutePath());
    +
    +        vdc.allocate();
    +
    +        Object obj1 = vdc.lookup("");
    +        Assert.assertTrue(obj1 instanceof FileDirContext);
    +    }
    +
    +
    +    @Test
    +    public void testBug62498d() throws NamingException {
    +        VirtualDirContext vdc = new VirtualDirContext();
    +        // No docBase
    +        vdc.setExtraResourcePaths("/=" + LoggingBaseTest.getBuildDirectory().getAbsolutePath());
    +
    +        vdc.allocate();
    +
    +        Object obj2 = vdc.lookup("/");
    +        Assert.assertTrue(obj2 instanceof FileDirContext);
    +    }
    +}
    
  • webapps/docs/changelog.xml+7 0 modified
    @@ -58,6 +58,13 @@
       issues do not "pop up" wrt. others).
     -->
     <section name="Tomcat 7.0.90 (violetagg)">
    +  <subsection name="Catalina">
    +    <changelog>
    +      <fix>62498</fix>: Correct a regression in the fix for CVE-2017-12617 that
    +      caused request failures for some requests when using the
    +      <code>VirtualDirContext</code>. (markt)
    +    </changelog>
    +  </subsection>
     </section>
     <section name="Tomcat 7.0.89 (violetagg)">
       <subsection name="Catalina">
    
24aea94807f9

Further alignment with 8.0.x and later. Fixes Linux test failures.

https://github.com/apache/tomcatMark ThomasSep 28, 2017via ghsa
1 file changed · +4 0
  • java/org/apache/naming/resources/FileDirContext.java+4 0 modified
    @@ -804,6 +804,10 @@ protected File file(String name) {
          * @param mustExist Must the specified resource exist?
          */
         protected File file(String name, boolean mustExist) {
    +        if (name.equals("/")) {
    +            name = "";
    +        }
    +
             File file = new File(base, name);
             return validate(file, name, mustExist, absoluteBase, canonicalBase);
         }
    
fd52f8601170

Tab police

https://github.com/apache/tomcatMark ThomasSep 28, 2017via ghsa
2 files changed · +4 4
  • java/org/apache/naming/resources/FileDirContext.java+1 1 modified
    @@ -810,7 +810,7 @@ protected File file(String name, boolean mustExist) {
     
     
         protected File validate(File file, String name, boolean mustExist, String absoluteBase,
    -    		String canonicalBase) {
    +            String canonicalBase) {
     
             // If the requested names ends in '/', the Java File API will return a
             // matching file if one exists. This isn't what we want as it is not
    
  • java/org/apache/naming/resources/VirtualDirContext.java+3 3 modified
    @@ -330,9 +330,9 @@ protected String doGetRealPath(String path) {
                 return null;
             }
         }
    -    
    -    
    +
    +
         protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
    -    	return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase);
    +        return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase);
         }
     }
    
cf0b37beb062

Fix failing tests on Windows

https://github.com/apache/tomcatMark ThomasSep 28, 2017via ghsa
2 files changed · +10 4
  • java/org/apache/naming/resources/FileDirContext.java+5 4 modified
    @@ -805,11 +805,12 @@ protected File file(String name) {
          */
         protected File file(String name, boolean mustExist) {
             File file = new File(base, name);
    -        return validate(file, name, mustExist, absoluteBase);
    +        return validate(file, name, mustExist, absoluteBase, canonicalBase);
         }
     
     
    -    protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
    +    protected File validate(File file, String name, boolean mustExist, String absoluteBase,
    +    		String canonicalBase) {
     
             // If the requested names ends in '/', the Java File API will return a
             // matching file if one exists. This isn't what we want as it is not
    @@ -850,8 +851,8 @@ protected File validate(File file, String name, boolean mustExist, String absolu
             // Ensure that the file is not outside the fileBase. This should not be
             // possible for standard requests (the request is normalized early in
             // the request processing) but might be possible for some access via the
    -        // Servlet API (RequestDispatcheretc.) therefore these checks are
    -        // retained as an additional safety measure absoluteBase has been
    +        // Servlet API (RequestDispatcher etc.) therefore these checks are
    +        // retained as an additional safety measure. absoluteBase has been
             // normalized so absPath needs to be normalized as well.
             String absPath = normalize(file.getAbsolutePath());
             if ((absoluteBase.length() > absPath.length())) {
    
  • java/org/apache/naming/resources/VirtualDirContext.java+5 0 modified
    @@ -330,4 +330,9 @@ protected String doGetRealPath(String path) {
                 return null;
             }
         }
    +    
    +    
    +    protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
    +    	return validate(file, name, mustExist, normalize(absoluteBase), absoluteBase);
    +    }
     }
    
bbcbb749c750

First pass at aligning 7.0.x checks with 8.0.x

https://github.com/apache/tomcatMark ThomasSep 28, 2017via ghsa
2 files changed · +110 17
  • java/org/apache/naming/resources/FileDirContext.java+51 17 modified
    @@ -14,8 +14,6 @@
      * See the License for the specific language governing permissions and
      * limitations under the License.
      */
    -
    -
     package org.apache.naming.resources;
     
     import java.io.File;
    @@ -97,6 +95,8 @@ public FileDirContext(Hashtable<String,Object> env) {
          */
         protected String absoluteBase = null;
     
    +    private String canonicalBase = null;
    +
     
         /**
          * Allow linking.
    @@ -106,7 +106,6 @@ public FileDirContext(Hashtable<String,Object> env) {
     
         // ------------------------------------------------------------- Properties
     
    -
         /**
          * Set the document root.
          *
    @@ -137,14 +136,14 @@ public void setDocBase(String docBase) {
                 throw new IllegalArgumentException(sm.getString("fileResources.base", docBase));
             }
     
    +        this.absoluteBase = normalize(base.getAbsolutePath());
    +
             // absoluteBase also needs to be normalized. Using the canonical path is
             // the simplest way of doing this.
             try {
    -            this.absoluteBase = base.getCanonicalPath();
    +            this.canonicalBase = base.getCanonicalPath();
             } catch (IOException e) {
    -            log.warn(sm.getString("fileResources.canonical.fail", base.getPath()));
    -            // Fall back to the absolute path
    -            this.absoluteBase = base.getAbsolutePath();
    +            throw new IllegalArgumentException(e);
             }
             super.setDocBase(docBase);
         }
    @@ -827,8 +826,15 @@ protected File validate(File file, String name, boolean mustExist, String absolu
     
             // If allow linking is enabled, files are not limited to being located
             // under the fileBase so all further checks are disabled.
    -        if (allowLinking)
    +        if (allowLinking) {
                 return file;
    +        }
    +
    +        // Additional Windows specific checks to handle known problems with
    +        // File.getCanonicalPath()
    +        if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
    +            return null;
    +        }
     
             // Check that this file is located under the web application root
             String canPath = null;
    @@ -837,17 +843,16 @@ protected File validate(File file, String name, boolean mustExist, String absolu
             } catch (IOException e) {
                 // Ignore
             }
    -        if (canPath == null || !canPath.startsWith(absoluteBase)) {
    +        if (canPath == null || !canPath.startsWith(canonicalBase)) {
                 return null;
             }
     
             // Ensure that the file is not outside the fileBase. This should not be
             // possible for standard requests (the request is normalized early in
             // the request processing) but might be possible for some access via the
    -        // Servlet API (RequestDispatcher, HTTP/2 push etc.) therefore these
    -        // checks are retained as an additional safety measure
    -        // absoluteBase has been normalized so absPath needs to be normalized as
    -        // well.
    +        // Servlet API (RequestDispatcheretc.) therefore these checks are
    +        // retained as an additional safety measure absoluteBase has been
    +        // normalized so absPath needs to be normalized as well.
             String absPath = normalize(file.getAbsolutePath());
             if ((absoluteBase.length() > absPath.length())) {
                 return null;
    @@ -857,7 +862,7 @@ protected File validate(File file, String name, boolean mustExist, String absolu
             // was not part of the requested path and the remaining check only
             // applies to the request path
             absPath = absPath.substring(absoluteBase.length());
    -        canPath = canPath.substring(absoluteBase.length());
    +        canPath = canPath.substring(canonicalBase.length());
     
             // Case sensitivity check
             // The normalized requested path should be an exact match the equivalent
    @@ -870,9 +875,8 @@ protected File validate(File file, String name, boolean mustExist, String absolu
             //
             // absPath is normalized so canPath needs to be normalized as well
             // Can't normalize canPath earlier as canonicalBase is not normalized
    -        canPath = normalize(canPath);
    -        if (absPath.length() == 0) {
    -            absPath = "/";
    +        if (canPath.length() > 0) {
    +            canPath = normalize(canPath);
             }
             if (!canPath.equals(absPath)) {
                 return null;
    @@ -882,6 +886,36 @@ protected File validate(File file, String name, boolean mustExist, String absolu
         }
     
     
    +    private boolean isInvalidWindowsFilename(String name) {
    +        final int len = name.length();
    +        if (len == 0) {
    +            return false;
    +        }
    +        // This consistently ~10 times faster than the equivalent regular
    +        // expression irrespective of input length.
    +        for (int i = 0; i < len; i++) {
    +            char c = name.charAt(i);
    +            if (c == '\"' || c == '<' || c == '>') {
    +                // These characters are disallowed in Windows file names and
    +                // there are known problems for file names with these characters
    +                // when using File#getCanonicalPath().
    +                // Note: There are additional characters that are disallowed in
    +                //       Windows file names but these are not known to cause
    +                //       problems when using File#getCanonicalPath().
    +                return true;
    +            }
    +        }
    +        // Windows does not allow file names to end in ' ' unless specific low
    +        // level APIs are used to create the files that bypass various checks.
    +        // File names that end in ' ' are known to cause problems when using
    +        // File#getCanonicalPath().
    +        if (name.charAt(len -1) == ' ') {
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +
         /**
          * List the resources which are members of a collection.
          *
    
  • java/org/apache/naming/resources/JrePlatform.java+59 0 added
    @@ -0,0 +1,59 @@
    +/*
    + *  Licensed to the Apache Software Foundation (ASF) under one or more
    + *  contributor license agreements.  See the NOTICE file distributed with
    + *  this work for additional information regarding copyright ownership.
    + *  The ASF 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.
    + */
    +package org.apache.naming.resources;
    +
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +
    +public class JrePlatform {
    +
    +    private static final String OS_NAME_PROPERTY = "os.name";
    +    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
    +
    +    static {
    +        /*
    +         * There are a few places where a) the behaviour of the Java API depends
    +         * on the underlying platform and b) those behavioural differences have
    +         * an impact on Tomcat.
    +         *
    +         * Tomcat therefore needs to be able to determine the platform it is
    +         * running on to account for those differences.
    +         *
    +         * In an ideal world this code would not exist.
    +         */
    +
    +        // This check is derived from the check in Apache Commons Lang
    +        String osName;
    +        if (System.getSecurityManager() == null) {
    +            osName = System.getProperty(OS_NAME_PROPERTY);
    +        } else {
    +            osName = AccessController.doPrivileged(
    +                    new PrivilegedAction<String>() {
    +
    +                    @Override
    +                    public String run() {
    +                        return System.getProperty(OS_NAME_PROPERTY);
    +                    }
    +                });
    +        }
    +
    +        IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
    +    }
    +
    +
    +    public static final boolean IS_WINDOWS;
    +}
    
74ad0e216c79

Updates after kkolinko review

https://github.com/apache/tomcatMark ThomasSep 27, 2017via ghsa
2 files changed · +38 14
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+10 10 modified
    @@ -136,14 +136,14 @@ protected final File file(String name, boolean mustExist) {
     
     
         private boolean isInvalidWindowsFilename(String name) {
    -        if (name.length() == 0) {
    +        final int len = name.length();
    +        if (len == 0) {
                 return false;
             }
    -        // For typical length file names, this is 2-3 times faster than the
    -        // equivalent regular expression. The cut-over point is file names (not
    -        // full paths) of ~65 characters.
    -        char[] chars = name.toCharArray();
    -        for (char c : chars) {
    +        // This consistently ~10 times faster than the equivalent regular
    +        // expression irrespective of input length.
    +        for (int i = 0; i < len; i++) {
    +            char c = name.charAt(i);
                 if (c == '\"' || c == '<' || c == '>') {
                     // These characters are disallowed in Windows file names and
                     // there are known problems for file names with these characters
    @@ -154,11 +154,11 @@ private boolean isInvalidWindowsFilename(String name) {
                     return true;
                 }
             }
    -        // Windows does allow file names to end in ' ' unless specific low level
    -        // APIs are used to create the files that bypass various checks. File
    -        // names that end in ' ' are known to cause problems when using
    +        // Windows does not allow file names to end in ' ' unless specific low
    +        // level APIs are used to create the files that bypass various checks.
    +        // File names that end in ' ' are known to cause problems when using
             // File#getCanonicalPath().
    -        if (chars[chars.length -1] == ' ') {
    +        if (name.charAt(len -1) == ' ') {
                 return true;
             }
             return false;
    
  • test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java+28 4 modified
    @@ -27,7 +27,7 @@ public class TestAbstractFileResourceSetPerformance {
         private static final int LOOPS = 10_000_000;
     
         /*
    -     * Checking individual characters is about 3 times faster on markt's dev
    +     * Checking individual characters is about 10 times faster on markt's dev
          * PC for typical length file names. The file names need to get to ~65
          * characters before the Pattern matching is faster.
          */
    @@ -36,22 +36,31 @@ public void testFileNameFiltering() {
     
             long start = System.nanoTime();
             for (int i = 0; i < LOOPS; i++) {
    -            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").matches();
    +            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").find();
             }
             long end = System.nanoTime();
             System.out.println("Regular expression took " + (end - start) + "ns or " +
                     (end-start)/LOOPS + "ns per iteration");
     
             start = System.nanoTime();
             for (int i = 0; i < LOOPS; i++) {
    -            checkForBadChars("testfile.jsp ");
    +            checkForBadCharsArray("testfile.jsp ");
             }
             end = System.nanoTime();
             System.out.println("char[] check took " + (end - start) + "ns or " +
                     (end-start)/LOOPS + "ns per iteration");
    +
    +        start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            checkForBadCharsAt("testfile.jsp ");
    +        }
    +        end = System.nanoTime();
    +        System.out.println("charAt() check took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +
         }
     
    -    private boolean checkForBadChars(String filename) {
    +    private boolean checkForBadCharsArray(String filename) {
             char[] chars = filename.toCharArray();
             for (char c : chars) {
                 if (c == '\"' || c == '<' || c == '>') {
    @@ -63,4 +72,19 @@ private boolean checkForBadChars(String filename) {
             }
             return true;
         }
    +
    +
    +    private boolean checkForBadCharsAt(String filename) {
    +        final int len = filename.length();
    +        for (int i = 0; i < len; i++) {
    +            char c = filename.charAt(i);
    +            if (c == '\"' || c == '<' || c == '>') {
    +                return false;
    +            }
    +        }
    +        if (filename.charAt(len - 1) == ' ') {
    +            return false;
    +        }
    +        return true;
    +    }
     }
    
31e99502e2c6

Cache the value earlier. Followup to r1809684

https://github.com/apache/tomcatKonstantin KolinkoSep 26, 2017via ghsa
1 file changed · +2 2
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+2 2 modified
    @@ -136,12 +136,12 @@ protected final File file(String name, boolean mustExist) {
     
     
         private boolean isInvalidWindowsFilename(String name) {
    -        if (name.length() == 0) {
    +        final int len = name.length();
    +        if (len == 0) {
                 return false;
             }
             // This consistently ~10 times faster than the equivalent regular
             // expression irrespective of input length.
    -        final int len = name.length();
             for (int i = 0; i < len; i++) {
                 char c = name.charAt(i);
                 if (c == '\"' || c == '<' || c == '>') {
    
e650cf1b83e4

Updates after kkolinko review

https://github.com/apache/tomcatMark ThomasSep 26, 2017via ghsa
2 files changed · +37 13
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+9 9 modified
    @@ -139,11 +139,11 @@ private boolean isInvalidWindowsFilename(String name) {
             if (name.length() == 0) {
                 return false;
             }
    -        // For typical length file names, this is 2-3 times faster than the
    -        // equivalent regular expression. The cut-over point is file names (not
    -        // full paths) of ~65 characters.
    -        char[] chars = name.toCharArray();
    -        for (char c : chars) {
    +        // This consistently ~10 times faster than the equivalent regular
    +        // expression irrespective of input length.
    +        final int len = name.length();
    +        for (int i = 0; i < len; i++) {
    +            char c = name.charAt(i);
                 if (c == '\"' || c == '<' || c == '>') {
                     // These characters are disallowed in Windows file names and
                     // there are known problems for file names with these characters
    @@ -154,11 +154,11 @@ private boolean isInvalidWindowsFilename(String name) {
                     return true;
                 }
             }
    -        // Windows does allow file names to end in ' ' unless specific low level
    -        // APIs are used to create the files that bypass various checks. File
    -        // names that end in ' ' are known to cause problems when using
    +        // Windows does not allow file names to end in ' ' unless specific low
    +        // level APIs are used to create the files that bypass various checks.
    +        // File names that end in ' ' are known to cause problems when using
             // File#getCanonicalPath().
    -        if (chars[chars.length -1] == ' ') {
    +        if (name.charAt(len -1) == ' ') {
                 return true;
             }
             return false;
    
  • test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java+28 4 modified
    @@ -27,7 +27,7 @@ public class TestAbstractFileResourceSetPerformance {
         private static final int LOOPS = 10_000_000;
     
         /*
    -     * Checking individual characters is about 3 times faster on markt's dev
    +     * Checking individual characters is about 10 times faster on markt's dev
          * PC for typical length file names. The file names need to get to ~65
          * characters before the Pattern matching is faster.
          */
    @@ -36,22 +36,31 @@ public void testFileNameFiltering() {
     
             long start = System.nanoTime();
             for (int i = 0; i < LOOPS; i++) {
    -            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").matches();
    +            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").find();
             }
             long end = System.nanoTime();
             System.out.println("Regular expression took " + (end - start) + "ns or " +
                     (end-start)/LOOPS + "ns per iteration");
     
             start = System.nanoTime();
             for (int i = 0; i < LOOPS; i++) {
    -            checkForBadChars("testfile.jsp ");
    +            checkForBadCharsArray("testfile.jsp ");
             }
             end = System.nanoTime();
             System.out.println("char[] check took " + (end - start) + "ns or " +
                     (end-start)/LOOPS + "ns per iteration");
    +
    +        start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            checkForBadCharsAt("testfile.jsp ");
    +        }
    +        end = System.nanoTime();
    +        System.out.println("charAt() check took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +
         }
     
    -    private boolean checkForBadChars(String filename) {
    +    private boolean checkForBadCharsArray(String filename) {
             char[] chars = filename.toCharArray();
             for (char c : chars) {
                 if (c == '\"' || c == '<' || c == '>') {
    @@ -63,4 +72,19 @@ private boolean checkForBadChars(String filename) {
             }
             return true;
         }
    +
    +
    +    private boolean checkForBadCharsAt(String filename) {
    +        final int len = filename.length();
    +        for (int i = 0; i < len; i++) {
    +            char c = filename.charAt(i);
    +            if (c == '\"' || c == '<' || c == '>') {
    +                return false;
    +            }
    +        }
    +        if (filename.charAt(len - 1) == ' ') {
    +            return false;
    +        }
    +        return true;
    +    }
     }
    
46dfedbc0523

Fix array index problem

https://github.com/apache/tomcatMark ThomasSep 25, 2017via ghsa
1 file changed · +3 0
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+3 0 modified
    @@ -136,6 +136,9 @@ protected final File file(String name, boolean mustExist) {
     
     
         private boolean isInvalidWindowsFilename(String name) {
    +        if (name.length() == 0) {
    +            return false;
    +        }
             // For typical length file names, this is 2-3 times faster than the
             // equivalent regular expression. The cut-over point is file names (not
             // full paths) of ~65 characters.
    
c177e9668d12

Fix array index problem

https://github.com/apache/tomcatMark ThomasSep 25, 2017via ghsa
1 file changed · +3 0
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+3 0 modified
    @@ -136,6 +136,9 @@ protected final File file(String name, boolean mustExist) {
     
     
         private boolean isInvalidWindowsFilename(String name) {
    +        if (name.length() == 0) {
    +            return false;
    +        }
             // For typical length file names, this is 2-3 times faster than the
             // equivalent regular expression. The cut-over point is file names (not
             // full paths) of ~65 characters.
    
d5b170705d24

Add some additional checks required on Windows to keep all the checks in one place and to avoid exceptions later in the processing.

https://github.com/apache/tomcatMark ThomasSep 25, 2017via ghsa
4 files changed · +170 0
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+35 0 modified
    @@ -22,6 +22,7 @@
     import java.net.URL;
     
     import org.apache.catalina.LifecycleException;
    +import org.apache.tomcat.util.compat.JrePlatform;
     import org.apache.tomcat.util.http.RequestUtil;
     
     public abstract class AbstractFileResourceSet extends AbstractResourceSet {
    @@ -77,6 +78,12 @@ protected final File file(String name, boolean mustExist) {
                 return file;
             }
     
    +        // Additional Windows specific checks to handle known problems with
    +        // File.getCanonicalPath()
    +        if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
    +            return null;
    +        }
    +
             // Check that this file is located under the WebResourceSet's base
             String canPath = null;
             try {
    @@ -127,6 +134,34 @@ protected final File file(String name, boolean mustExist) {
             return file;
         }
     
    +
    +    private boolean isInvalidWindowsFilename(String name) {
    +        // For typical length file names, this is 2-3 times faster than the
    +        // equivalent regular expression. The cut-over point is file names (not
    +        // full paths) of ~65 characters.
    +        char[] chars = name.toCharArray();
    +        for (char c : chars) {
    +            if (c == '\"' || c == '<' || c == '>') {
    +                // These characters are disallowed in Windows file names and
    +                // there are known problems for file names with these characters
    +                // when using File#getCanonicalPath().
    +                // Note: There are additional characters that are disallowed in
    +                //       Windows file names but these are not known to cause
    +                //       problems when using File#getCanonicalPath().
    +                return true;
    +            }
    +        }
    +        // Windows does allow file names to end in ' ' unless specific low level
    +        // APIs are used to create the files that bypass various checks. File
    +        // names that end in ' ' are known to cause problems when using
    +        // File#getCanonicalPath().
    +        if (chars[chars.length -1] == ' ') {
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +
         /**
          * Return a context-relative path, beginning with a "/", that represents
          * the canonical version of the specified path after ".." and "." elements
    
  • java/org/apache/tomcat/util/compat/JrePlatform.java+59 0 added
    @@ -0,0 +1,59 @@
    +/*
    + *  Licensed to the Apache Software Foundation (ASF) under one or more
    + *  contributor license agreements.  See the NOTICE file distributed with
    + *  this work for additional information regarding copyright ownership.
    + *  The ASF 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.
    + */
    +package org.apache.tomcat.util.compat;
    +
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +
    +public class JrePlatform {
    +
    +    private static final String OS_NAME_PROPERTY = "os.name";
    +    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
    +
    +    static {
    +        /*
    +         * There are a few places where a) the behaviour of the Java API depends
    +         * on the underlying platform and b) those behavioural differences have
    +         * an impact on Tomcat.
    +         *
    +         * Tomcat therefore needs to be able to determine the platform it is
    +         * running on to account for those differences.
    +         *
    +         * In an ideal world this code would not exist.
    +         */
    +
    +        // This check is derived from the check in Apache Commons Lang
    +        String osName;
    +        if (System.getSecurityManager() == null) {
    +            osName = System.getProperty(OS_NAME_PROPERTY);
    +        } else {
    +            osName = AccessController.doPrivileged(
    +                    new PrivilegedAction<String>() {
    +
    +                    @Override
    +                    public String run() {
    +                        return System.getProperty(OS_NAME_PROPERTY);
    +                    }
    +                });
    +        }
    +
    +        IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
    +    }
    +
    +
    +    public static final boolean IS_WINDOWS;
    +}
    
  • test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java+66 0 added
    @@ -0,0 +1,66 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.
    + */
    +package org.apache.catalina.webresources;
    +
    +import java.util.regex.Pattern;
    +
    +import org.junit.Test;
    +
    +public class TestAbstractFileResourceSetPerformance {
    +
    +    private static final Pattern UNSAFE_WINDOWS_FILENAME_PATTERN = Pattern.compile(" $|[\"<>]");
    +
    +    private static final int LOOPS = 10_000_000;
    +
    +    /*
    +     * Checking individual characters is about 3 times faster on markt's dev
    +     * PC for typical length file names. The file names need to get to ~65
    +     * characters before the Pattern matching is faster.
    +     */
    +    @Test
    +    public void testFileNameFiltering() {
    +
    +        long start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").matches();
    +        }
    +        long end = System.nanoTime();
    +        System.out.println("Regular expression took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +
    +        start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            checkForBadChars("testfile.jsp ");
    +        }
    +        end = System.nanoTime();
    +        System.out.println("char[] check took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +    }
    +
    +    private boolean checkForBadChars(String filename) {
    +        char[] chars = filename.toCharArray();
    +        for (char c : chars) {
    +            if (c == '\"' || c == '<' || c == '>') {
    +                return false;
    +            }
    +        }
    +        if (chars[chars.length -1] == ' ') {
    +            return false;
    +        }
    +        return true;
    +    }
    +}
    
  • webapps/docs/changelog.xml+10 0 modified
    @@ -45,6 +45,16 @@
       issues do not "pop up" wrt. others).
     -->
     <section name="Tomcat 8.5.23 (markt)" rtext="in development">
    +  <subsection name="Catalina">
    +    <changelog>
    +      <fix>
    +        Add additional validation to the resource handling required to fix
    +        CVE-2017-12617 on Windows. The checks were being performed elsewhere but
    +        adding them to the resource handling ensures that the checks are always
    +        performed. (markt)
    +      </fix>
    +    </changelog>
    +  </subsection>
       <subsection name="Other">
         <changelog>
           <fix>
    
a9dd96046d7a

Add some additional checks required on Windows to keep all the checks in one place and to avoid exceptions later in the processing.

https://github.com/apache/tomcatMark ThomasSep 25, 2017via ghsa
3 files changed · +160 0
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+35 0 modified
    @@ -22,6 +22,7 @@
     import java.net.URL;
     
     import org.apache.catalina.LifecycleException;
    +import org.apache.tomcat.util.compat.JrePlatform;
     import org.apache.tomcat.util.http.RequestUtil;
     
     public abstract class AbstractFileResourceSet extends AbstractResourceSet {
    @@ -77,6 +78,12 @@ protected final File file(String name, boolean mustExist) {
                 return file;
             }
     
    +        // Additional Windows specific checks to handle known problems with
    +        // File.getCanonicalPath()
    +        if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
    +            return null;
    +        }
    +
             // Check that this file is located under the WebResourceSet's base
             String canPath = null;
             try {
    @@ -127,6 +134,34 @@ protected final File file(String name, boolean mustExist) {
             return file;
         }
     
    +
    +    private boolean isInvalidWindowsFilename(String name) {
    +        // For typical length file names, this is 2-3 times faster than the
    +        // equivalent regular expression. The cut-over point is file names (not
    +        // full paths) of ~65 characters.
    +        char[] chars = name.toCharArray();
    +        for (char c : chars) {
    +            if (c == '\"' || c == '<' || c == '>') {
    +                // These characters are disallowed in Windows file names and
    +                // there are known problems for file names with these characters
    +                // when using File#getCanonicalPath().
    +                // Note: There are additional characters that are disallowed in
    +                //       Windows file names but these are not known to cause
    +                //       problems when using File#getCanonicalPath().
    +                return true;
    +            }
    +        }
    +        // Windows does allow file names to end in ' ' unless specific low level
    +        // APIs are used to create the files that bypass various checks. File
    +        // names that end in ' ' are known to cause problems when using
    +        // File#getCanonicalPath().
    +        if (chars[chars.length -1] == ' ') {
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +
         /**
          * Return a context-relative path, beginning with a "/", that represents
          * the canonical version of the specified path after ".." and "." elements
    
  • java/org/apache/tomcat/util/compat/JrePlatform.java+59 0 added
    @@ -0,0 +1,59 @@
    +/*
    + *  Licensed to the Apache Software Foundation (ASF) under one or more
    + *  contributor license agreements.  See the NOTICE file distributed with
    + *  this work for additional information regarding copyright ownership.
    + *  The ASF 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.
    + */
    +package org.apache.tomcat.util.compat;
    +
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +
    +public class JrePlatform {
    +
    +    private static final String OS_NAME_PROPERTY = "os.name";
    +    private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
    +
    +    static {
    +        /*
    +         * There are a few places where a) the behaviour of the Java API depends
    +         * on the underlying platform and b) those behavioural differences have
    +         * an impact on Tomcat.
    +         *
    +         * Tomcat therefore needs to be able to determine the platform it is
    +         * running on to account for those differences.
    +         *
    +         * In an ideal world this code would not exist.
    +         */
    +
    +        // This check is derived from the check in Apache Commons Lang
    +        String osName;
    +        if (System.getSecurityManager() == null) {
    +            osName = System.getProperty(OS_NAME_PROPERTY);
    +        } else {
    +            osName = AccessController.doPrivileged(
    +                    new PrivilegedAction<String>() {
    +
    +                    @Override
    +                    public String run() {
    +                        return System.getProperty(OS_NAME_PROPERTY);
    +                    }
    +                });
    +        }
    +
    +        IS_WINDOWS = osName.startsWith(OS_NAME_WINDOWS_PREFIX);
    +    }
    +
    +
    +    public static final boolean IS_WINDOWS;
    +}
    
  • test/org/apache/catalina/webresources/TestAbstractFileResourceSetPerformance.java+66 0 added
    @@ -0,0 +1,66 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.
    + */
    +package org.apache.catalina.webresources;
    +
    +import java.util.regex.Pattern;
    +
    +import org.junit.Test;
    +
    +public class TestAbstractFileResourceSetPerformance {
    +
    +    private static final Pattern UNSAFE_WINDOWS_FILENAME_PATTERN = Pattern.compile(" $|[\"<>]");
    +
    +    private static final int LOOPS = 10_000_000;
    +
    +    /*
    +     * Checking individual characters is about 3 times faster on markt's dev
    +     * PC for typical length file names. The file names need to get to ~65
    +     * characters before the Pattern matching is faster.
    +     */
    +    @Test
    +    public void testFileNameFiltering() {
    +
    +        long start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            UNSAFE_WINDOWS_FILENAME_PATTERN.matcher("testfile.jsp ").matches();
    +        }
    +        long end = System.nanoTime();
    +        System.out.println("Regular expression took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +
    +        start = System.nanoTime();
    +        for (int i = 0; i < LOOPS; i++) {
    +            checkForBadChars("testfile.jsp ");
    +        }
    +        end = System.nanoTime();
    +        System.out.println("char[] check took " + (end - start) + "ns or " +
    +                (end-start)/LOOPS + "ns per iteration");
    +    }
    +
    +    private boolean checkForBadChars(String filename) {
    +        char[] chars = filename.toCharArray();
    +        for (char c : chars) {
    +            if (c == '\"' || c == '<' || c == '>') {
    +                return false;
    +            }
    +        }
    +        if (chars[chars.length -1] == ' ') {
    +            return false;
    +        }
    +        return true;
    +    }
    +}
    
327e8a6644e1

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=61542

https://github.com/apache/tomcatMark ThomasSep 22, 2017via ghsa
2 files changed · +12 2
  • java/org/apache/naming/resources/FileDirContext.java+7 2 modified
    @@ -476,11 +476,16 @@ public void modifyAttributes(String name, ModificationItem[] mods)
          * @exception NamingException if a naming exception is encountered
          */
         @Override
    -    public void bind(String name, Object obj, Attributes attrs)
    -        throws NamingException {
    +    public void bind(String name, Object obj, Attributes attrs) throws NamingException {
     
             // Note: No custom attributes allowed
     
    +        // bind() is meant to create a file so ensure that the path doesn't end
    +        // in '/'
    +        if (name.endsWith("/")) {
    +            throw new NamingException(sm.getString("resources.bindFailed", name));
    +        }
    +
             File file = file(name, false);
             if (file == null) {
                 throw new NamingException(sm.getString("resources.bindFailed", name));
    
  • webapps/docs/changelog.xml+5 0 modified
    @@ -90,6 +90,11 @@
             <code>DirContext</code> that represented the web application in a
             <code>ProxyDirContext</code> twice rather than just once. (markt)
           </fix>
    +      <fix>
    +        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
    +        uploaded via a specially crafted request when HTTP PUT was enabled.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
512a3c3aecdb

Partial fix for CVE-2017-12617

https://github.com/apache/tomcatMark ThomasSep 22, 2017via ghsa
4 files changed · +64 28
  • java/org/apache/catalina/servlets/DefaultServlet.java+0 17 modified
    @@ -860,23 +860,6 @@ protected void serveResource(HttpServletRequest request,
                 return;
             }
     
    -        // If the resource is not a collection, and the resource path
    -        // ends with "/" or "\", return NOT FOUND
    -        if (cacheEntry.context == null) {
    -            if (path.endsWith("/") || (path.endsWith("\\"))) {
    -                // Check if we're included so we can return the appropriate
    -                // missing resource name in the error
    -                String requestUri = (String) request.getAttribute(
    -                        RequestDispatcher.INCLUDE_REQUEST_URI);
    -                if (requestUri == null) {
    -                    requestUri = request.getRequestURI();
    -                }
    -                response.sendError(HttpServletResponse.SC_NOT_FOUND,
    -                                   requestUri);
    -                return;
    -            }
    -        }
    -
             // Check if the conditions specified in the optional If headers are
             // satisfied.
             if (cacheEntry.context == null) {
    
  • java/org/apache/naming/resources/FileDirContext.java+9 2 modified
    @@ -801,11 +801,18 @@ protected File file(String name) {
          */
         protected File file(String name, boolean mustExist) {
             File file = new File(base, name);
    -        return validate(file, mustExist, absoluteBase);
    +        return validate(file, name, mustExist, absoluteBase);
         }
     
     
    -    protected File validate(File file, boolean mustExist, String absoluteBase) {
    +    protected File validate(File file, String name, boolean mustExist, String absoluteBase) {
    +
    +        // If the requested names ends in '/', the Java File API will return a
    +        // matching file if one exists. This isn't what we want as it is not
    +        // consistent with the Servlet spec rules for request mapping.
    +        if (file.isFile() && name.endsWith("/")) {
    +            return null;
    +        }
     
             if (!mustExist || file.exists() && file.canRead()) {
     
    
  • java/org/apache/naming/resources/VirtualDirContext.java+9 9 modified
    @@ -163,16 +163,16 @@ public Attributes getAttributes(String name) throws NamingException {
                     String resourcesDir = dirList.get(0);
                     if (name.equals(path)) {
                         File f = new File(resourcesDir);
    -                    f = validate(f, true, resourcesDir);
    +                    f = validate(f, name, true, resourcesDir);
                         if (f != null) {
                             return new FileResourceAttributes(f);
                         }
                     }
                     path += "/";
                     if (name.startsWith(path)) {
                         String res = name.substring(path.length());
    -                    File f = new File(resourcesDir + "/" + res);
    -                    f = validate(f, true, resourcesDir);
    +                    File f = new File(resourcesDir, res);
    +                    f = validate(f, res, true, resourcesDir);
                         if (f != null) {
                             return new FileResourceAttributes(f);
                         }
    @@ -206,7 +206,7 @@ protected File file(String name, boolean mustExist) {
                 if (name.equals(path)) {
                     for (String resourcesDir : dirList) {
                         file = new File(resourcesDir);
    -                    file = validate(file, true, resourcesDir);
    +                    file = validate(file, name, true, resourcesDir);
                         if (file != null) {
                             return file;
                         }
    @@ -216,7 +216,7 @@ protected File file(String name, boolean mustExist) {
                     String res = name.substring(path.length());
                     for (String resourcesDir : dirList) {
                         file = new File(resourcesDir, res);
    -                    file = validate(file, true, resourcesDir);
    +                    file = validate(file, res, true, resourcesDir);
                         if (file != null) {
                             return file;
                         }
    @@ -252,7 +252,7 @@ protected List<NamingEntry> list(File file) {
                         if (res != null) {
                             for (String resourcesDir : dirList) {
                                 File f = new File(resourcesDir, res);
    -                            f = validate(f, true, resourcesDir);
    +                            f = validate(f, res, true, resourcesDir);
                                 if (f != null && f.isDirectory()) {
                                     List<NamingEntry> virtEntries = super.list(f);
                                     for (NamingEntry entry : virtEntries) {
    @@ -288,7 +288,7 @@ protected Object doLookup(String name) {
                 if (name.equals(path)) {
                     for (String resourcesDir : dirList) {
                         File f = new File(resourcesDir);
    -                    f = validate(f, true, resourcesDir);
    +                    f = validate(f, name, true, resourcesDir);
                         if (f != null) {
                             if (f.isFile()) {
                                 return new FileResource(f);
    @@ -304,8 +304,8 @@ protected Object doLookup(String name) {
                 if (name.startsWith(path)) {
                     String res = name.substring(path.length());
                     for (String resourcesDir : dirList) {
    -                    File f = new File(resourcesDir + "/" + res);
    -                    f = validate(f, true, resourcesDir);
    +                    File f = new File(resourcesDir, res);
    +                    f = validate(f, res, true, resourcesDir);
                         if (f != null) {
                             if (f.isFile()) {
                                 return new FileResource(f);
    
  • test/org/apache/naming/resources/TestFileDirContext.java+46 0 added
    @@ -0,0 +1,46 @@
    +/*
    + * Licensed to the Apache Software Foundation (ASF) under one or more
    + * contributor license agreements.  See the NOTICE file distributed with
    + * this work for additional information regarding copyright ownership.
    + * The ASF 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.
    + */
    +package org.apache.naming.resources;
    +
    +import java.io.File;
    +
    +import javax.servlet.http.HttpServletResponse;
    +
    +import org.junit.Assert;
    +import org.junit.Test;
    +
    +import org.apache.catalina.startup.Tomcat;
    +import org.apache.catalina.startup.TomcatBaseTest;
    +import org.apache.tomcat.util.buf.ByteChunk;
    +
    +public class TestFileDirContext extends TomcatBaseTest {
    +
    +    @Test
    +    public void testLookupResourceWithTrailingSlash() throws Exception {
    +        Tomcat tomcat = getTomcatInstance();
    +
    +        File appDir = new File("test/webapp-3.0");
    +        // app dir is relative to server home
    +        tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
    +
    +        tomcat.start();
    +
    +        int sc = getUrl("http://localhost:" + getPort() +
    +                "/test/index.html/", new ByteChunk(), null);
    +        Assert.assertEquals(HttpServletResponse.SC_NOT_FOUND, sc);
    +    }
    +}
    
4cf7dab88282

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=61542

https://github.com/apache/tomcatMark ThomasSep 22, 2017via ghsa
3 files changed · +20 7
  • java/org/apache/catalina/webresources/DirResourceSet.java+6 0 modified
    @@ -217,6 +217,12 @@ public boolean write(String path, InputStream is, boolean overwrite) {
                 return false;
             }
     
    +        // write() is meant to create a file so ensure that the path doesn't
    +        // end in '/'
    +        if (path.endsWith("/")) {
    +            return false;
    +        }
    +
             File dest = null;
             String webAppMount = getWebAppMount();
             if (path.startsWith(webAppMount)) {
    
  • test/org/apache/catalina/webresources/AbstractTestResourceSet.java+9 7 modified
    @@ -447,14 +447,8 @@ public final void testWriteDirA() {
         public final void testWriteDirB() {
             WebResource d1 = resourceRoot.getResource(getMount() + "/d1/");
             InputStream is = new ByteArrayInputStream("test".getBytes());
    -        if (d1.exists()) {
    +        if (d1.exists() || d1.isVirtual()) {
                 Assert.assertFalse(resourceRoot.write(getMount() + "/d1/", is, false));
    -        } else if (d1.isVirtual()) {
    -            Assert.assertTrue(resourceRoot.write(
    -                    getMount() + "/d1/", is, false));
    -            File file = new File(getBaseDir(), "d1");
    -            Assert.assertTrue(file.exists());
    -            Assert.assertTrue(file.delete());
             } else {
                 Assert.fail("Unhandled condition in unit test");
             }
    @@ -490,6 +484,14 @@ public final void testWrite() {
             }
         }
     
    +    @Test
    +    public final void testWriteWithTrailingSlash() {
    +        String newFileName = getNewFileName() + "/";
    +        InputStream is = new ByteArrayInputStream("test".getBytes());
    +        Assert.assertFalse(resourceRoot.write(
    +                getMount() + "/" + newFileName, is, false));
    +    }
    +
         protected abstract String getNewFileName();
     
         // ------------------------------------------------------ getCanonicalPath()
    
  • webapps/docs/changelog.xml+5 0 modified
    @@ -57,6 +57,11 @@
             Add an option to reject requests that contain HTTP headers with invalid
             (non-token) header names with a 400 response. (markt)
           </add>
    +      <fix>
    +        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
    +        uploaded via a specially crafted request when HTTP PUT was enabled.
    +        (markt)
    +      </fix>
         </changelog>
       </subsection>
       <subsection name="Coyote">
    
506d862e7edf

Partial fix for CVE-2017-12617

https://github.com/apache/tomcatMark ThomasSep 22, 2017via ghsa
3 files changed · +15 14
  • java/org/apache/catalina/servlets/DefaultServlet.java+0 14 modified
    @@ -804,20 +804,6 @@ protected void serveResource(HttpServletRequest request,
                 return;
             }
     
    -        // If the resource is not a collection, and the resource path
    -        // ends with "/" or "\", return NOT FOUND
    -        if (resource.isFile() && (path.endsWith("/") || path.endsWith("\\"))) {
    -            // Check if we're included so we can return the appropriate
    -            // missing resource name in the error
    -            String requestUri = (String) request.getAttribute(
    -                    RequestDispatcher.INCLUDE_REQUEST_URI);
    -            if (requestUri == null) {
    -                requestUri = request.getRequestURI();
    -            }
    -            response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri);
    -            return;
    -        }
    -
             boolean included = false;
             // Check if the conditions specified in the optional If headers are
             // satisfied.
    
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+8 0 modified
    @@ -57,6 +57,14 @@ protected final File file(String name, boolean mustExist) {
                 name = "";
             }
             File file = new File(fileBase, name);
    +
    +        // If the requested names ends in '/', the Java File API will return a
    +        // matching file if one exists. This isn't what we want as it is not
    +        // consistent with the Servlet spec rules for request mapping.
    +        if (file.isFile() && name.endsWith("/")) {
    +            return null;
    +        }
    +
             if (!mustExist || file.canRead()) {
     
                 if (getRoot().getAllowLinking()) {
    
  • test/org/apache/catalina/webresources/AbstractTestResourceSet.java+7 0 modified
    @@ -131,6 +131,13 @@ public final void testGetResourceFile() {
             Assert.assertNotNull(webResource.getInputStream());
         }
     
    +    @Test
    +    public final void testGetResourceFileWithTrailingSlash() {
    +        WebResource webResource =
    +                resourceRoot.getResource(getMount() + "/d1/d1-f1.txt/");
    +        Assert.assertFalse(webResource.exists());
    +    }
    +
         @Test
         public final void testGetResourceCaseSensitive() {
             WebResource webResource =
    
b7e0435d17ab

Partial fix for CVE-2017-12617

https://github.com/apache/tomcatMark ThomasSep 20, 2017via ghsa
3 files changed · +24 7
  • java/org/apache/catalina/webresources/DirResourceSet.java+6 0 modified
    @@ -217,6 +217,12 @@ public boolean write(String path, InputStream is, boolean overwrite) {
                 return false;
             }
     
    +        // write() is meant to create a file so ensure that the path doesn't
    +        // end in '/'
    +        if (path.endsWith("/")) {
    +            return false;
    +        }
    +
             File dest = null;
             String webAppMount = getWebAppMount();
             if (path.startsWith(webAppMount)) {
    
  • test/org/apache/catalina/webresources/AbstractTestResourceSet.java+9 7 modified
    @@ -447,14 +447,8 @@ public final void testWriteDirA() {
         public final void testWriteDirB() {
             WebResource d1 = resourceRoot.getResource(getMount() + "/d1/");
             InputStream is = new ByteArrayInputStream("test".getBytes());
    -        if (d1.exists()) {
    +        if (d1.exists() || d1.isVirtual()) {
                 Assert.assertFalse(resourceRoot.write(getMount() + "/d1/", is, false));
    -        } else if (d1.isVirtual()) {
    -            Assert.assertTrue(resourceRoot.write(
    -                    getMount() + "/d1/", is, false));
    -            File file = new File(getBaseDir(), "d1");
    -            Assert.assertTrue(file.exists());
    -            Assert.assertTrue(file.delete());
             } else {
                 Assert.fail("Unhandled condition in unit test");
             }
    @@ -490,6 +484,14 @@ public final void testWrite() {
             }
         }
     
    +    @Test
    +    public final void testWriteWithTrailingSlash() {
    +        String newFileName = getNewFileName() + "/";
    +        InputStream is = new ByteArrayInputStream("test".getBytes());
    +        Assert.assertFalse(resourceRoot.write(
    +                getMount() + "/" + newFileName, is, false));
    +    }
    +
         protected abstract String getNewFileName();
     
         // ------------------------------------------------------ getCanonicalPath()
    
  • webapps/docs/changelog.xml+9 0 modified
    @@ -45,6 +45,15 @@
       issues do not "pop up" wrt. others).
     -->
     <section name="Tomcat 9.0.0.M28 (markt)" rtext="in development">
    +  <subsection name="Catalina">
    +    <changelog>
    +      <fix>
    +        <bug>61542</bug>: Fix CVE-2017-12617 and prevent JSPs from being
    +        uploaded via a specially crafted request when HTTP PUT was enabled.
    +        (markt)
    +      </fix>
    +    </changelog>
    +  </subsection>
       <subsection name="Coyote">
         <changelog>
           <add>
    
b577f9a7996b

Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=61542

https://github.com/apache/tomcatMark ThomasSep 20, 2017via ghsa
3 files changed · +15 14
  • java/org/apache/catalina/servlets/DefaultServlet.java+0 14 modified
    @@ -820,20 +820,6 @@ protected void serveResource(HttpServletRequest request,
                 return;
             }
     
    -        // If the resource is not a collection, and the resource path
    -        // ends with "/" or "\", return NOT FOUND
    -        if (resource.isFile() && (path.endsWith("/") || path.endsWith("\\"))) {
    -            // Check if we're included so we can return the appropriate
    -            // missing resource name in the error
    -            String requestUri = (String) request.getAttribute(
    -                    RequestDispatcher.INCLUDE_REQUEST_URI);
    -            if (requestUri == null) {
    -                requestUri = request.getRequestURI();
    -            }
    -            response.sendError(HttpServletResponse.SC_NOT_FOUND, requestUri);
    -            return;
    -        }
    -
             boolean included = false;
             // Check if the conditions specified in the optional If headers are
             // satisfied.
    
  • java/org/apache/catalina/webresources/AbstractFileResourceSet.java+8 0 modified
    @@ -57,6 +57,14 @@ protected final File file(String name, boolean mustExist) {
                 name = "";
             }
             File file = new File(fileBase, name);
    +
    +        // If the requested names ends in '/', the Java File API will return a
    +        // matching file if one exists. This isn't what we want as it is not
    +        // consistent with the Servlet spec rules for request mapping.
    +        if (file.isFile() && name.endsWith("/")) {
    +            return null;
    +        }
    +
             if (!mustExist || file.canRead()) {
     
                 if (getRoot().getAllowLinking()) {
    
  • test/org/apache/catalina/webresources/AbstractTestResourceSet.java+7 0 modified
    @@ -131,6 +131,13 @@ public final void testGetResourceFile() {
             Assert.assertNotNull(webResource.getInputStream());
         }
     
    +    @Test
    +    public final void testGetResourceFileWithTrailingSlash() {
    +        WebResource webResource =
    +                resourceRoot.getResource(getMount() + "/d1/d1-f1.txt/");
    +        Assert.assertFalse(webResource.exists());
    +    }
    +
         @Test
         public final void testGetResourceCaseSensitive() {
             WebResource webResource =
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

89

News mentions

0

No linked articles in our index yet.