int mm_answer_pty(int sock, Buffer *m) { extern struct monitor *pmonitor; Session *s; int res, fd0; debug3("%s entering", __func__); buffer_clear(m); s = session_new(); if (s == NULL) goto error; s->authctxt = authctxt; s->pw = authctxt->pw; s->pid = pmonitor->m_pid; res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); if (res == 0) goto error; pty_setowner(authctxt->pw, s->tty); buffer_put_int(m, 1); buffer_put_cstring(m, s->tty); /* We need to trick ttyslot */ if (dup2(s->ttyfd, 0) == -1) fatal("%s: dup2", __func__); mm_record_login(s, authctxt->pw); /* Now we can close the file descriptor again */ close(0); /* send messages generated by record_login */ buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); buffer_clear(&loginmsg); mm_request_send(sock, MONITOR_ANS_PTY, m); mm_send_fd(sock, s->ptyfd); mm_send_fd(sock, s->ttyfd); /* make sure nothing uses fd 0 */ if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) fatal("%s: open(/dev/null): %s", __func__, strerror(errno)); if (fd0 != 0) error("%s: fd0 %d != 0", __func__, fd0); /* slave is not needed */ close(s->ttyfd); s->ttyfd = s->ptyfd; /* no need to dup() because nobody closes ptyfd */ s->ptymaster = s->ptyfd; debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ttyfd); return (0); error: if (s != NULL) mm_session_close(s); buffer_put_int(m, 0); mm_request_send(sock, MONITOR_ANS_PTY, m); return (0); }
/* Set up a session pty which will be used to execute the shell or program. * The pty is allocated now, and kept for when the shell/program executes. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int sessionpty(struct ChanSess * chansess) { unsigned int termlen; unsigned char namebuf[65]; struct termios termio; TRACE(("enter sessionpty")); chansess->term = buf_getstring(ses.payload, &termlen); if (termlen > MAX_TERM_LEN) { /* TODO send disconnect ? */ TRACE(("leave sessionpty: term len too long")); return DROPBEAR_FAILURE; } chansess->termc = buf_getint(ses.payload); chansess->termr = buf_getint(ses.payload); chansess->termw = buf_getint(ses.payload); chansess->termh = buf_getint(ses.payload); /* allocate the pty */ assert(chansess->master == -1); /* haven't already got one */ if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) { TRACE(("leave sessionpty: failed to allocate pty")); return DROPBEAR_FAILURE; } chansess->tty = (char*)strdup(namebuf); if (!chansess->tty) { dropbear_exit("out of memory"); /* TODO disconnect */ } pty_setowner(ses.authstate.pw, chansess->tty); pty_change_window_size(chansess->master, chansess->termr, chansess->termc, chansess->termw, chansess->termh); /* Term modes */ /* We'll ignore errors and continue if we can't set modes. * We're ignoring baud rates since they seem evil */ if (tcgetattr(chansess->master, &termio) == 0) { unsigned char opcode; unsigned int value; const struct TermCode * termcode; while (((opcode = buf_getbyte(ses.payload)) != 0x00) && opcode <= 159) { /* handle types of code */ if (opcode > MAX_TERMCODE) { continue; } termcode = &termcodes[(unsigned int)opcode]; value = buf_getint(ses.payload); switch (termcode->type) { case TERMCODE_NONE: break; case TERMCODE_CONTROLCHAR: termio.c_cc[termcode->mapcode] = value; break; case TERMCODE_INPUT: if (value) { termio.c_iflag |= termcode->mapcode; } else { termio.c_iflag &= ~(termcode->mapcode); } break; case TERMCODE_OUTPUT: if (value) { termio.c_oflag |= termcode->mapcode; } else { termio.c_oflag &= ~(termcode->mapcode); } break; case TERMCODE_LOCAL: if (value) { termio.c_lflag |= termcode->mapcode; } else { termio.c_lflag &= ~(termcode->mapcode); } break; case TERMCODE_CONTROL: if (value) { termio.c_cflag |= termcode->mapcode; } else { termio.c_cflag &= ~(termcode->mapcode); } break; } } if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) { dropbear_log(LOG_INFO, "error setting terminal attributes"); } } TRACE(("leave sessionpty")); return DROPBEAR_SUCCESS; }