1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package edu.internet2.middleware.grouper.ws.security;
21
22 import java.io.File;
23 import java.util.LinkedHashMap;
24 import java.util.Map;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import javax.security.auth.login.LoginContext;
29 import javax.security.auth.login.LoginException;
30 import javax.servlet.http.HttpServletRequest;
31
32 import org.apache.commons.codec.binary.Base64;
33 import org.apache.commons.lang.StringUtils;
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import edu.internet2.middleware.grouper.cache.GrouperCache;
38 import edu.internet2.middleware.grouper.j2ee.Authentication;
39 import edu.internet2.middleware.grouper.util.GrouperUtil;
40 import edu.internet2.middleware.grouper.ws.GrouperWsConfig;
41 import edu.internet2.middleware.grouper.ws.util.GrouperServiceUtils;
42 import edu.internet2.middleware.morphString.Morph;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class WsGrouperKerberosAuthentication implements WsCustomAuthentication {
59
60
61
62
63
64
65 public static void main(String[] args) throws Exception {
66 GrouperUtil.waitForInput();
67 for (int i=0; i<2; i++) {
68 for (int j=0;j<100;j++) {
69
70 if (!authenticateKerberos("penngroups/medley.isc-seo.upenn.edu",
71 Morph.decryptIfFile("R:/home/appadmin/pass/pennGroups/pennGroupsMedley.pass"))) {
72 throw new RuntimeException("Problem!");
73 }
74 System.gc();
75 System.out.println(j + ":" + i + ", "
76 + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/(double)(1024*1024)) + " megs used");
77 Thread.sleep(100);
78 }
79 GrouperUtil.waitForInput();
80 }
81 }
82
83
84 private static final Log LOG = GrouperUtil.getLog(WsGrouperKerberosAuthentication.class);
85
86
87
88
89 private static GrouperCache<String, String> loginCache = new GrouperCache<String, String>(
90 WsGrouperKerberosAuthentication.class.getName() + ".userCache", 10000, false, 60*1, 60*1, false);
91
92
93
94
95
96 public String retrieveLoggedInSubjectId(HttpServletRequest httpServletRequest)
97 throws RuntimeException {
98
99 String authHeader = httpServletRequest.getHeader("Authorization");
100
101
102 if (StringUtils.isBlank(authHeader)) {
103 LOG.error("No authorization header in HTTP");
104 return null;
105 }
106
107
108 String authHeaderHash = GrouperUtil.encryptSha(authHeader);
109
110 String cachedLogin = loginCache.get(authHeaderHash);
111 if (!StringUtils.isBlank(cachedLogin)) {
112 LOG.debug("Retrieved cached login");
113 return cachedLogin;
114 }
115 LOG.debug("Login not in cache");
116
117 String user = Authentication.retrieveUsername(authHeader);
118 String pass = Authentication.retrievePassword(authHeader);
119
120 if (authenticateKerberos(user, pass)) {
121
122 loginCache.put(authHeaderHash, user);
123
124 return user;
125 }
126
127 LOG.error("Error authenticating user: " + user);
128 return null;
129 }
130
131
132
133
134
135
136 private static String timeMillis(long startNanos) {
137 return ((System.nanoTime() - startNanos) / 1000000) + "ms";
138 }
139
140
141
142
143
144
145
146 public static boolean authenticateKerberos(String principal, String password) {
147
148 Map<String, Object> debugMap = LOG.isDebugEnabled() ? new LinkedHashMap<String, Object>() : null;
149 long startNanos = System.nanoTime();
150
151 try {
152
153 if (LOG.isDebugEnabled()) {
154 debugMap.put("method", "authenticateKerberos()");
155 }
156
157
158
159
160
161
162 File jaasConf = GrouperServiceUtils.fileFromResourceName("jaas.conf");
163
164 if (LOG.isDebugEnabled()) {
165 debugMap.put("jaasConfFound", jaasConf != null);
166 debugMap.put("jaasConfLocation", jaasConf == null ? null : jaasConf.getAbsolutePath());
167 }
168
169 if (jaasConf == null) {
170 throw new RuntimeException("Cant find jaas.conf!");
171 }
172
173 String krb5Location = GrouperWsConfig.retrieveConfig().propertyValueString("kerberos.krb5.conf.location");
174
175 if (LOG.isDebugEnabled()) {
176 debugMap.put("krb5Location", krb5Location);
177 }
178
179 File krb5confFile = null;
180
181
182 if (!StringUtils.isBlank(krb5Location)) {
183 krb5confFile = new File(krb5Location);
184
185 if (LOG.isDebugEnabled()) {
186 debugMap.put("krb5confFile", krb5confFile.getAbsolutePath());
187 debugMap.put("krb5confFileFound", krb5confFile.exists() || krb5confFile.isFile());
188 }
189
190 if (!krb5confFile.exists() || !krb5confFile.isFile()) {
191 throw new RuntimeException("krb5 conf file in " + krb5Location + " does not exist or is not a file");
192 }
193 } else {
194
195 krb5confFile = GrouperUtil.fileFromResourceName("krb5.conf");
196
197 if (LOG.isDebugEnabled()) {
198 debugMap.put("krb5confFile", krb5confFile == null ? null : krb5confFile.getAbsolutePath());
199 debugMap.put("krb5confFileFound", krb5confFile.exists() || krb5confFile.isFile());
200 }
201 }
202
203 if (krb5confFile == null) {
204 if (LOG.isDebugEnabled()) {
205 debugMap.put("krb5confFileNotFoundFound", true);
206 debugMap.put("kerberos.realm", GrouperWsConfig.retrieveConfig().propertyValueString("kerberos.realm"));
207 debugMap.put("kerberos.kdc.address", GrouperWsConfig.retrieveConfig().propertyValueString("kerberos.kdc.address"));
208 }
209
210 System.setProperty("java.security.krb5.realm", GrouperWsConfig.retrieveConfig().propertyValueStringRequired("kerberos.realm"));
211 System.setProperty("java.security.krb5.kdc", GrouperWsConfig.retrieveConfig().propertyValueStringRequired("kerberos.kdc.address"));
212 } else {
213
214 System.setProperty("java.security.krb5.conf", krb5confFile.getAbsolutePath());
215 }
216
217
218 System.setProperty("java.security.auth.login.config", jaasConf.getAbsolutePath());
219
220
221
222
223 if (GrouperWsConfig.retrieveConfig().propertyValueBoolean("kerberos.debug", false)) {
224 if (LOG.isDebugEnabled()) {
225 debugMap.put("kerberos.debug", true);
226 }
227
228 System.setProperty("sun.security.krb5.debug", "true");
229 }
230
231 LoginContext lc = null;
232 try {
233 lc = new LoginContext("JaasSample", new GrouperWsKerberosHandler(principal, password));
234
235 if (LOG.isDebugEnabled()) {
236 debugMap.put("loginContextCreated", true + " " + timeMillis(startNanos));
237 }
238
239 } catch (LoginException le) {
240 if (LOG.isDebugEnabled()) {
241 debugMap.put("errorCreatingLoginContext", true + " " + timeMillis(startNanos));
242 }
243 LOG.error("Cannot create LoginContext. ", le);
244 return false;
245 } catch (SecurityException se) {
246 if (LOG.isDebugEnabled()) {
247 debugMap.put("errorCreatingLoginContext", true + " " + timeMillis(startNanos));
248 }
249 LOG.error("Cannot create LoginContext. " , se);
250 return false;
251 }
252
253 try {
254
255
256 lc.login();
257
258 if (LOG.isDebugEnabled()) {
259 debugMap.put("loggedIn", true + " " + timeMillis(startNanos));
260 }
261
262 try {
263 lc.logout();
264 if (LOG.isDebugEnabled()) {
265 debugMap.put("loggedOut", true + " " + timeMillis(startNanos));
266 }
267 } catch (Exception e) {
268 LOG.warn(e);
269 }
270 return true;
271 } catch (LoginException le) {
272
273 if (LOG.isDebugEnabled()) {
274 debugMap.put("loginException", true + " " + timeMillis(startNanos));
275 }
276 LOG.warn(le);
277 }
278 } catch (RuntimeException re) {
279
280 if (LOG.isDebugEnabled()) {
281 debugMap.put("took", timeMillis(startNanos));
282 }
283 } finally {
284 if (LOG.isDebugEnabled()) {
285 debugMap.put("took", timeMillis(startNanos));
286 LOG.debug(GrouperUtil.mapToString(debugMap));
287 }
288 }
289 return false;
290 }
291
292
293 }