comparison svr-chansession.c @ 4:fe6bca95afa7

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