1 /* 2 * Dropbear - a SSH2 server 3 * 4 * Copyright (c) 2002,2003 Matt Johnston 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and associated documentation files (the "Software"), to deal 9 * in the Software without restriction, including without limitation the rights 10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 * copies of the Software, and to permit persons to whom the Software is 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice shall be included in 15 * all copies or substantial portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 * SOFTWARE. */ 24 25 #include "includes.h" 26 #include "session.h" 27 #include "dbutil.h" 28 #include "packet.h" 29 #include "algo.h" 30 #include "buffer.h" 31 #include "dss.h" 32 #include "ssh.h" 33 #include "random.h" 34 #include "kex.h" 35 #include "channel.h" 36 #include "atomicio.h" 37 38 static void checktimeouts(); 39 static int ident_readln(int fd, char* buf, int count); 40 41 struct sshsession ses; /* GLOBAL */ 42 43 /* need to know if the session struct has been initialised, this way isn't the 44 * cleanest, but works OK */ 45 int sessinitdone = 0; /* GLOBAL */ 46 47 /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */ 48 int exitflag = 0; /* GLOBAL */ 49 50 51 52 /* called only at the start of a session, set up initial state */ 53 void common_session_init(int sock, char* remotehost) { 54 55 TRACE(("enter session_init")) 56 57 ses.remotehost = remotehost; 58 59 ses.sock = sock; 60 ses.maxfd = sock; 61 62 ses.connecttimeout = 0; 63 64 if (pipe(ses.signal_pipe) < 0) { 65 dropbear_exit("signal pipe failed"); 66 } 67 setnonblocking(ses.signal_pipe[0]); 68 setnonblocking(ses.signal_pipe[1]); 69 70 kexfirstinitialise(); /* initialise the kex state */ 71 72 ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN); 73 ses.transseq = 0; 74 75 ses.readbuf = NULL; 76 ses.decryptreadbuf = NULL; 77 ses.payload = NULL; 78 ses.recvseq = 0; 79 80 initqueue(&ses.writequeue); 81 82 ses.requirenext = SSH_MSG_KEXINIT; 83 ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */ 84 ses.ignorenext = 0; 85 ses.lastpacket = 0; 86 87 /* set all the algos to none */ 88 ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); 89 ses.newkeys = NULL; 90 ses.keys->recv_algo_crypt = &dropbear_nocipher; 91 ses.keys->trans_algo_crypt = &dropbear_nocipher; 92 93 ses.keys->recv_algo_mac = &dropbear_nohash; 94 ses.keys->trans_algo_mac = &dropbear_nohash; 95 96 ses.keys->algo_kex = -1; 97 ses.keys->algo_hostkey = -1; 98 ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE; 99 ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE; 100 101 #ifndef DISABLE_ZLIB 102 ses.keys->recv_zstream = NULL; 103 ses.keys->trans_zstream = NULL; 104 #endif 105 106 /* key exchange buffers */ 107 ses.session_id = NULL; 108 ses.kexhashbuf = NULL; 109 ses.transkexinit = NULL; 110 ses.dh_K = NULL; 111 ses.remoteident = NULL; 112 113 ses.chantypes = NULL; 114 115 ses.allowprivport = 0; 116 117 TRACE(("leave session_init")) 118 } 119 120 void session_loop(void(*loophandler)()) { 121 122 fd_set readfd, writefd; 123 struct timeval timeout; 124 int val; 125 126 /* main loop, select()s for all sockets in use */ 127 for(;;) { 128 129 timeout.tv_sec = SELECT_TIMEOUT; 130 timeout.tv_usec = 0; 131 FD_ZERO(&writefd); 132 FD_ZERO(&readfd); 133 dropbear_assert(ses.payload == NULL); 134 if (ses.sock != -1) { 135 FD_SET(ses.sock, &readfd); 136 if (!isempty(&ses.writequeue)) { 137 FD_SET(ses.sock, &writefd); 138 } 139 } 140 141 /* We get woken up when signal handlers write to this pipe. 142 SIGCHLD in svr-chansession is the only one currently. */ 143 FD_SET(ses.signal_pipe[0], &readfd); 144 145 /* set up for channels which require reading/writing */ 146 if (ses.dataallowed) { 147 setchannelfds(&readfd, &writefd); 148 } 149 val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); 150 151 if (exitflag) { 152 dropbear_exit("Terminated by signal"); 153 } 154 155 if (val < 0 && errno != EINTR) { 156 dropbear_exit("Error in select"); 157 } 158 159 if (val <= 0) { 160 /* If we were interrupted or the select timed out, we still 161 * want to iterate over channels etc for reading, to handle 162 * server processes exiting etc. 163 * We don't want to read/write FDs. */ 164 FD_ZERO(&writefd); 165 FD_ZERO(&readfd); 166 } 167 168 /* We'll just empty out the pipe if required. We don't do 169 any thing with the data, since the pipe's purpose is purely to 170 wake up the select() above. */ 171 if (FD_ISSET(ses.signal_pipe[0], &readfd)) { 172 char x; 173 while (read(ses.signal_pipe[0], &x, 1) > 0) {} 174 } 175 176 /* check for auth timeout, rekeying required etc */ 177 checktimeouts(); 178 179 /* process session socket's incoming/outgoing data */ 180 if (ses.sock != -1) { 181 if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { 182 write_packet(); 183 } 184 185 if (FD_ISSET(ses.sock, &readfd)) { 186 read_packet(); 187 } 188 189 /* Process the decrypted packet. After this, the read buffer 190 * will be ready for a new packet */ 191 if (ses.payload != NULL) { 192 process_packet(); 193 } 194 } 195 196 /* process pipes etc for the channels, ses.dataallowed == 0 197 * during rekeying ) */ 198 if (ses.dataallowed) { 199 channelio(&readfd, &writefd); 200 } 201 202 if (loophandler) { 203 loophandler(); 204 } 205 206 } /* for(;;) */ 207 208 /* Not reached */ 209 } 210 211 /* clean up a session on exit */ 212 void common_session_cleanup() { 213 214 TRACE(("enter session_cleanup")) 215 216 /* we can't cleanup if we don't know the session state */ 217 if (!sessinitdone) { 218 TRACE(("leave session_cleanup: !sessinitdone")) 219 return; 220 } 221 222 m_free(ses.session_id); 223 m_burn(ses.keys, sizeof(struct key_context)); 224 m_free(ses.keys); 225 226 chancleanup(); 227 228 TRACE(("leave session_cleanup")) 229 } 230 231 232 void session_identification() { 233 234 /* max length of 255 chars */ 235 char linebuf[256]; 236 int len = 0; 237 char done = 0; 238 int i; 239 240 /* write our version string, this blocks */ 241 if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n", 242 strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) { 243 ses.remoteclosed(); 244 } 245 246 /* If they send more than 50 lines, something is wrong */ 247 for (i = 0; i < 50; i++) { 248 len = ident_readln(ses.sock, linebuf, sizeof(linebuf)); 249 250 if (len < 0 && errno != EINTR) { 251 /* It failed */ 252 break; 253 } 254 255 if (len >= 4 && memcmp(linebuf, "SSH-", 4) == 0) { 256 /* start of line matches */ 257 done = 1; 258 break; 259 } 260 } 261 262 if (!done) { 263 TRACE(("err: %s for '%s'\n", strerror(errno), linebuf)) 264 ses.remoteclosed(); 265 } else { 266 /* linebuf is already null terminated */ 267 ses.remoteident = m_malloc(len); 268 memcpy(ses.remoteident, linebuf, len); 269 } 270 271 /* Shall assume that 2.x will be backwards compatible. */ 272 if (strncmp(ses.remoteident, "SSH-2.", 6) != 0 273 && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) { 274 dropbear_exit("Incompatible remote version '%s'", ses.remoteident); 275 } 276 277 TRACE(("remoteident: %s", ses.remoteident)) 278 279 } 280 281 /* returns the length including null-terminating zero on success, 282 * or -1 on failure */ 283 static int ident_readln(int fd, char* buf, int count) { 284 285 char in; 286 int pos = 0; 287 int num = 0; 288 fd_set fds; 289 struct timeval timeout; 290 291 TRACE(("enter ident_readln")) 292 293 if (count < 1) { 294 return -1; 295 } 296 297 FD_ZERO(&fds); 298 299 /* select since it's a non-blocking fd */ 300 301 /* leave space to null-terminate */ 302 while (pos < count-1) { 303 304 FD_SET(fd, &fds); 305 306 timeout.tv_sec = 1; 307 timeout.tv_usec = 0; 308 if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) { 309 if (errno == EINTR) { 310 continue; 311 } 312 TRACE(("leave ident_readln: select error")) 313 return -1; 314 } 315 316 checktimeouts(); 317 318 /* Have to go one byte at a time, since we don't want to read past 319 * the end, and have to somehow shove bytes back into the normal 320 * packet reader */ 321 if (FD_ISSET(fd, &fds)) { 322 num = read(fd, &in, 1); 323 /* a "\n" is a newline, "\r" we want to read in and keep going 324 * so that it won't be read as part of the next line */ 325 if (num < 0) { 326 /* error */ 327 if (errno == EINTR) { 328 continue; /* not a real error */ 329 } 330 TRACE(("leave ident_readln: read error")) 331 return -1; 332 } 333 if (num == 0) { 334 /* EOF */ 335 TRACE(("leave ident_readln: EOF")) 336 return -1; 337 } 338 if (in == '\n') { 339 /* end of ident string */ 340 break; 341 } 342 /* we don't want to include '\r's */ 343 if (in != '\r') { 344 buf[pos] = in; 345 pos++; 346 } 347 } 348 } 349 350 buf[pos] = '\0'; 351 TRACE(("leave ident_readln: return %d", pos+1)) 352 return pos+1; 353 } 354 355 /* Check all timeouts which are required. Currently these are the time for 356 * user authentication, and the automatic rekeying. */ 357 static void checktimeouts() { 358 359 struct timeval tv; 360 long secs; 361 362 if (gettimeofday(&tv, 0) < 0) { 363 dropbear_exit("Error getting time"); 364 } 365 366 secs = tv.tv_sec; 367 368 if (ses.connecttimeout != 0 && secs > ses.connecttimeout) { 369 dropbear_close("Timeout before auth"); 370 } 371 372 /* we can't rekey if we haven't done remote ident exchange yet */ 373 if (ses.remoteident == NULL) { 374 return; 375 } 376 377 if (!ses.kexstate.sentkexinit 378 && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT 379 || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ 380 TRACE(("rekeying after timeout or max data reached")) 381 send_msg_kexinit(); 382 } 383 } 384 385