001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.harmony.auth.module;
019:
020: import java.security.Principal;
021: import java.util.ArrayList;
022: import java.util.HashSet;
023: import java.util.Hashtable;
024: import java.util.Map;
025: import java.util.Set;
026:
027: import javax.naming.Context;
028: import javax.naming.NameClassPair;
029: import javax.naming.NamingEnumeration;
030: import javax.naming.NamingException;
031: import javax.naming.directory.Attribute;
032: import javax.naming.directory.Attributes;
033: import javax.naming.directory.BasicAttributes;
034: import javax.naming.directory.DirContext;
035: import javax.naming.directory.InitialDirContext;
036: import javax.naming.directory.SearchControls;
037: import javax.security.auth.Subject;
038: import javax.security.auth.callback.Callback;
039: import javax.security.auth.callback.CallbackHandler;
040: import javax.security.auth.callback.NameCallback;
041: import javax.security.auth.callback.PasswordCallback;
042: import javax.security.auth.login.LoginException;
043: import javax.security.auth.spi.LoginModule;
044:
045: import org.apache.harmony.auth.UnixNumericGroupPrincipal;
046: import org.apache.harmony.auth.UnixNumericUserPrincipal;
047: import org.apache.harmony.auth.UnixPrincipal;
048:
049: public class JndiLoginModule extends SharedStateManager implements
050: LoginModule {
051:
052: public final String USER_PROVIDER = "group.provider.url";
053:
054: public final String GROUP_PROVIDER = "user.provider.url";
055:
056: //harmony lacks jndi provider
057: private final String JNDI_FACTORY = "";
058:
059: private LoginModuleUtils.LoginModuleStatus status = new LoginModuleUtils.LoginModuleStatus();
060:
061: private Subject subject;
062:
063: private CallbackHandler callbackHandler;
064:
065: private Map<String, ?> options;
066:
067: private String jndiUserProvider;
068:
069: private String jndiGroupProvider;
070:
071: private String userID;
072:
073: private char[] userPassword;
074:
075: private Long uidNumber;
076:
077: private Long gidNumber;
078:
079: private UnixPrincipal unixPrincipal;
080:
081: private UnixNumericUserPrincipal unixNumericUserPrincipal;
082:
083: private Set<UnixNumericGroupPrincipal> unixNumericGroupPrincipals;
084:
085: public boolean abort() throws LoginException {
086: LoginModuleUtils.ACTION action = status.checkAbout();
087: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
088: if (status.isLoggined()) {
089: return true;
090: } else {
091: return false;
092: }
093: }
094: clear();
095: debugUtil
096: .recordDebugInfo("[JndiLoginModule] aborted authentication failed\n");
097: if (status.isCommitted()) {
098: debugUtil
099: .recordDebugInfo("[JndiLoginModule]: logged out Subject\n");
100: }
101: debugUtil.printAndClearDebugInfo();
102: status.logouted();
103: return true;
104: }
105:
106: public boolean commit() throws LoginException {
107: LoginModuleUtils.ACTION action = status.checkCommit();
108: switch (action) {
109: case no_action:
110: return true;
111: case logout:
112: clear();
113: throw new LoginException("Fail to login");
114: default:
115: if (subject.isReadOnly()) {
116: clear();
117: throw new LoginException("Subject is readonly.");
118: }
119: subject.getPrincipals().add(unixPrincipal);
120: debugUtil
121: .recordDebugInfo("[JndiLoginModule] added UnixPrincipal to Subject\n");
122: subject.getPrincipals().add(unixNumericUserPrincipal);
123: debugUtil
124: .recordDebugInfo("[JndiLoginModule] added UnixNumericUserPrincipal to Subject\n");
125: for (Principal principal : unixNumericGroupPrincipals) {
126: subject.getPrincipals().add(principal);
127: }
128: debugUtil
129: .recordDebugInfo("[JndiLoginModule] added UnixNumericGroupPrincipal(s) to Subject\n");
130: debugUtil.printAndClearDebugInfo();
131: status.committed();
132: clearPass();
133: return true;
134: }
135: }
136:
137: @SuppressWarnings("unchecked")
138: public void initialize(Subject subject,
139: CallbackHandler callbackHandler,
140: Map<String, ?> sharedState, Map<String, ?> options) {
141: this .subject = subject;
142: this .callbackHandler = callbackHandler;
143: if (null == options) {
144: throw new NullPointerException();
145: }
146: this .options = options;
147: debugUtil = new DebugUtil(options);
148: prepareSharedState(sharedState, options);
149: status.initialized();
150: }
151:
152: public boolean login() throws LoginException {
153: LoginModuleUtils.ACTION action = status.checkLogin();
154: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
155: return true;
156: }
157: getJndiParameters();
158: loginWithSharedState();
159: debugUtil.recordDebugInfo("[JndiLoginModule] user: '" + userID
160: + "' has UID: " + uidNumber + "\n");
161: debugUtil.recordDebugInfo("[JndiLoginModule] user: '" + userID
162: + "' has GID: " + gidNumber + "\n");
163: getPrinclpalsFromJndi();
164: debugUtil.printAndClearDebugInfo();
165: status.logined();
166: return true;
167: }
168:
169: public boolean logout() throws LoginException {
170: LoginModuleUtils.ACTION action = status.checkLogout();
171: if (action.equals(LoginModuleUtils.ACTION.no_action)) {
172: return true;
173: }
174: clear();
175: debugUtil
176: .recordDebugInfo("[JndiLoginModule] logged out Subject\n");
177: debugUtil.printAndClearDebugInfo();
178: status.logouted();
179: return true;
180: }
181:
182: private void getJndiParameters() throws LoginException {
183: jndiUserProvider = (String) options.get("user.provider.url");
184: jndiGroupProvider = (String) options.get("group.provider.url");
185: if (jndiUserProvider == null) {
186: throw new LoginException(
187: "Unable to locate JNDI user provider");
188: }
189: if (jndiGroupProvider == null) {
190: throw new LoginException(
191: "Unable to locate JNDI group provider");
192: }
193: debugUtil.recordDebugInfo("[JndiLoginModule] user provider: "
194: + jndiUserProvider + "\n"
195: + "[JndiLoginModule] group provider: "
196: + jndiGroupProvider + "\n");
197: }
198:
199: //not accomplished yet
200: protected boolean mainAuthenticationProcess() throws LoginException {
201:
202: //check group provider
203: Hashtable<String, String> env = new Hashtable<String, String>();
204: env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
205: env.put(Context.PROVIDER_URL, jndiGroupProvider);
206: try {
207: DirContext context = new InitialDirContext(env);
208: context.close();
209: } catch (NamingException e) {
210: throw new LoginException(e.toString());
211: }
212: //check user
213: env = new Hashtable<String, String>();
214: env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
215: env.put(Context.PROVIDER_URL, jndiUserProvider);
216: Attribute passwordAttr;
217: Attribute uidNumberAttr;
218: Attribute gidNumberAttr;
219: String jndiUserPassword = "";
220: try {
221: DirContext context = new InitialDirContext(env);
222: SearchControls constraints = new SearchControls();
223: constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
224: Attributes attrs = new BasicAttributes("uid", userID);
225: NamingEnumeration ne = context.search("", attrs);
226: String[] attrIds = new String[] { "userPassword",
227: "uidNumber", "gidNumber" };
228: if (ne.hasMore()) {
229: NameClassPair item = (NameClassPair) ne.next();
230: Attributes userAttrs = context.getAttributes(item
231: .getName(), attrIds);
232: passwordAttr = userAttrs.get("userPassword");
233: if (passwordAttr == null) {
234: throw new LoginException("Cannot get user password");
235: }
236: jndiUserPassword = new String((byte[]) passwordAttr
237: .get());
238: if (!jndiUserPassword.equals(crypto(new String(
239: userPassword)))) {
240: return false;
241: }
242:
243: uidNumberAttr = userAttrs.get("uidNumber");
244: if (uidNumberAttr == null) {
245: throw new LoginException(
246: "Cannot get uidNumber information");
247: }
248: uidNumber = Long.valueOf((String) uidNumberAttr.get());
249:
250: gidNumberAttr = userAttrs.get("gidNumber");
251: if (gidNumberAttr == null) {
252: throw new LoginException(
253: "Cannot get gidNumber information");
254: }
255: gidNumber = Long.valueOf((String) gidNumberAttr.get());
256: }
257: } catch (NamingException e) {
258: throw new LoginException(e.toString());
259: }
260:
261: return true;
262: }
263:
264: private void getPrinclpalsFromJndi() {
265: unixPrincipal = new UnixPrincipal(userID);
266: unixNumericUserPrincipal = new UnixNumericUserPrincipal(
267: uidNumber);
268: unixNumericGroupPrincipals = new HashSet<UnixNumericGroupPrincipal>();
269: unixNumericGroupPrincipals.add(new UnixNumericGroupPrincipal(
270: gidNumber, true));
271: }
272:
273: protected void getUserIdentityFromCallbackHandler()
274: throws LoginException {
275:
276: if (callbackHandler == null) {
277: throw new LoginException("no CallbackHandler available");
278: }
279: ArrayList<Callback> callbacks = new ArrayList<Callback>();
280: NameCallback jndiNameCallback = new NameCallback("User ID");
281: callbacks.add(jndiNameCallback);
282: PasswordCallback jndiPasswordCallback = new PasswordCallback(
283: "User Password", false);
284: callbacks.add(jndiPasswordCallback);
285: try {
286: callbackHandler.handle(callbacks
287: .toArray(new Callback[callbacks.size()]));
288: } catch (Exception e) {
289: throw new LoginException(e.toString());
290: }
291: userID = jndiNameCallback.getName();
292: userPassword = jndiPasswordCallback.getPassword();
293: }
294:
295: private void clear() throws LoginException {
296: LoginModuleUtils.clearPassword(userPassword);
297: userPassword = null;
298: if (unixPrincipal != null) {
299: subject.getPrincipals().remove(unixPrincipal);
300: unixPrincipal = null;
301: }
302:
303: if (unixNumericUserPrincipal != null) {
304: subject.getPrincipals().remove(unixNumericUserPrincipal);
305: unixNumericUserPrincipal = null;
306: }
307:
308: if (unixNumericGroupPrincipals != null) {
309: for (UnixNumericGroupPrincipal ungp : unixNumericGroupPrincipals)
310: subject.getPrincipals().remove(ungp);
311: unixNumericGroupPrincipals.clear();
312: unixNumericGroupPrincipals = null;
313: }
314: status.logouted();
315: }
316:
317: private String crypto(String userPassword) {
318: //need to implement a crypto algorithm
319: return userPassword;
320: }
321:
322: protected void setUserName(String userName) {
323: this .userID = userName;
324: }
325:
326: protected void setUserPassword(char[] userPassword) {
327: this .userPassword = userPassword;
328: }
329:
330: protected String getUserName() {
331: return userID;
332: }
333:
334: protected char[] getUserPassword() {
335: return userPassword;
336: }
337:
338: protected String getModuleName() {
339: return "JndiLoginModule";
340: }
341: }
|