52
52
import javax .servlet .http .HttpServlet ;
53
53
import javax .servlet .http .HttpServletRequest ;
54
54
import javax .servlet .http .HttpServletResponse ;
55
+ import javax .xml .parsers .DocumentBuilder ;
56
+ import javax .xml .parsers .DocumentBuilderFactory ;
57
+ import javax .xml .parsers .ParserConfigurationException ;
55
58
import javax .xml .transform .Source ;
56
59
import javax .xml .transform .Transformer ;
57
60
import javax .xml .transform .TransformerException ;
58
61
import javax .xml .transform .TransformerFactory ;
62
+ import javax .xml .transform .dom .DOMSource ;
59
63
import javax .xml .transform .stream .StreamResult ;
60
64
import javax .xml .transform .stream .StreamSource ;
61
65
70
74
import org .apache .naming .resources .Resource ;
71
75
import org .apache .naming .resources .ResourceAttributes ;
72
76
import org .apache .tomcat .util .res .StringManager ;
77
+ import org .w3c .dom .Document ;
78
+ import org .xml .sax .InputSource ;
79
+ import org .xml .sax .SAXException ;
80
+ import org .xml .sax .ext .EntityResolver2 ;
73
81
74
82
75
83
/**
@@ -119,8 +127,13 @@ public class DefaultServlet
119
127
120
128
private static final long serialVersionUID = 1L ;
121
129
122
- // ----------------------------------------------------- Instance Variables
130
+ private static final DocumentBuilderFactory factory ;
131
+
132
+ private static final SecureEntityResolver secureEntityResolver =
133
+ new SecureEntityResolver ();
134
+
123
135
136
+ // ----------------------------------------------------- Instance Variables
124
137
125
138
/**
126
139
* The debugging detail level for this servlet.
@@ -224,6 +237,10 @@ public class DefaultServlet
224
237
urlEncoder .addSafeCharacter ('.' );
225
238
urlEncoder .addSafeCharacter ('*' );
226
239
urlEncoder .addSafeCharacter ('/' );
240
+
241
+ factory = DocumentBuilderFactory .newInstance ();
242
+ factory .setNamespaceAware (true );
243
+ factory .setValidating (false );
227
244
}
228
245
229
246
@@ -1233,23 +1250,22 @@ protected ArrayList<Range> parseRange(HttpServletRequest request,
1233
1250
}
1234
1251
1235
1252
1236
-
1237
1253
/**
1238
1254
* Decide which way to render. HTML or XML.
1239
1255
*/
1240
1256
protected InputStream render (String contextPath , CacheEntry cacheEntry )
1241
1257
throws IOException , ServletException {
1242
1258
1243
- InputStream xsltInputStream =
1244
- findXsltInputStream (cacheEntry .context );
1259
+ Source xsltSource = findXsltInputStream (cacheEntry .context );
1245
1260
1246
- if (xsltInputStream == null ) {
1261
+ if (xsltSource == null ) {
1247
1262
return renderHtml (contextPath , cacheEntry );
1248
1263
}
1249
- return renderXml (contextPath , cacheEntry , xsltInputStream );
1264
+ return renderXml (contextPath , cacheEntry , xsltSource );
1250
1265
1251
1266
}
1252
1267
1268
+
1253
1269
/**
1254
1270
* Return an InputStream to an HTML representation of the contents
1255
1271
* of this directory.
@@ -1259,7 +1275,7 @@ protected InputStream render(String contextPath, CacheEntry cacheEntry)
1259
1275
*/
1260
1276
protected InputStream renderXml (String contextPath ,
1261
1277
CacheEntry cacheEntry ,
1262
- InputStream xsltInputStream )
1278
+ Source xsltSource )
1263
1279
throws IOException , ServletException {
1264
1280
1265
1281
StringBuilder sb = new StringBuilder ();
@@ -1353,8 +1369,7 @@ protected InputStream renderXml(String contextPath,
1353
1369
try {
1354
1370
TransformerFactory tFactory = TransformerFactory .newInstance ();
1355
1371
Source xmlSource = new StreamSource (new StringReader (sb .toString ()));
1356
- Source xslSource = new StreamSource (xsltInputStream );
1357
- Transformer transformer = tFactory .newTransformer (xslSource );
1372
+ Transformer transformer = tFactory .newTransformer (xsltSource );
1358
1373
1359
1374
ByteArrayOutputStream stream = new ByteArrayOutputStream ();
1360
1375
OutputStreamWriter osWriter = new OutputStreamWriter (stream , "UTF8" );
@@ -1573,18 +1588,23 @@ protected String getReadme(DirContext directory)
1573
1588
1574
1589
1575
1590
/**
1576
- * Return the xsl template inputstream (if possible)
1591
+ * Return a Source for the xsl template (if possible)
1577
1592
*/
1578
- protected InputStream findXsltInputStream (DirContext directory )
1593
+ protected Source findXsltInputStream (DirContext directory )
1579
1594
throws IOException {
1580
1595
1581
1596
if (localXsltFile != null ) {
1582
1597
try {
1583
1598
Object obj = directory .lookup (localXsltFile );
1584
1599
if ((obj != null ) && (obj instanceof Resource )) {
1585
1600
InputStream is = ((Resource ) obj ).streamContent ();
1586
- if (is != null )
1587
- return is ;
1601
+ if (is != null ) {
1602
+ if (Globals .IS_SECURITY_ENABLED ) {
1603
+ return secureXslt (is );
1604
+ } else {
1605
+ return new StreamSource (is );
1606
+ }
1607
+ }
1588
1608
}
1589
1609
} catch (NamingException e ) {
1590
1610
if (debug > 10 )
@@ -1595,8 +1615,13 @@ protected InputStream findXsltInputStream(DirContext directory)
1595
1615
if (contextXsltFile != null ) {
1596
1616
InputStream is =
1597
1617
getServletContext ().getResourceAsStream (contextXsltFile );
1598
- if (is != null )
1599
- return is ;
1618
+ if (is != null ) {
1619
+ if (Globals .IS_SECURITY_ENABLED ) {
1620
+ return secureXslt (is );
1621
+ } else {
1622
+ return new StreamSource (is );
1623
+ }
1624
+ }
1600
1625
1601
1626
if (debug > 10 )
1602
1627
log ("contextXsltFile '" + contextXsltFile + "' not found" );
@@ -1607,13 +1632,13 @@ protected InputStream findXsltInputStream(DirContext directory)
1607
1632
*/
1608
1633
if (globalXsltFile != null ) {
1609
1634
File f = validateGlobalXsltFile ();
1610
- if (f != null && f . exists () ){
1635
+ if (f != null ){
1611
1636
FileInputStream fis = null ;
1612
1637
try {
1613
1638
fis = new FileInputStream (f );
1614
1639
byte b [] = new byte [(int )f .length ()]; /* danger! */
1615
1640
fis .read (b );
1616
- return new ByteArrayInputStream (b );
1641
+ return new StreamSource ( new ByteArrayInputStream (b ) );
1617
1642
} finally {
1618
1643
if (fis != null ) {
1619
1644
try {
@@ -1643,7 +1668,7 @@ private File validateGlobalXsltFile() {
1643
1668
1644
1669
if (result == null ) {
1645
1670
String home = System .getProperty (Globals .CATALINA_HOME_PROP );
1646
- if (home != null ) {
1671
+ if (home != null && ! home . equals ( base ) ) {
1647
1672
File homeConf = new File (home , "conf" );
1648
1673
result = validateGlobalXsltFile (homeConf );
1649
1674
}
@@ -1654,7 +1679,14 @@ private File validateGlobalXsltFile() {
1654
1679
1655
1680
1656
1681
private File validateGlobalXsltFile (File base ) {
1657
- File candidate = new File (base , globalXsltFile );
1682
+ File candidate = new File (globalXsltFile );
1683
+ if (!candidate .isAbsolute ()) {
1684
+ candidate = new File (base , globalXsltFile );
1685
+ }
1686
+
1687
+ if (!candidate .isFile ()) {
1688
+ return null ;
1689
+ }
1658
1690
1659
1691
// First check that the resulting path is under the provided base
1660
1692
try {
@@ -1665,19 +1697,51 @@ private File validateGlobalXsltFile(File base) {
1665
1697
return null ;
1666
1698
}
1667
1699
1668
- // Next check that an .xlt or .xslt file has been specified
1700
+ // Next check that an .xsl or .xslt file has been specified
1669
1701
String nameLower = candidate .getName ().toLowerCase (Locale .ENGLISH );
1670
- if (!nameLower .endsWith (".xslt" ) && !nameLower .endsWith (".xlt " )) {
1702
+ if (!nameLower .endsWith (".xslt" ) && !nameLower .endsWith (".xsl " )) {
1671
1703
return null ;
1672
1704
}
1673
1705
1674
1706
return candidate ;
1675
1707
}
1676
1708
1677
1709
1678
- // -------------------------------------------------------- protected Methods
1710
+ private Source secureXslt (InputStream is ) {
1711
+ // Need to filter out any external entities
1712
+ Source result = null ;
1713
+ try {
1714
+ DocumentBuilder builder = factory .newDocumentBuilder ();
1715
+ builder .setEntityResolver (secureEntityResolver );
1716
+ Document document = builder .parse (is );
1717
+ result = new DOMSource (document );
1718
+ } catch (ParserConfigurationException e ) {
1719
+ if (debug > 0 ) {
1720
+ log (e .getMessage (), e );
1721
+ }
1722
+ } catch (SAXException e ) {
1723
+ if (debug > 0 ) {
1724
+ log (e .getMessage (), e );
1725
+ }
1726
+ } catch (IOException e ) {
1727
+ if (debug > 0 ) {
1728
+ log (e .getMessage (), e );
1729
+ }
1730
+ } finally {
1731
+ if (is != null ) {
1732
+ try {
1733
+ is .close ();
1734
+ } catch (IOException e ) {
1735
+ // Ignore
1736
+ }
1737
+ }
1738
+ }
1739
+ return result ;
1740
+ }
1679
1741
1680
1742
1743
+ // -------------------------------------------------------- protected Methods
1744
+
1681
1745
/**
1682
1746
* Check if sendfile can be used.
1683
1747
*/
@@ -2177,9 +2241,6 @@ protected IOException copyRange(InputStream istream,
2177
2241
}
2178
2242
2179
2243
2180
- // ------------------------------------------------------ Range Inner Class
2181
-
2182
-
2183
2244
protected static class Range {
2184
2245
2185
2246
public long start ;
@@ -2195,4 +2256,34 @@ public boolean validate() {
2195
2256
return (start >= 0 ) && (end >= 0 ) && (start <= end ) && (length > 0 );
2196
2257
}
2197
2258
}
2259
+
2260
+
2261
+ /**
2262
+ * This is secure in the sense that any attempt to use an external entity
2263
+ * will trigger an exception.
2264
+ */
2265
+ private static class SecureEntityResolver implements EntityResolver2 {
2266
+
2267
+ @ Override
2268
+ public InputSource resolveEntity (String publicId , String systemId )
2269
+ throws SAXException , IOException {
2270
+ throw new SAXException (sm .getString ("defaultServlet.blockExternalEntity" ,
2271
+ publicId , systemId ));
2272
+ }
2273
+
2274
+ @ Override
2275
+ public InputSource getExternalSubset (String name , String baseURI )
2276
+ throws SAXException , IOException {
2277
+ throw new SAXException (sm .getString ("defaultServlet.blockExternalSubset" ,
2278
+ name , baseURI ));
2279
+ }
2280
+
2281
+ @ Override
2282
+ public InputSource resolveEntity (String name , String publicId ,
2283
+ String baseURI , String systemId ) throws SAXException ,
2284
+ IOException {
2285
+ throw new SAXException (sm .getString ("defaultServlet.blockExternalEntity2" ,
2286
+ name , publicId , baseURI , systemId ));
2287
+ }
2288
+ }
2198
2289
}
0 commit comments