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