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.jndi.provider.ldap.sasl;
019:
020: import java.io.IOException;
021: import java.util.HashSet;
022: import java.util.Hashtable;
023: import java.util.Set;
024:
025: import javax.naming.AuthenticationNotSupportedException;
026: import javax.naming.Context;
027: import javax.naming.ldap.Control;
028: import javax.security.auth.callback.CallbackHandler;
029: import javax.security.sasl.Sasl;
030: import javax.security.sasl.SaslClient;
031: import javax.security.sasl.SaslException;
032:
033: import org.apache.harmony.jndi.provider.ldap.BindOp;
034: import org.apache.harmony.jndi.provider.ldap.LdapClient;
035: import org.apache.harmony.jndi.provider.ldap.LdapResult;
036: import org.apache.harmony.jndi.provider.ldap.parser.ParseException;
037:
038: /**
039: * A class used to perform SASL Bind Operation
040: */
041: public class SaslBind {
042:
043: // provider supported sasl mechanisms
044: public static final String DIGEST_MD5 = "DIGEST-MD5";
045:
046: public static final String CRAM_MD5 = "CRAM-MD5";
047:
048: public static final String GSSAPI = "GSSAPI";
049:
050: public static final String EXTERNAL = "EXTERNAL";
051:
052: private static Set<String> supportedSaslMechs = new HashSet<String>();
053:
054: static {
055: supportedSaslMechs.add(DIGEST_MD5);
056: supportedSaslMechs.add(CRAM_MD5);
057: supportedSaslMechs.add(GSSAPI);
058: supportedSaslMechs.add(EXTERNAL);
059: }
060:
061: public enum AuthMech {
062: None, Simple, SASL
063: };
064:
065: private AuthMech authMech;
066:
067: private String saslMech;
068:
069: // -------------------------------------------------------------------
070: // Constructor
071: // -------------------------------------------------------------------
072:
073: public SaslBind() {
074:
075: }
076:
077: // -------------------------------------------------------------------
078: // Methods
079: // -------------------------------------------------------------------
080:
081: public AuthMech getAuthMech() {
082: return this .authMech;
083: }
084:
085: /**
086: * Perform a LDAP SASL bind operation
087: *
088: * @param env
089: * @throws IOException
090: * @throws AuthenticationNotSupportedException
091: * @return int result code of bind operation, see RFC4411. -1 means
092: * authentication mechanisms in this binding operation is not SASL.
093: * @throws ParseException
094: */
095: public LdapResult doSaslBindOperation(Hashtable env,
096: LdapClient client, Control[] controls) throws IOException,
097: AuthenticationNotSupportedException {
098: // This method only deals with SASL bind. It will return
099: // immediatly if authentication mechanism is not SASL
100: externalValueAuthMech(env);
101: if (authMech == AuthMech.None || authMech == AuthMech.Simple) {
102: return null;
103: }
104:
105: // Initial server name for SaslClient
106: String host = client.getAddress();
107:
108: // Initial CallbackHandler for SaslClient
109: CallbackHandler cbh = (env
110: .get("java.naming.security.sasl.callback") != null ? (CallbackHandler) env
111: .get("java.naming.security.sasl.callback")
112: : new DefaultCallbackHandler(env));
113:
114: // Initial authrization Id for SaslClient
115: String authorizationId = "";
116: if (env.get("java.naming.security.sasl.authorizationId") != null) {
117: authorizationId = (String) env
118: .get("java.naming.security.sasl.authorizationId");
119: } else {
120: authorizationId = (String) env
121: .get(Context.SECURITY_PRINCIPAL);
122: }
123:
124: // Create SASL client to use for authentication
125: SaslClient saslClnt = Sasl.createSaslClient(
126: new String[] { saslMech }, authorizationId, "ldap",
127: host, env, cbh);
128:
129: if (saslClnt == null) {
130: throw new SaslException("SASL client not available");
131: }
132:
133: // If the specific mechanism needs initial response, get one
134: byte[] response = (saslClnt.hasInitialResponse() ? saslClnt
135: .evaluateChallenge(new byte[0]) : null);
136:
137: // do bind operation, including the initial
138: // response (if any)
139: BindOp bind = new BindOp("", "", saslMech, response);
140: client.doOperation(bind, controls);
141: LdapResult res = bind.getResult();
142:
143: // If DefaultCallbackHandler is used, DIGEST-MD5 needs realm in
144: // callbacke handler
145: if (DIGEST_MD5.equals(saslMech)
146: && cbh instanceof DefaultCallbackHandler) {
147: ((DefaultCallbackHandler) cbh)
148: .setRealm(getRealm(new String(bind
149: .getServerSaslCreds())));
150: }
151:
152: // Authentication done?
153: while (!saslClnt.isComplete()
154: && (res.getResultCode() == LdapResult.SASL_BIND_IN_PROGRESS || res
155: .getResultCode() == LdapResult.SUCCESS)) {
156:
157: // No, process challenge to get an appropriate next
158: // response
159: byte[] challenge = bind.getServerSaslCreds();
160: response = saslClnt.evaluateChallenge(challenge);
161:
162: // May be a success message with no further response
163: if (res.getResultCode() == LdapResult.SUCCESS) {
164:
165: if (response != null) {
166: // Protocol error; supposed to be done already
167: throw new SaslException("Protocol error in "
168: + "SASL session");
169: }
170: System.out.println("success");
171: break; // done
172: }
173:
174: // Wrap the response in another bind request and send
175: // it off
176: bind.setSaslCredentials(response);
177: client.doOperation(bind, controls);
178: res = bind.getResult();
179: }
180:
181: return bind.getResult();
182: }
183:
184: public AuthMech valueAuthMech(Hashtable env)
185: throws AuthenticationNotSupportedException {
186: return externalValueAuthMech(env);
187: }
188:
189: private AuthMech externalValueAuthMech(Hashtable env)
190: throws AuthenticationNotSupportedException {
191: if (env == null) {
192: // FIXME: handle exception here?
193: return null;
194: }
195:
196: if (env.get(Context.SECURITY_AUTHENTICATION) == null) {
197: if (env.get(Context.SECURITY_PRINCIPAL) == null) {
198: this .authMech = AuthMech.None;
199: } else {
200: this .authMech = AuthMech.Simple;
201: }
202: } else if (((String) env.get(Context.SECURITY_AUTHENTICATION))
203: .equalsIgnoreCase("none")) {
204: this .authMech = AuthMech.None;
205: } else if (((String) env.get(Context.SECURITY_AUTHENTICATION))
206: .equalsIgnoreCase("simple")) {
207: this .authMech = AuthMech.Simple;
208: } else if (valueSaslMech((String) env
209: .get(Context.SECURITY_AUTHENTICATION))) {
210: this .authMech = AuthMech.SASL;
211: } else {
212: throw new AuthenticationNotSupportedException((String) env
213: .get(Context.SECURITY_AUTHENTICATION));
214: }
215:
216: return this .authMech;
217: }
218:
219: /**
220: * Value if those mechanisms in the string are supported
221: *
222: * @param auth
223: * a space separated string of sasl mechanisms
224: * @return
225: */
226: private boolean valueSaslMech(String auth) {
227: boolean flag = false;
228: String[] saslMechs = auth.trim().split(" ");
229:
230: for (int i = 0; i < saslMechs.length; i++) {
231: if (saslMechs != null && saslMechs[i] != "") {
232: if (supportedSaslMechs.contains(saslMechs[i])) {
233: flag = true;
234: saslMech = saslMechs[i];
235: break;
236: }
237: }
238: }
239: return flag;
240: }
241:
242: private String getRealm(String creds) {
243: String[] credsProps = creds.split(",");
244: for (int i = 0; i < credsProps.length; i++) {
245: if (credsProps[i].startsWith("realm")) {
246: System.out.println(credsProps[i]);
247: String realm = credsProps[i].substring(7, credsProps[i]
248: .length() - 1);
249: System.out.println(realm);
250: return realm;
251: }
252: }
253: return "";
254: }
255: }
|