#include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "pp.h" #include "oq.h" #include "util.h" #include "msgs.h" #include "cmdline.h" #include "pkt-util.h" #include "channels.h" #include "pollloop.h" #include "transport.h" #include "userauth-conn.h" #include "server.h" #define BUFFERSPACE 65536 typedef struct acc ACC; typedef struct session SESSION; typedef struct session_setup SESSION_SETUP; typedef struct pty_req PTY_REQ; typedef struct env_req ENV_REQ; struct session { SESSION *link; int chan; int fd; char *ptydev; SESSION_SETUP *setup; pid_t kid; int advwin; int leof; int reof; int id; OQ oq; } ; struct env_req { ENV_REQ *link; STR var; STR val; } ; struct pty_req { unsigned int x_c; unsigned int y_c; unsigned int x_p; unsigned int y_p; struct termios val; struct termios set; } ; struct session_setup { PTY_REQ *ptyreq; /*X11_REQ *x11req;*/ ENV_REQ *envreq; } ; struct acc { int n; struct sockaddr *sa; char *accstr; int fd; int id; } ; static ACC **accs; static int nacc; static struct sockaddr_storage lcl; static socklen_t lcllen; static struct sockaddr_storage rem; static socklen_t remlen; static SESSION *sessions; static void (*chanreq_parse_fail)(const void *, const char *, ...); static LAYER *ualayer; static int deathwatcher = 0; static int deathpipe[2]; static void set_iflag(struct termios *t, unsigned int s, unsigned int c) { t->c_iflag = (t->c_iflag & ~c) | s; } static void set_lflag(struct termios *t, unsigned int s, unsigned int c) { t->c_lflag = (t->c_lflag & ~c) | s; } static void set_oflag(struct termios *t, unsigned int s, unsigned int c) { t->c_oflag = (t->c_oflag & ~c) | s; } static void set_cflag(struct termios *t, unsigned int s, unsigned int c) { t->c_cflag = (t->c_cflag & ~c) | s; } static void set_tty_modes_std(PTY_REQ *r, const void *rest, int restlen) { const void *rest2; int restlen2; unsigned int v; while (restlen) { rest2 = 0; switch (*(const unsigned char *)rest) { case TTY_OP_END: restlen2 = 0; break; { int ccx; { #ifdef VINTR case TTY_OP_VINTR: ccx = VINTR; } if (0) { #endif #ifdef VQUIT case TTY_OP_VQUIT: ccx = VQUIT; } if (0) { #endif #ifdef VERASE case TTY_OP_VERASE: ccx = VERASE; } if (0) { #endif #ifdef VKILL case TTY_OP_VKILL: ccx = VKILL; } if (0) { #endif #ifdef VEOF case TTY_OP_VEOF: ccx = VEOF; } if (0) { #endif #ifdef VEOL case TTY_OP_VEOL: ccx = VEOL; } if (0) { #endif #ifdef VEOL2 case TTY_OP_VEOL2: ccx = VEOL2; } if (0) { #endif #ifdef VSTART case TTY_OP_VSTART: ccx = VSTART; } if (0) { #endif #ifdef VSTOP case TTY_OP_VSTOP: ccx = VSTOP; } if (0) { #endif #ifdef VSUSP case TTY_OP_VSUSP: ccx = VSUSP; } if (0) { #endif #ifdef VDSUSP case TTY_OP_VDSUSP: ccx = VDSUSP; } if (0) { #endif #ifdef VREPRINT case TTY_OP_VREPRINT: ccx = VREPRINT; } if (0) { #endif #ifdef VWERASE case TTY_OP_VWERASE: ccx = VWERASE; } if (0) { #endif #ifdef VLNEXT case TTY_OP_VLNEXT: ccx = VLNEXT; } if (0) { #endif #ifdef VFLUSH case TTY_OP_VFLUSH: ccx = VFLUSH; } if (0) { #endif #ifdef VSWTCH case TTY_OP_VSWTCH: ccx = VSWTCH; } if (0) { #endif #ifdef VSTATUS case TTY_OP_VSTATUS: ccx = VSTATUS; } if (0) { #endif #ifdef VDISCARD case TTY_OP_VDISCARD: ccx = VDISCARD; } if (0) { #endif } parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_UINT32(&v), PP_REST(&rest2,&restlen2) ); r->set.c_cc[ccx] = 1; r->val.c_cc[ccx] = (v == 255) ? _POSIX_VDISABLE : v; } break; { unsigned int bit; void (*setbit)(struct termios *, unsigned int, unsigned int); { #ifdef ICRNL case TTY_OP_ICRNL: bit = ICRNL; } if (0) { #endif #ifdef IGNCR case TTY_OP_IGNCR: bit = IGNCR; } if (0) { #endif #ifdef IGNPAR case TTY_OP_IGNPAR: bit = IGNPAR; } if (0) { #endif #ifdef IMAXBEL case TTY_OP_IMAXBEL: bit = IMAXBEL; } if (0) { #endif #ifdef INLCR case TTY_OP_INLCR: bit = INLCR; } if (0) { #endif #ifdef INPCK case TTY_OP_INPCK: bit = INPCK; } if (0) { #endif #ifdef ISTRIP case TTY_OP_ISTRIP: bit = ISTRIP; } if (0) { #endif #ifdef IXANY case TTY_OP_IXANY: bit = IXANY; } if (0) { #endif #ifdef IXOFF case TTY_OP_IXOFF: bit = IXOFF; } if (0) { #endif #ifdef IXON case TTY_OP_IXON: bit = IXON; } if (0) { #endif #ifdef PARMRK case TTY_OP_PARMRK: bit = PARMRK; } if (0) { #endif } setbit = &set_iflag; if (0) { { #ifdef ECHO case TTY_OP_ECHO: bit = ECHO; } if (0) { #endif #ifdef ECHOCTL case TTY_OP_ECHOCTL: bit = ECHOCTL; } if (0) { #endif #ifdef ECHOE case TTY_OP_ECHOE: bit = ECHOE; } if (0) { #endif #ifdef ECHOK case TTY_OP_ECHOK: bit = ECHOK; } if (0) { #endif #ifdef ECHOKE case TTY_OP_ECHOKE: bit = ECHOKE; } if (0) { #endif #ifdef ECHONL case TTY_OP_ECHONL: bit = ECHONL; } if (0) { #endif #ifdef ICANON case TTY_OP_ICANON: bit = ICANON; } if (0) { #endif #ifdef IEXTEN case TTY_OP_IEXTEN: bit = IEXTEN; } if (0) { #endif #ifdef ISIG case TTY_OP_ISIG: bit = ISIG; } if (0) { #endif #ifdef NOFLSH case TTY_OP_NOFLSH: bit = NOFLSH; } if (0) { #endif #ifdef TOSTOP case TTY_OP_TOSTOP: bit = TOSTOP; } if (0) { #endif } setbit = &set_lflag; } if (0) { { #ifdef OPOST case TTY_OP_OPOST: bit = OPOST; } if (0) { #endif #ifdef ONLCR case TTY_OP_ONLCR: bit = ONLCR; } if (0) { #endif #ifdef OCRNL case TTY_OP_OCRNL: bit = OCRNL; } if (0) { #endif #ifdef ONOCR case TTY_OP_ONOCR: bit = ONOCR; } if (0) { #endif #ifdef ONLRET case TTY_OP_ONLRET: bit = ONLRET; } if (0) { #endif } setbit = &set_oflag; } if (0) { { #ifdef PARENB case TTY_OP_PARENB: bit = PARENB; } if (0) { #endif #ifdef PARODD case TTY_OP_PARODD: bit = PARODD; } if (0) { #endif } setbit = &set_cflag; } parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_UINT32(&v), PP_REST(&rest2,&restlen2) ); (*setbit)(&r->val,v?bit:0,v?0:bit); (*setbit)(&r->set,bit,0); } break; { unsigned int csval; { #ifdef CS7 case TTY_OP_CS7: csval = CS7; } if (0) { #endif #ifdef CS8 case TTY_OP_CS8: csval = CS8; } if (0) { #endif } parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_UINT32(&v), PP_REST(&rest2,&restlen2) ); if (v) { r->val.c_cflag = (r->val.c_cflag & ~CSIZE) | csval; r->set.c_cflag |= CSIZE; } } break; case TTY_OP_ISPEED: parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_UINT32(&v), PP_REST(&rest2,&restlen2) ); r->val.c_ispeed = v; r->set.c_ispeed = 1; break; case TTY_OP_OSPEED: parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_UINT32(&v), PP_REST(&rest2,&restlen2) ); r->val.c_ospeed = v; r->set.c_ospeed = 1; break; default: switch (*(const unsigned char *)rest) { case 1 ... 159: parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_SKIP(PP_UINT32(0)), PP_REST(&rest2,&restlen2) ); break; default: restlen2 = 0; break; } break; } rest = rest2; restlen = restlen2; } } static void set_tty_modes_mouse(PTY_REQ *r, const void *rest, int restlen) { const void *rest2; int restlen2; unsigned int v; while (restlen) { rest2 = 0; switch (*(const unsigned char *)rest) { unsigned int bit; { #ifdef ECHOPRT case MOUSETTY_OP_ECHOPRT: bit = ECHOPRT; } if (0) { #endif #ifdef ALTWERASE case MOUSETTY_OP_ALTWERASE: bit = ALTWERASE; } if (0) { #endif #ifdef NOKERNINFO case MOUSETTY_OP_NOKERNINFO: bit = NOKERNINFO; } if (0) { #endif } parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_BLOB(1,&v), PP_REST(&rest2,&restlen2) ); v = *(char *)&v; r->set.c_lflag |= bit; set_lflag(&r->val,v?bit:0,v?0:bit); break; case MOUSETTY_OP_CS: parse_data(rest,restlen,chanreq_parse_fail, PP_IGNORE(1), PP_BLOB(1,&v), PP_REST(&rest2,&restlen2) ); v = *(char *)&v; switch (v) { { #ifdef CS5 case 5: v = CS5; } if (0) { #endif #ifdef CS6 case 6: v = CS6; } if (0) { #endif #ifdef CS7 case 7: v = CS7; } if (0) { #endif #ifdef CS8 case 8: v = CS8; } if (0) { #endif } r->val.c_cflag = (r->val.c_cflag & ~CSIZE) | v; r->set.c_cflag |= CSIZE; break; } break; default: fprintf(stderr,"%s: unknown mouse tty mode %02x\n",__progname,*(const unsigned char *)rest); restlen2 = 0; break; } rest = rest2; restlen = restlen2; } } static void session_setup(SESSION *s) { if (! s->setup) { s->setup = malloc(sizeof(SESSION_SETUP)); s->setup->ptyreq = 0; s->setup->envreq = 0; } } static void set_env_req(SESSION_SETUP *ss, ROSTR name, ROSTR val) { ENV_REQ *er; for (er=ss->envreq;er;er=er->link) { if (str_equalcs(name,er->var)) { free_str(er->val); er->val = str_copyro(val); return; } } er = malloc(sizeof(ENV_REQ)); er->var = str_copyro(name); er->val = str_copyro(val); er->link = ss->envreq; ss->envreq = er; } static int chanreq_pty_req(SESSION *s, const void *rest, int restlen) { STR term_env; unsigned int x_c; unsigned int y_c; unsigned int x_p; unsigned int y_p; STR modestr; PTY_REQ *req; if (s->fd >= 0) { fprintf(stderr,"%s: pty-req on an already-started session\n",__progname); return(CHANREQRET_FAIL); } parse_data(rest,restlen,chanreq_parse_fail, PP_STRING(&term_env), PP_UINT32(&x_c), PP_UINT32(&y_c), PP_UINT32(&x_p), PP_UINT32(&y_p), PP_STRING(&modestr), PP_ENDSHERE ); session_setup(s); if (s->setup->ptyreq) { fprintf(stderr,"%s: duplicate pty-req received\n",__progname); req = s->setup->ptyreq; } else { req = malloc(sizeof(PTY_REQ)); s->setup->ptyreq = req; bzero(&req->set.c_cc[0],sizeof(req->set.c_cc)); req->set.c_iflag = 0; req->set.c_oflag = 0; req->set.c_cflag = 0; req->set.c_lflag = 0; req->set.c_ispeed = 0; req->set.c_ospeed = 0; } req->x_c = x_c; req->y_c = y_c; req->x_p = x_p; req->y_p = y_p; set_tty_modes_std(req,modestr.data,modestr.len); free_str(modestr); set_env_req(s->setup,cstr_to_rostr("TERM"),str_to_rostr(term_env)); free_str(term_env); return(CHANREQRET_OK); } static int chanreq_pty_fixup(SESSION *s, const void *rest, int restlen) { if (s->fd >= 0) { fprintf(stderr,"%s: missing-pty-modes@rodents.montreal.qc.ca on an already-started session\n",__progname); return(CHANREQRET_FAIL); } if (! s->setup->ptyreq) { fprintf(stderr,"%s: missing-pty-modes@rodents.montreal.qc.ca with no pty-req\n",__progname); return(CHANREQRET_OK); } set_tty_modes_mouse(s->setup->ptyreq,rest,restlen); return(CHANREQRET_OK); } static int open_pty_pair(int *mfdp, int *sfdp, char **sptydev, PTY_REQ *prq) { char mdevname[16]; char sdevname[16]; int i; int j; int mfd; int sfd; struct termios tio; struct winsize wsz; for (i=0;;i++) { sprintf(&mdevname[0],"/dev/pty%c%x",'p'+(i>>4),i&15); strcpy(&sdevname[0],&mdevname[0]); sdevname[5] = 't'; mfd = open(&mdevname[0],O_RDWR,0); if (mfd < 0) { if (errno == ENOENT) { fprintf(stderr,"%s: out of ptys\n",__progname); return(1); } continue; } sfd = open(&sdevname[0],O_RDWR|O_NONBLOCK,0); if (sfd < 0) { fprintf(stderr,"%s: opened %s but can't open %s: %s\n",__progname,&mdevname[0],&sdevname[0],strerror(errno)); close(mfd); continue; } if (tcgetattr(sfd,&tio) < 0) { fprintf(stderr,"%s: tcgetattr failed on %s: %s\n",__progname,&sdevname[0],strerror(errno)); close(mfd); close(sfd); continue; } #define FOO(f) tio.f = (tio.f & ~prq->set.f) | (prq->val.f & prq->set.f) FOO(c_iflag); FOO(c_oflag); FOO(c_cflag); FOO(c_lflag); #undef FOO for (j=0;jset.c_cc[j]) tio.c_cc[j] = prq->val.c_cc[j]; if (prq->set.c_ispeed) tio.c_ispeed = prq->val.c_ispeed; if (prq->set.c_ospeed) tio.c_ospeed = prq->val.c_ospeed; if (tcsetattr(sfd,TCSAFLUSH,&tio) < 0) { fprintf(stderr,"%s: tcsetattr failed on %s: %s\n",__progname,&sdevname[0],strerror(errno)); close(mfd); close(sfd); continue; } wsz.ws_row = prq->y_c; wsz.ws_col = prq->x_c; wsz.ws_xpixel = prq->x_p; wsz.ws_ypixel = prq->y_p; ioctl(sfd,TIOCSWINSZ,&wsz); fcntl(sfd,F_SETFL,fcntl(sfd,F_GETFL,0)&~O_NONBLOCK); { int on; on = 1; ioctl(mfd,TIOCPKT,&on); } *mfdp = mfd; *sfdp = sfd; *sptydev = strdup(&sdevname[0]); return(0); } } static int open_pipe(int *mfdp, int *sfdp) { int fds[2]; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&fds[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); return(1); } *mfdp = fds[0]; *sfdp = fds[1]; return(0); } static int rtest_sess(int id __attribute__((__unused__)), void *sv) { SESSION *s; s = sv; return((s->ptydev || !s->leof) && (chan_get_wwin(s->chan) > 0)); } static int wtest_sess(int id __attribute__((__unused__)), void *sv) { return(oq_nonempty(&((SESSION *)sv)->oq)); } static void rd_sess(int id __attribute__((__unused__)), void *sv) { SESSION *s; int n; int r; unsigned char buf[8192]; s = sv; if (s->leof) return; n = chan_get_wwin(s->chan); if (n < 1) return; if (n > sizeof(buf)) n = sizeof(buf); r = read(s->fd,&buf[0],n); if (r < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: session read error: %s\n",__progname,strerror(errno)); } if (r <= 0) { s->leof = 1; shutdown(s->fd,SHUT_RD); chan_send_eof(s->chan); return; } if (s->ptydev) { if (buf[0] == TIOCPKT_DATA) { chan_send_data(s->chan,0,0,&buf[1],r-1); } else { /* for the moment, ignore non-data packets */ } } else { chan_send_data(s->chan,0,0,&buf[0],r); } } static void wr_sess(int id __attribute__((__unused__)), void *sv) { SESSION *s; int w; int goteof; int n; static int ateof(void *vp __attribute__((__unused__)), int i __attribute__((__unused__))) { goteof = 1; return(0); } s = sv; goteof = 0; w = oq_writev(&s->oq,s->fd,&ateof); if (goteof) { shutdown(s->fd,SHUT_WR); return; } if (w < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } fprintf(stderr,"%s: session write error: %s\n",__progname,strerror(errno)); exit(1); } oq_dropdata(&s->oq,w); if (! s->reof) { n = s->advwin + oq_qlen(&s->oq); if (n < BUFFERSPACE/2) { chan_add_rwin(s->chan,BUFFERSPACE-n); s->advwin += BUFFERSPACE-n; } } } static void free_session_setup(SESSION_SETUP *s) { if (! s) return; if (s->ptyreq) free(s->ptyreq); while (s->envreq) { ENV_REQ *er; er = s->envreq; s->envreq = er->link; free_str(er->var); free_str(er->val); free(er); } free(s); } static char **build_env(SESSION_SETUP *ss) { ENV_REQ *er; int n; char **env; int i; n = 0; for (er=ss->envreq;er;er=er->link) n ++; env = malloc((n+1)*sizeof(char *)); i = 0; for (er=ss->envreq;er;er=er->link) asprintf(&env[i++],"%.*s=%.*s",er->var.len,er->var.data,er->val.len,er->val.data); env[i] = 0; return(env); } static void handle_sigchld(int sig __attribute__((__unused__))) { int status; int pid; while (1) { pid = wait3(&status,WNOHANG,0); if (pid <= 0) break; write(deathpipe[1],&pid,sizeof(int)); write(deathpipe[1],&status,sizeof(int)); } } static void report_death(SESSION *s, int status) { unsigned char *opp; if (WIFEXITED(status)) { opp = chan_req_hdr(s->chan,cstr_to_rostr("exit-status")); opp = put_uint32(opp,WEXITSTATUS(status)); chan_send_req_blind(s->chan,opp); } else if (WIFSIGNALED(status)) { int sig; const char *sigstr; char buf[32]; opp = chan_req_hdr(s->chan,cstr_to_rostr("exit-signal")); sig = WTERMSIG(status); switch (sig) { case SIGHUP: sigstr = "HUP"; break; case SIGINT: sigstr = "INT"; break; case SIGQUIT: sigstr = "QUIT"; break; case SIGILL: sigstr = "ILL"; break; case SIGABRT: sigstr = "ABRT"; break; case SIGFPE: sigstr = "FPE"; break; case SIGKILL: sigstr = "KILL"; break; case SIGSEGV: sigstr = "SEGV"; break; case SIGPIPE: sigstr = "PIPE"; break; case SIGALRM: sigstr = "ALRM"; break; case SIGTERM: sigstr = "TERM"; break; case SIGUSR1: sigstr = "USR1"; break; case SIGUSR2: sigstr = "USR2"; break; case SIGEMT: sigstr = "EMT@NetBSD"; break; case SIGBUS: sigstr = "BUS@NetBSD"; break; case SIGSYS: sigstr = "SYS@NetBSD"; break; case SIGURG: sigstr = "URG@NetBSD"; break; case SIGSTOP: sigstr = "STOP@NetBSD"; break; case SIGTSTP: sigstr = "TSTP@NetBSD"; break; case SIGCONT: sigstr = "CONT@NetBSD"; break; case SIGCHLD: sigstr = "CHLD@NetBSD"; break; case SIGTTIN: sigstr = "TTIN@NetBSD"; break; case SIGTTOU: sigstr = "TTOU@NetBSD"; break; case SIGIO: sigstr = "IO@NetBSD"; break; case SIGXCPU: sigstr = "XCPU@NetBSD"; break; case SIGXFSZ: sigstr = "XFSZ@NetBSD"; break; case SIGVTALRM: sigstr = "VTALRM@NetBSD"; break; case SIGPROF: sigstr = "PROF@NetBSD"; break; case SIGWINCH: sigstr = "WINCH@NetBSD"; break; case SIGINFO: sigstr = "INFO@NetBSD"; break; case SIGPWR: sigstr = "PWR@NetBSD"; break; default: sprintf(&buf[0],"%d@NetBSD",sig); sigstr = &buf[0]; break; } opp = put_string(opp,sigstr,-1); *opp++ = WCOREDUMP(status) ? 1 : 0; opp = put_string(opp,0,0); opp = put_string(opp,0,0); chan_send_req_blind(s->chan,opp); } } static void rd_deathpipe(int id __attribute__((__unused__)), void *sv __attribute__((__unused__))) { int pkt[2]; int r; SESSION *s; s = sv; r = recv(deathpipe[0],&pkt[0],sizeof(pkt),MSG_WAITALL); if (verbose) printf("Child death: pid=%d status=%#x\n",pkt[0],pkt[1]); for (s=sessions;s;s=s->link) { if (pkt[0] == s->kid) { if (verbose) printf("Child death: session found\n"); report_death(s,pkt[1]); chan_close(s->chan); s->kid = -1; } } } static void start_deathwatcher(void) { struct sigaction sa; if (! deathwatcher) { if (socketpair(AF_LOCAL,SOCK_STREAM,0,&deathpipe[0]) < 0) { fprintf(stderr,"%s: socketpair: %s\n",__progname,strerror(errno)); exit(1); } fcntl(deathpipe[0],F_SETFD,1); fcntl(deathpipe[1],F_SETFD,1); sa.sa_handler = handle_sigchld; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD,&sa,0); add_poll_fd(deathpipe[0],&rwtest_always,&rwtest_never,&rd_deathpipe,0,0); deathwatcher = 1; } } static int session_start(SESSION *s, void (*runfn)(SUASTATE *, char **)) { int mfd; int sfd; pid_t kid; int me; sigset_t m; sigset_t om; SUASTATE *uas; char **env; char lhn[NI_MAXHOST]; char lsn[NI_MAXSERV]; char rhn[NI_MAXHOST]; char rsn[NI_MAXSERV]; int e; if (s->setup && s->setup->ptyreq) { if (open_pty_pair(&mfd,&sfd,&s->ptydev,s->setup->ptyreq)) return(CHANREQRET_FAIL); if (verbose) printf("Starting pty session\n"); } else { if (open_pipe(&mfd,&sfd)) return(CHANREQRET_FAIL); s->ptydev = 0; if (verbose) printf("Starting pipe session\n"); } uas = userauth_suastate(ualayer); if (! uas->haveuser) abort(); start_deathwatcher(); fflush(0); kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid > 0) { close(sfd); s->kid = kid; s->fd = mfd; fcntl(mfd,F_SETFL,fcntl(mfd,F_GETFL,0)|O_NONBLOCK); fcntl(mfd,F_SETFD,1); free_session_setup(s->setup); s->setup = 0; s->id = add_poll_fd(mfd,&rtest_sess,&wtest_sess,&rd_sess,&wr_sess,s); return(CHANREQRET_OK); } session_setup(s); set_env_req(s->setup,cstr_to_rostr("USER"),str_to_rostr(uas->curuser)); set_env_req(s->setup,cstr_to_rostr("HOME"),cstr_to_rostr(uas->homedir)); set_env_req(s->setup,cstr_to_rostr("SHELL"),cstr_to_rostr(uas->shell)); e = getnameinfo((void *)&lcl,lcllen,&lhn[0],sizeof(lhn),&lsn[0],sizeof(lsn),NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID) | getnameinfo((void *)&rem,remlen,&rhn[0],sizeof(rhn),&rsn[0],sizeof(rsn),NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID); if (! e) { char *t; asprintf(&t,"%s %s %s %s",&rhn[0],&rsn[0],&lhn[0],&lsn[0]); set_env_req(s->setup,cstr_to_rostr("SSH_CLIENT"),cstr_to_rostr(t)); free(t); } if (s->ptydev) set_env_req(s->setup,cstr_to_rostr("SSH_TTY"),cstr_to_rostr(s->ptydev)); env = build_env(s->setup); if (verbose) { int i; printf("Environment:\n"); for (i=0;env[i];i++) printf("\t%s\n",env[i]); fflush(stdout); } me = setsid(); if (s->ptydev) { sigfillset(&m); sigemptyset(&om); sigprocmask(SIG_BLOCK,&m,&om); ioctl(sfd,TIOCSCTTY,0); ioctl(sfd,TIOCSPGRP,&me); sigprocmask(SIG_SETMASK,&om,0); } close(mfd); if (sfd != 0) { dup2(sfd,0); close(sfd); } dup2(0,1); dup2(0,2); (*runfn)(uas,env); fprintf(stderr,"%s: can't exec %s: %s\n",__progname,uas->shell,strerror(errno)); exit(1); /* if ((*runfn)(s,sfd)) { close(sfd); close(mfd); return(CHANREQRET_FAIL); } */ } static void run_shell(SUASTATE *uas, char **env) { char *t; if (chdir(uas->homedir) < 0) chdir("/"); t = strdup(uas->shell); t[0] = '-'; execle(uas->shell,t,(char *)0,(void *)env); } static void run_exec(SUASTATE *uas, char **env, ROSTR cmd) { execle(uas->shell,uas->shell,"-c",blk_to_cstr(cmd.data,cmd.len),(char *)0,(void *)env); } static int chanreq_shell(SESSION *s, const void *rest, int restlen) { if (s->fd >= 0) { fprintf(stderr,"%s: shell request on an already-started session\n",__progname); return(CHANREQRET_FAIL); } parse_data(rest,restlen,chanreq_parse_fail,PP_ENDSHERE); return(session_start(s,&run_shell)); } static int chanreq_exec(SESSION *s, const void *rest, int restlen) { STR cmd; static void foo(SUASTATE *uas, char **env) { run_exec(uas,env,str_to_rostr(cmd)); } if (s->fd >= 0) { fprintf(stderr,"%s: exec request on an already-started session\n",__progname); return(CHANREQRET_FAIL); } parse_data(rest,restlen,chanreq_parse_fail, PP_STRING(&cmd), PP_ENDSHERE ); return(session_start(s,&foo)); } static int chanreq_window_change(SESSION *s, const void *rest, int restlen) { unsigned int x_c; unsigned int y_c; unsigned int x_p; unsigned int y_p; struct winsize wsz; sigset_t m; sigset_t om; if (s->fd < 0) { fprintf(stderr,"%s: window-change request on a non-started session\n",__progname); return(CHANREQRET_FAIL); } if (! s->ptydev) { fprintf(stderr,"%s: window-change request on a non-pty session\n",__progname); return(CHANREQRET_FAIL); } parse_data(rest,restlen,chanreq_parse_fail, PP_UINT32(&x_c), PP_UINT32(&y_c), PP_UINT32(&x_p), PP_UINT32(&y_p), PP_ENDSHERE ); sigfillset(&m); wsz.ws_row = y_c; wsz.ws_col = x_c; wsz.ws_xpixel = x_p; wsz.ws_ypixel = y_p; sigprocmask(SIG_BLOCK,&m,&om); ioctl(s->fd,TIOCSWINSZ,&wsz); sigprocmask(SIG_SETMASK,&om,0); return(CHANREQRET_OK); } static void session_gotdata(void *sv, int chan, int ext, unsigned int code, const void *buf, int len) { SESSION *s; s = sv; if (chan != s->chan) abort(); if (verbose > 1) printf("[gotdata: %d]",len); if (len == 0) { if (s->ptydev) { /* what does EOF map to when using a pty? */ } else { oq_queue_special(&s->oq,0,0); s->reof = 1; } return; } if (ext) { fprintf(stderr,"%s: extended data (code %d) on session channel\n",__progname,code); fprintf(stderr,"%s: merging with regular data stream\n",__progname); } oq_queue_copy(&s->oq,buf,len); } static void session_morewin(void *sv, int chan, unsigned int addl __attribute__((__unused__)), unsigned int newwin __attribute__((__unused__))) { if (verbose) printf("[+%u -> %u]",addl,newwin); if (chan != ((SESSION *)sv)->chan) abort(); } static int session_chanreq(void *sv, int chan, ROSTR req, int wantrep __attribute__((__unused__)), const void *rest, int restlen) { SESSION *s; int rv; static void fail(const void *at __attribute__((__unused__)), const char *fmt, ...) { va_list ap; int i; fprintf(stderr,"%s: protocol error: unparseable `%.*s': ",__progname,req.len,req.data); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); for (i=0;ichan) abort(); if (verbose) printf("Channel request `%.*s'\n",req.len,req.data); rv = CHANREQRET_UNK; chanreq_parse_fail = &fail; if (str_equalcC(req,"pty-req")) { rv = chanreq_pty_req(s,rest,restlen); } else if (str_equalcC(req,"missing-pty-modes@rodents.montreal.qc.ca")) { rv = chanreq_pty_fixup(s,rest,restlen); } else if (str_equalcC(req,"shell")) { rv = chanreq_shell(s,rest,restlen); } else if (str_equalcC(req,"exec")) { rv = chanreq_exec(s,rest,restlen); } else if (str_equalcC(req,"window-change")) { rv = chanreq_window_change(s,rest,restlen); } return(rv); } static int exit_if_drained(void *arg __attribute__((__unused__))) { if (oq_empty(&ualayer->b->output)) { if (verbose) printf("No sessions left, exiting\n"); exit(0); } return(0); } static void session_closed(void *sv, int chan, int final) { SESSION *s; SESSION **spp; SESSION *sp; s = sv; if (chan != s->chan) abort(); if (verbose) printf("Session channel closed\n"); if (!final && !chan_close(chan)) abort(); spp = &sessions; while ((sp = *spp)) { if (sp == s) { *spp = s->link; free(s->ptydev); if (s->setup) free_session_setup(s->setup); if (s->fd >= 0) { close(s->fd); remove_poll_id(s->id); } oq_flush(&s->oq); free(s); break; } else { spp = &s->link; } } if (sessions == 0) add_block_fn(&exit_if_drained,0); } static CHANOPS session_ops = { 0, &session_gotdata, &session_morewin, &session_chanreq, &session_closed }; static void chanopen_s(ROSTR type, int chan, const void *rest, int restlen) { static void failure(const void *errat __attribute__((__unused__)), const char *fmt, ...) { va_list ap; fprintf(stderr,"%s: protocol error: bad %.*s request: ",__progname,type.len,type.data); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); exit(1); } if (str_equalcC(type,"session")) { SESSION *s; parse_data(rest,restlen,&failure,PP_ENDSHERE); s = malloc(sizeof(SESSION)); s->link = sessions; sessions = s; s->chan = chan; s->fd = -1; s->ptydev = 0; s->setup = 0; s->kid = -1; s->advwin = BUFFERSPACE; s->leof = 0; s->reof = 0; oq_init(&s->oq); if (verbose) printf("[chanopen: type session]\n"); chan_open_ok(chan,ROSTRZERO,&session_ops,s); chan_add_rwin(chan,BUFFERSPACE); } else { printf("[chanopen: type %.*s restlen %d]\n",type.len,type.data,restlen); chan_open_fail(chan,SSH_OPEN_UNKNOWN_CHANNEL_TYPE,cstr_to_rostr(""),cstr_to_rostr("")); } } static int globalreq_s(ROSTR name, int wantrepl, const void *rest, int restlen) { rest=rest; printf("[globalreq: name %.*s wantreply %d restlen %d]\n",name.len,name.data,wantrepl,restlen); return(GLOBALREQ_UNK); } static NONCHANOPS gbl_ops_s = { &chanopen_s, &globalreq_s }; static void acc_accept(int id __attribute__((__unused__)), void *accv) { ACC *a; int new; pid_t kid; int i; BPP *b; a = accv; remlen = sizeof(lcl); new = accept(a->fd,(void *)&rem,&remlen); if (new < 0) { fprintf(stderr,"%s: accept (%s): %s\n",__progname,a->accstr,strerror(errno)); return; } lcllen = sizeof(lcl); if (getsockname(new,(void *)&lcl,&lcllen) < 0) { fprintf(stderr,"%s: getsockname (%s): %s\n",__progname,a->accstr,strerror(errno)); close(new); return; } if (! oneconn) { kid = fork(); if (kid != 0) { close(new); return; } } for (i=0;iid); close(a->fd); free(a); } free(accs); sessions = 0; b = malloc(sizeof(BPP)); bpp_setup(b,1); fcntl(new,F_SETFD,1); b->fd = new; sendversion(b); push_layer(b,&layer_base); push_layer(b,&layer_d_i_d); push_layer(b,&layer_transport_s); ualayer = push_layer(b,&layer_userauth_conn_s); push_layer(b,&layer_channels); push_layer(b,&layer_catchall); set_globalops(&gbl_ops_s); bpp_add_poll(b); } static void add_acc(const char *s) { const char *slash; char *host; const char *port; struct addrinfo *ai0; struct addrinfo *ai; struct addrinfo hints; int err; int sock; char *txt; int v; char hn[NI_MAXHOST]; char sn[NI_MAXSERV]; slash = index(s,'/'); if (slash) { host = blk_to_cstr(s,slash-s); port = slash[1] ? slash+1 : "22"; } else { host = 0; port = s; } hints.ai_flags = AI_PASSIVE; hints.ai_family = 0; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; err = getaddrinfo(host,port,&hints,&ai0); if (err) { fprintf(stderr,"%s: %s: %s\n",__progname,s,gai_strerror(err)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: lookup succeeded with no addresses?\n",__progname,s); exit(1); } for (ai=ai0;ai;ai=ai->ai_next) { ACC *a; err = getnameinfo(ai->ai_addr,ai->ai_addrlen,&hn[0],sizeof(hn),&sn[0],sizeof(sn),NI_NUMERICHOST|NI_NUMERICSERV|NI_WITHSCOPEID); if (err) { asprintf(&txt,"%s [getnameinfo failed: %s]",s,strerror(err)); } else { asprintf(&txt,"%s/%s",&hn[0],&sn[0]); } sock = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (sock < 0) { fprintf(stderr,"%s: %s: socket: %s\n",__progname,txt,strerror(errno)); } else if ((v=1),(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&v,sizeof(v)) < 0)) { fprintf(stderr,"%s: %s: setsockopt SO_REUSEADDR: %s\n",__progname,txt,strerror(errno)); close(sock); } else if (bind(sock,ai->ai_addr,ai->ai_addrlen) < 0) { fprintf(stderr,"%s: %s: bind: %s\n",__progname,txt,strerror(errno)); close(sock); } else { listen(sock,10); a = malloc(sizeof(ACC)); a->n = nacc; a->sa = malloc(ai->ai_addrlen); bcopy(ai->ai_addr,a->sa,ai->ai_addrlen); a->accstr = txt; txt = 0; a->fd = sock; a->id = add_poll_fd(sock,&rwtest_always,&rwtest_never,&acc_accept,0,a); accs = realloc(accs,(nacc+1)*sizeof(*accs)); accs[nacc++] = a; } free(txt); } freeaddrinfo(ai0); free(host); } void server_setup(void) { int i; struct sigaction sa; accs = 0; nacc = 0; if (nports < 1) { add_acc("22"); } else { for (i=0;i