1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /** 6 * Licensed to the Apache Software Foundation (ASF) under one 7 * or more contributor license agreements. See the NOTICE file 8 * distributed with this work for additional information 9 * regarding copyright ownership. The ASF licenses this file 10 * to you under the Apache License, Version 2.0 (the 11 * "License"); you may not use this file except in compliance 12 * with the License. You may obtain a copy of the License at 13 * 14 * http://www.apache.org/licenses/LICENSE-2.0 15 * 16 * Unless required by applicable law or agreed to in writing, 17 * software distributed under the License is distributed on an 18 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19 * KIND, either express or implied. See the License for the 20 * specific language governing permissions and limitations 21 * under the License. 22 */ 23 package com.sun.org.apache.xml.internal.security.utils.resolver.implementations; 24 25 import java.io.ByteArrayOutputStream; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.net.InetSocketAddress; 29 import java.net.MalformedURLException; 30 import java.net.Proxy; 31 import java.net.URISyntaxException; 32 import java.net.URI; 33 import java.net.URL; 34 import java.net.URLConnection; 35 import java.nio.charset.StandardCharsets; 36 37 import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput; 38 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 39 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverContext; 40 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException; 41 import com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverSpi; 42 43 /** 44 * A simple ResourceResolver for HTTP requests. This class handles only 'pure' 45 * HTTP URIs which means without a fragment. The Fragment handling is done by the 46 * {@link ResolverFragment} class. 47 * <BR> 48 * If the user has a corporate HTTP proxy which is to be used, the usage can be 49 * switched on by setting properties for the resolver: 50 * <PRE> 51 * resourceResolver.setProperty("http.proxy.host", "proxy.company.com"); 52 * resourceResolver.setProperty("http.proxy.port", "8080"); 53 * 54 * // if we need a password for the proxy 55 * resourceResolver.setProperty("http.proxy.username", "proxyuser3"); 56 * resourceResolver.setProperty("http.proxy.password", "secretca"); 57 * </PRE> 58 * 59 * @see <A HREF="http://www.javaworld.com/javaworld/javatips/jw-javatip42_p.html">Java Tip 42: Write Java apps that work with proxy-based firewalls</A> 60 * @see <A HREF="https://docs.oracle.com/javase/8/docs/technotes/guides/net/properties.html">JDK docs for network properties</A> 61 * @see <A HREF="http://metalab.unc.edu/javafaq/javafaq.html#proxy">The JAVA FAQ Question 9.5: How do I make Java work with a proxy server?</A> 62 */ 63 public class ResolverDirectHTTP extends ResourceResolverSpi { 64 65 private static final com.sun.org.slf4j.internal.Logger LOG = 66 com.sun.org.slf4j.internal.LoggerFactory.getLogger(ResolverDirectHTTP.class); 67 68 /** Field properties[] */ 69 private static final String properties[] = { 70 "http.proxy.host", "http.proxy.port", 71 "http.proxy.username", "http.proxy.password", 72 "http.basic.username", "http.basic.password" 73 }; 74 75 /** Field HttpProxyHost */ 76 private static final int HttpProxyHost = 0; 77 78 /** Field HttpProxyPort */ 79 private static final int HttpProxyPort = 1; 80 81 /** Field HttpProxyUser */ 82 private static final int HttpProxyUser = 2; 83 84 /** Field HttpProxyPass */ 85 private static final int HttpProxyPass = 3; 86 87 /** Field HttpProxyUser */ 88 private static final int HttpBasicUser = 4; 89 90 /** Field HttpProxyPass */ 91 private static final int HttpBasicPass = 5; 92 93 @Override engineIsThreadSafe()94 public boolean engineIsThreadSafe() { 95 return true; 96 } 97 98 /** 99 * {@inheritDoc} 100 */ 101 @Override engineResolveURI(ResourceResolverContext context)102 public XMLSignatureInput engineResolveURI(ResourceResolverContext context) 103 throws ResourceResolverException { 104 105 try { 106 // calculate new URI 107 URI uriNew = getNewURI(context.uriToResolve, context.baseUri); 108 URL url = uriNew.toURL(); 109 URLConnection urlConnection = openConnection(url); 110 111 // check if Basic authentication is required 112 String auth = urlConnection.getHeaderField("WWW-Authenticate"); 113 114 if (auth != null && auth.startsWith("Basic")) { 115 // do http basic authentication 116 String user = 117 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpBasicUser]); 118 String pass = 119 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpBasicPass]); 120 121 if (user != null && pass != null) { 122 urlConnection = openConnection(url); 123 124 String password = user + ":" + pass; 125 String encodedPassword = XMLUtils.encodeToString(password.getBytes(StandardCharsets.ISO_8859_1)); 126 127 // set authentication property in the http header 128 urlConnection.setRequestProperty("Authorization", 129 "Basic " + encodedPassword); 130 } 131 } 132 133 String mimeType = urlConnection.getHeaderField("Content-Type"); 134 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); 135 InputStream inputStream = urlConnection.getInputStream()) { 136 byte[] buf = new byte[4096]; 137 int read = 0; 138 int summarized = 0; 139 140 while ((read = inputStream.read(buf)) >= 0) { 141 baos.write(buf, 0, read); 142 summarized += read; 143 } 144 145 LOG.debug("Fetched {} bytes from URI {}", summarized, uriNew.toString()); 146 147 XMLSignatureInput result = new XMLSignatureInput(baos.toByteArray()); 148 result.setSecureValidation(context.secureValidation); 149 150 result.setSourceURI(uriNew.toString()); 151 result.setMIMEType(mimeType); 152 153 return result; 154 } 155 156 } catch (URISyntaxException ex) { 157 throw new ResourceResolverException(ex, context.uriToResolve, context.baseUri, "generic.EmptyMessage"); 158 } catch (MalformedURLException ex) { 159 throw new ResourceResolverException(ex, context.uriToResolve, context.baseUri, "generic.EmptyMessage"); 160 } catch (IOException ex) { 161 throw new ResourceResolverException(ex, context.uriToResolve, context.baseUri, "generic.EmptyMessage"); 162 } catch (IllegalArgumentException e) { 163 throw new ResourceResolverException(e, context.uriToResolve, context.baseUri, "generic.EmptyMessage"); 164 } 165 } 166 openConnection(URL url)167 private URLConnection openConnection(URL url) throws IOException { 168 169 String proxyHostProp = 170 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyHost]); 171 String proxyPortProp = 172 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyPort]); 173 String proxyUser = 174 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyUser]); 175 String proxyPass = 176 engineGetProperty(ResolverDirectHTTP.properties[ResolverDirectHTTP.HttpProxyPass]); 177 178 Proxy proxy = null; 179 if (proxyHostProp != null && proxyPortProp != null) { 180 int port = Integer.parseInt(proxyPortProp); 181 proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHostProp, port)); 182 } 183 184 URLConnection urlConnection; 185 if (proxy != null) { 186 urlConnection = url.openConnection(proxy); 187 188 if (proxyUser != null && proxyPass != null) { 189 String password = proxyUser + ":" + proxyPass; 190 String authString = "Basic " + XMLUtils.encodeToString(password.getBytes(StandardCharsets.ISO_8859_1)); 191 192 urlConnection.setRequestProperty("Proxy-Authorization", authString); 193 } 194 } else { 195 urlConnection = url.openConnection(); 196 } 197 198 return urlConnection; 199 } 200 201 /** 202 * We resolve http URIs <I>without</I> fragment... 203 * 204 * @param context 205 * @return true if can be resolved 206 */ engineCanResolveURI(ResourceResolverContext context)207 public boolean engineCanResolveURI(ResourceResolverContext context) { 208 if (context.uriToResolve == null) { 209 LOG.debug("quick fail, uri == null"); 210 return false; 211 } 212 213 if (context.uriToResolve.equals("") || context.uriToResolve.charAt(0) == '#') { 214 LOG.debug("quick fail for empty URIs and local ones"); 215 return false; 216 } 217 218 LOG.debug("I was asked whether I can resolve {}", context.uriToResolve); 219 220 if (context.uriToResolve.startsWith("http:") || 221 context.baseUri != null && context.baseUri.startsWith("http:")) { 222 LOG.debug("I state that I can resolve {}", context.uriToResolve); 223 return true; 224 } 225 226 LOG.debug("I state that I can't resolve {}", context.uriToResolve); 227 228 return false; 229 } 230 231 /** 232 * {@inheritDoc} 233 */ engineGetPropertyKeys()234 public String[] engineGetPropertyKeys() { 235 return ResolverDirectHTTP.properties.clone(); 236 } 237 getNewURI(String uri, String baseURI)238 private static URI getNewURI(String uri, String baseURI) throws URISyntaxException { 239 URI newUri = null; 240 if (baseURI == null || "".equals(baseURI)) { 241 newUri = new URI(uri); 242 } else { 243 newUri = new URI(baseURI).resolve(uri); 244 } 245 246 // if the URI contains a fragment, ignore it 247 if (newUri.getFragment() != null) { 248 URI uriNewNoFrag = 249 new URI(newUri.getScheme(), newUri.getSchemeSpecificPart(), null); 250 return uriNewNoFrag; 251 } 252 return newUri; 253 } 254 255 } 256