1 /*
2 * SSHTools - Java SSH2 API
3 *
4 * Copyright (C) 2002-2003 Lee David Painter and Contributors.
5 *
6 * Contributions made by:
7 *
8 * Brett Smith
9 * Richard Pernavas
10 * Erwin Bolwidt
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 */
26 package com.sshtools.j2ssh.authentication;
27
28 import com.sshtools.j2ssh.SshException;
29 import com.sshtools.j2ssh.transport.MessageNotAvailableException;
30 import com.sshtools.j2ssh.transport.MessageStoreEOFException;
31 import com.sshtools.j2ssh.transport.Service;
32 import com.sshtools.j2ssh.transport.SshMessage;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36
37 import java.io.IOException;
38
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Vector;
42
43
44 /**
45 *
46 *
47 * @author $author$
48 * @version $Revision: 1.27 $
49 */
50 public class AuthenticationProtocolClient extends Service {
51 private static Log log = LogFactory.getLog(AuthenticationProtocolClient.class);
52 private int[] resultFilter = new int[2];
53 private int[] singleIdFilter = new int[3];
54 private Vector listeners = new Vector();
55
56 /**
57 * Creates a new AuthenticationProtocolClient object.
58 */
59 public AuthenticationProtocolClient() {
60 super("ssh-userauth");
61 resultFilter[0] = SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS;
62 resultFilter[1] = SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE;
63 singleIdFilter[0] = SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS;
64 singleIdFilter[1] = SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE;
65 }
66
67 /**
68 *
69 *
70 * @throws java.io.IOException
71 */
72 protected void onServiceAccept() throws java.io.IOException {
73 }
74
75 /**
76 *
77 */
78 protected void onStart() {
79 }
80
81 /**
82 *
83 *
84 * @param startMode
85 *
86 * @throws java.io.IOException
87 * @throws IOException
88 */
89 protected void onServiceInit(int startMode) throws java.io.IOException {
90 if (startMode == Service.ACCEPTING_SERVICE) {
91 throw new IOException(
92 "The Authentication Protocol client cannot be accepted");
93 }
94
95 messageStore.registerMessage(SshMsgUserAuthFailure.SSH_MSG_USERAUTH_FAILURE,
96 SshMsgUserAuthFailure.class);
97 messageStore.registerMessage(SshMsgUserAuthSuccess.SSH_MSG_USERAUTH_SUCCESS,
98 SshMsgUserAuthSuccess.class);
99 messageStore.registerMessage(SshMsgUserAuthBanner.SSH_MSG_USERAUTH_BANNER,
100 SshMsgUserAuthBanner.class);
101
102 //messageStore.registerMessage(SshMsgUserAuthPwdChangeReq.SSH_MSG_USERAUTH_PWD_CHANGEREQ,
103 // SshMsgUserAuthPwdChangeReq.class);
104 }
105
106 /**
107 *
108 *
109 * @throws java.io.IOException
110 * @throws IOException
111 */
112 protected void onServiceRequest() throws java.io.IOException {
113 throw new IOException("This class implements the client protocol only!");
114 }
115
116 /**
117 *
118 *
119 * @param listener
120 */
121 public void addEventListener(AuthenticationProtocolListener listener) {
122 if (listener != null) {
123 listeners.add(listener);
124 }
125 }
126
127 /**
128 *
129 *
130 * @param username
131 * @param serviceName
132 *
133 * @return
134 *
135 * @throws IOException
136 * @throws SshException
137 */
138 public List getAvailableAuths(String username, String serviceName)
139 throws IOException {
140 log.info("Requesting authentication methods");
141
142 SshMessage msg = new SshMsgUserAuthRequest(username, serviceName,
143 "none", null);
144 transport.sendMessage(msg, this);
145
146 try {
147 msg = messageStore.getMessage(resultFilter);
148 } catch (InterruptedException ex) {
149 throw new SshException(
150 "The thread was interrupted whilst waiting for an authentication message");
151 }
152
153 if (msg instanceof SshMsgUserAuthFailure) {
154 return ((SshMsgUserAuthFailure) msg).getAvailableAuthentications();
155 } else {
156 throw new IOException(
157 "None request returned success! Insecure feature not supported");
158 }
159 }
160
161 /**
162 *
163 *
164 * @param auth
165 * @param serviceToStart
166 *
167 * @return
168 *
169 * @throws IOException
170 * @throws SshException
171 */
172 public int authenticate(SshAuthenticationClient auth, Service serviceToStart)
173 throws IOException {
174 try {
175 if (!auth.canAuthenticate() && auth.canPrompt()) {
176 SshAuthenticationPrompt prompt = auth.getAuthenticationPrompt();
177
178 if (!prompt.showPrompt(auth)) {
179 return AuthenticationProtocolState.CANCELLED;
180 }
181 }
182
183 auth.authenticate(this, serviceToStart.getServiceName());
184
185 SshMessage msg = parseMessage(messageStore.getMessage(resultFilter));
186
187 // We should not get this far
188 throw new AuthenticationProtocolException(
189 "Unexpected authentication message " + msg.getMessageName());
190 } catch (TerminatedStateException tse) {
191 if (tse.getState() == AuthenticationProtocolState.COMPLETE) {
192 serviceToStart.init(Service.ACCEPTING_SERVICE, transport); //, nativeSettings);
193 serviceToStart.start();
194
195 for (Iterator it = listeners.iterator(); it.hasNext();) {
196 AuthenticationProtocolListener listener = (AuthenticationProtocolListener) it.next();
197
198 if (listener != null) {
199 listener.onAuthenticationComplete();
200 }
201 }
202 }
203
204 return tse.getState();
205 } catch (InterruptedException ex) {
206 throw new SshException(
207 "The thread was interrupted whilst waiting for an authentication message");
208 }
209 }
210
211 /**
212 *
213 *
214 * @param msg
215 *
216 * @throws IOException
217 */
218 public void sendMessage(SshMessage msg) throws IOException {
219 transport.sendMessage(msg, this);
220 }
221
222 /**
223 *
224 *
225 * @return
226 */
227 public byte[] getSessionIdentifier() {
228 return transport.getSessionIdentifier();
229 }
230
231 /**
232 *
233 *
234 * @param cls
235 * @param messageId
236 */
237 public void registerMessage(Class cls, int messageId) {
238 messageStore.registerMessage(messageId, cls);
239 }
240
241 /**
242 *
243 *
244 * @param messageId
245 *
246 * @return
247 *
248 * @throws TerminatedStateException
249 * @throws IOException
250 */
251 public SshMessage readMessage(int messageId)
252 throws TerminatedStateException, IOException {
253 singleIdFilter[2] = messageId;
254
255 return internalReadMessage(singleIdFilter);
256 }
257
258 private SshMessage internalReadMessage(int[] messageIdFilter)
259 throws TerminatedStateException, IOException {
260 try {
261 SshMessage msg = messageStore.getMessage(messageIdFilter);
262
263 return parseMessage(msg);
264 } catch (MessageStoreEOFException meof) {
265 throw new AuthenticationProtocolException("Failed to read messages");
266 } catch (InterruptedException ex) {
267 throw new SshException(
268 "The thread was interrupted whilst waiting for an authentication message");
269 }
270 }
271
272 /**
273 *
274 *
275 * @param messageId
276 *
277 * @return
278 *
279 * @throws TerminatedStateException
280 * @throws IOException
281 */
282 public SshMessage readMessage(int[] messageId)
283 throws TerminatedStateException, IOException {
284 int[] messageIdFilter = new int[messageId.length + resultFilter.length];
285 System.arraycopy(resultFilter, 0, messageIdFilter, 0,
286 resultFilter.length);
287 System.arraycopy(messageId, 0, messageIdFilter, resultFilter.length,
288 messageId.length);
289
290 return internalReadMessage(messageIdFilter);
291 }
292
293 /**
294 *
295 *
296 * @throws IOException
297 * @throws TerminatedStateException
298 */
299 public void readAuthenticationState()
300 throws IOException, TerminatedStateException {
301 internalReadMessage(resultFilter);
302 }
303
304 private SshMessage parseMessage(SshMessage msg)
305 throws TerminatedStateException {
306 if (msg instanceof SshMsgUserAuthFailure) {
307 if (((SshMsgUserAuthFailure) msg).getPartialSuccess()) {
308 throw new TerminatedStateException(AuthenticationProtocolState.PARTIAL);
309 } else {
310 throw new TerminatedStateException(AuthenticationProtocolState.FAILED);
311 }
312 } else if (msg instanceof SshMsgUserAuthSuccess) {
313 throw new TerminatedStateException(AuthenticationProtocolState.COMPLETE);
314 } else {
315 return msg;
316 }
317 }
318
319 /**
320 *
321 *
322 * @param timeout
323 *
324 * @return
325 *
326 * @throws IOException
327 * @throws SshException
328 */
329 public String getBannerMessage(int timeout) throws IOException {
330 try {
331 log.debug(
332 "getBannerMessage is attempting to read the authentication banner");
333
334 SshMessage msg = messageStore.peekMessage(SshMsgUserAuthBanner.SSH_MSG_USERAUTH_BANNER,
335 timeout);
336
337 return ((SshMsgUserAuthBanner) msg).getBanner();
338 } catch (MessageNotAvailableException e) {
339 return "";
340 } catch (MessageStoreEOFException eof) {
341 log.error(
342 "Failed to retreive banner becasue the message store is EOF");
343
344 return "";
345 } catch (InterruptedException ex) {
346 throw new SshException(
347 "The thread was interrupted whilst waiting for an authentication message");
348 }
349 }
350 }