Mercurial > dropbear
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 } |