001: /*
002: * $Revision: 1.3 $
003: * $Date: 2004/03/16 01:58:43 $
004: *
005: * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * - Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * - Redistribution in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * Neither the name of Sun Microsystems, Inc. or the names of
020: * contributors may be used to endorse or promote products derived
021: * from this software without specific prior written permission.
022: *
023: * This software is provided "AS IS," without a warranty of any
024: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
025: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
026: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
027: * EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
028: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
029: * DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN
030: * OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR
031: * FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
032: * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
033: * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE,
034: * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that Software is not designed, licensed or intended
037: * for use in the design, construction, operation or maintenance of
038: * any nuclear facility.
039: */
040:
041: package com.sun.identity.authentication.modules.idlookup;
042:
043: import java.util.ResourceBundle;
044: import java.util.Set;
045: import java.util.Map;
046: import java.util.Iterator;
047: import java.util.HashMap;
048:
049: import javax.servlet.http.HttpServletRequest;
050: import javax.servlet.http.Cookie;
051:
052: import netscape.ldap.LDAPConnection;
053: import netscape.ldap.LDAPSearchResults;
054: import netscape.ldap.LDAPEntry;
055: import netscape.ldap.LDAPAttribute;
056: import netscape.ldap.LDAPException;
057:
058: import com.sun.identity.authentication.spi.AMLoginModule;
059: import com.iplanet.am.util.Misc;
060: import com.iplanet.am.util.SSLSocketFactoryManager;
061:
062: import javax.security.auth.Subject;
063: import javax.security.auth.callback.CallbackHandler;
064: import javax.security.auth.callback.Callback;
065: import javax.security.auth.callback.NameCallback;
066: import javax.security.auth.callback.PasswordCallback;
067: import javax.security.auth.login.LoginException;
068:
069: import com.iplanet.am.util.AMPasswordUtil;
070:
071: public class IdLookup extends AMLoginModule {
072:
073: private ResourceBundle bundle = null;
074: private static com.iplanet.am.util.Debug debug = null;
075:
076: private static final int DEFAULT_IDLOOKUP_AUTH_LEVEL = 0;
077:
078: private String userTokenId;
079:
080: private String errorMsg = null;
081: private final int FINISHED = -1;
082:
083: private IdLookupAuthPrincipal userPrincipal;
084: private CallbackHandler callbackHandler;
085:
086: private Map options;
087:
088: private String parameterNameForPin;
089: private String parameterNameForId;
090:
091: private int authLevel;
092: private String searchAttributeForId;
093: private String searchAttributeForPin;
094: private String serverHost;
095: private int serverPort = 389;
096: private String startSearchLoc;
097: private String principleUser;
098: private String principlePasswd;
099: private String useSSL;
100:
101: public IdLookup() throws LoginException {
102: }
103:
104: public void init(Subject subject, Map sharedState, Map options) {
105: if (debug == null) {
106: debug = com.iplanet.am.util.Debug
107: .getInstance("amAuthIdLookup");
108: }
109:
110: java.util.Locale locale = getLoginLocale();
111: bundle = amCache.getResBundle("amAuthIdLookup", locale);
112: if (debug.messageEnabled()) {
113: debug.message("IdLookup resbundle locale=" + locale);
114: }
115:
116: this .options = options;
117: initAuthConfig();
118: }
119:
120: /**
121: * Gets IdLookup auth config parameters.
122: */
123: private void initAuthConfig() {
124: if (options != null) {
125: debug.message("IdLookup: getting attributes.");
126:
127: parameterNameForPin = Misc.getMapAttr(options,
128: "iplanet-am-auth-idlookup-pin-parameter-name");
129: parameterNameForId = Misc.getMapAttr(options,
130: "iplanet-am-auth-idlookup-id-parameter-name");
131:
132: String authLevelAsString = Misc.getMapAttr(options,
133: "iplanet-am-auth-idlookup-auth-level");
134: if (authLevelAsString == null
135: || authLevelAsString.equals("")) {
136: authLevel = DEFAULT_IDLOOKUP_AUTH_LEVEL;
137: } else {
138: try {
139: authLevel = Integer.parseInt(authLevelAsString);
140: } catch (Exception e) {
141: debug.error("Invalid auth level "
142: + authLevelAsString);
143: authLevel = DEFAULT_IDLOOKUP_AUTH_LEVEL;
144: }
145: }
146:
147: searchAttributeForId = Misc
148: .getMapAttr(options,
149: "iplanet-am-auth-idlookup-user-search-attribute-for-id");
150: searchAttributeForPin = Misc
151: .getMapAttr(options,
152: "iplanet-am-auth-idlookup-user-search-attribute-for-pin");
153:
154: principleUser = Misc.getMapAttr(options,
155: "iplanet-am-auth-idlookup-principal-user");
156: principlePasswd = Misc.getMapAttr(options,
157: "iplanet-am-auth-idlookup-principal-passwd");
158: useSSL = Misc.getMapAttr(options,
159: "iplanet-am-auth-idlookup-use-ssl");
160: serverHost = Misc.getServerMapAttr(options,
161: "iplanet-am-auth-idlookup-ldap-provider-url");
162:
163: if (serverHost == null) {
164: debug
165: .error("Fatal error: LDAP Server and Port misconfigured");
166: errorMsg = bundle.getString("wrongLDAPServer");
167: return;
168: }
169:
170: String port = null;
171: if (serverHost != null) {
172: // set LDAP Parameters
173: int index = serverHost.indexOf(':');
174: if (index != -1) {
175: port = serverHost.substring(index + 1);
176: serverPort = Integer.parseInt(port);
177: serverHost = serverHost.substring(0, index);
178: }
179: }
180:
181: startSearchLoc = Misc.getServerMapAttr(options,
182: "iplanet-am-auth-idlookup-base-dn");
183: if (startSearchLoc == null) {
184: debug
185: .error("Fatal error: LDAP Start Search DN misconfigured");
186: errorMsg = bundle.getString("wrongStartDN");
187:
188: return;
189: }
190:
191: if (debug.messageEnabled()) {
192: debug.message("\n ldapProviderUrl=" + serverHost
193: + "\n\t serverPort (" + port + ") = "
194: + serverPort + "\n\t startSearchLoc="
195: + startSearchLoc + "\n\t searchAttributeForId="
196: + searchAttributeForId
197: + "\n\t searchAttributeForPin="
198: + searchAttributeForPin + "\n\t principleUser="
199: + principleUser + "\n\t authLevel=" + authLevel
200: + "\n\t useSSL=" + useSSL);
201: }
202: } else {
203: debug.error("options is null");
204: errorMsg = bundle.getString("IdLookupValidateEx");
205: }
206: }
207:
208: public int process(Callback[] callbacks, int state)
209: throws LoginException {
210: debug.message("IdLookup : in process ..");
211: if (errorMsg != null) {
212: throw new LoginException(errorMsg);
213: }
214:
215: String id = null;
216: String pin = null;
217:
218: HttpServletRequest req = getHttpServletRequest();
219: if (req != null) {
220: id = req.getParameter(parameterNameForId);
221: pin = req.getParameter(parameterNameForPin);
222: } else {
223: debug
224: .message("IdLookup : null request calling sendCallback");
225: Map map = sendCallback();
226: if (map == null) {
227: debug.error(" null map from sendCallback : ");
228: throw new LoginException(bundle
229: .getString("IdLookupValidateEx"));
230: }
231:
232: id = (String) map.get("id");
233: pin = (String) map.get("pin");
234: }
235:
236: if (debug.messageEnabled()) {
237: debug.message("IdLookup : " + id + " pin: " + pin);
238: }
239:
240: if (id == null || pin == null) {
241: debug
242: .error(" null value IdLookup : " + id + " pin: "
243: + pin);
244: throw new LoginException(bundle
245: .getString("IdLookupValidateEx"));
246: }
247:
248: userTokenId = getUserId(id, pin);
249:
250: if (userTokenId != null) {
251: if (debug.messageEnabled()) {
252: debug.message("User Found : " + userTokenId);
253: }
254: } else {
255: debug.error(" id not matched : " + id);
256: throw new LoginException(bundle
257: .getString("IdLookupValidateEx"));
258: }
259:
260: return FINISHED;
261: }
262:
263: private String getUserId(String id, String pin)
264: throws LoginException {
265: LDAPConnection ldc = null;
266: if (useSSL.equals("true")) {
267: debug.message("IdLookup: initial ldc using ssl.");
268: try {
269: ldc = new LDAPConnection(SSLSocketFactoryManager
270: .getSSLSocketFactory());
271: debug.message("validate(): SSLSocketFactory called");
272: } catch (Exception e) {
273: debug.error("validate.JSSSocketFactory", e);
274: throw new LoginException(bundle
275: .getString("jssSokFactoryFail"));
276: }
277: } else { // non-ssl
278: ldc = new LDAPConnection();
279: }
280:
281: try {
282: ldc.connect(serverHost, serverPort);
283: ldc.authenticate(principleUser, principlePasswd);
284: } catch (LDAPException e) {
285: debug.error("IdLookup : dircontext", e);
286: throw new LoginException(bundle
287: .getString("IdLookupNoContext"));
288: }
289:
290: try {
291: if (debug.messageEnabled()) {
292: debug.message("IdLookup - ldc.search: searching "
293: + startSearchLoc + " " + ldc);
294: }
295:
296: //
297: // we are interested only in dn & Pin. Also saves memory (especially
298: // when the entry contains the display profile !)
299: //
300: String[] getAttrs = { "dn", searchAttributeForPin };
301:
302: String searchFilter = new StringBuffer(250).append("(")
303: .append(searchAttributeForId).append("=")
304: .append(id).append(")").toString();
305:
306: if (debug.messageEnabled()) {
307: debug
308: .message("IdLookup - ldc.search: using this filter: "
309: + searchFilter);
310: }
311:
312: LDAPSearchResults results = ldc.search(startSearchLoc,
313: LDAPConnection.SCOPE_SUB, searchFilter, getAttrs,
314: false);
315:
316: if (results != null) {
317: int num_results = 0;
318: LDAPEntry entry = null;
319:
320: //
321: // LDAP API expects the user to loop thro' the LDAPSearchResults
322: // to get all the results.
323: //
324: while (results.hasMoreElements()) {
325: num_results++;
326: entry = results.next();
327: }
328:
329: if (num_results == 0) {
330: debug
331: .error("IdLookup - User not found with given Id Number:"
332: + id);
333: throw new LoginException(bundle
334: .getString("UserNotFound"));
335: } else if (num_results > 1) {
336: debug.error("Multiple entries matched");
337: throw new LoginException(bundle
338: .getString("MultipleUIDMatch"));
339: }
340:
341: String userDN = entry.getDN();
342: LDAPAttribute lAttr = entry
343: .getAttribute(searchAttributeForPin);
344:
345: String[] attrs = null;
346: if (lAttr != null) {
347: attrs = lAttr.getStringValueArray();
348: }
349:
350: if (attrs == null) {
351: if (debug.messageEnabled()) {
352: debug.message("Pin not found for: " + userDN);
353: }
354:
355: throw new LoginException(bundle
356: .getString("PinNotFound"));
357: }
358:
359: String pinFromLDAP = AMPasswordUtil.decrypt(attrs[0]);
360: if (!pinFromLDAP.equals(pin)) {
361: if (debug.messageEnabled()) {
362: debug.message("Incorrect id/Pin: " + userDN);
363: }
364:
365: throw new LoginException(bundle
366: .getString("IncorrectPin"));
367: }
368:
369: if (debug.messageEnabled()) {
370: debug.message("IdLookup - userDN " + userDN);
371: }
372:
373: return userDN;
374: }
375: } catch (LoginException le) {
376: throw le; // we are throw'ing LoginExceptions inside try { .. }
377: } catch (Exception e) {
378: debug.error("IdLookup - Error finding user: ", e);
379: throw new LoginException(bundle.getString("UserNotFound"));
380: }
381:
382: if (debug.messageEnabled()) {
383: debug
384: .message("IdLookup - User not found with given Id Number:"
385: + id);
386: }
387:
388: return null;
389: }
390:
391: public java.security.Principal getPrincipal() {
392: if (userPrincipal != null) {
393: return userPrincipal;
394: } else if (userTokenId != null) {
395: userPrincipal = new IdLookupAuthPrincipal(userTokenId);
396: return userPrincipal;
397: } else {
398: return null;
399: }
400: }
401:
402: /**
403: * Send callbacks to get id and pin
404: * @return Map contains id and pin
405: */
406: private Map sendCallback() {
407: try {
408: CallbackHandler callbackHandler = getCallbackHandler();
409: if (callbackHandler == null) {
410: throw new LoginException(bundle
411: .getString("NoCallbackHandler"));
412: }
413:
414: Callback[] callbacks = new Callback[2];
415:
416: callbacks[0] = new NameCallback(bundle.getString("id"));
417: callbacks[1] = new PasswordCallback(
418: bundle.getString("pin"), true);
419: callbackHandler.handle(callbacks);
420:
421: if (debug.messageEnabled()) {
422: debug.message("Callback is.. : " + callbacks);
423: }
424:
425: // map to hold return
426: Map map = new HashMap();
427:
428: // process return
429: int len = callbacks.length;
430: for (int i = 0; i < len; i++) {
431: Callback cb = callbacks[i];
432: if (cb instanceof PasswordCallback) {
433: char[] pass = ((PasswordCallback) cb).getPassword();
434: if (pass != null) {
435: map.put("pin", new String(pass));
436: }
437: } else if (cb instanceof NameCallback) {
438: String id = ((NameCallback) cb).getName();
439: if (id != null) {
440: map.put("id", id);
441: }
442: }
443: }
444:
445: return map;
446: } catch (Exception e) {
447: debug.error("sendCallback", e);
448: }
449:
450: return null;
451: }
452: }
|