1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.idp.session;
18
19 import java.io.IOException;
20 import java.security.GeneralSecurityException;
21 import java.security.MessageDigest;
22 import java.util.Arrays;
23
24 import javax.servlet.Filter;
25 import javax.servlet.FilterChain;
26 import javax.servlet.FilterConfig;
27 import javax.servlet.ServletException;
28 import javax.servlet.ServletRequest;
29 import javax.servlet.ServletResponse;
30 import javax.servlet.http.Cookie;
31 import javax.servlet.http.HttpServletRequest;
32
33 import org.joda.time.DateTime;
34 import org.opensaml.ws.transport.http.HTTPTransportUtils;
35 import org.opensaml.xml.util.Base64;
36 import org.opensaml.xml.util.DatatypeHelper;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.slf4j.MDC;
40
41 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
42 import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationEngine;
43
44
45
46
47 public class IdPSessionFilter implements Filter {
48
49
50 private final Logger log = LoggerFactory.getLogger(IdPSessionFilter.class);
51
52
53 private boolean consistentAddress;
54
55
56 private SessionManager<Session> sessionManager;
57
58
59 public void destroy() {
60
61 }
62
63
64 public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
65 ServletException {
66 HttpServletRequest httpRequest = (HttpServletRequest) request;
67
68 MDC.put("JSESSIONID", httpRequest.getSession().getId());
69 MDC.put("clientIP", httpRequest.getRemoteAddr());
70
71 Cookie sessionCookie = getIdPSessionCookie(httpRequest);
72 Session idpSession = getUserSession(sessionCookie, httpRequest);
73 if (idpSession != null) {
74 log.trace("Updating IdP session activity time and adding session object to the request");
75 idpSession.setLastActivityInstant(new DateTime());
76 MDC.put("idpSessionId", idpSession.getSessionID());
77 httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
78 }
79
80 filterChain.doFilter(request, response);
81 }
82
83
84 public void init(FilterConfig filterConfig) throws ServletException {
85 String sessionManagerId = filterConfig.getInitParameter("sessionManagedId");
86 if (DatatypeHelper.isEmpty(sessionManagerId)) {
87 sessionManagerId = "shibboleth.SessionManager";
88 }
89
90 sessionManager = (SessionManager<Session>) filterConfig.getServletContext().getAttribute(sessionManagerId);
91
92 String consistentAddressParam = filterConfig.getInitParameter("ensureConsistentClientAddress");
93 if (DatatypeHelper.isEmpty(consistentAddressParam)) {
94 consistentAddress = true;
95 } else {
96 consistentAddress = Boolean.parseBoolean(consistentAddressParam);
97 }
98 }
99
100
101
102
103
104
105
106
107 protected Cookie getIdPSessionCookie(HttpServletRequest httpRequest) {
108 log.trace("Attempting to retrieve IdP session cookie.");
109 Cookie[] requestCookies = httpRequest.getCookies();
110
111 if (requestCookies != null) {
112 for (Cookie requestCookie : requestCookies) {
113 if (DatatypeHelper.safeEquals(requestCookie.getName(), AuthenticationEngine.IDP_SESSION_COOKIE_NAME)) {
114 log.trace("Found IdP session cookie.");
115 return requestCookie;
116 }
117 }
118 }
119
120 return null;
121 }
122
123
124
125
126
127
128
129
130
131 protected Session getUserSession(Cookie sessionCookie, HttpServletRequest httpRequest) {
132 if (sessionCookie == null || DatatypeHelper.isEmpty(sessionCookie.getValue())) {
133 return null;
134 }
135
136
137
138
139 String cookieValue = HTTPTransportUtils.urlDecode(sessionCookie.getValue());
140 String[] valueComponents = cookieValue.split("\\|");
141 if (valueComponents.length != 3) {
142 log.warn("IdP session cookie has an improperly formated value: {}", cookieValue);
143 return null;
144 }
145
146 byte[] remoteAddressBytes = Base64.decode(valueComponents[0]);
147 byte[] sessionIdBytes = Base64.decode(valueComponents[1]);
148 byte[] signatureBytes = Base64.decode(valueComponents[2]);
149
150 String sessionId = new String(sessionIdBytes);
151 Session userSession = sessionManager.getSession(sessionId);
152
153 if (userSession != null) {
154 if (isCookieValid(httpRequest, remoteAddressBytes, sessionIdBytes, signatureBytes, userSession
155 .getSessionSecret())) {
156 return userSession;
157 }
158 } else {
159 log.debug("No session associated with session ID {} - session must have timed out", valueComponents[1]);
160 }
161 return null;
162 }
163
164
165
166
167
168
169
170
171
172
173
174
175
176 protected boolean isCookieValid(HttpServletRequest httpRequest, byte[] remoteAddressBytes, byte[] sessionIdBytes,
177 byte[] signatureBytes, byte[] sessionSecret) {
178 if (consistentAddress) {
179 String remoteAddress = new String(remoteAddressBytes);
180 if (!httpRequest.getRemoteAddr().equals(remoteAddress)) {
181 log.error("Client sent a cookie from address {} but the cookie was issued to address {}", httpRequest
182 .getRemoteAddr(), remoteAddress);
183 return false;
184 }
185 }
186
187 try {
188 MessageDigest digester = MessageDigest.getInstance("SHA");
189 digester.update(sessionSecret);
190 digester.update(remoteAddressBytes);
191 digester.update(sessionIdBytes);
192 if (!Arrays.equals(digester.digest(), signatureBytes)) {
193 log.error("Session cookie has been tampered with, its signature no longer matches expected value");
194 return false;
195 }
196 } catch (GeneralSecurityException e) {
197 log.error("Unable to compute signature over session cookie material", e);
198 }
199
200 return true;
201 }
202 }