001: /*
002: * Copyright 2000-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:
030: /**
031: * Implements the PLAIN SASL client mechanism.
032: * (<A
033: * HREF="http://ftp.isi.edu/in-notes/rfc2595.txt">RFC 2595</A>)
034: *
035: * @author Rosanna Lee
036: */
037: final class PlainClient implements SaslClient {
038: private boolean completed = false;
039: private byte[] pw;
040: private String authorizationID;
041: private String authenticationID;
042: private static byte SEP = 0; // US-ASCII <NUL>
043:
044: /**
045: * Creates a SASL mechanism with client credentials that it needs
046: * to participate in Plain authentication exchange with the server.
047: *
048: * @param authorizationID A possibly null string representing the principal
049: * for which authorization is being granted; if null, same as
050: * authenticationID
051: * @param authenticationID A non-null string representing the principal
052: * being authenticated. pw is associated with with this principal.
053: * @param pw A non-null byte[] containing the password.
054: */
055: PlainClient(String authorizationID, String authenticationID,
056: byte[] pw) throws SaslException {
057: if (authenticationID == null || pw == null) {
058: throw new SaslException(
059: "PLAIN: authorization ID and password must be specified");
060: }
061:
062: this .authorizationID = authorizationID;
063: this .authenticationID = authenticationID;
064: this .pw = pw; // caller should have already cloned
065: }
066:
067: /**
068: * Retrieves this mechanism's name for to initiate the PLAIN protocol
069: * exchange.
070: *
071: * @return The string "PLAIN".
072: */
073: public String getMechanismName() {
074: return "PLAIN";
075: }
076:
077: public boolean hasInitialResponse() {
078: return true;
079: }
080:
081: public void dispose() throws SaslException {
082: clearPassword();
083: }
084:
085: /**
086: * Retrieves the initial response for the SASL command, which for
087: * PLAIN is the concatenation of authorization ID, authentication ID
088: * and password, with each component separated by the US-ASCII <NUL> byte.
089: *
090: * @param challengeData Ignored
091: * @return A non-null byte array containing the response to be sent to the server.
092: * @throws SaslException If cannot encode ids in UTF-8
093: * @throw IllegalStateException if authentication already completed
094: */
095: public byte[] evaluateChallenge(byte[] challengeData)
096: throws SaslException {
097: if (completed) {
098: throw new IllegalStateException(
099: "PLAIN authentication already completed");
100: }
101: completed = true;
102:
103: try {
104: byte[] authz = (authorizationID != null) ? authorizationID
105: .getBytes("UTF8") : null;
106: byte[] auth = authenticationID.getBytes("UTF8");
107:
108: byte[] answer = new byte[pw.length + auth.length + 2
109: + (authz == null ? 0 : authz.length)];
110:
111: int pos = 0;
112: if (authz != null) {
113: System.arraycopy(authz, 0, answer, 0, authz.length);
114: pos = authz.length;
115: }
116: answer[pos++] = SEP;
117: System.arraycopy(auth, 0, answer, pos, auth.length);
118:
119: pos += auth.length;
120: answer[pos++] = SEP;
121:
122: System.arraycopy(pw, 0, answer, pos, pw.length);
123:
124: clearPassword();
125: return answer;
126: } catch (java.io.UnsupportedEncodingException e) {
127: throw new SaslException("Cannot get UTF-8 encoding of ids",
128: e);
129: }
130: }
131:
132: /**
133: * Determines whether this mechanism has completed.
134: * Plain completes after returning one response.
135: *
136: * @return true if has completed; false otherwise;
137: */
138: public boolean isComplete() {
139: return completed;
140: }
141:
142: /**
143: * Unwraps the incoming buffer.
144: *
145: * @throws SaslException Not applicable to this mechanism.
146: */
147: public byte[] unwrap(byte[] incoming, int offset, int len)
148: throws SaslException {
149: if (completed) {
150: throw new SaslException(
151: "PLAIN supports neither integrity nor privacy");
152: } else {
153: throw new IllegalStateException(
154: "PLAIN authentication not completed");
155: }
156: }
157:
158: /**
159: * Wraps the outgoing buffer.
160: *
161: * @throws SaslException Not applicable to this mechanism.
162: */
163: public byte[] wrap(byte[] outgoing, int offset, int len)
164: throws SaslException {
165: if (completed) {
166: throw new SaslException(
167: "PLAIN supports neither integrity nor privacy");
168: } else {
169: throw new IllegalStateException(
170: "PLAIN authentication not completed");
171: }
172: }
173:
174: /**
175: * Retrieves the negotiated property.
176: * This method can be called only after the authentication exchange has
177: * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, a
178: * <tt>SaslException</tt> is thrown.
179: *
180: * @return value of property; only QOP is applicable to PLAIN.
181: * @exception IllegalStateException if this authentication exchange
182: * has not completed
183: */
184: public Object getNegotiatedProperty(String propName) {
185: if (completed) {
186: if (propName.equals(Sasl.QOP)) {
187: return "auth";
188: } else {
189: return null;
190: }
191: } else {
192: throw new IllegalStateException(
193: "PLAIN authentication not completed");
194: }
195: }
196:
197: private void clearPassword() {
198: if (pw != null) {
199: // zero out password
200: for (int i = 0; i < pw.length; i++) {
201: pw[i] = (byte) 0;
202: }
203: pw = null;
204: }
205: }
206:
207: protected void finalize() {
208: clearPassword();
209: }
210: }
|