Home | History | Annotate | Download | only in dropbear
      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