static void svr_sessionloop() { if (svr_ses.connect_time != 0 && monotonic_now() - svr_ses.connect_time >= AUTH_TIMEOUT) { dropbear_close("Timeout before auth"); } }
/* called when the remote side closes the connection */ void session_remoteclosed() { close(ses.sock); ses.sock = -1; dropbear_close("Exited normally"); }
/* Check all timeouts which are required. Currently these are the time for * user authentication, and the automatic rekeying. */ static void checktimeouts() { struct timeval tv; long secs; if (gettimeofday(&tv, 0) < 0) { dropbear_exit("Error getting time"); } secs = tv.tv_sec; if (!ses.authstate.authdone) { if (secs - ses.connecttime >= AUTH_TIMEOUT) { dropbear_close("Timeout before userauth"); } } /* we can't rekey if we haven't done remote ident exchange yet */ if (ses.remoteident == NULL) { return; } if (!ses.kexstate.sentkexinit && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ TRACE(("rekeying after timeout or max data reached")); send_msg_kexinit(); } }
/* called when the remote side closes the connection */ static void svr_remoteclosed() { close(ses.sock); ses.sock = -1; dropbear_close("Exited normally"); }
/* called when the remote side closes the connection */ static void svr_remoteclosed() { m_close(ses.sock_in); m_close(ses.sock_out); ses.sock_in = -1; ses.sock_out = -1; dropbear_close("Exited normally"); }
/* Non-blocking function reading available portion of a packet into the * ses's buffer, decrypting the length if encrypted, decrypting the * full portion if possible */ void read_packet() { int len; unsigned int maxlen; unsigned char blocksize; TRACE(("enter read_packet")); blocksize = ses.keys->recv_algo_crypt->blocksize; if (ses.readbuf == NULL || ses.readbuf->len < blocksize) { /* In the first blocksize of a packet */ /* Read the first blocksize of the packet, so we can decrypt it and * find the length of the whole packet */ read_packet_init(); /* If we don't have the length of decryptreadbuf, we didn't read * a whole blocksize and should exit */ if (ses.decryptreadbuf->len == 0) { TRACE(("leave read_packet: packetinit done")); return; } } /* Attempt to read the remainder of the packet, note that there * mightn't be any available (EAGAIN) */ assert(ses.readbuf != NULL); maxlen = ses.readbuf->len - ses.readbuf->pos; len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen); buf_incrpos(ses.readbuf, len); if (len == 0) { dropbear_close("remote host closed connection"); } if (len < 0) { if (errno == EINTR || errno == EAGAIN) { TRACE(("leave read_packet: EINTR or EAGAIN")); return; } else { dropbear_exit("error reading"); } } if ((unsigned int)len == maxlen) { /* The whole packet has been read */ decrypt_packet(); /* The main select() loop in session.h will process_packet() to * handle the packet contents... */ } TRACE(("leave read_packet")); }
/* non-blocking function writing out a current encrypted packet */ void write_packet() { int len, written; buffer * writebuf; TRACE(("enter write_packet")); assert(!isempty(&ses.writequeue)); /* Get the next buffer in the queue of encrypted packets to write*/ writebuf = (buffer*)examine(&ses.writequeue); len = writebuf->len - writebuf->pos; assert(len > 0); /* Try to write as much as possible */ written = write(ses.sock, buf_getptr(writebuf, len), len); if (written < 0) { if (errno == EINTR) { TRACE(("leave writepacket: EINTR")); return; } else { dropbear_exit("error writing"); } } if (written == 0) { dropbear_close("remote host closed connection"); } if (written == len) { /* We've finished with the packet, free it */ dequeue(&ses.writequeue); buf_free(writebuf); } else { /* More packet left to write, leave it in the queue for later */ buf_incrpos(writebuf, written); } TRACE(("leave write_packet")); }
/* process a decrypted packet, call the appropriate handler */ void process_packet() { unsigned char type; TRACE(("enter process_packet")); type = buf_getbyte(ses.payload); TRACE(("process_packet: packet type = %d", type)); /* these packets we can receive at any time, regardless of expecting * other packets: */ switch(type) { case SSH_MSG_IGNORE: case SSH_MSG_DEBUG: TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")); goto out; case SSH_MSG_UNIMPLEMENTED: /* debugging XXX */ TRACE(("SSH_MSG_UNIMPLEMENTED")); dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); case SSH_MSG_DISCONNECT: /* TODO cleanup? */ dropbear_close("Disconnect received"); } /* check that we aren't expecting a particular packet */ if (ses.expecting && ses.expecting != type) { /* TODO send disconnect? */ dropbear_exit("unexpected packet type %d, expected %d", type, ses.expecting); } /* handle the packet depending on type */ ses.expecting = 0; switch (type) { case SSH_MSG_SERVICE_REQUEST: recv_msg_service_request(); break; case SSH_MSG_USERAUTH_REQUEST: recv_msg_userauth_request(); break; case SSH_MSG_KEXINIT: recv_msg_kexinit(); break; case SSH_MSG_KEXDH_INIT: recv_msg_kexdh_init(); break; case SSH_MSG_NEWKEYS: recv_msg_newkeys(); break; /* this is ugly, need to make a cleaner way to do it */ case SSH_MSG_CHANNEL_DATA: case SSH_MSG_CHANNEL_WINDOW_ADJUST: case SSH_MSG_CHANNEL_REQUEST: case SSH_MSG_CHANNEL_OPEN: case SSH_MSG_CHANNEL_EOF: case SSH_MSG_CHANNEL_CLOSE: case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH_MSG_CHANNEL_OPEN_FAILURE: /* these should be checked for authdone below */ process_postauth_packet(type); break; default: /* TODO this possibly should be handled */ TRACE(("unknown packet")); recv_unimplemented(); break; } out: buf_free(ses.payload); ses.payload = NULL; TRACE(("leave process_packet")); }
/* Function used to read the initial portion of a packet, and determine the * length. Only called during the first BLOCKSIZE of a packet. */ static void read_packet_init() { unsigned int maxlen; int len; unsigned char blocksize; unsigned char macsize; blocksize = ses.keys->recv_algo_crypt->blocksize; macsize = ses.keys->recv_algo_mac->hashsize; if (ses.readbuf == NULL) { /* start of a new packet */ ses.readbuf = buf_new(INIT_READBUF); assert(ses.decryptreadbuf == NULL); ses.decryptreadbuf = buf_new(blocksize); } maxlen = blocksize - ses.readbuf->pos; /* read the rest of the packet if possible */ len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen), maxlen); if (len == 0) { dropbear_close("remote host closed connection"); } if (len < 0) { if (errno == EINTR) { TRACE(("leave read_packet_init: EINTR")); return; } dropbear_exit("error reading"); } buf_incrwritepos(ses.readbuf, len); if ((unsigned int)len != maxlen) { /* don't have enough bytes to determine length, get next time */ return; } /* now we have the first block, need to get packet length, so we decrypt * the first block (only need first 4 bytes) */ buf_setpos(ses.readbuf, 0); if (ses.keys->recv_algo_crypt->cipherdesc == NULL) { /* copy it */ memcpy(buf_getwriteptr(ses.decryptreadbuf, blocksize), buf_getptr(ses.readbuf, blocksize), blocksize); } else { /* decrypt it */ if (cbc_decrypt(buf_getptr(ses.readbuf, blocksize), buf_getwriteptr(ses.decryptreadbuf,blocksize), &ses.keys->recv_symmetric_struct) != CRYPT_OK) { dropbear_exit("error decrypting"); } } buf_setlen(ses.decryptreadbuf, blocksize); len = buf_getint(ses.decryptreadbuf) + 4 + macsize; buf_setpos(ses.readbuf, blocksize); /* check packet length */ if ((len > MAX_PACKET_LEN) || (len < MIN_PACKET_LEN + macsize) || ((len - macsize) % blocksize != 0)) { dropbear_exit("bad packet size"); } buf_resize(ses.readbuf, len); buf_setlen(ses.readbuf, len); }
/* process a decrypted packet, call the appropriate handler */ void process_packet() { unsigned char type; unsigned int i; TRACE2(("enter process_packet")) type = buf_getbyte(ses.payload); TRACE(("process_packet: packet type = %d, len %d", type, ses.payload->len)) ses.lastpacket = type; ses.last_packet_time = time(NULL); /* These packets we can receive at any time */ switch(type) { case SSH_MSG_IGNORE: goto out; case SSH_MSG_DEBUG: goto out; case SSH_MSG_UNIMPLEMENTED: /* debugging XXX */ TRACE(("SSH_MSG_UNIMPLEMENTED")) dropbear_exit("Received SSH_MSG_UNIMPLEMENTED"); case SSH_MSG_DISCONNECT: /* TODO cleanup? */ dropbear_close("Disconnect received"); } /* This applies for KEX, where the spec says the next packet MUST be * NEWKEYS */ if (ses.requirenext != 0) { if (ses.requirenext == type) { /* Got what we expected */ TRACE(("got expected packet %d during kexinit", type)) } else { /* RFC4253 7.1 - various messages are allowed at this point. The only ones we know about have already been handled though, so just return "unimplemented" */ if (type >= 1 && type <= 49 && type != SSH_MSG_SERVICE_REQUEST && type != SSH_MSG_SERVICE_ACCEPT && type != SSH_MSG_KEXINIT) { TRACE(("unknown allowed packet during kexinit")) recv_unimplemented(); goto out; } else { TRACE(("disallowed packet during kexinit")) dropbear_exit("Unexpected packet type %d, expected %d", type, ses.requirenext); } } } /* Check if we should ignore this packet. Used currently only for * KEX code, with first_kex_packet_follows */ if (ses.ignorenext) { TRACE(("Ignoring packet, type = %d", type)) ses.ignorenext = 0; goto out; } /* Only clear the flag after we have checked ignorenext */ if (ses.requirenext != 0 && ses.requirenext == type) { ses.requirenext = 0; } /* Kindly the protocol authors gave all the preauth packets type values * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ). * NOTE: if the protocol changes and new types are added, revisit this * assumption */ if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) { dropbear_exit("Received message %d before userauth", type); } for (i = 0; ; i++) { if (ses.packettypes[i].type == 0) { /* end of list */ break; } if (ses.packettypes[i].type == type) { ses.packettypes[i].handler(); goto out; } } /* TODO do something more here? */ TRACE(("preauth unknown packet")) recv_unimplemented(); out: buf_free(ses.payload); ses.payload = NULL; TRACE2(("leave process_packet")) }
/* process a decrypted packet, call the appropriate handler */ void process_packet() { unsigned char type; unsigned int i; TRACE(("enter process_packet")) type = buf_getbyte(ses.payload); TRACE(("process_packet: packet type = %d", type)) ses.lastpacket = type; /* These packets we can receive at any time */ switch(type) { case SSH_MSG_IGNORE: case SSH_MSG_DEBUG: TRACE(("received SSH_MSG_IGNORE or SSH_MSG_DEBUG")) goto out; case SSH_MSG_UNIMPLEMENTED: /* debugging XXX */ TRACE(("SSH_MSG_UNIMPLEMENTED")) dropbear_exit("received SSH_MSG_UNIMPLEMENTED"); case SSH_MSG_DISCONNECT: /* TODO cleanup? */ dropbear_close("Disconnect received"); } /* This applies for KEX, where the spec says the next packet MUST be * NEWKEYS */ if (ses.requirenext != 0) { if (ses.requirenext != type) { /* TODO send disconnect? */ dropbear_exit("unexpected packet type %d, expected %d", type, ses.requirenext); } else { /* Got what we expected */ ses.requirenext = 0; } } /* Check if we should ignore this packet. Used currently only for * KEX code, with first_kex_packet_follows */ if (ses.ignorenext) { TRACE(("Ignoring packet, type = %d", type)) ses.ignorenext = 0; goto out; } /* Kindly the protocol authors gave all the preauth packets type values * less-than-or-equal-to 60 ( == MAX_UNAUTH_PACKET_TYPE ). * NOTE: if the protocol changes and new types are added, revisit this * assumption */ if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) { dropbear_exit("received message %d before userauth", type); } for (i = 0; ; i++) { if (ses.packettypes[i].type == 0) { /* end of list */ break; } if (ses.packettypes[i].type == type) { ses.packettypes[i].handler(); goto out; } } /* TODO do something more here? */ TRACE(("preauth unknown packet")) recv_unimplemented(); out: buf_free(ses.payload); ses.payload = NULL; TRACE(("leave process_packet")) }