001: /*
002: * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.security.sasl;
027:
028: import javax.security.sasl.*;
029: import com.sun.security.sasl.util.PolicyUtils;
030:
031: import java.util.Map;
032: import java.io.IOException;
033: import javax.security.auth.callback.Callback;
034: import javax.security.auth.callback.CallbackHandler;
035: import javax.security.auth.callback.NameCallback;
036: import javax.security.auth.callback.PasswordCallback;
037: import javax.security.auth.callback.UnsupportedCallbackException;
038:
039: /**
040: * Client factory for EXTERNAL, CRAM-MD5, PLAIN.
041: *
042: * Requires the following callbacks to be satisfied by callback handler
043: * when using CRAM-MD5 or PLAIN.
044: * - NameCallback (to get username)
045: * - PasswordCallback (to get password)
046: *
047: * @author Rosanna Lee
048: */
049: final public class ClientFactoryImpl implements SaslClientFactory {
050: private static final String myMechs[] = { "EXTERNAL", "CRAM-MD5",
051: "PLAIN", };
052:
053: private static final int mechPolicies[] = {
054: // %%% RL: Policies should actually depend on the external channel
055: PolicyUtils.NOPLAINTEXT | PolicyUtils.NOACTIVE
056: | PolicyUtils.NODICTIONARY,
057: PolicyUtils.NOPLAINTEXT | PolicyUtils.NOANONYMOUS, // CRAM-MD5
058: PolicyUtils.NOANONYMOUS, // PLAIN
059: };
060:
061: private static final int EXTERNAL = 0;
062: private static final int CRAMMD5 = 1;
063: private static final int PLAIN = 2;
064:
065: public ClientFactoryImpl() {
066: }
067:
068: public SaslClient createSaslClient(String[] mechs,
069: String authorizationId, String protocol, String serverName,
070: Map<String, ?> props, CallbackHandler cbh)
071: throws SaslException {
072:
073: for (int i = 0; i < mechs.length; i++) {
074: if (mechs[i].equals(myMechs[EXTERNAL])
075: && PolicyUtils.checkPolicy(mechPolicies[EXTERNAL],
076: props)) {
077: return new ExternalClient(authorizationId);
078:
079: } else if (mechs[i].equals(myMechs[CRAMMD5])
080: && PolicyUtils.checkPolicy(mechPolicies[CRAMMD5],
081: props)) {
082:
083: Object[] uinfo = getUserInfo("CRAM-MD5",
084: authorizationId, cbh);
085:
086: // Callee responsible for clearing bytepw
087: return new CramMD5Client((String) uinfo[0],
088: (byte[]) uinfo[1]);
089:
090: } else if (mechs[i].equals(myMechs[PLAIN])
091: && PolicyUtils.checkPolicy(mechPolicies[PLAIN],
092: props)) {
093:
094: Object[] uinfo = getUserInfo("PLAIN", authorizationId,
095: cbh);
096:
097: // Callee responsible for clearing bytepw
098: return new PlainClient(authorizationId,
099: (String) uinfo[0], (byte[]) uinfo[1]);
100: }
101: }
102: return null;
103: };
104:
105: public String[] getMechanismNames(Map<String, ?> props) {
106: return PolicyUtils.filterMechs(myMechs, mechPolicies, props);
107: }
108:
109: /**
110: * Gets the authentication id and password. The
111: * password is converted to bytes using UTF-8 and stored in bytepw.
112: * The authentication id is stored in authId.
113: *
114: * @param prefix The non-null prefix to use for the prompt (e.g., mechanism
115: * name)
116: * @param authorizationId The possibly null authorization id. This is used
117: * as a default for the NameCallback. If null, it is not used in prompt.
118: * @param cbh The non-null callback handler to use.
119: * @return an {authid, passwd} pair
120: */
121: private Object[] getUserInfo(String prefix, String authorizationId,
122: CallbackHandler cbh) throws SaslException {
123: if (cbh == null) {
124: throw new SaslException(
125: "Callback handler to get username/password required");
126: }
127: try {
128: String userPrompt = prefix + " authentication id: ";
129: String passwdPrompt = prefix + " password: ";
130:
131: NameCallback ncb = authorizationId == null ? new NameCallback(
132: userPrompt)
133: : new NameCallback(userPrompt, authorizationId);
134:
135: PasswordCallback pcb = new PasswordCallback(passwdPrompt,
136: false);
137:
138: cbh.handle(new Callback[] { ncb, pcb });
139:
140: char[] pw = pcb.getPassword();
141:
142: byte[] bytepw;
143: String authId;
144:
145: if (pw != null) {
146: bytepw = new String(pw).getBytes("UTF8");
147: pcb.clearPassword();
148: } else {
149: bytepw = null;
150: }
151:
152: authId = ncb.getName();
153:
154: return new Object[] { authId, bytepw };
155:
156: } catch (IOException e) {
157: throw new SaslException("Cannot get password", e);
158: } catch (UnsupportedCallbackException e) {
159: throw new SaslException("Cannot get userid/password", e);
160: }
161: }
162: }
|