Subversion Repositories Programming Utils

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
86 rm5248 1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements.  See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership.  The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License.  You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied.  See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 */
19
package org.apache.sshd.client.session;
20
 
21
import java.io.IOException;
22
import java.util.ArrayList;
23
import java.util.Arrays;
24
import java.util.List;
25
 
26
import org.apache.sshd.client.ClientFactoryManager;
27
import org.apache.sshd.client.UserAuth;
28
import org.apache.sshd.client.UserInteraction;
29
import org.apache.sshd.client.auth.UserAuthKeyboardInteractive;
30
import org.apache.sshd.client.auth.UserAuthPassword;
31
import org.apache.sshd.client.auth.UserAuthPublicKey;
32
import org.apache.sshd.client.future.AuthFuture;
33
import org.apache.sshd.client.future.DefaultAuthFuture;
34
import org.apache.sshd.common.NamedFactory;
35
import org.apache.sshd.common.Service;
36
import org.apache.sshd.common.Session;
37
import org.apache.sshd.common.SshConstants;
38
import org.apache.sshd.common.SshException;
39
import org.apache.sshd.common.future.CloseFuture;
40
import org.apache.sshd.common.future.DefaultCloseFuture;
41
import org.apache.sshd.common.util.Buffer;
42
import org.apache.sshd.common.util.CloseableUtils;
43
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
45
 
46
import static org.apache.sshd.common.util.KeyUtils.getKeyType;
47
 
48
/**
49
 * Client side <code>ssh-auth</code> service.
50
 *
51
 * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
52
 */
53
public class ClientUserAuthServiceNew extends CloseableUtils.AbstractCloseable implements Service {
54
 
55
    /**
56
     * The AuthFuture that is being used by the current auth request.  This encodes the state.
57
     * isSuccess -> authenticated, else if isDone -> server waiting for user auth, else authenticating.
58
     */
59
    private final AuthFuture authFuture;
60
 
61
    protected final ClientSessionImpl session;
62
 
63
    private List<Object> identities;
64
    private String service;
65
 
66
    List<NamedFactory<UserAuth>> authFactories;
67
    List<String> clientMethods;
68
    List<String> serverMethods;
69
    UserAuth userAuth;
70
 
71
    public ClientUserAuthServiceNew(Session s) {
72
        if (!(s instanceof ClientSessionImpl)) {
73
            throw new IllegalStateException("Client side service used on server side");
74
        }
75
        session = (ClientSessionImpl) s;
76
        authFuture = new DefaultAuthFuture(session.getLock());
77
        authFactories = session.getFactoryManager().getUserAuthFactories();
78
        clientMethods = new ArrayList<String>();
79
        String prefs = session.getFactoryManager().getProperties().get(ClientFactoryManager.PREFERRED_AUTHS);
80
        if (prefs != null) {
81
            for (String pref : prefs.split(",")) {
82
                if (NamedFactory.Utils.get(authFactories, pref) != null) {
83
                    clientMethods.add(pref);
84
                }
85
            }
86
        } else {
87
            for (NamedFactory<UserAuth> factory : authFactories) {
88
                clientMethods.add(factory.getName());
89
            }
90
        }
91
    }
92
 
93
    public ClientSessionImpl getSession() {
94
        return session;
95
    }
96
 
97
    public void start() {
98
    }
99
 
100
    public AuthFuture auth(List<Object> identities, String service) throws IOException {
101
        log.debug("Start authentication");
102
        this.identities = new ArrayList<Object>(identities);
103
        this.service = service;
104
 
105
        log.debug("Send SSH_MSG_USERAUTH_REQUEST for none");
106
        Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
107
        buffer.putString(session.getUsername());
108
        buffer.putString(service);
109
        buffer.putString("none");
110
        session.writePacket(buffer);
111
 
112
        return authFuture;
113
    }
114
 
115
    public void process(byte cmd, Buffer buffer) throws Exception {
116
        if (this.authFuture.isSuccess()) {
117
            throw new IllegalStateException("UserAuth message delivered to authenticated client");
118
        } else if (this.authFuture.isDone()) {
119
            log.debug("Ignoring random message");
120
            // ignore for now; TODO: random packets
121
        } else if (cmd == SshConstants.SSH_MSG_USERAUTH_BANNER) {
122
            String welcome = buffer.getString();
123
            String lang = buffer.getString();
124
            log.debug("Welcome banner: {}", welcome);
125
            UserInteraction ui = session.getFactoryManager().getUserInteraction();
126
            if (ui != null) {
127
                ui.welcome(welcome);
128
            }
129
        } else {
130
            buffer.rpos(buffer.rpos() - 1);
131
            processUserAuth(buffer);
132
        }
133
    }
134
 
135
    int currentMethod;
136
 
137
    /**
138
     * execute one step in user authentication.
139
     * @param buffer
140
     * @throws java.io.IOException
141
     */
142
    private void processUserAuth(Buffer buffer) throws Exception {
143
        byte cmd = buffer.getByte();
144
        if (cmd == SshConstants.SSH_MSG_USERAUTH_SUCCESS) {
145
            log.info("Received SSH_MSG_USERAUTH_SUCCESS");
146
            log.debug("Succeeded with {}", userAuth);
147
            if (userAuth != null) {
148
                userAuth.destroy();
149
                userAuth = null;
150
            }
151
            session.setAuthenticated();
152
            session.switchToNextService();
153
            // Will wake up anyone sitting in waitFor
154
            authFuture.setAuthed(true);
155
            return;
156
        }
157
        if (cmd == SshConstants.SSH_MSG_USERAUTH_FAILURE) {
158
            log.info("Received SSH_MSG_USERAUTH_FAILURE");
159
            String mths = buffer.getString();
160
            boolean partial = buffer.getBoolean();
161
            if (partial || serverMethods == null) {
162
                serverMethods = Arrays.asList(mths.split(","));
163
                if (log.isDebugEnabled()) {
164
                    StringBuilder sb = new StringBuilder("Authentications that can continue: ");
165
                    for (int i = 0; i < serverMethods.size(); i++) {
166
                        if (i > 0) {
167
                            sb.append(", ");
168
                        }
169
                        sb.append(serverMethods.get(i));
170
                    }
171
                    log.debug(sb.toString());
172
                }
173
                if (userAuth != null) {
174
                    userAuth.destroy();
175
                    userAuth = null;
176
                }
177
            }
178
            tryNext();
179
            return;
180
        }
181
        if (userAuth == null) {
182
            throw new IllegalStateException("Received unknown packet");
183
        }
184
        buffer.rpos(buffer.rpos() - 1);
185
        if (!userAuth.process(buffer)) {
186
            tryNext();
187
        }
188
    }
189
 
190
    private void tryNext() throws Exception {
191
        // Loop until we find something to try
192
        while (true) {
193
            if (userAuth == null) {
194
                currentMethod = 0;
195
            } else if (!userAuth.process(null)) {
196
                userAuth.destroy();
197
                currentMethod++;
198
            } else {
199
                return;
200
            }
201
            while (currentMethod < clientMethods.size() && !serverMethods.contains(clientMethods.get(currentMethod))) {
202
                currentMethod++;
203
            }
204
            if (currentMethod >= clientMethods.size()) {
205
                // Failure
206
                authFuture.setAuthed(false);
207
                return;
208
            }
209
            String method = clientMethods.get(currentMethod);
210
            userAuth = NamedFactory.Utils.create(authFactories, method);
211
            assert userAuth != null;
212
            userAuth.init(session, service, identities);
213
        }
214
    }
215
 
216
    @Override
217
    protected void preClose() {
218
        super.preClose();
219
        if (!authFuture.isDone()) {
220
            authFuture.setException(new SshException("Session is closed"));
221
        }
222
    }
223
 
224
}