Add Shibboleth header to SOAP requests.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / ShibHttpHook.java
1 /*
2  * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * ShibHttpHook - Receive callbacks from OpenSAML HTTP Session processing.
19  */
20 package edu.internet2.middleware.shibboleth.serviceprovider;
21
22 import java.net.HttpURLConnection;
23 import java.net.Socket;
24 import java.security.KeyManagementException;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.Principal;
27 import java.security.PrivateKey;
28 import java.security.cert.CertificateException;
29 import java.security.cert.X509Certificate;
30
31 import javax.net.ssl.HttpsURLConnection;
32 import javax.net.ssl.KeyManager;
33 import javax.net.ssl.SSLContext;
34 import javax.net.ssl.SSLSocketFactory;
35 import javax.net.ssl.TrustManager;
36 import javax.net.ssl.X509KeyManager;
37 import javax.net.ssl.X509TrustManager;
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40
41 import org.apache.log4j.Logger;
42 import org.opensaml.SAMLException;
43 import org.opensaml.SAMLSOAPHTTPBinding.HTTPHook;
44
45 import edu.internet2.middleware.shibboleth.common.Constants;
46 import edu.internet2.middleware.shibboleth.common.Credential;
47 import edu.internet2.middleware.shibboleth.common.Credentials;
48 import edu.internet2.middleware.shibboleth.common.Trust;
49 import edu.internet2.middleware.shibboleth.metadata.AttributeAuthorityDescriptor;
50
51 /**
52  * During Attribute Query, SAML creates the HTTP(S) session with
53  * the AA. Objects of this class provide a callback for special
54  * processing of the Session that SAML has established before data
55  * is exchanged. This allows Shib to add its own Metadata and Trust
56  * processing to validate the AA identity.
57  
58  * @author Howard Gilbert
59  *
60  */
61 public class ShibHttpHook implements HTTPHook {
62
63     private static Logger log = Logger.getLogger(HTTPHook.class);
64     
65     ServiceProviderContext context = ServiceProviderContext.getInstance();
66     ServiceProviderConfig config = context.getServiceProviderConfig();
67     
68     // If we present a ClientCert, it will be this one.
69     Credentials credentials = config.getCredentials();
70     
71     // SAML Doesn't know the Shibboleth objects, so they have to be saved
72     // by the constructor so they can be used in callbacks without being
73     // passed as arguments
74     AttributeAuthorityDescriptor role; // The AA object from the Metadata
75     Trust trust; // A ShibbolethTrust object
76     
77     /**
78      * @param role
79      */
80     public ShibHttpHook(AttributeAuthorityDescriptor role, Trust trust) {
81         super();
82         this.role = role;  // Save the AA Role for the callback
83         this.trust = trust; // Save the ShibTrust for the callback
84     }
85
86     public boolean incoming(HttpServletRequest r, Object globalCtx,
87             Object callCtx) throws SAMLException {
88         log.error("ShibHttpHook method incoming-1 should not have been called.");
89         return true;
90     }
91
92     public boolean outgoing(HttpServletResponse r, Object globalCtx,
93             Object callCtx) throws SAMLException {
94         log.error("ShibHttpHook method outgoing-1 should not have been called.");
95         return true;
96     }
97
98     public boolean incoming(HttpURLConnection conn, Object globalCtx,
99             Object callCtx) throws SAMLException {
100         // Called with the AA response, but I have nothing to add here
101         return true;
102     }
103
104     /**
105      * After the URLConnection has been initialized and before 
106      * the connect() method is called, this exit has a chance to
107      * do additional processing.
108      * 
109      * <p>If this is an HTTPS session, configure the SocketFactory
110      * to use a custom TrustManager for Certificate processing.</p>
111      */
112     public boolean outgoing(HttpURLConnection conn, Object globalCtx,
113             Object callCtx) throws SAMLException {
114         
115         conn.setRequestProperty("Shibboleth", Constants.SHIB_VERSION);
116         
117         if (!(conn instanceof HttpsURLConnection)) {
118             return true; // HTTP (non-SSL) sessions need no additional processing
119         }
120         // Cast to subclass with extra info
121         HttpsURLConnection sslconn = (HttpsURLConnection) conn;
122         
123         // To get your own Certificate Processing exits, you have 
124         // to create a custom SSLContext, configure it, and then
125         // obtain a SocketFactory
126         SSLContext sslContext = null;
127         try {
128             sslContext = SSLContext.getInstance("SSL");
129         } catch (NoSuchAlgorithmException e) {
130             // Cannot happen in code that is already doing SSL
131             log.error("Cannot find required SSL support");
132             return true;
133         }
134         
135         // Arrays with one element (for init)
136         TrustManager[] tms = new TrustManager[] {new ShibTrustManager()};
137         KeyManager[] kms = new KeyManager[] {new ShibKeyManager()};
138         
139         try {
140             // Attach the KeyManager and TrustManager to the Context
141             sslContext.init(kms,tms,new java.security.SecureRandom());
142         } catch (KeyManagementException e) {
143             return false;
144         }
145         
146         // Now we can get our own custom SocketFactory and replace
147         // the default factory in the caller's URLConnection
148         SSLSocketFactory socketFactory = sslContext.getSocketFactory();
149         sslconn.setSSLSocketFactory(socketFactory);
150         
151         // The KeyManager and TrustManager get callbacks from JSSE during
152         // the URLConnection.connect() call
153         return true;
154     }
155     
156     /**
157      * Called to select the Client Certificate the SP will present to 
158      * the AA.
159      * 
160      * <p>Normally a user KeyManager extends some class backed by a 
161      * KeyStore. It just chooses an alias, and lets the parent class 
162      * do the dirty work of extracting the Certificate chain from the 
163      * backing file. However, in Shibboleth the SP Credentials come
164      * from the configuration file and are in memory. There is no
165      * meaningful alias, so we make one up.
166      */
167     class ShibKeyManager implements X509KeyManager {
168         
169         public String fred ="Fred";
170         public String[] freds = {fred};
171
172         public String[] getClientAliases(String arg0, Principal[] arg1) {
173             return freds;
174         }
175
176         public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
177             return fred;
178         }
179
180         public String[] getServerAliases(String arg0, Principal[] arg1) {
181             return freds;
182         }
183
184         public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
185             return fred;
186         }
187
188         public X509Certificate[] getCertificateChain(String arg0) {
189             // Obtain the Client Cert from the Credentials object
190             // in the configuration file. Ignore argument "fred".
191             Credential credential = credentials.getCredential();
192             X509Certificate[] certificateChain = credential.getX509CertificateChain();
193             return certificateChain;
194         }
195
196         public PrivateKey getPrivateKey(String arg0) {
197             // Obtain the Private Key from the Credentials object.
198             Credential credential = credentials.getCredential();
199             PrivateKey privateKey = credential.getPrivateKey();
200             return privateKey;
201         }
202         
203     }
204     
205     /**
206      * Called to approve or reject an SSL Server Certificate.
207      * In practice this is the Certificate of the AA.
208      * 
209      * <p>A TrustManager handles Certificate approval at either end
210      * of an SSL connection, but this code is in the SP and is only 
211      * inserted into the Attribute Query to the AA. When the AA is
212      * configured to use HTTPS and presents an SSL Server Certficate,
213      * call the commmon code to validate that this Certificate is in
214      * the Metadata.</p>
215      */
216     class ShibTrustManager  implements X509TrustManager {
217
218         public X509Certificate[] getAcceptedIssuers() {
219             log.error("ShibHttpHook method getAcceptedIssuers should not have been called.");
220             return new X509Certificate[0]; 
221         }
222         
223         public void checkClientTrusted(X509Certificate[] arg0, String arg1) 
224             throws CertificateException {
225             log.error("ShibHttpHook method checkClientTrusted should not have been called.");
226         }
227
228         public void checkServerTrusted(X509Certificate[] certs, String arg1) 
229             throws CertificateException {
230             if (trust.validate(certs[0],certs,role)) {
231                 log.debug("ShibHttpHook accepted AA Server Certificate.");
232                 return;
233             }
234             log.info("ShibHttpHook rejected AA Server Certificate.");
235             throw new CertificateException("Cannot validate AA Server Certificate in Metadata");
236             
237         }
238         
239     }
240
241 }