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 "packet.h"
     27 #include "buffer.h"
     28 #include "session.h"
     29 #include "dbutil.h"
     30 #include "channel.h"
     31 #include "chansession.h"
     32 #include "sshpty.h"
     33 #include "termcodes.h"
     34 #include "ssh.h"
     35 #include "random.h"
     36 #include "utmp.h"
     37 #include "x11fwd.h"
     38 #include "agentfwd.h"
     39 #include "runopts.h"
     40 
     41 /* Handles sessions (either shells or programs) requested by the client */
     42 
     43 static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
     44 		int iscmd, int issubsys);
     45 static int sessionpty(struct ChanSess * chansess);
     46 static int sessionsignal(struct ChanSess *chansess);
     47 static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
     48 static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
     49 static int sessionwinchange(struct ChanSess *chansess);
     50 static void execchild(struct ChanSess *chansess);
     51 static void addchildpid(struct ChanSess *chansess, pid_t pid);
     52 static void sesssigchild_handler(int val);
     53 static void closechansess(struct Channel *channel);
     54 static int newchansess(struct Channel *channel);
     55 static void chansessionrequest(struct Channel *channel);
     56 
     57 static void send_exitsignalstatus(struct Channel *channel);
     58 static void send_msg_chansess_exitstatus(struct Channel * channel,
     59 		struct ChanSess * chansess);
     60 static void send_msg_chansess_exitsignal(struct Channel * channel,
     61 		struct ChanSess * chansess);
     62 static void get_termmodes(struct ChanSess *chansess);
     63 
     64 
     65 /* required to clear environment */
     66 extern char** environ;
     67 
     68 static int sesscheckclose(struct Channel *channel) {
     69 	struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
     70 	TRACE(("sesscheckclose, pid is %d", chansess->exit.exitpid))
     71 	return chansess->exit.exitpid != -1;
     72 }
     73 
     74 /* Handler for childs exiting, store the state for return to the client */
     75 
     76 /* There's a particular race we have to watch out for: if the forked child
     77  * executes, exits, and this signal-handler is called, all before the parent
     78  * gets to run, then the childpids[] array won't have the pid in it. Hence we
     79  * use the svr_ses.lastexit struct to hold the exit, which is then compared by
     80  * the parent when it runs. This work correctly at least in the case of a
     81  * single shell spawned (ie the usual case) */
     82 static void sesssigchild_handler(int UNUSED(dummy)) {
     83 
     84 	int status;
     85 	pid_t pid;
     86 	unsigned int i;
     87 	struct sigaction sa_chld;
     88 	struct exitinfo *exit = NULL;
     89 
     90 	TRACE(("enter sigchld handler"))
     91 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
     92 		TRACE(("sigchld handler: pid %d", pid))
     93 
     94 		exit = NULL;
     95 		/* find the corresponding chansess */
     96 		for (i = 0; i < svr_ses.childpidsize; i++) {
     97 			if (svr_ses.childpids[i].pid == pid) {
     98 				TRACE(("found match session"));
     99 				exit = &svr_ses.childpids[i].chansess->exit;
    100 				break;
    101 			}
    102 		}
    103 
    104 		/* If the pid wasn't matched, then we might have hit the race mentioned
    105 		 * above. So we just store the info for the parent to deal with */
    106 		if (exit == NULL) {
    107 			TRACE(("using lastexit"));
    108 			exit = &svr_ses.lastexit;
    109 		}
    110 
    111 		exit->exitpid = pid;
    112 		if (WIFEXITED(status)) {
    113 			exit->exitstatus = WEXITSTATUS(status);
    114 		}
    115 		if (WIFSIGNALED(status)) {
    116 			exit->exitsignal = WTERMSIG(status);
    117 #if !defined(AIX) && defined(WCOREDUMP)
    118 			exit->exitcore = WCOREDUMP(status);
    119 #else
    120 			exit->exitcore = 0;
    121 #endif
    122 		} else {
    123 			/* we use this to determine how pid exited */
    124 			exit->exitsignal = -1;
    125 		}
    126 
    127 		/* Make sure that the main select() loop wakes up */
    128 		while (1) {
    129 			/* isserver is just a random byte to write. We can't do anything
    130 			about an error so should just ignore it */
    131 			if (write(ses.signal_pipe[1], &ses.isserver, 1) == 1
    132 					|| errno != EINTR) {
    133 				break;
    134 			}
    135 		}
    136 	}
    137 
    138 	sa_chld.sa_handler = sesssigchild_handler;
    139 	sa_chld.sa_flags = SA_NOCLDSTOP;
    140 	sigaction(SIGCHLD, &sa_chld, NULL);
    141 	TRACE(("leave sigchld handler"))
    142 }
    143 
    144 /* send the exit status or the signal causing termination for a session */
    145 static void send_exitsignalstatus(struct Channel *channel) {
    146 
    147 	struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
    148 
    149 	if (chansess->exit.exitpid >= 0) {
    150 		if (chansess->exit.exitsignal > 0) {
    151 			send_msg_chansess_exitsignal(channel, chansess);
    152 		} else {
    153 			send_msg_chansess_exitstatus(channel, chansess);
    154 		}
    155 	}
    156 }
    157 
    158 /* send the exitstatus to the client */
    159 static void send_msg_chansess_exitstatus(struct Channel * channel,
    160 		struct ChanSess * chansess) {
    161 
    162 	dropbear_assert(chansess->exit.exitpid != -1);
    163 	dropbear_assert(chansess->exit.exitsignal == -1);
    164 
    165 	CHECKCLEARTOWRITE();
    166 
    167 	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
    168 	buf_putint(ses.writepayload, channel->remotechan);
    169 	buf_putstring(ses.writepayload, "exit-status", 11);
    170 	buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
    171 	buf_putint(ses.writepayload, chansess->exit.exitstatus);
    172 
    173 	encrypt_packet();
    174 
    175 }
    176 
    177 /* send the signal causing the exit to the client */
    178 static void send_msg_chansess_exitsignal(struct Channel * channel,
    179 		struct ChanSess * chansess) {
    180 
    181 	int i;
    182 	char* signame = NULL;
    183 	dropbear_assert(chansess->exit.exitpid != -1);
    184 	dropbear_assert(chansess->exit.exitsignal > 0);
    185 
    186 	TRACE(("send_msg_chansess_exitsignal %d", chansess->exit.exitsignal))
    187 
    188 	CHECKCLEARTOWRITE();
    189 
    190 	/* we check that we can match a signal name, otherwise
    191 	 * don't send anything */
    192 	for (i = 0; signames[i].name != NULL; i++) {
    193 		if (signames[i].signal == chansess->exit.exitsignal) {
    194 			signame = signames[i].name;
    195 			break;
    196 		}
    197 	}
    198 
    199 	if (signame == NULL) {
    200 		return;
    201 	}
    202 
    203 	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
    204 	buf_putint(ses.writepayload, channel->remotechan);
    205 	buf_putstring(ses.writepayload, "exit-signal", 11);
    206 	buf_putbyte(ses.writepayload, 0); /* boolean FALSE */
    207 	buf_putstring(ses.writepayload, signame, strlen(signame));
    208 	buf_putbyte(ses.writepayload, chansess->exit.exitcore);
    209 	buf_putstring(ses.writepayload, "", 0); /* error msg */
    210 	buf_putstring(ses.writepayload, "", 0); /* lang */
    211 
    212 	encrypt_packet();
    213 }
    214 
    215 /* set up a session channel */
    216 static int newchansess(struct Channel *channel) {
    217 
    218 	struct ChanSess *chansess;
    219 
    220 	dropbear_assert(channel->typedata == NULL);
    221 
    222 	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
    223 	chansess->cmd = NULL;
    224 	chansess->pid = 0;
    225 
    226 	/* pty details */
    227 	chansess->master = -1;
    228 	chansess->slave = -1;
    229 	chansess->tty = NULL;
    230 	chansess->term = NULL;
    231 
    232 	chansess->exit.exitpid = -1;
    233 
    234 	channel->typedata = chansess;
    235 
    236 #ifndef DISABLE_X11FWD
    237 	chansess->x11listener = NULL;
    238 	chansess->x11authprot = NULL;
    239 	chansess->x11authcookie = NULL;
    240 #endif
    241 
    242 #ifndef DISABLE_AGENTFWD
    243 	chansess->agentlistener = NULL;
    244 	chansess->agentfile = NULL;
    245 	chansess->agentdir = NULL;
    246 #endif
    247 
    248 	return 0;
    249 
    250 }
    251 
    252 /* clean a session channel */
    253 static void closechansess(struct Channel *channel) {
    254 
    255 	struct ChanSess *chansess;
    256 	unsigned int i;
    257 	struct logininfo *li;
    258 
    259 	TRACE(("enter closechansess"))
    260 
    261 	chansess = (struct ChanSess*)channel->typedata;
    262 
    263 	if (chansess == NULL) {
    264 		TRACE(("leave closechansess: chansess == NULL"))
    265 		return;
    266 	}
    267 
    268 	send_exitsignalstatus(channel);
    269 
    270 	m_free(chansess->cmd);
    271 	m_free(chansess->term);
    272 
    273 	if (chansess->tty) {
    274 		/* write the utmp/wtmp login record */
    275 		li = login_alloc_entry(chansess->pid, ses.authstate.username,
    276 				ses.remotehost, chansess->tty);
    277 		login_logout(li);
    278 		login_free_entry(li);
    279 
    280 		pty_release(chansess->tty);
    281 		m_free(chansess->tty);
    282 	}
    283 
    284 #ifndef DISABLE_X11FWD
    285 	x11cleanup(chansess);
    286 #endif
    287 
    288 #ifndef DISABLE_AGENTFWD
    289 	agentcleanup(chansess);
    290 #endif
    291 
    292 	/* clear child pid entries */
    293 	for (i = 0; i < svr_ses.childpidsize; i++) {
    294 		if (svr_ses.childpids[i].chansess == chansess) {
    295 			dropbear_assert(svr_ses.childpids[i].pid > 0);
    296 			TRACE(("closing pid %d", svr_ses.childpids[i].pid))
    297 			TRACE(("exitpid is %d", chansess->exit.exitpid))
    298 			svr_ses.childpids[i].pid = -1;
    299 			svr_ses.childpids[i].chansess = NULL;
    300 		}
    301 	}
    302 
    303 	m_free(chansess);
    304 
    305 	TRACE(("leave closechansess"))
    306 }
    307 
    308 /* Handle requests for a channel. These can be execution requests,
    309  * or x11/authagent forwarding. These are passed to appropriate handlers */
    310 static void chansessionrequest(struct Channel *channel) {
    311 
    312 	unsigned char * type = NULL;
    313 	unsigned int typelen;
    314 	unsigned char wantreply;
    315 	int ret = 1;
    316 	struct ChanSess *chansess;
    317 
    318 	TRACE(("enter chansessionrequest"))
    319 
    320 	type = buf_getstring(ses.payload, &typelen);
    321 	wantreply = buf_getbool(ses.payload);
    322 
    323 	if (typelen > MAX_NAME_LEN) {
    324 		TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
    325 		goto out;
    326 	}
    327 
    328 	chansess = (struct ChanSess*)channel->typedata;
    329 	dropbear_assert(chansess != NULL);
    330 	TRACE(("type is %s", type))
    331 
    332 	if (strcmp(type, "window-change") == 0) {
    333 		ret = sessionwinchange(chansess);
    334 	} else if (strcmp(type, "shell") == 0) {
    335 		ret = sessioncommand(channel, chansess, 0, 0);
    336 	} else if (strcmp(type, "pty-req") == 0) {
    337 		ret = sessionpty(chansess);
    338 	} else if (strcmp(type, "exec") == 0) {
    339 		ret = sessioncommand(channel, chansess, 1, 0);
    340 	} else if (strcmp(type, "subsystem") == 0) {
    341 		ret = sessioncommand(channel, chansess, 1, 1);
    342 #ifndef DISABLE_X11FWD
    343 	} else if (strcmp(type, "x11-req") == 0) {
    344 		ret = x11req(chansess);
    345 #endif
    346 #ifndef DISABLE_AGENTFWD
    347 	} else if (strcmp(type, "auth-agent-req (at) openssh.com") == 0) {
    348 		ret = agentreq(chansess);
    349 #endif
    350 	} else if (strcmp(type, "signal") == 0) {
    351 		ret = sessionsignal(chansess);
    352 	} else {
    353 		/* etc, todo "env", "subsystem" */
    354 	}
    355 
    356 out:
    357 
    358 	if (wantreply) {
    359 		if (ret == DROPBEAR_SUCCESS) {
    360 			send_msg_channel_success(channel);
    361 		} else {
    362 			send_msg_channel_failure(channel);
    363 		}
    364 	}
    365 
    366 	m_free(type);
    367 	TRACE(("leave chansessionrequest"))
    368 }
    369 
    370 
    371 /* Send a signal to a session's process as requested by the client*/
    372 static int sessionsignal(struct ChanSess *chansess) {
    373 
    374 	int sig = 0;
    375 	unsigned char* signame = NULL;
    376 	int i;
    377 
    378 	if (chansess->pid == 0) {
    379 		/* haven't got a process pid yet */
    380 		return DROPBEAR_FAILURE;
    381 	}
    382 
    383 	signame = buf_getstring(ses.payload, NULL);
    384 
    385 	i = 0;
    386 	while (signames[i].name != 0) {
    387 		if (strcmp(signames[i].name, signame) == 0) {
    388 			sig = signames[i].signal;
    389 			break;
    390 		}
    391 		i++;
    392 	}
    393 
    394 	m_free(signame);
    395 
    396 	if (sig == 0) {
    397 		/* failed */
    398 		return DROPBEAR_FAILURE;
    399 	}
    400 
    401 	if (kill(chansess->pid, sig) < 0) {
    402 		return DROPBEAR_FAILURE;
    403 	}
    404 
    405 	return DROPBEAR_SUCCESS;
    406 }
    407 
    408 /* Let the process know that the window size has changed, as notified from the
    409  * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
    410 static int sessionwinchange(struct ChanSess *chansess) {
    411 
    412 	int termc, termr, termw, termh;
    413 
    414 	if (chansess->master < 0) {
    415 		/* haven't got a pty yet */
    416 		return DROPBEAR_FAILURE;
    417 	}
    418 
    419 	termc = buf_getint(ses.payload);
    420 	termr = buf_getint(ses.payload);
    421 	termw = buf_getint(ses.payload);
    422 	termh = buf_getint(ses.payload);
    423 
    424 	pty_change_window_size(chansess->master, termr, termc, termw, termh);
    425 
    426 	return DROPBEAR_SUCCESS;
    427 }
    428 
    429 static void get_termmodes(struct ChanSess *chansess) {
    430 
    431 	struct termios termio;
    432 	unsigned char opcode;
    433 	unsigned int value;
    434 	const struct TermCode * termcode;
    435 	unsigned int len;
    436 
    437 	TRACE(("enter get_termmodes"))
    438 
    439 	/* Term modes */
    440 	/* We'll ignore errors and continue if we can't set modes.
    441 	 * We're ignoring baud rates since they seem evil */
    442 	if (tcgetattr(chansess->master, &termio) == -1) {
    443 		return;
    444 	}
    445 
    446 	len = buf_getint(ses.payload);
    447 	TRACE(("term mode str %d p->l %d p->p %d",
    448 				len, ses.payload->len , ses.payload->pos));
    449 	if (len != ses.payload->len - ses.payload->pos) {
    450 		dropbear_exit("bad term mode string");
    451 	}
    452 
    453 	if (len == 0) {
    454 		TRACE(("leave get_termmodes: empty terminal modes string"))
    455 		return;
    456 	}
    457 
    458 	while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) {
    459 
    460 		/* must be before checking type, so that value is consumed even if
    461 		 * we don't use it */
    462 		value = buf_getint(ses.payload);
    463 
    464 		/* handle types of code */
    465 		if (opcode > MAX_TERMCODE) {
    466 			continue;
    467 		}
    468 		termcode = &termcodes[(unsigned int)opcode];
    469 
    470 
    471 		switch (termcode->type) {
    472 
    473 			case TERMCODE_NONE:
    474 				break;
    475 
    476 			case TERMCODE_CONTROLCHAR:
    477 				termio.c_cc[termcode->mapcode] = value;
    478 				break;
    479 
    480 			case TERMCODE_INPUT:
    481 				if (value) {
    482 					termio.c_iflag |= termcode->mapcode;
    483 				} else {
    484 					termio.c_iflag &= ~(termcode->mapcode);
    485 				}
    486 				break;
    487 
    488 			case TERMCODE_OUTPUT:
    489 				if (value) {
    490 					termio.c_oflag |= termcode->mapcode;
    491 				} else {
    492 					termio.c_oflag &= ~(termcode->mapcode);
    493 				}
    494 				break;
    495 
    496 			case TERMCODE_LOCAL:
    497 				if (value) {
    498 					termio.c_lflag |= termcode->mapcode;
    499 				} else {
    500 					termio.c_lflag &= ~(termcode->mapcode);
    501 				}
    502 				break;
    503 
    504 			case TERMCODE_CONTROL:
    505 				if (value) {
    506 					termio.c_cflag |= termcode->mapcode;
    507 				} else {
    508 					termio.c_cflag &= ~(termcode->mapcode);
    509 				}
    510 				break;
    511 
    512 		}
    513 	}
    514 	if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
    515 		dropbear_log(LOG_INFO, "error setting terminal attributes");
    516 	}
    517 	TRACE(("leave get_termmodes"))
    518 }
    519 
    520 /* Set up a session pty which will be used to execute the shell or program.
    521  * The pty is allocated now, and kept for when the shell/program executes.
    522  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
    523 static int sessionpty(struct ChanSess * chansess) {
    524 
    525 	unsigned int termlen;
    526 	unsigned char namebuf[65];
    527 
    528 	TRACE(("enter sessionpty"))
    529 	chansess->term = buf_getstring(ses.payload, &termlen);
    530 	if (termlen > MAX_TERM_LEN) {
    531 		/* TODO send disconnect ? */
    532 		TRACE(("leave sessionpty: term len too long"))
    533 		return DROPBEAR_FAILURE;
    534 	}
    535 
    536 	/* allocate the pty */
    537 	if (chansess->master != -1) {
    538 		dropbear_exit("multiple pty requests");
    539 	}
    540 	if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) {
    541 		TRACE(("leave sessionpty: failed to allocate pty"))
    542 		return DROPBEAR_FAILURE;
    543 	}
    544 
    545 	chansess->tty = (char*)m_strdup(namebuf);
    546 	if (!chansess->tty) {
    547 		dropbear_exit("out of memory"); /* TODO disconnect */
    548 	}
    549 
    550 	pty_setowner(ses.authstate.pw, chansess->tty);
    551 
    552 	/* Set up the rows/col counts */
    553 	sessionwinchange(chansess);
    554 
    555 	/* Read the terminal modes */
    556 	get_termmodes(chansess);
    557 
    558 	TRACE(("leave sessionpty"))
    559 	return DROPBEAR_SUCCESS;
    560 }
    561 
    562 /* Handle a command request from the client. This is used for both shell
    563  * and command-execution requests, and passes the command to
    564  * noptycommand or ptycommand as appropriate.
    565  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
    566 static int sessioncommand(struct Channel *channel, struct ChanSess *chansess,
    567 		int iscmd, int issubsys) {
    568 
    569 	unsigned int cmdlen;
    570 	int ret;
    571 
    572 	TRACE(("enter sessioncommand"))
    573 
    574 	if (chansess->cmd != NULL) {
    575 		/* Note that only one command can _succeed_. The client might try
    576 		 * one command (which fails), then try another. Ie fallback
    577 		 * from sftp to scp */
    578 		return DROPBEAR_FAILURE;
    579 	}
    580 
    581 	if (iscmd) {
    582 		/* "exec" */
    583 		chansess->cmd = buf_getstring(ses.payload, &cmdlen);
    584 
    585 		if (cmdlen > MAX_CMD_LEN) {
    586 			m_free(chansess->cmd);
    587 			/* TODO - send error - too long ? */
    588 			return DROPBEAR_FAILURE;
    589 		}
    590 		if (issubsys) {
    591 #ifdef SFTPSERVER_PATH
    592 			if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) {
    593 				m_free(chansess->cmd);
    594 				chansess->cmd = m_strdup(SFTPSERVER_PATH);
    595 			} else
    596 #endif
    597 			{
    598 				m_free(chansess->cmd);
    599 				return DROPBEAR_FAILURE;
    600 			}
    601 		}
    602 	}
    603 
    604 #ifdef LOG_COMMANDS
    605 	if (chansess->cmd) {
    606 		dropbear_log(LOG_INFO, "user %s executing '%s'",
    607 						ses.authstate.printableuser, chansess->cmd);
    608 	} else {
    609 		dropbear_log(LOG_INFO, "user %s executing login shell",
    610 						ses.authstate.printableuser);
    611 	}
    612 #endif
    613 
    614 	if (chansess->term == NULL) {
    615 		/* no pty */
    616 		ret = noptycommand(channel, chansess);
    617 	} else {
    618 		/* want pty */
    619 		ret = ptycommand(channel, chansess);
    620 	}
    621 
    622 	if (ret == DROPBEAR_FAILURE) {
    623 		m_free(chansess->cmd);
    624 	}
    625 	return ret;
    626 }
    627 
    628 /* Execute a command and set up redirection of stdin/stdout/stderr without a
    629  * pty.
    630  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
    631 static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
    632 
    633 	int infds[2];
    634 	int outfds[2];
    635 	int errfds[2];
    636 	pid_t pid;
    637 	unsigned int i;
    638 
    639 	TRACE(("enter noptycommand"))
    640 
    641 	/* redirect stdin/stdout/stderr */
    642 	if (pipe(infds) != 0)
    643 		return DROPBEAR_FAILURE;
    644 	if (pipe(outfds) != 0)
    645 		return DROPBEAR_FAILURE;
    646 	if (pipe(errfds) != 0)
    647 		return DROPBEAR_FAILURE;
    648 
    649 #ifdef __uClinux__
    650 	pid = vfork();
    651 #else
    652 	pid = fork();
    653 #endif
    654 
    655 	if (pid < 0)
    656 		return DROPBEAR_FAILURE;
    657 
    658 	if (!pid) {
    659 		/* child */
    660 
    661 		TRACE(("back to normal sigchld"))
    662 		/* Revert to normal sigchld handling */
    663 		if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
    664 			dropbear_exit("signal() error");
    665 		}
    666 
    667 		/* redirect stdin/stdout */
    668 #define FDIN 0
    669 #define FDOUT 1
    670 		if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
    671 			(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
    672 			(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
    673 			TRACE(("leave noptycommand: error redirecting FDs"))
    674 			return DROPBEAR_FAILURE;
    675 		}
    676 
    677 		close(infds[FDOUT]);
    678 		close(infds[FDIN]);
    679 		close(outfds[FDIN]);
    680 		close(outfds[FDOUT]);
    681 		close(errfds[FDIN]);
    682 		close(errfds[FDOUT]);
    683 
    684 		execchild(chansess);
    685 		/* not reached */
    686 
    687 	} else {
    688 		/* parent */
    689 		TRACE(("continue noptycommand: parent"))
    690 		chansess->pid = pid;
    691 		TRACE(("child pid is %d", pid))
    692 
    693 		addchildpid(chansess, pid);
    694 
    695 		if (svr_ses.lastexit.exitpid != -1) {
    696 			TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
    697 			/* The child probably exited and the signal handler triggered
    698 			 * possibly before we got around to adding the childpid. So we fill
    699 			 * out its data manually */
    700 			for (i = 0; i < svr_ses.childpidsize; i++) {
    701 				if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
    702 					TRACE(("found match for lastexitpid"))
    703 					svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
    704 					svr_ses.lastexit.exitpid = -1;
    705 				}
    706 			}
    707 		}
    708 
    709 		close(infds[FDIN]);
    710 		close(outfds[FDOUT]);
    711 		close(errfds[FDOUT]);
    712 		channel->writefd = infds[FDOUT];
    713 		channel->readfd = outfds[FDIN];
    714 		channel->errfd = errfds[FDIN];
    715 		ses.maxfd = MAX(ses.maxfd, channel->writefd);
    716 		ses.maxfd = MAX(ses.maxfd, channel->readfd);
    717 		ses.maxfd = MAX(ses.maxfd, channel->errfd);
    718 
    719 		setnonblocking(channel->readfd);
    720 		setnonblocking(channel->writefd);
    721 		setnonblocking(channel->errfd);
    722 
    723 	}
    724 #undef FDIN
    725 #undef FDOUT
    726 
    727 	TRACE(("leave noptycommand"))
    728 	return DROPBEAR_SUCCESS;
    729 }
    730 
    731 /* Execute a command or shell within a pty environment, and set up
    732  * redirection as appropriate.
    733  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
    734 static int ptycommand(struct Channel *channel, struct ChanSess *chansess) {
    735 
    736 	pid_t pid;
    737 	struct logininfo *li = NULL;
    738 #ifdef DO_MOTD
    739 	buffer * motdbuf = NULL;
    740 	int len;
    741 	struct stat sb;
    742 	char *hushpath = NULL;
    743 #endif
    744 
    745 	TRACE(("enter ptycommand"))
    746 
    747 	/* we need to have a pty allocated */
    748 	if (chansess->master == -1 || chansess->tty == NULL) {
    749 		dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute");
    750 		return DROPBEAR_FAILURE;
    751 	}
    752 
    753 #ifdef __uClinux__
    754 	pid = vfork();
    755 #else
    756 	pid = fork();
    757 #endif
    758 	if (pid < 0)
    759 		return DROPBEAR_FAILURE;
    760 
    761 	if (pid == 0) {
    762 		/* child */
    763 
    764 		TRACE(("back to normal sigchld"))
    765 		/* Revert to normal sigchld handling */
    766 		if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
    767 			dropbear_exit("signal() error");
    768 		}
    769 
    770 		/* redirect stdin/stdout/stderr */
    771 		close(chansess->master);
    772 
    773 		pty_make_controlling_tty(&chansess->slave, chansess->tty);
    774 
    775 		if ((dup2(chansess->slave, STDIN_FILENO) < 0) ||
    776 			(dup2(chansess->slave, STDERR_FILENO) < 0) ||
    777 			(dup2(chansess->slave, STDOUT_FILENO) < 0)) {
    778 			TRACE(("leave ptycommand: error redirecting filedesc"))
    779 			return DROPBEAR_FAILURE;
    780 		}
    781 
    782 		close(chansess->slave);
    783 
    784 		/* write the utmp/wtmp login record - must be after changing the
    785 		 * terminal used for stdout with the dup2 above */
    786 		li= login_alloc_entry(getpid(), ses.authstate.username,
    787 				ses.remotehost, chansess->tty);
    788 		login_login(li);
    789 		login_free_entry(li);
    790 
    791 		m_free(chansess->tty);
    792 
    793 #ifdef DO_MOTD
    794 		if (svr_opts.domotd) {
    795 			/* don't show the motd if ~/.hushlogin exists */
    796 
    797 			/* 11 == strlen("/hushlogin\0") */
    798 			len = strlen(ses.authstate.pw->pw_dir) + 11;
    799 
    800 			hushpath = m_malloc(len);
    801 			snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
    802 
    803 			if (stat(hushpath, &sb) < 0) {
    804 				/* more than a screenful is stupid IMHO */
    805 				motdbuf = buf_new(80 * 25);
    806 				if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) {
    807 					buf_setpos(motdbuf, 0);
    808 					while (motdbuf->pos != motdbuf->len) {
    809 						len = motdbuf->len - motdbuf->pos;
    810 						len = write(STDOUT_FILENO,
    811 								buf_getptr(motdbuf, len), len);
    812 						buf_incrpos(motdbuf, len);
    813 					}
    814 				}
    815 				buf_free(motdbuf);
    816 			}
    817 			m_free(hushpath);
    818 		}
    819 #endif /* DO_MOTD */
    820 
    821 		execchild(chansess);
    822 		/* not reached */
    823 
    824 	} else {
    825 		/* parent */
    826 		TRACE(("continue ptycommand: parent"))
    827 		chansess->pid = pid;
    828 
    829 		/* add a child pid */
    830 		addchildpid(chansess, pid);
    831 
    832 		close(chansess->slave);
    833 		channel->writefd = chansess->master;
    834 		channel->readfd = chansess->master;
    835 		/* don't need to set stderr here */
    836 		ses.maxfd = MAX(ses.maxfd, chansess->master);
    837 
    838 		setnonblocking(chansess->master);
    839 
    840 	}
    841 
    842 	TRACE(("leave ptycommand"))
    843 	return DROPBEAR_SUCCESS;
    844 }
    845 
    846 /* Add the pid of a child to the list for exit-handling */
    847 static void addchildpid(struct ChanSess *chansess, pid_t pid) {
    848 
    849 	unsigned int i;
    850 	for (i = 0; i < svr_ses.childpidsize; i++) {
    851 		if (svr_ses.childpids[i].pid == -1) {
    852 			break;
    853 		}
    854 	}
    855 
    856 	/* need to increase size */
    857 	if (i == svr_ses.childpidsize) {
    858 		svr_ses.childpids = (struct ChildPid*)m_realloc(svr_ses.childpids,
    859 				sizeof(struct ChildPid) * (svr_ses.childpidsize+1));
    860 		svr_ses.childpidsize++;
    861 	}
    862 
    863 	svr_ses.childpids[i].pid = pid;
    864 	svr_ses.childpids[i].chansess = chansess;
    865 
    866 }
    867 
    868 /* Clean up, drop to user privileges, set up the environment and execute
    869  * the command/shell. This function does not return. */
    870 static void execchild(struct ChanSess *chansess) {
    871 
    872 	char *argv[4];
    873 	char * usershell = NULL;
    874 	char * baseshell = NULL;
    875 	unsigned int i;
    876 
    877     /* with uClinux we'll have vfork()ed, so don't want to overwrite the
    878      * hostkey. can't think of a workaround to clear it */
    879 #ifndef __uClinux__
    880 	/* wipe the hostkey */
    881 	sign_key_free(svr_opts.hostkey);
    882 	svr_opts.hostkey = NULL;
    883 
    884 	/* overwrite the prng state */
    885 	reseedrandom();
    886 #endif
    887 
    888 	/* close file descriptors except stdin/stdout/stderr
    889 	 * Need to be sure FDs are closed here to avoid reading files as root */
    890 	for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
    891 		m_close(i);
    892 	}
    893 
    894 	/* clear environment */
    895 	/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
    896 	 * etc. This is hazardous, so should only be used for debugging. */
    897 #ifndef DEBUG_VALGRIND
    898 #ifdef HAVE_CLEARENV
    899 	clearenv();
    900 #else /* don't HAVE_CLEARENV */
    901 	/* Yay for posix. */
    902 	if (environ) {
    903 		environ[0] = NULL;
    904 	}
    905 #endif /* HAVE_CLEARENV */
    906 #endif /* DEBUG_VALGRIND */
    907 
    908 	/* We can only change uid/gid as root ... */
    909 	if (getuid() == 0) {
    910 
    911 		if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
    912 			(initgroups(ses.authstate.pw->pw_name,
    913 						ses.authstate.pw->pw_gid) < 0)) {
    914 			dropbear_exit("error changing user group");
    915 		}
    916 		if (setuid(ses.authstate.pw->pw_uid) < 0) {
    917 			dropbear_exit("error changing user");
    918 		}
    919 	} else {
    920 		/* ... but if the daemon is the same uid as the requested uid, we don't
    921 		 * need to */
    922 
    923 		/* XXX - there is a minor issue here, in that if there are multiple
    924 		 * usernames with the same uid, but differing groups, then the
    925 		 * differing groups won't be set (as with initgroups()). The solution
    926 		 * is for the sysadmin not to give out the UID twice */
    927 		if (getuid() != ses.authstate.pw->pw_uid) {
    928 			dropbear_exit("couldn't	change user as non-root");
    929 		}
    930 	}
    931 
    932 	/* an empty shell should be interpreted as "/bin/sh" */
    933 	if (ses.authstate.pw->pw_shell[0] == '\0') {
    934 		usershell = "/bin/sh";
    935 	} else {
    936 		usershell = ses.authstate.pw->pw_shell;
    937 	}
    938 
    939 	/* set env vars */
    940 	addnewvar("USER", ses.authstate.pw->pw_name);
    941 	addnewvar("LOGNAME", ses.authstate.pw->pw_name);
    942 	addnewvar("HOME", ses.authstate.pw->pw_dir);
    943 	addnewvar("SHELL", usershell);
    944 	if (chansess->term != NULL) {
    945 		addnewvar("TERM", chansess->term);
    946 	}
    947 
    948 	/* change directory */
    949 	if (chdir(ses.authstate.pw->pw_dir) < 0) {
    950 		dropbear_exit("error changing directory");
    951 	}
    952 
    953 #ifndef DISABLE_X11FWD
    954 	/* set up X11 forwarding if enabled */
    955 	x11setauth(chansess);
    956 #endif
    957 #ifndef DISABLE_AGENTFWD
    958 	/* set up agent env variable */
    959 	agentset(chansess);
    960 #endif
    961 
    962 	/* Re-enable SIGPIPE for the executed process */
    963 	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
    964 		dropbear_exit("signal() error");
    965 	}
    966 
    967 	baseshell = basename(usershell);
    968 
    969 	if (chansess->cmd != NULL) {
    970 		argv[0] = baseshell;
    971 	} else {
    972 		/* a login shell should be "-bash" for "/bin/bash" etc */
    973 		int len = strlen(baseshell) + 2; /* 2 for "-" */
    974 		argv[0] = (char*)m_malloc(len);
    975 		snprintf(argv[0], len, "-%s", baseshell);
    976 	}
    977 
    978 	if (chansess->cmd != NULL) {
    979 		argv[1] = "-c";
    980 		argv[2] = chansess->cmd;
    981 		argv[3] = NULL;
    982 	} else {
    983 		/* construct a shell of the form "-bash" etc */
    984 		argv[1] = NULL;
    985 	}
    986 
    987 	execv(usershell, argv);
    988 
    989 	/* only reached on error */
    990 	dropbear_exit("child failed");
    991 }
    992 
    993 const struct ChanType svrchansess = {
    994 	0, /* sepfds */
    995 	"session", /* name */
    996 	newchansess, /* inithandler */
    997 	sesscheckclose, /* checkclosehandler */
    998 	chansessionrequest, /* reqhandler */
    999 	closechansess, /* closehandler */
   1000 };
   1001 
   1002 
   1003 /* Set up the general chansession environment, in particular child-exit
   1004  * handling */
   1005 void svr_chansessinitialise() {
   1006 
   1007 	struct sigaction sa_chld;
   1008 
   1009 	/* single child process intially */
   1010 	svr_ses.childpids = (struct ChildPid*)m_malloc(sizeof(struct ChildPid));
   1011 	svr_ses.childpids[0].pid = -1; /* unused */
   1012 	svr_ses.childpids[0].chansess = NULL;
   1013 	svr_ses.childpidsize = 1;
   1014 	svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */
   1015 	sa_chld.sa_handler = sesssigchild_handler;
   1016 	sa_chld.sa_flags = SA_NOCLDSTOP;
   1017 	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
   1018 		dropbear_exit("signal() error");
   1019 	}
   1020 
   1021 }
   1022 
   1023 /* add a new environment variable, allocating space for the entry */
   1024 void addnewvar(const char* param, const char* var) {
   1025 
   1026 	char* newvar = NULL;
   1027 	int plen, vlen;
   1028 
   1029 	plen = strlen(param);
   1030 	vlen = strlen(var);
   1031 
   1032 	newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */
   1033 	memcpy(newvar, param, plen);
   1034 	newvar[plen] = '=';
   1035 	memcpy(&newvar[plen+1], var, vlen);
   1036 	newvar[plen+vlen+1] = '\0';
   1037 	/* newvar is leaked here, but that's part of putenv()'s semantics */
   1038 	if (putenv(newvar) < 0) {
   1039 		dropbear_exit("environ error");
   1040 	}
   1041 }
   1042