blob: 75aaf102b135be5447babe0ba30060bdf9ab4f7f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package com.sun.security.sasl;
27
28import javax.security.sasl.*;
29
30/**
31 * Implements the PLAIN SASL client mechanism.
32 * (<A
33 * HREF="http://ftp.isi.edu/in-notes/rfc2595.txt">RFC 2595</A>)
34 *
35 * @author Rosanna Lee
36 */
37final class PlainClient implements SaslClient {
38 private boolean completed = false;
39 private byte[] pw;
40 private String authorizationID;
41 private String authenticationID;
42 private static byte SEP = 0; // US-ASCII <NUL>
43
44 /**
45 * Creates a SASL mechanism with client credentials that it needs
46 * to participate in Plain authentication exchange with the server.
47 *
48 * @param authorizationID A possibly null string representing the principal
49 * for which authorization is being granted; if null, same as
50 * authenticationID
51 * @param authenticationID A non-null string representing the principal
52 * being authenticated. pw is associated with with this principal.
53 * @param pw A non-null byte[] containing the password.
54 */
55 PlainClient(String authorizationID, String authenticationID, byte[] pw)
56 throws SaslException {
57 if (authenticationID == null || pw == null) {
58 throw new SaslException(
59 "PLAIN: authorization ID and password must be specified");
60 }
61
62 this.authorizationID = authorizationID;
63 this.authenticationID = authenticationID;
64 this.pw = pw; // caller should have already cloned
65 }
66
67 /**
68 * Retrieves this mechanism's name for to initiate the PLAIN protocol
69 * exchange.
70 *
71 * @return The string "PLAIN".
72 */
73 public String getMechanismName() {
74 return "PLAIN";
75 }
76
77 public boolean hasInitialResponse() {
78 return true;
79 }
80
81 public void dispose() throws SaslException {
82 clearPassword();
83 }
84
85 /**
86 * Retrieves the initial response for the SASL command, which for
87 * PLAIN is the concatenation of authorization ID, authentication ID
88 * and password, with each component separated by the US-ASCII <NUL> byte.
89 *
90 * @param challengeData Ignored
91 * @return A non-null byte array containing the response to be sent to the server.
92 * @throws SaslException If cannot encode ids in UTF-8
93 * @throw IllegalStateException if authentication already completed
94 */
95 public byte[] evaluateChallenge(byte[] challengeData) throws SaslException {
96 if (completed) {
97 throw new IllegalStateException(
98 "PLAIN authentication already completed");
99 }
100 completed = true;
101
102 try {
103 byte[] authz = (authorizationID != null)?
104 authorizationID.getBytes("UTF8") :
105 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", e);
128 }
129 }
130
131 /**
132 * Determines whether this mechanism has completed.
133 * Plain completes after returning one response.
134 *
135 * @return true if has completed; false otherwise;
136 */
137 public boolean isComplete() {
138 return completed;
139 }
140
141 /**
142 * Unwraps the incoming buffer.
143 *
144 * @throws SaslException Not applicable to this mechanism.
145 */
146 public byte[] unwrap(byte[] incoming, int offset, int len)
147 throws SaslException {
148 if (completed) {
149 throw new SaslException(
150 "PLAIN supports neither integrity nor privacy");
151 } else {
152 throw new IllegalStateException("PLAIN authentication not completed");
153 }
154 }
155
156 /**
157 * Wraps the outgoing buffer.
158 *
159 * @throws SaslException Not applicable to this mechanism.
160 */
161 public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException {
162 if (completed) {
163 throw new SaslException(
164 "PLAIN supports neither integrity nor privacy");
165 } else {
166 throw new IllegalStateException("PLAIN authentication not completed");
167 }
168 }
169
170 /**
171 * Retrieves the negotiated property.
172 * This method can be called only after the authentication exchange has
173 * completed (i.e., when <tt>isComplete()</tt> returns true); otherwise, a
174 * <tt>SaslException</tt> is thrown.
175 *
176 * @return value of property; only QOP is applicable to PLAIN.
177 * @exception IllegalStateException if this authentication exchange
178 * has not completed
179 */
180 public Object getNegotiatedProperty(String propName) {
181 if (completed) {
182 if (propName.equals(Sasl.QOP)) {
183 return "auth";
184 } else {
185 return null;
186 }
187 } else {
188 throw new IllegalStateException("PLAIN authentication not completed");
189 }
190 }
191
192 private void clearPassword() {
193 if (pw != null) {
194 // zero out password
195 for (int i = 0; i < pw.length; i++) {
196 pw[i] = (byte)0;
197 }
198 pw = null;
199 }
200 }
201
202 protected void finalize() {
203 clearPassword();
204 }
205}