Mercurial > dropbear
comparison svr-chansession.c @ 1511:5916af64acd4 fuzz
merge from main
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 17 Feb 2018 19:29:51 +0800 |
parents | 47f36d8565cf |
children | 79eef94ccea9 |
comparison
equal
deleted
inserted
replaced
1457:32f990cc96b1 | 1511:5916af64acd4 |
---|---|
41 /* Handles sessions (either shells or programs) requested by the client */ | 41 /* Handles sessions (either shells or programs) requested by the client */ |
42 | 42 |
43 static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, | 43 static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, |
44 int iscmd, int issubsys); | 44 int iscmd, int issubsys); |
45 static int sessionpty(struct ChanSess * chansess); | 45 static int sessionpty(struct ChanSess * chansess); |
46 static int sessionsignal(struct ChanSess *chansess); | 46 static int sessionsignal(const struct ChanSess *chansess); |
47 static int noptycommand(struct Channel *channel, struct ChanSess *chansess); | 47 static int noptycommand(struct Channel *channel, struct ChanSess *chansess); |
48 static int ptycommand(struct Channel *channel, struct ChanSess *chansess); | 48 static int ptycommand(struct Channel *channel, struct ChanSess *chansess); |
49 static int sessionwinchange(struct ChanSess *chansess); | 49 static int sessionwinchange(const struct ChanSess *chansess); |
50 static void execchild(void *user_data_chansess); | 50 static void execchild(const void *user_data_chansess); |
51 static void addchildpid(struct ChanSess *chansess, pid_t pid); | 51 static void addchildpid(struct ChanSess *chansess, pid_t pid); |
52 static void sesssigchild_handler(int val); | 52 static void sesssigchild_handler(int val); |
53 static void closechansess(struct Channel *channel); | 53 static void closechansess(const struct Channel *channel); |
54 static int newchansess(struct Channel *channel); | 54 static int newchansess(struct Channel *channel); |
55 static void chansessionrequest(struct Channel *channel); | 55 static void chansessionrequest(struct Channel *channel); |
56 static int sesscheckclose(struct Channel *channel); | 56 static int sesscheckclose(const struct Channel *channel); |
57 | 57 |
58 static void send_exitsignalstatus(struct Channel *channel); | 58 static void send_exitsignalstatus(const struct Channel *channel); |
59 static void send_msg_chansess_exitstatus(struct Channel * channel, | 59 static void send_msg_chansess_exitstatus(const struct Channel * channel, |
60 struct ChanSess * chansess); | 60 const struct ChanSess * chansess); |
61 static void send_msg_chansess_exitsignal(struct Channel * channel, | 61 static void send_msg_chansess_exitsignal(const struct Channel * channel, |
62 struct ChanSess * chansess); | 62 const struct ChanSess * chansess); |
63 static void get_termmodes(struct ChanSess *chansess); | 63 static void get_termmodes(const struct ChanSess *chansess); |
64 | 64 |
65 const struct ChanType svrchansess = { | 65 const struct ChanType svrchansess = { |
66 0, /* sepfds */ | 66 0, /* sepfds */ |
67 "session", /* name */ | 67 "session", /* name */ |
68 newchansess, /* inithandler */ | 68 newchansess, /* inithandler */ |
72 }; | 72 }; |
73 | 73 |
74 /* required to clear environment */ | 74 /* required to clear environment */ |
75 extern char** environ; | 75 extern char** environ; |
76 | 76 |
77 static int sesscheckclose(struct Channel *channel) { | 77 static int sesscheckclose(const struct Channel *channel) { |
78 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; | 78 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; |
79 TRACE(("sesscheckclose, pid is %d", chansess->exit.exitpid)) | 79 TRACE(("sesscheckclose, pid is %d", chansess->exit.exitpid)) |
80 return chansess->exit.exitpid != -1; | 80 return chansess->exit.exitpid != -1; |
81 } | |
82 | |
83 void svr_chansess_checksignal(void) { | |
84 int status; | |
85 pid_t pid; | |
86 | |
87 if (!ses.channel_signal_pending) { | |
88 return; | |
89 } | |
90 | |
91 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { | |
92 unsigned int i; | |
93 struct exitinfo *ex = NULL; | |
94 TRACE(("sigchld handler: pid %d", pid)) | |
95 | |
96 ex = NULL; | |
97 /* find the corresponding chansess */ | |
98 for (i = 0; i < svr_ses.childpidsize; i++) { | |
99 if (svr_ses.childpids[i].pid == pid) { | |
100 TRACE(("found match session")); | |
101 ex = &svr_ses.childpids[i].chansess->exit; | |
102 break; | |
103 } | |
104 } | |
105 | |
106 /* If the pid wasn't matched, then we might have hit the race mentioned | |
107 * above. So we just store the info for the parent to deal with */ | |
108 if (ex == NULL) { | |
109 TRACE(("using lastexit")); | |
110 ex = &svr_ses.lastexit; | |
111 } | |
112 | |
113 ex->exitpid = pid; | |
114 if (WIFEXITED(status)) { | |
115 ex->exitstatus = WEXITSTATUS(status); | |
116 } | |
117 if (WIFSIGNALED(status)) { | |
118 ex->exitsignal = WTERMSIG(status); | |
119 #if !defined(AIX) && defined(WCOREDUMP) | |
120 ex->exitcore = WCOREDUMP(status); | |
121 #else | |
122 ex->exitcore = 0; | |
123 #endif | |
124 } else { | |
125 /* we use this to determine how pid exited */ | |
126 ex->exitsignal = -1; | |
127 } | |
128 | |
129 } | |
81 } | 130 } |
82 | 131 |
83 /* Handler for childs exiting, store the state for return to the client */ | 132 /* Handler for childs exiting, store the state for return to the client */ |
84 | 133 |
85 /* There's a particular race we have to watch out for: if the forked child | 134 /* There's a particular race we have to watch out for: if the forked child |
87 * gets to run, then the childpids[] array won't have the pid in it. Hence we | 136 * gets to run, then the childpids[] array won't have the pid in it. Hence we |
88 * use the svr_ses.lastexit struct to hold the exit, which is then compared by | 137 * use the svr_ses.lastexit struct to hold the exit, which is then compared by |
89 * the parent when it runs. This work correctly at least in the case of a | 138 * the parent when it runs. This work correctly at least in the case of a |
90 * single shell spawned (ie the usual case) */ | 139 * single shell spawned (ie the usual case) */ |
91 static void sesssigchild_handler(int UNUSED(dummy)) { | 140 static void sesssigchild_handler(int UNUSED(dummy)) { |
92 | |
93 int status; | |
94 pid_t pid; | |
95 unsigned int i; | |
96 struct sigaction sa_chld; | 141 struct sigaction sa_chld; |
97 struct exitinfo *exit = NULL; | |
98 | 142 |
99 const int saved_errno = errno; | 143 const int saved_errno = errno; |
100 | 144 |
101 /* Make channel handling code look for closed channels */ | |
102 ses.channel_signal_pending = 1; | |
103 | |
104 TRACE(("enter sigchld handler")) | 145 TRACE(("enter sigchld handler")) |
105 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { | 146 |
106 TRACE(("sigchld handler: pid %d", pid)) | 147 /* Make sure that the main select() loop wakes up */ |
107 | 148 while (1) { |
108 exit = NULL; | 149 /* isserver is just a random byte to write. We can't do anything |
109 /* find the corresponding chansess */ | 150 about an error so should just ignore it */ |
110 for (i = 0; i < svr_ses.childpidsize; i++) { | 151 if (write(ses.signal_pipe[1], &ses.isserver, 1) == 1 |
111 if (svr_ses.childpids[i].pid == pid) { | 152 || errno != EINTR) { |
112 TRACE(("found match session")); | 153 break; |
113 exit = &svr_ses.childpids[i].chansess->exit; | |
114 break; | |
115 } | |
116 } | |
117 | |
118 /* If the pid wasn't matched, then we might have hit the race mentioned | |
119 * above. So we just store the info for the parent to deal with */ | |
120 if (exit == NULL) { | |
121 TRACE(("using lastexit")); | |
122 exit = &svr_ses.lastexit; | |
123 } | |
124 | |
125 exit->exitpid = pid; | |
126 if (WIFEXITED(status)) { | |
127 exit->exitstatus = WEXITSTATUS(status); | |
128 } | |
129 if (WIFSIGNALED(status)) { | |
130 exit->exitsignal = WTERMSIG(status); | |
131 #if !defined(AIX) && defined(WCOREDUMP) | |
132 exit->exitcore = WCOREDUMP(status); | |
133 #else | |
134 exit->exitcore = 0; | |
135 #endif | |
136 } else { | |
137 /* we use this to determine how pid exited */ | |
138 exit->exitsignal = -1; | |
139 } | |
140 | |
141 /* Make sure that the main select() loop wakes up */ | |
142 while (1) { | |
143 /* isserver is just a random byte to write. We can't do anything | |
144 about an error so should just ignore it */ | |
145 if (write(ses.signal_pipe[1], &ses.isserver, 1) == 1 | |
146 || errno != EINTR) { | |
147 break; | |
148 } | |
149 } | 154 } |
150 } | 155 } |
151 | 156 |
152 sa_chld.sa_handler = sesssigchild_handler; | 157 sa_chld.sa_handler = sesssigchild_handler; |
153 sa_chld.sa_flags = SA_NOCLDSTOP; | 158 sa_chld.sa_flags = SA_NOCLDSTOP; |
157 | 162 |
158 errno = saved_errno; | 163 errno = saved_errno; |
159 } | 164 } |
160 | 165 |
161 /* send the exit status or the signal causing termination for a session */ | 166 /* send the exit status or the signal causing termination for a session */ |
162 static void send_exitsignalstatus(struct Channel *channel) { | 167 static void send_exitsignalstatus(const struct Channel *channel) { |
163 | 168 |
164 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; | 169 struct ChanSess *chansess = (struct ChanSess*)channel->typedata; |
165 | 170 |
166 if (chansess->exit.exitpid >= 0) { | 171 if (chansess->exit.exitpid >= 0) { |
167 if (chansess->exit.exitsignal > 0) { | 172 if (chansess->exit.exitsignal > 0) { |
171 } | 176 } |
172 } | 177 } |
173 } | 178 } |
174 | 179 |
175 /* send the exitstatus to the client */ | 180 /* send the exitstatus to the client */ |
176 static void send_msg_chansess_exitstatus(struct Channel * channel, | 181 static void send_msg_chansess_exitstatus(const struct Channel * channel, |
177 struct ChanSess * chansess) { | 182 const struct ChanSess * chansess) { |
178 | 183 |
179 dropbear_assert(chansess->exit.exitpid != -1); | 184 dropbear_assert(chansess->exit.exitpid != -1); |
180 dropbear_assert(chansess->exit.exitsignal == -1); | 185 dropbear_assert(chansess->exit.exitsignal == -1); |
181 | 186 |
182 CHECKCLEARTOWRITE(); | 187 CHECKCLEARTOWRITE(); |
190 encrypt_packet(); | 195 encrypt_packet(); |
191 | 196 |
192 } | 197 } |
193 | 198 |
194 /* send the signal causing the exit to the client */ | 199 /* send the signal causing the exit to the client */ |
195 static void send_msg_chansess_exitsignal(struct Channel * channel, | 200 static void send_msg_chansess_exitsignal(const struct Channel * channel, |
196 struct ChanSess * chansess) { | 201 const struct ChanSess * chansess) { |
197 | 202 |
198 int i; | 203 int i; |
199 char* signame = NULL; | 204 char* signame = NULL; |
200 dropbear_assert(chansess->exit.exitpid != -1); | 205 dropbear_assert(chansess->exit.exitpid != -1); |
201 dropbear_assert(chansess->exit.exitsignal > 0); | 206 dropbear_assert(chansess->exit.exitsignal > 0); |
271 return 0; | 276 return 0; |
272 | 277 |
273 } | 278 } |
274 | 279 |
275 static struct logininfo* | 280 static struct logininfo* |
276 chansess_login_alloc(struct ChanSess *chansess) { | 281 chansess_login_alloc(const struct ChanSess *chansess) { |
277 struct logininfo * li; | 282 struct logininfo * li; |
278 li = login_alloc_entry(chansess->pid, ses.authstate.username, | 283 li = login_alloc_entry(chansess->pid, ses.authstate.username, |
279 svr_ses.remotehost, chansess->tty); | 284 svr_ses.remotehost, chansess->tty); |
280 return li; | 285 return li; |
281 } | 286 } |
282 | 287 |
283 /* clean a session channel */ | 288 /* clean a session channel */ |
284 static void closechansess(struct Channel *channel) { | 289 static void closechansess(const struct Channel *channel) { |
285 | 290 |
286 struct ChanSess *chansess; | 291 struct ChanSess *chansess; |
287 unsigned int i; | 292 unsigned int i; |
288 struct logininfo *li; | 293 struct logininfo *li; |
289 | 294 |
401 TRACE(("leave chansessionrequest")) | 406 TRACE(("leave chansessionrequest")) |
402 } | 407 } |
403 | 408 |
404 | 409 |
405 /* Send a signal to a session's process as requested by the client*/ | 410 /* Send a signal to a session's process as requested by the client*/ |
406 static int sessionsignal(struct ChanSess *chansess) { | 411 static int sessionsignal(const struct ChanSess *chansess) { |
407 | 412 |
408 int sig = 0; | 413 int sig = 0; |
409 char* signame = NULL; | 414 char* signame = NULL; |
410 int i; | 415 int i; |
411 | 416 |
439 return DROPBEAR_SUCCESS; | 444 return DROPBEAR_SUCCESS; |
440 } | 445 } |
441 | 446 |
442 /* Let the process know that the window size has changed, as notified from the | 447 /* Let the process know that the window size has changed, as notified from the |
443 * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ | 448 * client. Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
444 static int sessionwinchange(struct ChanSess *chansess) { | 449 static int sessionwinchange(const struct ChanSess *chansess) { |
445 | 450 |
446 int termc, termr, termw, termh; | 451 int termc, termr, termw, termh; |
447 | 452 |
448 if (chansess->master < 0) { | 453 if (chansess->master < 0) { |
449 /* haven't got a pty yet */ | 454 /* haven't got a pty yet */ |
458 pty_change_window_size(chansess->master, termr, termc, termw, termh); | 463 pty_change_window_size(chansess->master, termr, termc, termw, termh); |
459 | 464 |
460 return DROPBEAR_SUCCESS; | 465 return DROPBEAR_SUCCESS; |
461 } | 466 } |
462 | 467 |
463 static void get_termmodes(struct ChanSess *chansess) { | 468 static void get_termmodes(const struct ChanSess *chansess) { |
464 | 469 |
465 struct termios termio; | 470 struct termios termio; |
466 unsigned char opcode; | 471 unsigned char opcode; |
467 unsigned int value; | 472 unsigned int value; |
468 const struct TermCode * termcode; | 473 const struct TermCode * termcode; |
656 /* TODO - send error - too long ? */ | 661 /* TODO - send error - too long ? */ |
657 return DROPBEAR_FAILURE; | 662 return DROPBEAR_FAILURE; |
658 } | 663 } |
659 } | 664 } |
660 if (issubsys) { | 665 if (issubsys) { |
661 #ifdef SFTPSERVER_PATH | 666 #if DROPBEAR_SFTPSERVER |
662 if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) { | 667 if ((cmdlen == 4) && strncmp(chansess->cmd, "sftp", 4) == 0) { |
663 m_free(chansess->cmd); | 668 m_free(chansess->cmd); |
664 chansess->cmd = m_strdup(SFTPSERVER_PATH); | 669 chansess->cmd = m_strdup(SFTPSERVER_PATH); |
665 } else | 670 } else |
666 #endif | 671 #endif |
680 /* take public key option 'command' into account */ | 685 /* take public key option 'command' into account */ |
681 svr_pubkey_set_forced_command(chansess); | 686 svr_pubkey_set_forced_command(chansess); |
682 } | 687 } |
683 | 688 |
684 | 689 |
685 #ifdef LOG_COMMANDS | 690 #if LOG_COMMANDS |
686 if (chansess->cmd) { | 691 if (chansess->cmd) { |
687 dropbear_log(LOG_INFO, "User %s executing '%s'", | 692 dropbear_log(LOG_INFO, "User %s executing '%s'", |
688 ses.authstate.pw_name, chansess->cmd); | 693 ses.authstate.pw_name, chansess->cmd); |
689 } else { | 694 } else { |
690 dropbear_log(LOG_INFO, "User %s executing login shell", | 695 dropbear_log(LOG_INFO, "User %s executing login shell", |
767 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ | 772 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
768 static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { | 773 static int ptycommand(struct Channel *channel, struct ChanSess *chansess) { |
769 | 774 |
770 pid_t pid; | 775 pid_t pid; |
771 struct logininfo *li = NULL; | 776 struct logininfo *li = NULL; |
772 #ifdef DO_MOTD | 777 #if DO_MOTD |
773 buffer * motdbuf = NULL; | 778 buffer * motdbuf = NULL; |
774 int len; | 779 int len; |
775 struct stat sb; | 780 struct stat sb; |
776 char *hushpath = NULL; | 781 char *hushpath = NULL; |
777 #endif | 782 #endif |
819 * terminal used for stdout with the dup2 above */ | 824 * terminal used for stdout with the dup2 above */ |
820 li = chansess_login_alloc(chansess); | 825 li = chansess_login_alloc(chansess); |
821 login_login(li); | 826 login_login(li); |
822 login_free_entry(li); | 827 login_free_entry(li); |
823 | 828 |
824 #ifdef DO_MOTD | 829 #if DO_MOTD |
825 if (svr_opts.domotd && !chansess->cmd) { | 830 if (svr_opts.domotd && !chansess->cmd) { |
826 /* don't show the motd if ~/.hushlogin exists */ | 831 /* don't show the motd if ~/.hushlogin exists */ |
827 | 832 |
828 /* 12 == strlen("/.hushlogin\0") */ | 833 /* 12 == strlen("/.hushlogin\0") */ |
829 len = strlen(ses.authstate.pw_dir) + 12; | 834 len = strlen(ses.authstate.pw_dir) + 12; |
896 | 901 |
897 } | 902 } |
898 | 903 |
899 /* Clean up, drop to user privileges, set up the environment and execute | 904 /* Clean up, drop to user privileges, set up the environment and execute |
900 * the command/shell. This function does not return. */ | 905 * the command/shell. This function does not return. */ |
901 static void execchild(void *user_data) { | 906 static void execchild(const void *user_data) { |
902 struct ChanSess *chansess = user_data; | 907 const struct ChanSess *chansess = user_data; |
903 char *usershell = NULL; | 908 char *usershell = NULL; |
904 | 909 |
905 /* with uClinux we'll have vfork()ed, so don't want to overwrite the | 910 /* with uClinux we'll have vfork()ed, so don't want to overwrite the |
906 * hostkey. can't think of a workaround to clear it */ | 911 * hostkey. can't think of a workaround to clear it */ |
907 #if !DROPBEAR_VFORK | 912 #if !DROPBEAR_VFORK |