dropbear: update to 2015.67
[tomato.git] / release / src / router / dropbear / cli-auth.c
blob70ace658610d1322c5aa402ff7294189eb12c776
1 /*
2 * Dropbear SSH
3 *
4 * Copyright (c) 2002,2003 Matt Johnston
5 * Copyright (c) 2004 by Mihnea Stoenescu
6 * All rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE. */
26 #include "includes.h"
27 #include "session.h"
28 #include "auth.h"
29 #include "dbutil.h"
30 #include "buffer.h"
31 #include "ssh.h"
32 #include "packet.h"
33 #include "runopts.h"
35 void cli_authinitialise() {
37 memset(&ses.authstate, 0, sizeof(ses.authstate));
41 /* Send a "none" auth request to get available methods */
42 void cli_auth_getmethods() {
43 TRACE(("enter cli_auth_getmethods"))
44 CHECKCLEARTOWRITE();
45 buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
46 buf_putstring(ses.writepayload, cli_opts.username,
47 strlen(cli_opts.username));
48 buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
49 SSH_SERVICE_CONNECTION_LEN);
50 buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
52 encrypt_packet();
54 #ifdef DROPBEAR_CLI_IMMEDIATE_AUTH
55 /* We can't haven't two auth requests in-flight with delayed zlib mode
56 since if the first one succeeds then the remote side will
57 expect the second one to be compressed.
58 Race described at
59 http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/zlib-openssh.html
61 if (ses.keys->trans.algo_comp != DROPBEAR_COMP_ZLIB_DELAY) {
62 ses.authstate.authtypes = AUTH_TYPE_PUBKEY;
63 if (getenv(DROPBEAR_PASSWORD_ENV)) {
64 ses.authstate.authtypes |= AUTH_TYPE_PASSWORD | AUTH_TYPE_INTERACT;
66 if (cli_auth_try() == DROPBEAR_SUCCESS) {
67 TRACE(("skipped initial none auth query"))
68 /* Note that there will be two auth responses in-flight */
69 cli_ses.ignore_next_auth_response = 1;
72 #endif
73 TRACE(("leave cli_auth_getmethods"))
76 void recv_msg_userauth_banner() {
78 unsigned char* banner = NULL;
79 unsigned int bannerlen;
80 unsigned int i, linecount;
82 TRACE(("enter recv_msg_userauth_banner"))
83 if (ses.authstate.authdone) {
84 TRACE(("leave recv_msg_userauth_banner: banner after auth done"))
85 return;
88 banner = buf_getstring(ses.payload, &bannerlen);
89 buf_eatstring(ses.payload); /* The language string */
91 if (bannerlen > MAX_BANNER_SIZE) {
92 TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen))
93 goto out;
96 cleantext(banner);
98 /* Limit to 25 lines */
99 linecount = 1;
100 for (i = 0; i < bannerlen; i++) {
101 if (banner[i] == '\n') {
102 if (linecount >= MAX_BANNER_LINES) {
103 banner[i] = '\0';
104 break;
106 linecount++;
110 fprintf(stderr, "%s\n", banner);
112 out:
113 m_free(banner);
114 TRACE(("leave recv_msg_userauth_banner"))
117 /* This handles the message-specific types which
118 * all have a value of 60. These are
119 * SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
120 * SSH_MSG_USERAUTH_PK_OK, &
121 * SSH_MSG_USERAUTH_INFO_REQUEST. */
122 void recv_msg_userauth_specific_60() {
124 #ifdef ENABLE_CLI_PUBKEY_AUTH
125 if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
126 recv_msg_userauth_pk_ok();
127 return;
129 #endif
131 #ifdef ENABLE_CLI_INTERACT_AUTH
132 if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT) {
133 recv_msg_userauth_info_request();
134 return;
136 #endif
138 #ifdef ENABLE_CLI_PASSWORD_AUTH
139 if (cli_ses.lastauthtype == AUTH_TYPE_PASSWORD) {
140 /* Eventually there could be proper password-changing
141 * support. However currently few servers seem to
142 * implement it, and password auth is last-resort
143 * regardless - keyboard-interactive is more likely
144 * to be used anyway. */
145 dropbear_close("Your password has expired.");
147 #endif
149 dropbear_exit("Unexpected userauth packet");
152 void recv_msg_userauth_failure() {
154 unsigned char * methods = NULL;
155 unsigned char * tok = NULL;
156 unsigned int methlen = 0;
157 unsigned int partial = 0;
158 unsigned int i = 0;
160 TRACE(("<- MSG_USERAUTH_FAILURE"))
161 TRACE(("enter recv_msg_userauth_failure"))
163 if (ses.authstate.authdone) {
164 TRACE(("leave recv_msg_userauth_failure, already authdone."))
165 return;
168 if (cli_ses.state != USERAUTH_REQ_SENT) {
169 /* Perhaps we should be more fatal? */
170 dropbear_exit("Unexpected userauth failure");
173 /* When DROPBEAR_CLI_IMMEDIATE_AUTH is set there will be an initial response for
174 the "none" auth request, and then a response to the immediate auth request.
175 We need to be careful handling them. */
176 if (cli_ses.ignore_next_auth_response) {
177 cli_ses.state = USERAUTH_REQ_SENT;
178 cli_ses.ignore_next_auth_response = 0;
179 TRACE(("leave recv_msg_userauth_failure, ignored response, state set to USERAUTH_REQ_SENT"));
180 return;
181 } else {
182 #ifdef ENABLE_CLI_PUBKEY_AUTH
183 /* If it was a pubkey auth request, we should cross that key
184 * off the list. */
185 if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
186 cli_pubkeyfail();
188 #endif
190 #ifdef ENABLE_CLI_INTERACT_AUTH
191 /* If we get a failure message for keyboard interactive without
192 * receiving any request info packet, then we don't bother trying
193 * keyboard interactive again */
194 if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT
195 && !cli_ses.interact_request_received) {
196 TRACE(("setting auth_interact_failed = 1"))
197 cli_ses.auth_interact_failed = 1;
199 #endif
200 cli_ses.state = USERAUTH_FAIL_RCVD;
201 cli_ses.lastauthtype = AUTH_TYPE_NONE;
204 methods = buf_getstring(ses.payload, &methlen);
206 partial = buf_getbool(ses.payload);
208 if (partial) {
209 dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required");
210 } else {
211 ses.authstate.failcount++;
214 TRACE(("Methods (len %d): '%s'", methlen, methods))
216 ses.authstate.authdone=0;
217 ses.authstate.authtypes=0;
219 /* Split with nulls rather than commas */
220 for (i = 0; i < methlen; i++) {
221 if (methods[i] == ',') {
222 methods[i] = '\0';
226 tok = methods; /* tok stores the next method we'll compare */
227 for (i = 0; i <= methlen; i++) {
228 if (methods[i] == '\0') {
229 TRACE(("auth method '%s'", tok))
230 #ifdef ENABLE_CLI_PUBKEY_AUTH
231 if (strncmp(AUTH_METHOD_PUBKEY, tok,
232 AUTH_METHOD_PUBKEY_LEN) == 0) {
233 ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
235 #endif
236 #ifdef ENABLE_CLI_INTERACT_AUTH
237 if (strncmp(AUTH_METHOD_INTERACT, tok,
238 AUTH_METHOD_INTERACT_LEN) == 0) {
239 ses.authstate.authtypes |= AUTH_TYPE_INTERACT;
241 #endif
242 #ifdef ENABLE_CLI_PASSWORD_AUTH
243 if (strncmp(AUTH_METHOD_PASSWORD, tok,
244 AUTH_METHOD_PASSWORD_LEN) == 0) {
245 ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
247 #endif
248 tok = &methods[i+1]; /* Must make sure we don't use it after the
249 last loop, since it'll point to something
250 undefined */
254 m_free(methods);
256 TRACE(("leave recv_msg_userauth_failure"))
259 void recv_msg_userauth_success() {
260 /* This function can validly get called multiple times
261 if DROPBEAR_CLI_IMMEDIATE_AUTH is set */
263 TRACE(("received msg_userauth_success"))
264 /* Note: in delayed-zlib mode, setting authdone here
265 * will enable compression in the transport layer */
266 ses.authstate.authdone = 1;
267 cli_ses.state = USERAUTH_SUCCESS_RCVD;
268 cli_ses.lastauthtype = AUTH_TYPE_NONE;
270 #ifdef ENABLE_CLI_PUBKEY_AUTH
271 cli_auth_pubkey_cleanup();
272 #endif
275 int cli_auth_try() {
277 int finished = 0;
278 TRACE(("enter cli_auth_try"))
280 CHECKCLEARTOWRITE();
282 /* Order to try is pubkey, interactive, password.
283 * As soon as "finished" is set for one, we don't do any more. */
284 #ifdef ENABLE_CLI_PUBKEY_AUTH
285 if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
286 finished = cli_auth_pubkey();
287 cli_ses.lastauthtype = AUTH_TYPE_PUBKEY;
289 #endif
291 #ifdef ENABLE_CLI_PASSWORD_AUTH
292 if (!finished && (ses.authstate.authtypes & AUTH_TYPE_PASSWORD)) {
293 if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
294 fprintf(stderr, "Sorry, I won't let you use password auth unencrypted.\n");
295 } else {
296 cli_auth_password();
297 finished = 1;
298 cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
301 #endif
303 #ifdef ENABLE_CLI_INTERACT_AUTH
304 if (!finished && (ses.authstate.authtypes & AUTH_TYPE_INTERACT)) {
305 if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
306 fprintf(stderr, "Sorry, I won't let you use interactive auth unencrypted.\n");
307 } else {
308 if (!cli_ses.auth_interact_failed) {
309 cli_auth_interactive();
310 cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
311 finished = 1;
315 #endif
317 TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype))
319 if (finished) {
320 TRACE(("leave cli_auth_try success"))
321 return DROPBEAR_SUCCESS;
323 TRACE(("leave cli_auth_try failure"))
324 return DROPBEAR_FAILURE;
327 /* A helper for getpass() that exits if the user cancels. The returned
328 * password is statically allocated by getpass() */
329 char* getpass_or_cancel(char* prompt)
331 char* password = NULL;
333 #ifdef DROPBEAR_PASSWORD_ENV
334 /* Password provided in an environment var */
335 password = getenv(DROPBEAR_PASSWORD_ENV);
336 if (password)
338 return password;
340 #endif
342 password = getpass(prompt);
344 /* 0x03 is a ctrl-c character in the buffer. */
345 if (password == NULL || strchr(password, '\3') != NULL) {
346 dropbear_close("Interrupted.");
348 return password;