comparison svr-chansession.c @ 478:d4f32c3443ac dbclient-netcat-alike

propagate from branch 'au.asn.ucc.matt.dropbear' (head f21045c791002d81fc6b8dde6537ea481e513eb2) to branch 'au.asn.ucc.matt.dropbear.dbclient-netcat-alike' (head d1f69334581dc4c35f9ca16aa5355074c9dd315d)
author Matt Johnston <matt@ucc.asn.au>
date Sun, 14 Sep 2008 06:47:51 +0000
parents f11544d96354
children 52a644e7b8e1 c302c7383282
comparison
equal deleted inserted replaced
296:6b41e2cbf071 478:d4f32c3443ac
57 static void send_exitsignalstatus(struct Channel *channel); 57 static void send_exitsignalstatus(struct Channel *channel);
58 static void send_msg_chansess_exitstatus(struct Channel * channel, 58 static void send_msg_chansess_exitstatus(struct Channel * channel,
59 struct ChanSess * chansess); 59 struct ChanSess * chansess);
60 static void send_msg_chansess_exitsignal(struct Channel * channel, 60 static void send_msg_chansess_exitsignal(struct Channel * channel,
61 struct ChanSess * chansess); 61 struct ChanSess * chansess);
62 static int sesscheckclose(struct Channel *channel);
63 static void get_termmodes(struct ChanSess *chansess); 62 static void get_termmodes(struct ChanSess *chansess);
64 63
65 64
66 /* required to clear environment */ 65 /* required to clear environment */
67 extern char** environ; 66 extern char** environ;
68 67
69 static int sesscheckclose(struct Channel *channel) { 68 static int sesscheckclose(struct Channel *channel) {
70 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; 69 struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
71 return chansess->exit.exitpid >= 0; 70 TRACE(("sesscheckclose, pid is %d", chansess->exit.exitpid))
71 return chansess->exit.exitpid != -1;
72 } 72 }
73 73
74 /* Handler for childs exiting, store the state for return to the client */ 74 /* Handler for childs exiting, store the state for return to the client */
75 75
76 /* There's a particular race we have to watch out for: if the forked child 76 /* There's a particular race we have to watch out for: if the forked child
87 struct sigaction sa_chld; 87 struct sigaction sa_chld;
88 struct exitinfo *exit = NULL; 88 struct exitinfo *exit = NULL;
89 89
90 TRACE(("enter sigchld handler")) 90 TRACE(("enter sigchld handler"))
91 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 91 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
92 TRACE(("sigchld handler: pid %d", pid))
93
94 exit = NULL;
92 /* find the corresponding chansess */ 95 /* find the corresponding chansess */
93 for (i = 0; i < svr_ses.childpidsize; i++) { 96 for (i = 0; i < svr_ses.childpidsize; i++) {
94 if (svr_ses.childpids[i].pid == pid) { 97 if (svr_ses.childpids[i].pid == pid) {
95 98 TRACE(("found match session"));
96 exit = &svr_ses.childpids[i].chansess->exit; 99 exit = &svr_ses.childpids[i].chansess->exit;
97 break; 100 break;
98 } 101 }
99 } 102 }
100 103
101 /* If the pid wasn't matched, then we might have hit the race mentioned 104 /* If the pid wasn't matched, then we might have hit the race mentioned
102 * above. So we just store the info for the parent to deal with */ 105 * above. So we just store the info for the parent to deal with */
103 if (i == svr_ses.childpidsize) { 106 if (exit == NULL) {
107 TRACE(("using lastexit"));
104 exit = &svr_ses.lastexit; 108 exit = &svr_ses.lastexit;
105 } 109 }
106 110
107 exit->exitpid = pid; 111 exit->exitpid = pid;
108 if (WIFEXITED(status)) { 112 if (WIFEXITED(status)) {
117 #endif 121 #endif
118 } else { 122 } else {
119 /* we use this to determine how pid exited */ 123 /* we use this to determine how pid exited */
120 exit->exitsignal = -1; 124 exit->exitsignal = -1;
121 } 125 }
122 exit = NULL; 126
123 } 127 /* Make sure that the main select() loop wakes up */
124 128 while (1) {
125 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
126 sa_chld.sa_handler = sesssigchild_handler; 138 sa_chld.sa_handler = sesssigchild_handler;
127 sa_chld.sa_flags = SA_NOCLDSTOP; 139 sa_chld.sa_flags = SA_NOCLDSTOP;
128 sigaction(SIGCHLD, &sa_chld, NULL); 140 sigaction(SIGCHLD, &sa_chld, NULL);
129 TRACE(("leave sigchld handler")) 141 TRACE(("leave sigchld handler"))
130 } 142 }
131 143
132 /* send the exit status or the signal causing termination for a session */ 144 /* send the exit status or the signal causing termination for a session */
133 /* XXX server */
134 static void send_exitsignalstatus(struct Channel *channel) { 145 static void send_exitsignalstatus(struct Channel *channel) {
135 146
136 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; 147 struct ChanSess *chansess = (struct ChanSess*)channel->typedata;
137 148
138 if (chansess->exit.exitpid >= 0) { 149 if (chansess->exit.exitpid >= 0) {
167 static void send_msg_chansess_exitsignal(struct Channel * channel, 178 static void send_msg_chansess_exitsignal(struct Channel * channel,
168 struct ChanSess * chansess) { 179 struct ChanSess * chansess) {
169 180
170 int i; 181 int i;
171 char* signame = NULL; 182 char* signame = NULL;
172
173 dropbear_assert(chansess->exit.exitpid != -1); 183 dropbear_assert(chansess->exit.exitpid != -1);
174 dropbear_assert(chansess->exit.exitsignal > 0); 184 dropbear_assert(chansess->exit.exitsignal > 0);
185
186 TRACE(("send_msg_chansess_exitsignal %d", chansess->exit.exitsignal))
175 187
176 CHECKCLEARTOWRITE(); 188 CHECKCLEARTOWRITE();
177 189
178 /* we check that we can match a signal name, otherwise 190 /* we check that we can match a signal name, otherwise
179 * don't send anything */ 191 * don't send anything */
242 254
243 struct ChanSess *chansess; 255 struct ChanSess *chansess;
244 unsigned int i; 256 unsigned int i;
245 struct logininfo *li; 257 struct logininfo *li;
246 258
259 TRACE(("enter closechansess"))
260
247 chansess = (struct ChanSess*)channel->typedata; 261 chansess = (struct ChanSess*)channel->typedata;
248 262
249 send_exitsignalstatus(channel);
250
251 TRACE(("enter closechansess"))
252 if (chansess == NULL) { 263 if (chansess == NULL) {
253 TRACE(("leave closechansess: chansess == NULL")) 264 TRACE(("leave closechansess: chansess == NULL"))
254 return; 265 return;
255 } 266 }
267
268 send_exitsignalstatus(channel);
256 269
257 m_free(chansess->cmd); 270 m_free(chansess->cmd);
258 m_free(chansess->term); 271 m_free(chansess->term);
259 272
260 if (chansess->tty) { 273 if (chansess->tty) {
279 /* clear child pid entries */ 292 /* clear child pid entries */
280 for (i = 0; i < svr_ses.childpidsize; i++) { 293 for (i = 0; i < svr_ses.childpidsize; i++) {
281 if (svr_ses.childpids[i].chansess == chansess) { 294 if (svr_ses.childpids[i].chansess == chansess) {
282 dropbear_assert(svr_ses.childpids[i].pid > 0); 295 dropbear_assert(svr_ses.childpids[i].pid > 0);
283 TRACE(("closing pid %d", svr_ses.childpids[i].pid)) 296 TRACE(("closing pid %d", svr_ses.childpids[i].pid))
284 TRACE(("exitpid = %d", chansess->exit.exitpid)) 297 TRACE(("exitpid is %d", chansess->exit.exitpid))
285 svr_ses.childpids[i].pid = -1; 298 svr_ses.childpids[i].pid = -1;
286 svr_ses.childpids[i].chansess = NULL; 299 svr_ses.childpids[i].chansess = NULL;
287 } 300 }
288 } 301 }
289 302
408 termw = buf_getint(ses.payload); 421 termw = buf_getint(ses.payload);
409 termh = buf_getint(ses.payload); 422 termh = buf_getint(ses.payload);
410 423
411 pty_change_window_size(chansess->master, termr, termc, termw, termh); 424 pty_change_window_size(chansess->master, termr, termc, termw, termh);
412 425
413 return DROPBEAR_FAILURE; 426 return DROPBEAR_SUCCESS;
414 } 427 }
415 428
416 static void get_termmodes(struct ChanSess *chansess) { 429 static void get_termmodes(struct ChanSess *chansess) {
417 430
418 struct termios termio; 431 struct termios termio;
509 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ 522 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
510 static int sessionpty(struct ChanSess * chansess) { 523 static int sessionpty(struct ChanSess * chansess) {
511 524
512 unsigned int termlen; 525 unsigned int termlen;
513 unsigned char namebuf[65]; 526 unsigned char namebuf[65];
527 struct passwd * pw = NULL;
514 528
515 TRACE(("enter sessionpty")) 529 TRACE(("enter sessionpty"))
516 chansess->term = buf_getstring(ses.payload, &termlen); 530 chansess->term = buf_getstring(ses.payload, &termlen);
517 if (termlen > MAX_TERM_LEN) { 531 if (termlen > MAX_TERM_LEN) {
518 /* TODO send disconnect ? */ 532 /* TODO send disconnect ? */
532 chansess->tty = (char*)m_strdup(namebuf); 546 chansess->tty = (char*)m_strdup(namebuf);
533 if (!chansess->tty) { 547 if (!chansess->tty) {
534 dropbear_exit("out of memory"); /* TODO disconnect */ 548 dropbear_exit("out of memory"); /* TODO disconnect */
535 } 549 }
536 550
537 pty_setowner(ses.authstate.pw, chansess->tty); 551 pw = getpwnam(ses.authstate.pw_name);
552 if (!pw)
553 dropbear_exit("getpwnam failed after succeeding previously");
554 pty_setowner(pw, chansess->tty);
538 555
539 /* Set up the rows/col counts */ 556 /* Set up the rows/col counts */
540 sessionwinchange(chansess); 557 sessionwinchange(chansess);
541 558
542 /* Read the terminal modes */ 559 /* Read the terminal modes */
586 return DROPBEAR_FAILURE; 603 return DROPBEAR_FAILURE;
587 } 604 }
588 } 605 }
589 } 606 }
590 607
608 #ifdef LOG_COMMANDS
609 if (chansess->cmd) {
610 dropbear_log(LOG_INFO, "user %s executing '%s'",
611 ses.authstate.pw_name, chansess->cmd);
612 } else {
613 dropbear_log(LOG_INFO, "user %s executing login shell",
614 ses.authstate.pw_name);
615 }
616 #endif
617
591 if (chansess->term == NULL) { 618 if (chansess->term == NULL) {
592 /* no pty */ 619 /* no pty */
593 ret = noptycommand(channel, chansess); 620 ret = noptycommand(channel, chansess);
594 } else { 621 } else {
595 /* want pty */ 622 /* want pty */
632 if (pid < 0) 659 if (pid < 0)
633 return DROPBEAR_FAILURE; 660 return DROPBEAR_FAILURE;
634 661
635 if (!pid) { 662 if (!pid) {
636 /* child */ 663 /* child */
664
665 TRACE(("back to normal sigchld"))
666 /* Revert to normal sigchld handling */
667 if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
668 dropbear_exit("signal() error");
669 }
637 670
638 /* redirect stdin/stdout */ 671 /* redirect stdin/stdout */
639 #define FDIN 0 672 #define FDIN 0
640 #define FDOUT 1 673 #define FDOUT 1
641 if ((dup2(infds[FDIN], STDIN_FILENO) < 0) || 674 if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
657 690
658 } else { 691 } else {
659 /* parent */ 692 /* parent */
660 TRACE(("continue noptycommand: parent")) 693 TRACE(("continue noptycommand: parent"))
661 chansess->pid = pid; 694 chansess->pid = pid;
695 TRACE(("child pid is %d", pid))
662 696
663 addchildpid(chansess, pid); 697 addchildpid(chansess, pid);
664 698
665 if (svr_ses.lastexit.exitpid != -1) { 699 if (svr_ses.lastexit.exitpid != -1) {
700 TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
666 /* The child probably exited and the signal handler triggered 701 /* The child probably exited and the signal handler triggered
667 * possibly before we got around to adding the childpid. So we fill 702 * possibly before we got around to adding the childpid. So we fill
668 * out it's data manually */ 703 * out its data manually */
669 for (i = 0; i < svr_ses.childpidsize; i++) { 704 for (i = 0; i < svr_ses.childpidsize; i++) {
670 if (svr_ses.childpids[i].pid == pid) { 705 if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
706 TRACE(("found match for lastexitpid"))
671 svr_ses.childpids[i].chansess->exit = svr_ses.lastexit; 707 svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
672 svr_ses.lastexit.exitpid = -1; 708 svr_ses.lastexit.exitpid = -1;
673 } 709 }
674 } 710 }
675 } 711 }
676
677 712
678 close(infds[FDIN]); 713 close(infds[FDIN]);
679 close(outfds[FDOUT]); 714 close(outfds[FDOUT]);
680 close(errfds[FDOUT]); 715 close(errfds[FDOUT]);
681 channel->writefd = infds[FDOUT]; 716 channel->writefd = infds[FDOUT];
728 return DROPBEAR_FAILURE; 763 return DROPBEAR_FAILURE;
729 764
730 if (pid == 0) { 765 if (pid == 0) {
731 /* child */ 766 /* child */
732 767
768 TRACE(("back to normal sigchld"))
769 /* Revert to normal sigchld handling */
770 if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
771 dropbear_exit("signal() error");
772 }
773
733 /* redirect stdin/stdout/stderr */ 774 /* redirect stdin/stdout/stderr */
734 close(chansess->master); 775 close(chansess->master);
735 776
736 pty_make_controlling_tty(&chansess->slave, chansess->tty); 777 pty_make_controlling_tty(&chansess->slave, chansess->tty);
737 778
755 796
756 #ifdef DO_MOTD 797 #ifdef DO_MOTD
757 if (svr_opts.domotd) { 798 if (svr_opts.domotd) {
758 /* don't show the motd if ~/.hushlogin exists */ 799 /* don't show the motd if ~/.hushlogin exists */
759 800
760 /* 11 == strlen("/hushlogin\0") */ 801 /* 12 == strlen("/.hushlogin\0") */
761 len = strlen(ses.authstate.pw->pw_dir) + 11; 802 len = strlen(ses.authstate.pw_dir) + 12;
762 803
763 hushpath = m_malloc(len); 804 hushpath = m_malloc(len);
764 snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir); 805 snprintf(hushpath, len, "%s/.hushlogin", ses.authstate.pw_dir);
765 806
766 if (stat(hushpath, &sb) < 0) { 807 if (stat(hushpath, &sb) < 0) {
767 /* more than a screenful is stupid IMHO */ 808 /* more than a screenful is stupid IMHO */
768 motdbuf = buf_new(80 * 25); 809 motdbuf = buf_new(80 * 25);
769 if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) { 810 if (buf_readfile(motdbuf, MOTD_FILENAME) == DROPBEAR_SUCCESS) {
869 #endif /* DEBUG_VALGRIND */ 910 #endif /* DEBUG_VALGRIND */
870 911
871 /* We can only change uid/gid as root ... */ 912 /* We can only change uid/gid as root ... */
872 if (getuid() == 0) { 913 if (getuid() == 0) {
873 914
874 if ((setgid(ses.authstate.pw->pw_gid) < 0) || 915 if ((setgid(ses.authstate.pw_gid) < 0) ||
875 (initgroups(ses.authstate.pw->pw_name, 916 (initgroups(ses.authstate.pw_name,
876 ses.authstate.pw->pw_gid) < 0)) { 917 ses.authstate.pw_gid) < 0)) {
877 dropbear_exit("error changing user group"); 918 dropbear_exit("error changing user group");
878 } 919 }
879 if (setuid(ses.authstate.pw->pw_uid) < 0) { 920 if (setuid(ses.authstate.pw_uid) < 0) {
880 dropbear_exit("error changing user"); 921 dropbear_exit("error changing user");
881 } 922 }
882 } else { 923 } else {
883 /* ... but if the daemon is the same uid as the requested uid, we don't 924 /* ... but if the daemon is the same uid as the requested uid, we don't
884 * need to */ 925 * need to */
885 926
886 /* XXX - there is a minor issue here, in that if there are multiple 927 /* XXX - there is a minor issue here, in that if there are multiple
887 * usernames with the same uid, but differing groups, then the 928 * usernames with the same uid, but differing groups, then the
888 * differing groups won't be set (as with initgroups()). The solution 929 * differing groups won't be set (as with initgroups()). The solution
889 * is for the sysadmin not to give out the UID twice */ 930 * is for the sysadmin not to give out the UID twice */
890 if (getuid() != ses.authstate.pw->pw_uid) { 931 if (getuid() != ses.authstate.pw_uid) {
891 dropbear_exit("couldn't change user as non-root"); 932 dropbear_exit("couldn't change user as non-root");
892 } 933 }
893 } 934 }
894 935
895 /* an empty shell should be interpreted as "/bin/sh" */ 936 /* an empty shell should be interpreted as "/bin/sh" */
896 if (ses.authstate.pw->pw_shell[0] == '\0') { 937 if (ses.authstate.pw_shell[0] == '\0') {
897 usershell = "/bin/sh"; 938 usershell = "/bin/sh";
898 } else { 939 } else {
899 usershell = ses.authstate.pw->pw_shell; 940 usershell = ses.authstate.pw_shell;
900 } 941 }
901 942
902 /* set env vars */ 943 /* set env vars */
903 addnewvar("USER", ses.authstate.pw->pw_name); 944 addnewvar("USER", ses.authstate.pw_name);
904 addnewvar("LOGNAME", ses.authstate.pw->pw_name); 945 addnewvar("LOGNAME", ses.authstate.pw_name);
905 addnewvar("HOME", ses.authstate.pw->pw_dir); 946 addnewvar("HOME", ses.authstate.pw_dir);
906 addnewvar("SHELL", usershell); 947 addnewvar("SHELL", usershell);
907 if (chansess->term != NULL) { 948 if (chansess->term != NULL) {
908 addnewvar("TERM", chansess->term); 949 addnewvar("TERM", chansess->term);
909 } 950 }
910 951
911 /* change directory */ 952 /* change directory */
912 if (chdir(ses.authstate.pw->pw_dir) < 0) { 953 if (chdir(ses.authstate.pw_dir) < 0) {
913 dropbear_exit("error changing directory"); 954 dropbear_exit("error changing directory");
914 } 955 }
915 956
916 #ifndef DISABLE_X11FWD 957 #ifndef DISABLE_X11FWD
917 /* set up X11 forwarding if enabled */ 958 /* set up X11 forwarding if enabled */
995 newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */ 1036 newvar = m_malloc(plen + vlen + 2); /* 2 is for '=' and '\0' */
996 memcpy(newvar, param, plen); 1037 memcpy(newvar, param, plen);
997 newvar[plen] = '='; 1038 newvar[plen] = '=';
998 memcpy(&newvar[plen+1], var, vlen); 1039 memcpy(&newvar[plen+1], var, vlen);
999 newvar[plen+vlen+1] = '\0'; 1040 newvar[plen+vlen+1] = '\0';
1041 /* newvar is leaked here, but that's part of putenv()'s semantics */
1000 if (putenv(newvar) < 0) { 1042 if (putenv(newvar) < 0) {
1001 dropbear_exit("environ error"); 1043 dropbear_exit("environ error");
1002 } 1044 }
1003 } 1045 }