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 94 printf("%s\n", banner); 95 96 out: 97 m_free(banner); 98 TRACE(("leave recv_msg_userauth_banner")) 99 } 100 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 } 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? */ 149 dropbear_exit("Unexpected userauth failure"); 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 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 173 methods = buf_getstring(ses.payload, &methlen); 174 175 partial = buf_getbool(ses.payload); 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 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 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")) 232 ses.authstate.authdone = 1; 233 cli_ses.state = USERAUTH_SUCCESS_RCVD; 234 cli_ses.lastauthtype = AUTH_TYPE_NONE; 235 } 236 237 void cli_auth_try() { 238 239 int finished = 0; 240 TRACE(("enter cli_auth_try")) 241 242 CHECKCLEARTOWRITE(); 243 244 /* Order to try is pubkey, interactive, password. 245 * As soon as "finished" is set for one, we don't do any more. */ 246 #ifdef ENABLE_CLI_PUBKEY_AUTH 247 if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) { 248 finished = cli_auth_pubkey(); 249 cli_ses.lastauthtype = AUTH_TYPE_PUBKEY; 250 } 251 #endif 252 253 #ifdef ENABLE_CLI_INTERACT_AUTH 254 if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) { 255 if (cli_ses.auth_interact_failed) { 256 finished = 0; 257 } else { 258 cli_auth_interactive(); 259 cli_ses.lastauthtype = AUTH_TYPE_INTERACT; 260 finished = 1; 261 } 262 } 263 #endif 264 265 #ifdef ENABLE_CLI_PASSWORD_AUTH 266 if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) { 267 cli_auth_password(); 268 finished = 1; 269 cli_ses.lastauthtype = AUTH_TYPE_PASSWORD; 270 } 271 #endif 272 273 TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype)) 274 275 if (!finished) { 276 dropbear_exit("No auth methods could be used."); 277 } 278 279 TRACE(("leave cli_auth_try")) 280 } 281 282 /* A helper for getpass() that exits if the user cancels. The returned 283 * password is statically allocated by getpass() */ 284 char* getpass_or_cancel(char* prompt) 285 { 286 char* password = NULL; 287 288 password = getpass(prompt); 289 290 /* 0x03 is a ctrl-c character in the buffer. */ 291 if (password == NULL || strchr(password, '\3') != NULL) { 292 dropbear_close("Interrupted."); 293 } 294 return password; 295 } 296