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