2 * Dropbear - a SSH2 server
4 * Copyright (c) 2002,2003 Matt Johnston
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
31 #include "chansession.h"
33 #include "termcodes.h"
41 /* Handles sessions (either shells or programs) requested by the client */
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(void *user_data_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
);
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
);
65 /* required to clear environment */
66 extern char** environ
;
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;
74 /* Handler for childs exiting, store the state for return to the client */
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
)) {
87 struct sigaction sa_chld
;
88 struct exitinfo
*exit
= NULL
;
90 TRACE(("enter sigchld handler"))
91 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
92 TRACE(("sigchld handler: pid %d", pid
))
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
;
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 */
107 TRACE(("using lastexit"));
108 exit
= &svr_ses
.lastexit
;
112 if (WIFEXITED(status
)) {
113 exit
->exitstatus
= WEXITSTATUS(status
);
115 if (WIFSIGNALED(status
)) {
116 exit
->exitsignal
= WTERMSIG(status
);
117 #if !defined(AIX) && defined(WCOREDUMP)
118 exit
->exitcore
= WCOREDUMP(status
);
123 /* we use this to determine how pid exited */
124 exit
->exitsignal
= -1;
127 /* Make sure that the main select() loop wakes up */
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
138 sa_chld
.sa_handler
= sesssigchild_handler
;
139 sa_chld
.sa_flags
= SA_NOCLDSTOP
;
140 sigemptyset(&sa_chld
.sa_mask
);
141 sigaction(SIGCHLD
, &sa_chld
, NULL
);
142 TRACE(("leave sigchld handler"))
145 /* send the exit status or the signal causing termination for a session */
146 static void send_exitsignalstatus(struct Channel
*channel
) {
148 struct ChanSess
*chansess
= (struct ChanSess
*)channel
->typedata
;
150 if (chansess
->exit
.exitpid
>= 0) {
151 if (chansess
->exit
.exitsignal
> 0) {
152 send_msg_chansess_exitsignal(channel
, chansess
);
154 send_msg_chansess_exitstatus(channel
, chansess
);
159 /* send the exitstatus to the client */
160 static void send_msg_chansess_exitstatus(struct Channel
* channel
,
161 struct ChanSess
* chansess
) {
163 dropbear_assert(chansess
->exit
.exitpid
!= -1);
164 dropbear_assert(chansess
->exit
.exitsignal
== -1);
168 buf_putbyte(ses
.writepayload
, SSH_MSG_CHANNEL_REQUEST
);
169 buf_putint(ses
.writepayload
, channel
->remotechan
);
170 buf_putstring(ses
.writepayload
, "exit-status", 11);
171 buf_putbyte(ses
.writepayload
, 0); /* boolean FALSE */
172 buf_putint(ses
.writepayload
, chansess
->exit
.exitstatus
);
178 /* send the signal causing the exit to the client */
179 static void send_msg_chansess_exitsignal(struct Channel
* channel
,
180 struct ChanSess
* chansess
) {
183 char* signame
= NULL
;
184 dropbear_assert(chansess
->exit
.exitpid
!= -1);
185 dropbear_assert(chansess
->exit
.exitsignal
> 0);
187 TRACE(("send_msg_chansess_exitsignal %d", chansess
->exit
.exitsignal
))
191 /* we check that we can match a signal name, otherwise
192 * don't send anything */
193 for (i
= 0; signames
[i
].name
!= NULL
; i
++) {
194 if (signames
[i
].signal
== chansess
->exit
.exitsignal
) {
195 signame
= signames
[i
].name
;
200 if (signame
== NULL
) {
204 buf_putbyte(ses
.writepayload
, SSH_MSG_CHANNEL_REQUEST
);
205 buf_putint(ses
.writepayload
, channel
->remotechan
);
206 buf_putstring(ses
.writepayload
, "exit-signal", 11);
207 buf_putbyte(ses
.writepayload
, 0); /* boolean FALSE */
208 buf_putstring(ses
.writepayload
, signame
, strlen(signame
));
209 buf_putbyte(ses
.writepayload
, chansess
->exit
.exitcore
);
210 buf_putstring(ses
.writepayload
, "", 0); /* error msg */
211 buf_putstring(ses
.writepayload
, "", 0); /* lang */
216 /* set up a session channel */
217 static int newchansess(struct Channel
*channel
) {
219 struct ChanSess
*chansess
;
221 TRACE(("new chansess %p", channel
))
223 dropbear_assert(channel
->typedata
== NULL
);
225 chansess
= (struct ChanSess
*)m_malloc(sizeof(struct ChanSess
));
226 chansess
->cmd
= NULL
;
227 chansess
->connection_string
= NULL
;
231 chansess
->master
= -1;
232 chansess
->slave
= -1;
233 chansess
->tty
= NULL
;
234 chansess
->term
= NULL
;
236 chansess
->exit
.exitpid
= -1;
238 channel
->typedata
= chansess
;
240 #ifndef DISABLE_X11FWD
241 chansess
->x11listener
= NULL
;
242 chansess
->x11authprot
= NULL
;
243 chansess
->x11authcookie
= NULL
;
246 #ifdef ENABLE_AGENTFWD
247 chansess
->agentlistener
= NULL
;
248 chansess
->agentfile
= NULL
;
249 chansess
->agentdir
= NULL
;
256 static struct logininfo
*
257 chansess_login_alloc(struct ChanSess
*chansess
) {
258 struct logininfo
* li
;
259 li
= login_alloc_entry(chansess
->pid
, ses
.authstate
.username
,
260 svr_ses
.remotehost
, chansess
->tty
);
264 /* clean a session channel */
265 static void closechansess(struct Channel
*channel
) {
267 struct ChanSess
*chansess
;
269 struct logininfo
*li
;
271 TRACE(("enter closechansess"))
273 chansess
= (struct ChanSess
*)channel
->typedata
;
275 if (chansess
== NULL
) {
276 TRACE(("leave closechansess: chansess == NULL"))
280 send_exitsignalstatus(channel
);
282 m_free(chansess
->cmd
);
283 m_free(chansess
->term
);
285 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
286 m_free(chansess
->original_command
);
290 /* write the utmp/wtmp login record */
291 li
= chansess_login_alloc(chansess
);
293 login_free_entry(li
);
295 pty_release(chansess
->tty
);
296 m_free(chansess
->tty
);
299 #ifndef DISABLE_X11FWD
300 x11cleanup(chansess
);
303 #ifdef ENABLE_AGENTFWD
304 svr_agentcleanup(chansess
);
307 /* clear child pid entries */
308 for (i
= 0; i
< svr_ses
.childpidsize
; i
++) {
309 if (svr_ses
.childpids
[i
].chansess
== chansess
) {
310 dropbear_assert(svr_ses
.childpids
[i
].pid
> 0);
311 TRACE(("closing pid %d", svr_ses
.childpids
[i
].pid
))
312 TRACE(("exitpid is %d", chansess
->exit
.exitpid
))
313 svr_ses
.childpids
[i
].pid
= -1;
314 svr_ses
.childpids
[i
].chansess
= NULL
;
320 TRACE(("leave closechansess"))
323 /* Handle requests for a channel. These can be execution requests,
324 * or x11/authagent forwarding. These are passed to appropriate handlers */
325 static void chansessionrequest(struct Channel
*channel
) {
327 unsigned char * type
= NULL
;
328 unsigned int typelen
;
329 unsigned char wantreply
;
331 struct ChanSess
*chansess
;
333 TRACE(("enter chansessionrequest"))
335 type
= buf_getstring(ses
.payload
, &typelen
);
336 wantreply
= buf_getbool(ses
.payload
);
338 if (typelen
> MAX_NAME_LEN
) {
339 TRACE(("leave chansessionrequest: type too long")) /* XXX send error?*/
343 chansess
= (struct ChanSess
*)channel
->typedata
;
344 dropbear_assert(chansess
!= NULL
);
345 TRACE(("type is %s", type
))
347 if (strcmp(type
, "window-change") == 0) {
348 ret
= sessionwinchange(chansess
);
349 } else if (strcmp(type
, "shell") == 0) {
350 ret
= sessioncommand(channel
, chansess
, 0, 0);
351 } else if (strcmp(type
, "pty-req") == 0) {
352 ret
= sessionpty(chansess
);
353 } else if (strcmp(type
, "exec") == 0) {
354 ret
= sessioncommand(channel
, chansess
, 1, 0);
355 } else if (strcmp(type
, "subsystem") == 0) {
356 ret
= sessioncommand(channel
, chansess
, 1, 1);
357 #ifndef DISABLE_X11FWD
358 } else if (strcmp(type
, "x11-req") == 0) {
359 ret
= x11req(chansess
);
361 #ifdef ENABLE_AGENTFWD
362 } else if (strcmp(type
, "auth-agent-req@openssh.com") == 0) {
363 ret
= svr_agentreq(chansess
);
365 } else if (strcmp(type
, "signal") == 0) {
366 ret
= sessionsignal(chansess
);
368 /* etc, todo "env", "subsystem" */
374 if (ret
== DROPBEAR_SUCCESS
) {
375 send_msg_channel_success(channel
);
377 send_msg_channel_failure(channel
);
382 TRACE(("leave chansessionrequest"))
386 /* Send a signal to a session's process as requested by the client*/
387 static int sessionsignal(struct ChanSess
*chansess
) {
390 unsigned char* signame
= NULL
;
393 if (chansess
->pid
== 0) {
394 /* haven't got a process pid yet */
395 return DROPBEAR_FAILURE
;
398 signame
= buf_getstring(ses
.payload
, NULL
);
401 while (signames
[i
].name
!= 0) {
402 if (strcmp(signames
[i
].name
, signame
) == 0) {
403 sig
= signames
[i
].signal
;
413 return DROPBEAR_FAILURE
;
416 if (kill(chansess
->pid
, sig
) < 0) {
417 return DROPBEAR_FAILURE
;
420 return DROPBEAR_SUCCESS
;
423 /* Let the process know that the window size has changed, as notified from the
424 * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
425 static int sessionwinchange(struct ChanSess
*chansess
) {
427 int termc
, termr
, termw
, termh
;
429 if (chansess
->master
< 0) {
430 /* haven't got a pty yet */
431 return DROPBEAR_FAILURE
;
434 termc
= buf_getint(ses
.payload
);
435 termr
= buf_getint(ses
.payload
);
436 termw
= buf_getint(ses
.payload
);
437 termh
= buf_getint(ses
.payload
);
439 pty_change_window_size(chansess
->master
, termr
, termc
, termw
, termh
);
441 return DROPBEAR_SUCCESS
;
444 static void get_termmodes(struct ChanSess
*chansess
) {
446 struct termios termio
;
447 unsigned char opcode
;
449 const struct TermCode
* termcode
;
452 TRACE(("enter get_termmodes"))
455 /* We'll ignore errors and continue if we can't set modes.
456 * We're ignoring baud rates since they seem evil */
457 if (tcgetattr(chansess
->master
, &termio
) == -1) {
461 len
= buf_getint(ses
.payload
);
462 TRACE(("term mode str %d p->l %d p->p %d",
463 len
, ses
.payload
->len
, ses
.payload
->pos
));
464 if (len
!= ses
.payload
->len
- ses
.payload
->pos
) {
465 dropbear_exit("Bad term mode string");
469 TRACE(("leave get_termmodes: empty terminal modes string"))
473 while (((opcode
= buf_getbyte(ses
.payload
)) != 0x00) && opcode
<= 159) {
475 /* must be before checking type, so that value is consumed even if
477 value
= buf_getint(ses
.payload
);
479 /* handle types of code */
480 if (opcode
> MAX_TERMCODE
) {
483 termcode
= &termcodes
[(unsigned int)opcode
];
486 switch (termcode
->type
) {
491 case TERMCODE_CONTROLCHAR
:
492 termio
.c_cc
[termcode
->mapcode
] = value
;
497 termio
.c_iflag
|= termcode
->mapcode
;
499 termio
.c_iflag
&= ~(termcode
->mapcode
);
503 case TERMCODE_OUTPUT
:
505 termio
.c_oflag
|= termcode
->mapcode
;
507 termio
.c_oflag
&= ~(termcode
->mapcode
);
513 termio
.c_lflag
|= termcode
->mapcode
;
515 termio
.c_lflag
&= ~(termcode
->mapcode
);
519 case TERMCODE_CONTROL
:
521 termio
.c_cflag
|= termcode
->mapcode
;
523 termio
.c_cflag
&= ~(termcode
->mapcode
);
529 if (tcsetattr(chansess
->master
, TCSANOW
, &termio
) < 0) {
530 dropbear_log(LOG_INFO
, "Error setting terminal attributes");
532 TRACE(("leave get_termmodes"))
535 /* Set up a session pty which will be used to execute the shell or program.
536 * The pty is allocated now, and kept for when the shell/program executes.
537 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
538 static int sessionpty(struct ChanSess
* chansess
) {
540 unsigned int termlen
;
541 unsigned char namebuf
[65];
542 struct passwd
* pw
= NULL
;
544 TRACE(("enter sessionpty"))
546 if (!svr_pubkey_allows_pty()) {
547 TRACE(("leave sessionpty : pty forbidden by public key option"))
548 return DROPBEAR_FAILURE
;
551 chansess
->term
= buf_getstring(ses
.payload
, &termlen
);
552 if (termlen
> MAX_TERM_LEN
) {
553 /* TODO send disconnect ? */
554 TRACE(("leave sessionpty: term len too long"))
555 return DROPBEAR_FAILURE
;
558 /* allocate the pty */
559 if (chansess
->master
!= -1) {
560 dropbear_exit("Multiple pty requests");
562 if (pty_allocate(&chansess
->master
, &chansess
->slave
, namebuf
, 64) == 0) {
563 TRACE(("leave sessionpty: failed to allocate pty"))
564 return DROPBEAR_FAILURE
;
567 chansess
->tty
= (char*)m_strdup(namebuf
);
568 if (!chansess
->tty
) {
569 dropbear_exit("Out of memory"); /* TODO disconnect */
572 pw
= getpwnam(ses
.authstate
.pw_name
);
574 dropbear_exit("getpwnam failed after succeeding previously");
575 pty_setowner(pw
, chansess
->tty
);
577 /* Set up the rows/col counts */
578 sessionwinchange(chansess
);
580 /* Read the terminal modes */
581 get_termmodes(chansess
);
583 TRACE(("leave sessionpty"))
584 return DROPBEAR_SUCCESS
;
587 static char* make_connection_string() {
588 char *local_ip
, *local_port
, *remote_ip
, *remote_port
;
591 get_socket_address(ses
.sock_in
, &local_ip
, &local_port
, &remote_ip
, &remote_port
, 0);
592 len
= strlen(local_ip
) + strlen(local_port
) + strlen(remote_ip
) + strlen(remote_port
) + 4;
594 snprintf(ret
, len
, "%s %s %s %s", remote_ip
, remote_port
, local_ip
, local_port
);
602 /* Handle a command request from the client. This is used for both shell
603 * and command-execution requests, and passes the command to
604 * noptycommand or ptycommand as appropriate.
605 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
606 static int sessioncommand(struct Channel
*channel
, struct ChanSess
*chansess
,
607 int iscmd
, int issubsys
) {
612 TRACE(("enter sessioncommand"))
614 if (chansess
->cmd
!= NULL
) {
615 /* Note that only one command can _succeed_. The client might try
616 * one command (which fails), then try another. Ie fallback
617 * from sftp to scp */
618 return DROPBEAR_FAILURE
;
623 if (chansess
->cmd
== NULL
) {
624 chansess
->cmd
= buf_getstring(ses
.payload
, &cmdlen
);
626 if (cmdlen
> MAX_CMD_LEN
) {
627 m_free(chansess
->cmd
);
628 /* TODO - send error - too long ? */
629 return DROPBEAR_FAILURE
;
633 #ifdef SFTPSERVER_PATH
634 if ((cmdlen
== 4) && strncmp(chansess
->cmd
, "sftp", 4) == 0) {
635 m_free(chansess
->cmd
);
636 chansess
->cmd
= m_strdup(SFTPSERVER_PATH
);
640 m_free(chansess
->cmd
);
641 return DROPBEAR_FAILURE
;
646 /* take public key option 'command' into account */
647 svr_pubkey_set_forced_command(chansess
);
651 dropbear_log(LOG_INFO
, "User %s executing '%s'",
652 ses
.authstate
.pw_name
, chansess
->cmd
);
654 dropbear_log(LOG_INFO
, "User %s executing login shell",
655 ses
.authstate
.pw_name
);
659 /* uClinux will vfork(), so there'll be a race as
660 connection_string is freed below. */
662 chansess
->connection_string
= make_connection_string();
665 if (chansess
->term
== NULL
) {
667 ret
= noptycommand(channel
, chansess
);
670 ret
= ptycommand(channel
, chansess
);
674 m_free(chansess
->connection_string
);
677 if (ret
== DROPBEAR_FAILURE
) {
678 m_free(chansess
->cmd
);
683 /* Execute a command and set up redirection of stdin/stdout/stderr without a
685 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
686 static int noptycommand(struct Channel
*channel
, struct ChanSess
*chansess
) {
689 TRACE(("enter noptycommand"))
690 ret
= spawn_command(execchild
, chansess
,
691 &channel
->writefd
, &channel
->readfd
, &channel
->errfd
,
694 if (ret
== DROPBEAR_FAILURE
) {
698 ses
.maxfd
= MAX(ses
.maxfd
, channel
->writefd
);
699 ses
.maxfd
= MAX(ses
.maxfd
, channel
->readfd
);
700 ses
.maxfd
= MAX(ses
.maxfd
, channel
->errfd
);
704 addchildpid(chansess
, chansess
->pid
);
706 if (svr_ses
.lastexit
.exitpid
!= -1) {
708 TRACE(("parent side: lastexitpid is %d", svr_ses
.lastexit
.exitpid
))
709 /* The child probably exited and the signal handler triggered
710 * possibly before we got around to adding the childpid. So we fill
711 * out its data manually */
712 for (i
= 0; i
< svr_ses
.childpidsize
; i
++) {
713 if (svr_ses
.childpids
[i
].pid
== svr_ses
.lastexit
.exitpid
) {
714 TRACE(("found match for lastexitpid"))
715 svr_ses
.childpids
[i
].chansess
->exit
= svr_ses
.lastexit
;
716 svr_ses
.lastexit
.exitpid
= -1;
722 TRACE(("leave noptycommand"))
723 return DROPBEAR_SUCCESS
;
726 /* Execute a command or shell within a pty environment, and set up
727 * redirection as appropriate.
728 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
729 static int ptycommand(struct Channel
*channel
, struct ChanSess
*chansess
) {
732 struct logininfo
*li
= NULL
;
734 buffer
* motdbuf
= NULL
;
737 char *hushpath
= NULL
;
740 TRACE(("enter ptycommand"))
742 /* we need to have a pty allocated */
743 if (chansess
->master
== -1 || chansess
->tty
== NULL
) {
744 dropbear_log(LOG_WARNING
, "No pty was allocated, couldn't execute");
745 return DROPBEAR_FAILURE
;
754 return DROPBEAR_FAILURE
;
759 TRACE(("back to normal sigchld"))
760 /* Revert to normal sigchld handling */
761 if (signal(SIGCHLD
, SIG_DFL
) == SIG_ERR
) {
762 dropbear_exit("signal() error");
765 /* redirect stdin/stdout/stderr */
766 close(chansess
->master
);
768 pty_make_controlling_tty(&chansess
->slave
, chansess
->tty
);
770 if ((dup2(chansess
->slave
, STDIN_FILENO
) < 0) ||
771 (dup2(chansess
->slave
, STDERR_FILENO
) < 0) ||
772 (dup2(chansess
->slave
, STDOUT_FILENO
) < 0)) {
773 TRACE(("leave ptycommand: error redirecting filedesc"))
774 return DROPBEAR_FAILURE
;
777 close(chansess
->slave
);
779 /* write the utmp/wtmp login record - must be after changing the
780 * terminal used for stdout with the dup2 above */
781 li
= chansess_login_alloc(chansess
);
783 login_free_entry(li
);
786 if (svr_opts
.domotd
) {
787 /* don't show the motd if ~/.hushlogin exists */
789 /* 12 == strlen("/.hushlogin\0") */
790 len
= strlen(ses
.authstate
.pw_dir
) + 12;
792 hushpath
= m_malloc(len
);
793 snprintf(hushpath
, len
, "%s/.hushlogin", ses
.authstate
.pw_dir
);
795 if (stat(hushpath
, &sb
) < 0) {
796 /* more than a screenful is stupid IMHO */
797 motdbuf
= buf_new(80 * 25);
798 if (buf_readfile(motdbuf
, MOTD_FILENAME
) == DROPBEAR_SUCCESS
) {
799 buf_setpos(motdbuf
, 0);
800 while (motdbuf
->pos
!= motdbuf
->len
) {
801 len
= motdbuf
->len
- motdbuf
->pos
;
802 len
= write(STDOUT_FILENO
,
803 buf_getptr(motdbuf
, len
), len
);
804 buf_incrpos(motdbuf
, len
);
818 TRACE(("continue ptycommand: parent"))
821 /* add a child pid */
822 addchildpid(chansess
, pid
);
824 close(chansess
->slave
);
825 channel
->writefd
= chansess
->master
;
826 channel
->readfd
= chansess
->master
;
827 /* don't need to set stderr here */
828 ses
.maxfd
= MAX(ses
.maxfd
, chansess
->master
);
830 setnonblocking(chansess
->master
);
834 TRACE(("leave ptycommand"))
835 return DROPBEAR_SUCCESS
;
838 /* Add the pid of a child to the list for exit-handling */
839 static void addchildpid(struct ChanSess
*chansess
, pid_t pid
) {
842 for (i
= 0; i
< svr_ses
.childpidsize
; i
++) {
843 if (svr_ses
.childpids
[i
].pid
== -1) {
848 /* need to increase size */
849 if (i
== svr_ses
.childpidsize
) {
850 svr_ses
.childpids
= (struct ChildPid
*)m_realloc(svr_ses
.childpids
,
851 sizeof(struct ChildPid
) * (svr_ses
.childpidsize
+1));
852 svr_ses
.childpidsize
++;
855 svr_ses
.childpids
[i
].pid
= pid
;
856 svr_ses
.childpids
[i
].chansess
= chansess
;
860 /* Clean up, drop to user privileges, set up the environment and execute
861 * the command/shell. This function does not return. */
862 static void execchild(void *user_data
) {
863 struct ChanSess
*chansess
= user_data
;
864 char *usershell
= NULL
;
866 /* with uClinux we'll have vfork()ed, so don't want to overwrite the
867 * hostkey. can't think of a workaround to clear it */
869 /* wipe the hostkey */
870 sign_key_free(svr_opts
.hostkey
);
871 svr_opts
.hostkey
= NULL
;
873 /* overwrite the prng state */
877 /* clear environment */
878 /* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
879 * etc. This is hazardous, so should only be used for debugging. */
880 #ifndef DEBUG_VALGRIND
883 #else /* don't HAVE_CLEARENV */
888 #endif /* HAVE_CLEARENV */
889 #endif /* DEBUG_VALGRIND */
891 /* We can only change uid/gid as root ... */
894 if ((setgid(ses
.authstate
.pw_gid
) < 0) ||
895 (initgroups(ses
.authstate
.pw_name
,
896 ses
.authstate
.pw_gid
) < 0)) {
897 dropbear_exit("Error changing user group");
899 if (setuid(ses
.authstate
.pw_uid
) < 0) {
900 dropbear_exit("Error changing user");
903 /* ... but if the daemon is the same uid as the requested uid, we don't
906 /* XXX - there is a minor issue here, in that if there are multiple
907 * usernames with the same uid, but differing groups, then the
908 * differing groups won't be set (as with initgroups()). The solution
909 * is for the sysadmin not to give out the UID twice */
910 if (getuid() != ses
.authstate
.pw_uid
) {
911 dropbear_exit("Couldn't change user as non-root");
916 addnewvar("USER", ses
.authstate
.pw_name
);
917 addnewvar("LOGNAME", ses
.authstate
.pw_name
);
918 addnewvar("HOME", ses
.authstate
.pw_dir
);
919 addnewvar("SHELL", get_user_shell());
920 addnewvar("PATH", DEFAULT_PATH
);
921 if (chansess
->term
!= NULL
) {
922 addnewvar("TERM", chansess
->term
);
926 addnewvar("SSH_TTY", chansess
->tty
);
929 if (chansess
->connection_string
) {
930 addnewvar("SSH_CONNECTION", chansess
->connection_string
);
933 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
934 if (chansess
->original_command
) {
935 addnewvar("SSH_ORIGINAL_COMMAND", chansess
->original_command
);
939 /* change directory */
940 if (chdir(ses
.authstate
.pw_dir
) < 0) {
941 dropbear_exit("Error changing directory");
944 #ifndef DISABLE_X11FWD
945 /* set up X11 forwarding if enabled */
946 x11setauth(chansess
);
948 #ifdef ENABLE_AGENTFWD
949 /* set up agent env variable */
950 svr_agentset(chansess
);
953 usershell
= m_strdup(get_user_shell());
954 run_shell_command(chansess
->cmd
, ses
.maxfd
, usershell
);
956 /* only reached on error */
957 dropbear_exit("Child failed");
960 const struct ChanType svrchansess
= {
962 "session", /* name */
963 newchansess
, /* inithandler */
964 sesscheckclose
, /* checkclosehandler */
965 chansessionrequest
, /* reqhandler */
966 closechansess
, /* closehandler */
970 /* Set up the general chansession environment, in particular child-exit
972 void svr_chansessinitialise() {
974 struct sigaction sa_chld
;
976 /* single child process intially */
977 svr_ses
.childpids
= (struct ChildPid
*)m_malloc(sizeof(struct ChildPid
));
978 svr_ses
.childpids
[0].pid
= -1; /* unused */
979 svr_ses
.childpids
[0].chansess
= NULL
;
980 svr_ses
.childpidsize
= 1;
981 svr_ses
.lastexit
.exitpid
= -1; /* Nothing has exited yet */
982 sa_chld
.sa_handler
= sesssigchild_handler
;
983 sa_chld
.sa_flags
= SA_NOCLDSTOP
;
984 sigemptyset(&sa_chld
.sa_mask
);
985 if (sigaction(SIGCHLD
, &sa_chld
, NULL
) < 0) {
986 dropbear_exit("signal() error");
991 /* add a new environment variable, allocating space for the entry */
992 void addnewvar(const char* param
, const char* var
) {
997 plen
= strlen(param
);
1000 newvar
= m_malloc(plen
+ vlen
+ 2); /* 2 is for '=' and '\0' */
1001 memcpy(newvar
, param
, plen
);
1003 memcpy(&newvar
[plen
+1], var
, vlen
);
1004 newvar
[plen
+vlen
+1] = '\0';
1005 /* newvar is leaked here, but that's part of putenv()'s semantics */
1006 if (putenv(newvar
) < 0) {
1007 dropbear_exit("environ error");