1.2.1
by Gerrit Pape
Import upstream version 0.45 |
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:
|
|
14 |
*
|
|
15 |
* The above copyright notice and this permission notice shall be included in
|
|
16 |
* all copies or substantial portions of the Software.
|
|
17 |
*
|
|
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. */
|
|
25 |
||
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" |
|
34 |
||
35 |
void cli_authinitialise() { |
|
36 |
||
37 |
memset(&ses.authstate, 0, sizeof(ses.authstate)); |
|
38 |
}
|
|
39 |
||
40 |
||
41 |
/* Send a "none" auth request to get available methods */
|
|
42 |
void cli_auth_getmethods() { |
|
43 |
||
44 |
TRACE(("enter cli_auth_getmethods")) |
|
45 |
||
46 |
CHECKCLEARTOWRITE(); |
|
47 |
||
48 |
buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); |
|
49 |
buf_putstring(ses.writepayload, cli_opts.username, |
|
50 |
strlen(cli_opts.username)); |
|
51 |
buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, |
|
52 |
SSH_SERVICE_CONNECTION_LEN); |
|
53 |
buf_putstring(ses.writepayload, "none", 4); /* 'none' method */ |
|
54 |
||
55 |
encrypt_packet(); |
|
56 |
TRACE(("leave cli_auth_getmethods")) |
|
57 |
||
58 |
}
|
|
59 |
||
60 |
void recv_msg_userauth_banner() { |
|
61 |
||
62 |
unsigned char* banner = NULL; |
|
63 |
unsigned int bannerlen; |
|
64 |
unsigned int i, linecount; |
|
65 |
||
66 |
TRACE(("enter recv_msg_userauth_banner")) |
|
67 |
if (ses.authstate.authdone) { |
|
68 |
TRACE(("leave recv_msg_userauth_banner: banner after auth done")) |
|
69 |
return; |
|
70 |
}
|
|
71 |
||
72 |
banner = buf_getstring(ses.payload, &bannerlen); |
|
73 |
buf_eatstring(ses.payload); /* The language string */ |
|
74 |
||
75 |
if (bannerlen > MAX_BANNER_SIZE) { |
|
76 |
TRACE(("recv_msg_userauth_banner: bannerlen too long: %d", bannerlen)) |
|
77 |
goto out; |
|
78 |
}
|
|
79 |
||
80 |
cleantext(banner); |
|
81 |
||
82 |
/* Limit to 25 lines */
|
|
83 |
linecount = 1; |
|
84 |
for (i = 0; i < bannerlen; i++) { |
|
85 |
if (banner[i] == '\n') { |
|
86 |
if (linecount >= MAX_BANNER_LINES) { |
|
87 |
banner[i] = '\0'; |
|
88 |
break; |
|
89 |
}
|
|
90 |
linecount++; |
|
91 |
}
|
|
92 |
}
|
|
93 |
||
1.4.3
by Gerrit Pape
Import upstream version 0.53.1 |
94 |
fprintf(stderr, "%s\n", banner); |
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
95 |
|
96 |
out: |
|
97 |
m_free(banner); |
|
98 |
TRACE(("leave recv_msg_userauth_banner")) |
|
99 |
}
|
|
100 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
101 |
/* This handles the message-specific types which
|
102 |
* all have a value of 60. These are
|
|
103 |
* SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
|
|
104 |
* SSH_MSG_USERAUTH_PK_OK, &
|
|
105 |
* SSH_MSG_USERAUTH_INFO_REQUEST. */
|
|
106 |
void recv_msg_userauth_specific_60() { |
|
107 |
||
108 |
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
|
109 |
if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { |
|
110 |
recv_msg_userauth_pk_ok(); |
|
111 |
return; |
|
112 |
}
|
|
113 |
#endif
|
|
114 |
||
115 |
#ifdef ENABLE_CLI_INTERACT_AUTH
|
|
116 |
if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT) { |
|
117 |
recv_msg_userauth_info_request(); |
|
118 |
return; |
|
119 |
}
|
|
120 |
#endif
|
|
121 |
||
122 |
#ifdef ENABLE_CLI_PASSWORD_AUTH
|
|
123 |
if (cli_ses.lastauthtype == AUTH_TYPE_PASSWORD) { |
|
124 |
/* Eventually there could be proper password-changing
|
|
125 |
* support. However currently few servers seem to
|
|
126 |
* implement it, and password auth is last-resort
|
|
127 |
* regardless - keyboard-interactive is more likely
|
|
128 |
* to be used anyway. */
|
|
129 |
dropbear_close("Your password has expired."); |
|
130 |
}
|
|
131 |
#endif
|
|
132 |
||
133 |
dropbear_exit("Unexpected userauth packet"); |
|
134 |
}
|
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
135 |
|
136 |
void recv_msg_userauth_failure() { |
|
137 |
||
138 |
unsigned char * methods = NULL; |
|
139 |
unsigned char * tok = NULL; |
|
140 |
unsigned int methlen = 0; |
|
141 |
unsigned int partial = 0; |
|
142 |
unsigned int i = 0; |
|
143 |
||
144 |
TRACE(("<- MSG_USERAUTH_FAILURE")) |
|
145 |
TRACE(("enter recv_msg_userauth_failure")) |
|
146 |
||
147 |
if (cli_ses.state != USERAUTH_REQ_SENT) { |
|
148 |
/* Perhaps we should be more fatal? */
|
|
1.2.2
by Matt Johnston
Import upstream version 0.47 |
149 |
dropbear_exit("Unexpected userauth failure"); |
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
150 |
}
|
151 |
||
152 |
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
|
153 |
/* If it was a pubkey auth request, we should cross that key
|
|
154 |
* off the list. */
|
|
155 |
if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) { |
|
156 |
cli_pubkeyfail(); |
|
157 |
}
|
|
158 |
#endif
|
|
159 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
160 |
#ifdef ENABLE_CLI_INTERACT_AUTH
|
161 |
/* If we get a failure message for keyboard interactive without
|
|
162 |
* receiving any request info packet, then we don't bother trying
|
|
163 |
* keyboard interactive again */
|
|
164 |
if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT |
|
165 |
&& !cli_ses.interact_request_received) { |
|
166 |
TRACE(("setting auth_interact_failed = 1")) |
|
167 |
cli_ses.auth_interact_failed = 1; |
|
168 |
}
|
|
169 |
#endif
|
|
170 |
||
171 |
cli_ses.lastauthtype = AUTH_TYPE_NONE; |
|
172 |
||
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
173 |
methods = buf_getstring(ses.payload, &methlen); |
174 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
175 |
partial = buf_getbool(ses.payload); |
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
176 |
|
177 |
if (partial) { |
|
178 |
dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required"); |
|
179 |
} else { |
|
180 |
ses.authstate.failcount++; |
|
181 |
}
|
|
182 |
||
183 |
TRACE(("Methods (len %d): '%s'", methlen, methods)) |
|
184 |
||
185 |
ses.authstate.authdone=0; |
|
186 |
ses.authstate.authtypes=0; |
|
187 |
||
188 |
/* Split with nulls rather than commas */
|
|
189 |
for (i = 0; i < methlen; i++) { |
|
190 |
if (methods[i] == ',') { |
|
191 |
methods[i] = '\0'; |
|
192 |
}
|
|
193 |
}
|
|
194 |
||
195 |
tok = methods; /* tok stores the next method we'll compare */ |
|
196 |
for (i = 0; i <= methlen; i++) { |
|
197 |
if (methods[i] == '\0') { |
|
198 |
TRACE(("auth method '%s'", tok)) |
|
199 |
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
|
200 |
if (strncmp(AUTH_METHOD_PUBKEY, tok, |
|
201 |
AUTH_METHOD_PUBKEY_LEN) == 0) { |
|
202 |
ses.authstate.authtypes |= AUTH_TYPE_PUBKEY; |
|
203 |
}
|
|
204 |
#endif
|
|
1.2.2
by Matt Johnston
Import upstream version 0.47 |
205 |
#ifdef ENABLE_CLI_INTERACT_AUTH
|
206 |
if (strncmp(AUTH_METHOD_INTERACT, tok, |
|
207 |
AUTH_METHOD_INTERACT_LEN) == 0) { |
|
208 |
ses.authstate.authtypes |= AUTH_TYPE_INTERACT; |
|
209 |
}
|
|
210 |
#endif
|
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
211 |
#ifdef ENABLE_CLI_PASSWORD_AUTH
|
212 |
if (strncmp(AUTH_METHOD_PASSWORD, tok, |
|
213 |
AUTH_METHOD_PASSWORD_LEN) == 0) { |
|
214 |
ses.authstate.authtypes |= AUTH_TYPE_PASSWORD; |
|
215 |
}
|
|
216 |
#endif
|
|
217 |
tok = &methods[i+1]; /* Must make sure we don't use it after the |
|
218 |
last loop, since it'll point to something
|
|
219 |
undefined */
|
|
220 |
}
|
|
221 |
}
|
|
222 |
||
223 |
m_free(methods); |
|
224 |
||
225 |
cli_ses.state = USERAUTH_FAIL_RCVD; |
|
226 |
||
227 |
TRACE(("leave recv_msg_userauth_failure")) |
|
228 |
}
|
|
229 |
||
230 |
void recv_msg_userauth_success() { |
|
231 |
TRACE(("received msg_userauth_success")) |
|
1.4.2
by Gerrit Pape
Import upstream version 0.52 |
232 |
/* Note: in delayed-zlib mode, setting authdone here
|
233 |
* will enable compression in the transport layer */
|
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
234 |
ses.authstate.authdone = 1; |
235 |
cli_ses.state = USERAUTH_SUCCESS_RCVD; |
|
1.2.2
by Matt Johnston
Import upstream version 0.47 |
236 |
cli_ses.lastauthtype = AUTH_TYPE_NONE; |
1.4.3
by Gerrit Pape
Import upstream version 0.53.1 |
237 |
|
238 |
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
|
239 |
cli_auth_pubkey_cleanup(); |
|
240 |
#endif
|
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
241 |
}
|
242 |
||
243 |
void cli_auth_try() { |
|
244 |
||
1.3.2
by Gerrit Pape
Import upstream version 0.49 |
245 |
int finished = 0; |
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
246 |
TRACE(("enter cli_auth_try")) |
247 |
||
248 |
CHECKCLEARTOWRITE(); |
|
249 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
250 |
/* Order to try is pubkey, interactive, password.
|
251 |
* As soon as "finished" is set for one, we don't do any more. */
|
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
252 |
#ifdef ENABLE_CLI_PUBKEY_AUTH
|
253 |
if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { |
|
254 |
finished = cli_auth_pubkey(); |
|
255 |
cli_ses.lastauthtype = AUTH_TYPE_PUBKEY; |
|
256 |
}
|
|
257 |
#endif
|
|
258 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
259 |
#ifdef ENABLE_CLI_INTERACT_AUTH
|
260 |
if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) { |
|
261 |
if (cli_ses.auth_interact_failed) { |
|
262 |
finished = 0; |
|
263 |
} else { |
|
264 |
cli_auth_interactive(); |
|
265 |
cli_ses.lastauthtype = AUTH_TYPE_INTERACT; |
|
266 |
finished = 1; |
|
267 |
}
|
|
268 |
}
|
|
269 |
#endif
|
|
270 |
||
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
271 |
#ifdef ENABLE_CLI_PASSWORD_AUTH
|
272 |
if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { |
|
1.2.2
by Matt Johnston
Import upstream version 0.47 |
273 |
cli_auth_password(); |
274 |
finished = 1; |
|
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
275 |
cli_ses.lastauthtype = AUTH_TYPE_PASSWORD; |
276 |
}
|
|
277 |
#endif
|
|
278 |
||
1.2.2
by Matt Johnston
Import upstream version 0.47 |
279 |
TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype)) |
280 |
||
1.2.1
by Gerrit Pape
Import upstream version 0.45 |
281 |
if (!finished) { |
282 |
dropbear_exit("No auth methods could be used."); |
|
283 |
}
|
|
284 |
||
285 |
TRACE(("leave cli_auth_try")) |
|
286 |
}
|
|
1.3.1
by Gerrit Pape
Import upstream version 0.48.1 |
287 |
|
288 |
/* A helper for getpass() that exits if the user cancels. The returned
|
|
289 |
* password is statically allocated by getpass() */
|
|
1.3.2
by Gerrit Pape
Import upstream version 0.49 |
290 |
char* getpass_or_cancel(char* prompt) |
1.3.1
by Gerrit Pape
Import upstream version 0.48.1 |
291 |
{
|
292 |
char* password = NULL; |
|
1.3.3
by Gerrit Pape
Import upstream version 0.50 |
293 |
|
294 |
#ifdef DROPBEAR_PASSWORD_ENV
|
|
295 |
/* Password provided in an environment var */
|
|
296 |
password = getenv(DROPBEAR_PASSWORD_ENV); |
|
297 |
if (password) |
|
298 |
{
|
|
299 |
return password; |
|
300 |
}
|
|
301 |
#endif
|
|
1.3.1
by Gerrit Pape
Import upstream version 0.48.1 |
302 |
|
1.3.2
by Gerrit Pape
Import upstream version 0.49 |
303 |
password = getpass(prompt); |
1.3.1
by Gerrit Pape
Import upstream version 0.48.1 |
304 |
|
305 |
/* 0x03 is a ctrl-c character in the buffer. */
|
|
306 |
if (password == NULL || strchr(password, '\3') != NULL) { |
|
307 |
dropbear_close("Interrupted."); |
|
308 |
}
|
|
309 |
return password; |
|
310 |
}
|