1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.ws.util.xml; 27 28 import com.sun.istack.internal.Nullable; 29 import com.sun.xml.internal.ws.util.ByteArrayBuffer; 30 import java.io.IOException; 31 import java.io.InputStream; 32 import java.io.OutputStreamWriter; 33 import java.io.Writer; 34 import java.net.URL; 35 import java.security.AccessController; 36 import java.security.PrivilegedAction; 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.StringTokenizer; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 import javax.xml.XMLConstants; 44 import javax.xml.namespace.QName; 45 import javax.xml.parsers.DocumentBuilderFactory; 46 import javax.xml.parsers.ParserConfigurationException; 47 import javax.xml.parsers.SAXParserFactory; 48 import javax.xml.stream.XMLInputFactory; 49 import javax.xml.transform.Result; 50 import javax.xml.transform.Source; 51 import javax.xml.transform.Transformer; 52 import javax.xml.transform.TransformerConfigurationException; 53 import javax.xml.transform.TransformerException; 54 import javax.xml.transform.TransformerFactory; 55 import javax.xml.transform.sax.SAXTransformerFactory; 56 import javax.xml.transform.sax.TransformerHandler; 57 import javax.xml.transform.stream.StreamSource; 58 import javax.xml.validation.SchemaFactory; 59 import javax.xml.xpath.XPathFactory; 60 import javax.xml.xpath.XPathFactoryConfigurationException; 61 import org.w3c.dom.Attr; 62 import org.w3c.dom.Element; 63 import org.w3c.dom.EntityReference; 64 import org.w3c.dom.Node; 65 import org.w3c.dom.NodeList; 66 import org.w3c.dom.Text; 67 import org.xml.sax.EntityResolver; 68 import org.xml.sax.ErrorHandler; 69 import org.xml.sax.InputSource; 70 import org.xml.sax.SAXException; 71 import org.xml.sax.SAXNotRecognizedException; 72 import org.xml.sax.SAXNotSupportedException; 73 import org.xml.sax.SAXParseException; 74 import org.xml.sax.XMLReader; 75 76 /** 77 * @author WS Development Team 78 */ 79 public class XmlUtil { 80 81 // not in older JDK, so must be duplicated here, otherwise javax.xml.XMLConstants should be used 82 private static final String ACCESS_EXTERNAL_SCHEMA = "http://javax.xml.XMLConstants/property/accessExternalSchema"; 83 84 private final static String LEXICAL_HANDLER_PROPERTY = 85 "http://xml.org/sax/properties/lexical-handler"; 86 87 private static final String DISALLOW_DOCTYPE_DECL = "http://apache.org/xml/features/disallow-doctype-decl"; 88 private static final String EXTERNAL_GE = "http://xml.org/sax/features/external-general-entities"; 89 private static final String EXTERNAL_PE = "http://xml.org/sax/features/external-parameter-entities"; 90 private static final String LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd"; 91 92 private static final Logger LOGGER = Logger.getLogger(XmlUtil.class.getName()); 93 94 private static final String DISABLE_XML_SECURITY = "com.sun.xml.internal.ws.disableXmlSecurity"; 95 96 private static boolean XML_SECURITY_DISABLED = AccessController.doPrivileged( 97 new PrivilegedAction<Boolean>() { 98 @Override 99 public Boolean run() { 100 return Boolean.getBoolean(DISABLE_XML_SECURITY); 101 } 102 } 103 ); 104 105 public static String getPrefix(String s) { 106 int i = s.indexOf(':'); 107 if (i == -1) 108 return null; 109 return s.substring(0, i); 110 } 111 112 public static String getLocalPart(String s) { 113 int i = s.indexOf(':'); 114 if (i == -1) 115 return s; 116 return s.substring(i + 1); 117 } 118 119 120 121 public static String getAttributeOrNull(Element e, String name) { 122 Attr a = e.getAttributeNode(name); 123 if (a == null) 124 return null; 125 return a.getValue(); 126 } 127 128 public static String getAttributeNSOrNull( 129 Element e, 130 String name, 131 String nsURI) { 132 Attr a = e.getAttributeNodeNS(nsURI, name); 133 if (a == null) 134 return null; 135 return a.getValue(); 136 } 137 138 public static String getAttributeNSOrNull( 139 Element e, 140 QName name) { 141 Attr a = e.getAttributeNodeNS(name.getNamespaceURI(), name.getLocalPart()); 142 if (a == null) 143 return null; 144 return a.getValue(); 145 } 146 147 /* public static boolean matchesTagNS(Element e, String tag, String nsURI) { 148 try { 149 return e.getLocalName().equals(tag) 150 && e.getNamespaceURI().equals(nsURI); 151 } catch (NullPointerException npe) { 152 153 // localname not null since parsing would fail before here 154 throw new WSDLParseException( 155 "null.namespace.found", 156 e.getLocalName()); 157 } 158 } 159 160 public static boolean matchesTagNS( 161 Element e, 162 javax.xml.namespace.QName name) { 163 try { 164 return e.getLocalName().equals(name.getLocalPart()) 165 && e.getNamespaceURI().equals(name.getNamespaceURI()); 166 } catch (NullPointerException npe) { 167 168 // localname not null since parsing would fail before here 169 throw new WSDLParseException( 170 "null.namespace.found", 171 e.getLocalName()); 172 } 173 }*/ 174 175 public static Iterator getAllChildren(Element element) { 176 return new NodeListIterator(element.getChildNodes()); 177 } 178 179 public static Iterator getAllAttributes(Element element) { 180 return new NamedNodeMapIterator(element.getAttributes()); 181 } 182 183 public static List<String> parseTokenList(String tokenList) { 184 List<String> result = new ArrayList<>(); 185 StringTokenizer tokenizer = new StringTokenizer(tokenList, " "); 186 while (tokenizer.hasMoreTokens()) { 187 result.add(tokenizer.nextToken()); 188 } 189 return result; 190 } 191 192 public static String getTextForNode(Node node) { 193 StringBuilder sb = new StringBuilder(); 194 195 NodeList children = node.getChildNodes(); 196 if (children.getLength() == 0) 197 return null; 198 199 for (int i = 0; i < children.getLength(); ++i) { 200 Node n = children.item(i); 201 202 if (n instanceof Text) 203 sb.append(n.getNodeValue()); 204 else if (n instanceof EntityReference) { 205 String s = getTextForNode(n); 206 if (s == null) 207 return null; 208 else 209 sb.append(s); 210 } else 211 return null; 212 } 213 214 return sb.toString(); 215 } 216 217 public static InputStream getUTF8Stream(String s) { 218 try { 219 ByteArrayBuffer bab = new ByteArrayBuffer(); 220 Writer w = new OutputStreamWriter(bab, "utf-8"); 221 w.write(s); 222 w.close(); 223 return bab.newInputStream(); 224 } catch (IOException e) { 225 throw new RuntimeException("should not happen"); 226 } 227 } 228 229 static final ContextClassloaderLocal<TransformerFactory> transformerFactory = new ContextClassloaderLocal<TransformerFactory>() { 230 @Override 231 protected TransformerFactory initialValue() throws Exception { 232 return TransformerFactory.newInstance(); 233 } 234 }; 235 236 static final ContextClassloaderLocal<SAXParserFactory> saxParserFactory = new ContextClassloaderLocal<SAXParserFactory>() { 237 @Override 238 protected SAXParserFactory initialValue() throws Exception { 239 SAXParserFactory factory = newSAXParserFactory(true); 240 factory.setNamespaceAware(true); 241 return factory; 242 } 243 }; 244 245 /** 246 * Creates a new identity transformer. 247 * @return 248 */ 249 public static Transformer newTransformer() { 250 try { 251 return transformerFactory.get().newTransformer(); 252 } catch (TransformerConfigurationException tex) { 253 throw new IllegalStateException("Unable to create a JAXP transformer"); 254 } 255 } 256 257 /** 258 * Performs identity transformation. 259 * @param <T> 260 * @param src 261 * @param result 262 * @return 263 * @throws javax.xml.transform.TransformerException 264 * @throws java.io.IOException 265 * @throws org.xml.sax.SAXException 266 * @throws javax.xml.parsers.ParserConfigurationException 267 */ 268 public static <T extends Result> T identityTransform(Source src, T result) 269 throws TransformerException, SAXException, ParserConfigurationException, IOException { 270 if (src instanceof StreamSource) { 271 // work around a bug in JAXP in JDK6u4 and earlier where the namespace processing 272 // is not turned on by default 273 StreamSource ssrc = (StreamSource) src; 274 TransformerHandler th = ((SAXTransformerFactory) transformerFactory.get()).newTransformerHandler(); 275 th.setResult(result); 276 XMLReader reader = saxParserFactory.get().newSAXParser().getXMLReader(); 277 reader.setContentHandler(th); 278 reader.setProperty(LEXICAL_HANDLER_PROPERTY, th); 279 reader.parse(toInputSource(ssrc)); 280 } else { 281 newTransformer().transform(src, result); 282 } 283 return result; 284 } 285 286 private static InputSource toInputSource(StreamSource src) { 287 InputSource is = new InputSource(); 288 is.setByteStream(src.getInputStream()); 289 is.setCharacterStream(src.getReader()); 290 is.setPublicId(src.getPublicId()); 291 is.setSystemId(src.getSystemId()); 292 return is; 293 } 294 295 /** 296 * Gets an EntityResolver using XML catalog 297 * 298 * @param catalogUrl 299 * @return 300 */ 301 public static EntityResolver createEntityResolver(@Nullable URL catalogUrl) { 302 return XmlCatalogUtil.createEntityResolver(catalogUrl); 303 } 304 305 /** 306 * Gets a default EntityResolver for catalog at META-INF/jaxws-catalog.xml 307 * 308 * @return 309 */ 310 public static EntityResolver createDefaultCatalogResolver() { 311 return XmlCatalogUtil.createDefaultCatalogResolver(); 312 } 313 314 /** 315 * {@link ErrorHandler} that always treat the error as fatal. 316 */ 317 public static final ErrorHandler DRACONIAN_ERROR_HANDLER = new ErrorHandler() { 318 @Override 319 public void warning(SAXParseException exception) { 320 } 321 322 @Override 323 public void error(SAXParseException exception) throws SAXException { 324 throw exception; 325 } 326 327 @Override 328 public void fatalError(SAXParseException exception) throws SAXException { 329 throw exception; 330 } 331 }; 332 333 public static DocumentBuilderFactory newDocumentBuilderFactory(boolean disableSecurity) { 334 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 335 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING; 336 try { 337 boolean securityOn = !xmlSecurityDisabled(disableSecurity); 338 factory.setFeature(featureToSet, securityOn); 339 factory.setNamespaceAware(true); 340 if (securityOn) { 341 factory.setExpandEntityReferences(false); 342 featureToSet = DISALLOW_DOCTYPE_DECL; 343 factory.setFeature(featureToSet, true); 344 featureToSet = EXTERNAL_GE; 345 factory.setFeature(featureToSet, false); 346 featureToSet = EXTERNAL_PE; 347 factory.setFeature(featureToSet, false); 348 featureToSet = LOAD_EXTERNAL_DTD; 349 factory.setFeature(featureToSet, false); 350 } 351 } catch (ParserConfigurationException e) { 352 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[] {factory.getClass().getName()} ); 353 } 354 return factory; 355 } 356 357 public static TransformerFactory newTransformerFactory(boolean disableSecurity) { 358 TransformerFactory factory = TransformerFactory.newInstance(); 359 try { 360 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 361 } catch (TransformerConfigurationException e) { 362 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[]{factory.getClass().getName()}); 363 } 364 return factory; 365 } 366 367 public static SAXParserFactory newSAXParserFactory(boolean disableSecurity) { 368 SAXParserFactory factory = SAXParserFactory.newInstance(); 369 String featureToSet = XMLConstants.FEATURE_SECURE_PROCESSING; 370 try { 371 boolean securityOn = !xmlSecurityDisabled(disableSecurity); 372 factory.setFeature(featureToSet, securityOn); 373 factory.setNamespaceAware(true); 374 if (securityOn) { 375 featureToSet = DISALLOW_DOCTYPE_DECL; 376 factory.setFeature(featureToSet, true); 377 featureToSet = EXTERNAL_GE; 378 factory.setFeature(featureToSet, false); 379 featureToSet = EXTERNAL_PE; 380 factory.setFeature(featureToSet, false); 381 featureToSet = LOAD_EXTERNAL_DTD; 382 factory.setFeature(featureToSet, false); 383 } 384 } catch (ParserConfigurationException | SAXNotRecognizedException | SAXNotSupportedException e) { 385 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support "+featureToSet+" feature!", new Object[]{factory.getClass().getName()}); 386 } 387 return factory; 388 } 389 390 public static XPathFactory newXPathFactory(boolean disableSecurity) { 391 XPathFactory factory = XPathFactory.newInstance(); 392 try { 393 factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, !xmlSecurityDisabled(disableSecurity)); 394 } catch (XPathFactoryConfigurationException e) { 395 LOGGER.log(Level.WARNING, "Factory [{0}] doesn't support secure xml processing!", new Object[] { factory.getClass().getName() } ); 396 } 397 return factory; 398 } 399 400 public static XMLInputFactory newXMLInputFactory(boolean disableSecurity) { 401 XMLInputFactory factory = XMLInputFactory.newInstance(); 402 if (xmlSecurityDisabled(disableSecurity)) { 403 // TODO-Miran: are those apppropriate defaults? 404 factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); 405 factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); 406 } 407 return factory; 408 } 409 410 private static boolean xmlSecurityDisabled(boolean runtimeDisabled) { 411 return XML_SECURITY_DISABLED || runtimeDisabled; 412 } 413 414 public static SchemaFactory allowExternalAccess(SchemaFactory sf, String value, boolean disableSecurity) { 415 416 // if xml security (feature secure processing) disabled, nothing to do, no restrictions applied 417 if (xmlSecurityDisabled(disableSecurity)) { 418 if (LOGGER.isLoggable(Level.FINE)) { 419 LOGGER.log(Level.FINE, "Xml Security disabled, no JAXP xsd external access configuration necessary."); 420 } 421 return sf; 422 } 423 424 if (System.getProperty("javax.xml.accessExternalSchema") != null) { 425 if (LOGGER.isLoggable(Level.FINE)) { 426 LOGGER.log(Level.FINE, "Detected explicitly JAXP configuration, no JAXP xsd external access configuration necessary."); 427 } 428 return sf; 429 } 430 431 try { 432 sf.setProperty(ACCESS_EXTERNAL_SCHEMA, value); 433 if (LOGGER.isLoggable(Level.FINE)) { 434 LOGGER.log(Level.FINE, "Property \"{0}\" is supported and has been successfully set by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 435 } 436 } catch (SAXException ignored) { 437 // nothing to do; support depends on version JDK or SAX implementation 438 if (LOGGER.isLoggable(Level.CONFIG)) { 439 LOGGER.log(Level.CONFIG, "Property \"{0}\" is not supported by used JAXP implementation.", new Object[]{ACCESS_EXTERNAL_SCHEMA}); 440 } 441 } 442 return sf; 443 } 444 445 }