1 /* 2 * Dropbear SSH 3 * 4 * Copyright (c) 2002,2003 Matt Johnston 5 * Copyright (c) 2004 by Mihnea Stoenescu 6 * All rights reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in 16 * all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 * SOFTWARE. */ 25 26 #include "includes.h" 27 #include "packet.h" 28 #include "buffer.h" 29 #include "session.h" 30 #include "dbutil.h" 31 #include "channel.h" 32 #include "ssh.h" 33 #include "runopts.h" 34 #include "termcodes.h" 35 #include "chansession.h" 36 37 static void cli_closechansess(struct Channel *channel); 38 static int cli_initchansess(struct Channel *channel); 39 static void cli_chansessreq(struct Channel *channel); 40 41 static void start_channel_request(struct Channel *channel, unsigned char *type); 42 43 static void send_chansess_pty_req(struct Channel *channel); 44 static void send_chansess_shell_req(struct Channel *channel); 45 46 static void cli_tty_setup(); 47 48 const struct ChanType clichansess = { 49 0, /* sepfds */ 50 "session", /* name */ 51 cli_initchansess, /* inithandler */ 52 NULL, /* checkclosehandler */ 53 cli_chansessreq, /* reqhandler */ 54 cli_closechansess, /* closehandler */ 55 }; 56 57 static void cli_chansessreq(struct Channel *channel) { 58 59 unsigned char* type = NULL; 60 int wantreply; 61 62 TRACE(("enter cli_chansessreq")) 63 64 type = buf_getstring(ses.payload, NULL); 65 wantreply = buf_getbool(ses.payload); 66 67 if (strcmp(type, "exit-status") == 0) { 68 cli_ses.retval = buf_getint(ses.payload); 69 TRACE(("got exit-status of '%d'", cli_ses.retval)) 70 } else if (strcmp(type, "exit-signal") == 0) { 71 TRACE(("got exit-signal, ignoring it")) 72 } else { 73 TRACE(("unknown request '%s'", type)) 74 send_msg_channel_failure(channel); 75 goto out; 76 } 77 78 out: 79 m_free(type); 80 } 81 82 83 /* If the main session goes, we close it up */ 84 static void cli_closechansess(struct Channel *UNUSED(channel)) { 85 86 /* This channel hasn't gone yet, so we have > 1 */ 87 if (ses.chancount > 1) { 88 dropbear_log(LOG_INFO, "Waiting for other channels to close..."); 89 } 90 91 cli_tty_cleanup(); /* Restore tty modes etc */ 92 93 } 94 95 static void start_channel_request(struct Channel *channel, 96 unsigned char *type) { 97 98 CHECKCLEARTOWRITE(); 99 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); 100 buf_putint(ses.writepayload, channel->remotechan); 101 102 buf_putstring(ses.writepayload, type, strlen(type)); 103 104 } 105 106 /* Taken from OpenSSH's sshtty.c: 107 * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */ 108 static void cli_tty_setup() { 109 110 struct termios tio; 111 112 TRACE(("enter cli_pty_setup")) 113 114 if (cli_ses.tty_raw_mode == 1) { 115 TRACE(("leave cli_tty_setup: already in raw mode!")) 116 return; 117 } 118 119 if (tcgetattr(STDIN_FILENO, &tio) == -1) { 120 dropbear_exit("Failed to set raw TTY mode"); 121 } 122 123 /* make a copy */ 124 cli_ses.saved_tio = tio; 125 126 tio.c_iflag |= IGNPAR; 127 tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); 128 #ifdef IUCLC 129 tio.c_iflag &= ~IUCLC; 130 #endif 131 tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); 132 #ifdef IEXTEN 133 tio.c_lflag &= ~IEXTEN; 134 #endif 135 tio.c_oflag &= ~OPOST; 136 tio.c_cc[VMIN] = 1; 137 tio.c_cc[VTIME] = 0; 138 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) { 139 dropbear_exit("Failed to set raw TTY mode"); 140 } 141 142 cli_ses.tty_raw_mode = 1; 143 TRACE(("leave cli_tty_setup")) 144 } 145 146 void cli_tty_cleanup() { 147 148 TRACE(("enter cli_tty_cleanup")) 149 150 if (cli_ses.tty_raw_mode == 0) { 151 TRACE(("leave cli_tty_cleanup: not in raw mode")) 152 return; 153 } 154 155 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) { 156 dropbear_log(LOG_WARNING, "Failed restoring TTY"); 157 } else { 158 cli_ses.tty_raw_mode = 0; 159 } 160 161 TRACE(("leave cli_tty_cleanup")) 162 } 163 164 static void put_termcodes() { 165 166 struct termios tio; 167 unsigned int sshcode; 168 const struct TermCode *termcode; 169 unsigned int value; 170 unsigned int mapcode; 171 172 unsigned int bufpos1, bufpos2; 173 174 TRACE(("enter put_termcodes")) 175 176 if (tcgetattr(STDIN_FILENO, &tio) == -1) { 177 dropbear_log(LOG_WARNING, "Failed reading termmodes"); 178 buf_putint(ses.writepayload, 1); /* Just the terminator */ 179 buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */ 180 return; 181 } 182 183 bufpos1 = ses.writepayload->pos; 184 buf_putint(ses.writepayload, 0); /* A placeholder for the final length */ 185 186 /* As with Dropbear server, we ignore baud rates for now */ 187 for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) { 188 189 termcode = &termcodes[sshcode]; 190 mapcode = termcode->mapcode; 191 192 switch (termcode->type) { 193 194 case TERMCODE_NONE: 195 continue; 196 197 case TERMCODE_CONTROLCHAR: 198 value = tio.c_cc[mapcode]; 199 break; 200 201 case TERMCODE_INPUT: 202 value = tio.c_iflag & mapcode; 203 break; 204 205 case TERMCODE_OUTPUT: 206 value = tio.c_oflag & mapcode; 207 break; 208 209 case TERMCODE_LOCAL: 210 value = tio.c_lflag & mapcode; 211 break; 212 213 case TERMCODE_CONTROL: 214 value = tio.c_cflag & mapcode; 215 break; 216 217 default: 218 continue; 219 220 } 221 222 /* If we reach here, we have something to say */ 223 buf_putbyte(ses.writepayload, sshcode); 224 buf_putint(ses.writepayload, value); 225 } 226 227 buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */ 228 229 /* Put the string length at the start of the buffer */ 230 bufpos2 = ses.writepayload->pos; 231 232 buf_setpos(ses.writepayload, bufpos1); /* Jump back */ 233 buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */ 234 buf_setpos(ses.writepayload, bufpos2); /* Back where we were */ 235 236 TRACE(("leave put_termcodes")) 237 } 238 239 static void put_winsize() { 240 241 struct winsize ws; 242 243 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) { 244 /* Some sane defaults */ 245 ws.ws_row = 25; 246 ws.ws_col = 80; 247 ws.ws_xpixel = 0; 248 ws.ws_ypixel = 0; 249 } 250 251 buf_putint(ses.writepayload, ws.ws_col); /* Cols */ 252 buf_putint(ses.writepayload, ws.ws_row); /* Rows */ 253 buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */ 254 buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */ 255 256 } 257 258 static void sigwinch_handler(int UNUSED(unused)) { 259 260 cli_ses.winchange = 1; 261 262 } 263 264 void cli_chansess_winchange() { 265 266 unsigned int i; 267 struct Channel *channel = NULL; 268 269 for (i = 0; i < ses.chansize; i++) { 270 channel = ses.channels[i]; 271 if (channel != NULL && channel->type == &clichansess) { 272 CHECKCLEARTOWRITE(); 273 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST); 274 buf_putint(ses.writepayload, channel->remotechan); 275 buf_putstring(ses.writepayload, "window-change", 13); 276 buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */ 277 put_winsize(); 278 encrypt_packet(); 279 } 280 } 281 cli_ses.winchange = 0; 282 } 283 284 static void send_chansess_pty_req(struct Channel *channel) { 285 286 unsigned char* term = NULL; 287 288 TRACE(("enter send_chansess_pty_req")) 289 290 start_channel_request(channel, "pty-req"); 291 292 /* Don't want replies */ 293 buf_putbyte(ses.writepayload, 0); 294 295 /* Get the terminal */ 296 term = getenv("TERM"); 297 if (term == NULL) { 298 term = "vt100"; /* Seems a safe default */ 299 } 300 buf_putstring(ses.writepayload, term, strlen(term)); 301 302 /* Window size */ 303 put_winsize(); 304 305 /* Terminal mode encoding */ 306 put_termcodes(); 307 308 encrypt_packet(); 309 310 /* Set up a window-change handler */ 311 if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) { 312 dropbear_exit("signal error"); 313 } 314 TRACE(("leave send_chansess_pty_req")) 315 } 316 317 static void send_chansess_shell_req(struct Channel *channel) { 318 319 unsigned char* reqtype = NULL; 320 321 TRACE(("enter send_chansess_shell_req")) 322 323 if (cli_opts.cmd) { 324 reqtype = "exec"; 325 } else { 326 reqtype = "shell"; 327 } 328 329 start_channel_request(channel, reqtype); 330 331 /* XXX TODO */ 332 buf_putbyte(ses.writepayload, 0); /* Don't want replies */ 333 if (cli_opts.cmd) { 334 buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd)); 335 } 336 337 encrypt_packet(); 338 TRACE(("leave send_chansess_shell_req")) 339 } 340 341 static int cli_initchansess(struct Channel *channel) { 342 343 344 channel->writefd = STDOUT_FILENO; 345 setnonblocking(STDOUT_FILENO); 346 347 channel->readfd = STDIN_FILENO; 348 setnonblocking(STDIN_FILENO); 349 350 channel->errfd = STDERR_FILENO; 351 setnonblocking(STDERR_FILENO); 352 353 channel->extrabuf = cbuf_new(RECV_MAXWINDOW); 354 355 if (cli_opts.wantpty) { 356 send_chansess_pty_req(channel); 357 } 358 359 send_chansess_shell_req(channel); 360 361 if (cli_opts.wantpty) { 362 cli_tty_setup(); 363 } 364 365 return 0; /* Success */ 366 367 } 368 369 void cli_send_chansess_request() { 370 371 TRACE(("enter cli_send_chansess_request")) 372 if (send_msg_channel_open_init(STDIN_FILENO, &clichansess) 373 == DROPBEAR_FAILURE) { 374 dropbear_exit("Couldn't open initial channel"); 375 } 376 377 /* No special channel request data */ 378 encrypt_packet(); 379 TRACE(("leave cli_send_chansess_request")) 380 381 } 382