• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
sesscheckclose(struct Channel * channel)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) */
sesssigchild_handler(int UNUSED (dummy))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 */
send_exitsignalstatus(struct Channel * channel)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 */
send_msg_chansess_exitstatus(struct Channel * channel,struct ChanSess * chansess)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 */
send_msg_chansess_exitsignal(struct Channel * channel,struct ChanSess * chansess)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 */
newchansess(struct Channel * 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 */
closechansess(struct Channel * 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 */
chansessionrequest(struct Channel * channel)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@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*/
sessionsignal(struct ChanSess * chansess)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 */
sessionwinchange(struct ChanSess * chansess)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 
get_termmodes(struct ChanSess * chansess)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 */
sessionpty(struct ChanSess * chansess)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 */
sessioncommand(struct Channel * channel,struct ChanSess * chansess,int iscmd,int issubsys)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 */
noptycommand(struct Channel * channel,struct ChanSess * chansess)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 */
ptycommand(struct Channel * channel,struct ChanSess * chansess)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 */
addchildpid(struct ChanSess * chansess,pid_t pid)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. */
execchild(struct ChanSess * chansess)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 */
svr_chansessinitialise()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 */
addnewvar(const char * param,const char * var)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