/* This file is in the public domain. */ #include "panic.h" /* * Get a 16-bit datum from a byte stream. The reason this is here, * rather than using the multi-byte support elsewhere, is that the * byte sex is specified in the X way, as a 0x42 or 0x6c byte. * * This routine is outside the NO_X11 test, below, because it's needed * to parse X data enough to handle it properly - even when we're * built without X support, we know a _little_ about X. */ static unsigned short int x_get16(unsigned char order, const void *data) { switch (order) { case 0x42: return( (((const unsigned char *)data)[0] * 256) + ((const unsigned char *)data)[1] ); break; case 0x6c: return( (((const unsigned char *)data)[1] * 256) + ((const unsigned char *)data)[0] ); break; default: panic("x_get16: bad byte order %#x\n",order); break; } } #ifdef NO_X11 /* * We're being built without X support. In this case, this whole file * contains just stubs which return appropriate failures - or, for * routines which should never get called under these circumstances, * panic. */ #include #include #include "x.h" CFXREQ *X_forward_req(void) { return(0); } void X_forward_done(CFXREQ *r __attribute__((__unused__))) { panic("X_forward_done with NO_X11"); } void *X_fwdreq_gen(void *opp __attribute__((__unused__)), CFXREQ *r __attribute__((__unused__))) { panic("X_fwdreq_gen with NO_X11"); } SFXREQ *X_fwdreq_start(const void *data __attribute__((__unused__)), int datalen __attribute__((__unused__)), const void **restp __attribute__((__unused__)), int *restlenp __attribute__((__unused__)), void (*fail)(const void *, const char *, ...) __attribute__((__unused__)), void (*gotfd)(int, unsigned char *, void *) __attribute__((__unused__)), void *arg __attribute__((__unused__)), unsigned int flags __attribute__((__unused__))) { return(0); } void X_fwdreq_abort(SFXREQ *r __attribute__((__unused__))) { panic("X_fwdreq_abort with NO_X11"); } void X_fwdreq_go(SFXREQ *r __attribute__((__unused__))) { panic("X_fwdreq_go with NO_X11"); } void X_fwdreq_env(SFXREQ *r __attribute__((__unused__)), void (*setvar)(const char *, const char *) __attribute__((__unused__))) { panic("X_fwdreq_env with NO_X11"); } void X_start_connect(CFXREQ *r __attribute__((__unused__)), const unsigned char *hdr __attribute__((__unused__)), void (*done)(int, void *) __attribute__((__unused__)), void (*fail)(void *) __attribute__((__unused__)), void *arg __attribute__((__unused__))) { panic("X_start_connect with NO_X11"); } #else /* * We're building with X support. */ #include #include #include #include #include #include #include #include #include #include #include #ifndef X11_NO_TCP #include #include #endif #ifndef X11_NO_LOCAL #include #endif #include #include #include "pp.h" #include "rnd.h" #include "dll.h" #include "util.h" #include "errf.h" #include "config.h" #include "nested.h" #include "pollloop.h" #include "pkt-util.h" #include "x.h" /* State of an SXCONN. */ typedef enum { SXS_HDR = 1, /* awaiting 12-byte header */ SXS_NAME, /* awaiting authorization-protocol-name */ SXS_DATA /* awaiting authorization-protocol-data */ } SXSTATE; /* Header phase of a CXIP. */ typedef enum { CXIP_HP_NONE = 1, /* not yet started */ CXIP_HP_HDR, /* sending first 6 bytes of header */ CXIP_HP_LENS, /* sending lengths (and padding 0) */ CXIP_HP_NAME, /* sending authorization method name */ CXIP_HP_DATA /* sending authorization data */ } CXIPHP; typedef struct sxconn SXCONN; typedef struct cxip CXIP; typedef struct xckind XCKIND; typedef struct kp_tcp KP_TCP; typedef struct kp_local KP_LOCAL; /* * An XCKIND describes what kind of connection an X connection can be. * These are connections between X clients and the ssh server's fake X * server. There is one of these for each kind of connection, * allocated statically. * * .inx is the index of this XCKIND in the xckinds[] array. * * .name is a text tag for debugging messages. * * .prep is part of the kind-specific code backing X_fwdreq_start(). * This is responsible for deciding whether this kind will be active * for this SFXREQ, and allocating the private data if so. It returns * true if the kind is participating, false if not; if it returns * true, it must set the kind's kindprivs[] element in the SFXREQ. * * .start is another part the kind-specific code backing * X_fwdreq_start(). This is responsible for checking whether the * proposed display number (second arg) is available and, if so, * listening for it. Returns true if all went well or false if the * proposed display number is not available. If .prep returned false, * .start will never be called. * * .unstart is another part the kind-specific code backing * X_fwdreq_start(). Even if .start finds a display number * acceptable, another kind may find it unacceptable. When this * happens, .unstart is called for the kinds that found it acceptable. * .unstart is never called unless .start returns true. * * .go is the kind-specific code backing X_fwdreq_go. The second * argument is the text form of the MIT-MAGIC-COOKIE-1 cookie, * suitable for (eg) an xauth command line. The cookie string is not * promised to continue to exist past (*.go)()'s returning. * * .listening returns true if the SFXREQ has listening service set up * for this XCKIND, false if not. * * .disable is the kind-specific code backing sfxreq_disable(). */ struct xckind { int inx; const char *name; int (*prep)(SFXREQ *); int (*start)(SFXREQ *, int); void (*unstart)(SFXREQ *); void (*go)(SFXREQ *, const char *); int (*listening)(SFXREQ *); void (*disable)(SFXREQ *); } ; /* * An SXCONN is an X connection in the (ssh) server - that is, it's a * connection between an X client and our ssh-server-side X * looks-like-an-X-server. These exist only during the X-specific * phase; once we've accepted and verified the X authorization * information, we call back through ->req->gotfd and bow out of any * further processing for that connection. * * .flink and .blink are a DLL of all the SXCONNs for the corresponding * SFXREQ, rooted in the SFXREQ's conns. * * .fd is the fd of our socket for the connection to the X client; .id * is the poll ID for I/O on this connection. * * .state is the state of this connection. * * .hdr[] is the 12-byte connection header data. * * .name and .nlen are the authorization method name and its length; * .data and .dlen are the corresponding variables for the * authorization data. * * .buf points to the base of the buffer we want to read incoming data * into. This can be &.hdr[0], .name, or .data. * * .err is the authorization failure message, or nil if there is no * authorization failure (at least not yet). * * .ioptr is a progress value for I/O, with .len being the total * length. This is normally used for reading into .buf, but when * returning an error to the client they index into .err, for write. * * .kind describes what kind of connection it is. * * .req is the corresponding SFXREQ. */ struct sxconn { SXCONN *flink; SXCONN *blink; int fd; int id; SXSTATE state; unsigned char hdr[12]; unsigned char *name; int nlen; unsigned char *data; int dlen; unsigned char *buf; unsigned char *err; int ioptr; int len; const XCKIND *kind; SFXREQ *req; } ; /* * A CXIP is an ssh-client-side connection in progress, that is, it * represents an ssh client process trying to contact a real X server * in response to an X connection attempt forwarded from the ssh * server process. * * .flink and .blink are a DLL of all CXIPs for this CFXREQ, rooted in * the CFXREQ's .ip. * * .r is the CFXREQ this attempt is for. * * .i is the current index into the CFXREQ's .sinv/.authv. * * .fd is the current attempt's socket, with .id its I/O poll ID. * * .wb is the buffer of data currently being written, with .wp and .wl * being pointer and length indices into it. * * .hdrphase describes which portion of the header is currently being * written. * * .hdr[] holds the first six bytes of the header; .lens[] holds the * next six. * * .done and .fail are callbacks (with argument .arg), called when * (respectively) the entire connection header has been written * successfully or it fails. */ struct cxip { CXIP *flink; CXIP *blink; CFXREQ *r; int i; int fd; int id; unsigned char *wb; int wp; int wl; CXIPHP hdrphase; unsigned char hdr[6]; unsigned char lens[6]; void (*done)(int, void *); void (*fail)(void *); void *arg; } ; /* * A CFXREQ is the ssh-client-side state for an X forwading request. * * .sinv and .authv are parallel arrays, of size .nsin, holding the * addresses and authorization information for all the attempts we * want to make for connections based on this request. * * .scrno is the X default screen number. * * .ip is the root of a DLL holding all in-progress connections for * this request. */ struct cfxreq { int nsin; struct sockaddr_in *sinv; Xauth **authv; int scrno; CXIP *ip; } ; /* * An SFXREQ is the ssh-server-side state for an X forwading request. * * .scrno is the X default screen number. * * .hostname is the local host name (from local-host-name); this is * what we put before the : in the $DISPLAY we set up for executed * processes. * * .kindprivs is an array [NXCKINDS] of data private to the various * XCKINDs. Normally this will include, eg, a file descriptor and a * poll ID, but the details are private to the XCKIND. * * .gotfd is a callback we call when we've accepted and vetted a * connection, with .arg its last argument; the other two arguments * are the socket file descriptor and the 12-byte connection header. * Making this callback marks the end of our responsibility for doing * anything with the connection. * * .dispno is the display number of our pseudo-display. * * .cookie[] is the cookie data we generated and gave to xauth. * * .conns is the root of a DLL of all SXCONNs for this SFXREQ. * * .flags is a bitmask of XFF_* flags (from x.h). */ struct sfxreq { int scrno; char *hostname; void **kindprivs; void (*gotfd)(int, unsigned char *, void *); void *arg; int dispno; unsigned char cookie[16]; SXCONN *conns; unsigned int flags; } ; #ifndef X11_NO_TCP /* * A KP_TCP is the XCKOND-private data for a TCP connection. * * .magic is a magic number. Because these are poinetd to with void * * pointers, the compiler gives us no type-safety help. The magic * number is to help compensate. * * .r is a backpointer to the SFXREQ. * * .sinv is a vector, of size .nsin, of addresses for the local host; * we care about this because we need to add/remove xauth entries for * each of them. * * .fd is the file descriptor of the connection; .id is the poll ID for * it. */ struct kp_tcp { unsigned long int magic; #define KP_TCP_MAGIC 0xe5c7c966 SFXREQ *r; struct sockaddr_in *sinv; int nsin; int fd; int id; } ; #endif #ifndef X11_NO_LOCAL /* * A KP_LOCAL is the XCKOND-private data for an AF_LOCAL connection. * * .magic is a magic number. Because these are poinetd to with void * * pointers, the compiler gives us no type-safety help. The magic * number is to help compensate. * * .r is a backpointer to the SFXREQ. * * .fd is the file descriptor of the connection; .id is the poll ID for * it. * * .path is the pat to which we bound the socket. */ struct kp_local { unsigned long int magic; #define KP_LOCAL_MAGIC 0x9dac27a7 SFXREQ *r; int fd; int id; char *path; } ; #endif /* * Value which matches none of the real KP_*_MAGIC values. */ #define KP_NO_MAGIC 0 /* Forwards */ #define FAMILY(tag) \ static int kind_##tag##_prep(SFXREQ *); \ static int kind_##tag##_start(SFXREQ *, int); \ static void kind_##tag##_unstart(SFXREQ *); \ static void kind_##tag##_go(SFXREQ *, const char *); \ static int kind_##tag##_listening(SFXREQ *); \ static void kind_##tag##_disable(SFXREQ *); #ifndef X11_NO_TCP FAMILY(tcp) #endif #ifndef X11_NO_LOCAL FAMILY(local) #endif #undef FAMILY static const XCKIND xckinds[] #define INIT(n,tag) { (n), #tag, \ &kind_##tag##_prep, \ &kind_##tag##_start, \ &kind_##tag##_unstart, \ &kind_##tag##_go, \ &kind_##tag##_listening, \ &kind_##tag##_disable } = { #define XCKX_PRE_TCP 0 #ifndef X11_NO_TCP INIT(XCKX_PRE_TCP,tcp), #define XCKX_TCP XCKX_PRE_TCP #define XCKX_POST_TCP (XCKX_PRE_TCP+1) #else #define XCKX_POST_TCP XCKX_PRE_TCP #endif #define XCKX_PRE_LOCAL XCKX_POST_TCP #ifndef X11_NO_LOCAL INIT(XCKX_PRE_LOCAL,local), #define XCKX_LOCAL XCKX_PRE_LOCAL #define XCKX_POST_LOCAL (XCKX_PRE_LOCAL+1) #else #define XCKX_POST_LOCAL XCKX_PRE_LOCAL #endif #define NXCKINDS XCKX_POST_LOCAL }; #undef INIT static const char *local_dir_path = "/tmp/.X11-unix"; static int get_sin_vector(const char *hn, int *np, struct sockaddr_in **svp) { struct addrinfo *ai; struct addrinfo *ai0; struct addrinfo hints; int n; hints.ai_flags = 0; hints.ai_family = PF_INET; 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; n = getaddrinfo(hn,0,&hints,&ai0); if (n || !ai0) return(1); n = 0; for (ai=ai0;ai;ai=ai->ai_next) { if ( (ai->ai_addr->sa_family != AF_INET) || (ai->ai_addrlen != sizeof(struct sockaddr_in)) ) { freeaddrinfo(ai0); return(1); } n ++; } if (n < 1) { freeaddrinfo(ai0); return(1); } *np = n; *svp = malloc(n*sizeof(struct sockaddr_in)); n = 0; for (ai=ai0;ai;ai=ai->ai_next) svp[0][n++] = *(struct sockaddr_in *)ai->ai_addr; freeaddrinfo(ai0); return(0); } static void run_xauth(const char *s0, ...) { typedef struct ev EV; struct ev { const char *ev; const char *cv; } ; static const EV evs[] = { { "USER", "user" }, { "LOGNAME", "user" }, { "HOME", "home" }, { "SHELL", "shell" }, { 0, 0 } }; va_list ap; int n; const char *s; const char **v; pid_t kid; sigset_t m; sigset_t om; int i; sigemptyset(&m); sigemptyset(&om); sigaddset(&m,SIGCHLD); sigprocmask(SIG_BLOCK,&m,&om); n = 1; va_start(ap,s0); while (va_arg(ap,const char *)) n ++; va_end(ap); v = malloc((n+2)*sizeof(const char *)); n = 0; v[n++] = "xauth"; v[n++] = s0; va_start(ap,s0); while ((s = va_arg(ap,const char *))) v[n++] = s; va_end(ap); v[n] = 0; if (VERB(X)) { verb(X,"running:"); for (i=0;iid != PL_NOID) remove_poll_id(c->id); if (c->fd >= 0) close(c->fd); free(c->name); free(c->data); free(c->err); free(c); } static void unlink_destroy_sxconn(SXCONN *c) { DLL_UNLINK(c,c->req->conns); destroy_sxconn(c); } static void wr_sxerr(int id __attribute__((__unused__)), void *cv) { SXCONN *c; int w; c = cv; w = write(c->fd,c->err+c->ioptr,c->len-c->ioptr); if (w < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } unlink_destroy_sxconn(c); return; } c->ioptr += w; if (c->ioptr >= c->len) unlink_destroy_sxconn(c); } static void sfxreq_disable(SFXREQ *r) { int i; for (i=NXCKINDS-1;i>=0;i--) (*xckinds[i].disable)(r); } static void rd_sxconn(int id, void *cv) { SXCONN *c; int r; c = cv; r = read(c->fd,c->buf+c->ioptr,c->len-c->ioptr); if (r < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; break; } } if (r <= 0) { unlink_destroy_sxconn(c); return; } c->ioptr += r; if (c->ioptr < c->len) return; switch (c->state) { case SXS_HDR: switch (c->hdr[0]) { case 0x42: case 0x6c: break; default: unlink_destroy_sxconn(c); return; break; } c->nlen = x_get16(c->hdr[0],&c->hdr[6]); c->dlen = x_get16(c->hdr[0],&c->hdr[8]); c->len = (c->nlen + 3) & ~3; c->ioptr = 0; c->name = malloc(c->len); c->buf = c->name; c->state = SXS_NAME; if (c->len) break; case SXS_NAME: c->state = SXS_DATA; c->len = (c->dlen + 3) & ~3; c->ioptr = 0; c->data = malloc(c->len); c->buf = c->data; if (c->len) break; case SXS_DATA: { const char *errmsg; int msglen; int errlen; if (VERB(X)) { verb(X,"X connection auth data len=%d: ",c->nlen); verb_vis(VERBOSE_X,c->name,c->nlen); verb(X,"\n"); } if ( !(*c->kind->listening)(c->req) || (c->nlen != 18) || (c->dlen != 16) || bcmp(c->name,"MIT-MAGIC-COOKIE-1",18) || bcmp(c->data,&c->req->cookie[0],16) ) { errmsg = "Client is not authorized to connect to Server"; } else { (*c->req->gotfd)(c->fd,&c->hdr[0],c->req->arg); c->fd = -1; unlink_destroy_sxconn(c); if (c->req->flags & XFF_SINGLE) sfxreq_disable(c->req); return; } msglen = strlen(errmsg); if (msglen > 255) panic("error message too long"); errlen = 1 + 1 + 2 + 2 + 2 + ((msglen+3)&~3); c->err = malloc(errlen); bzero(c->err,errlen); c->err[1] = msglen; bcopy(&c->hdr[2],&c->err[2],4); X_put_2(c->hdr[0],&c->err[6],(msglen+3)>>2); bcopy(errmsg,&c->err[8],msglen); c->len = errlen; c->ioptr = 0; remove_poll_id(id); c->id = add_poll_fd(c->fd,&rwtest_never,&rwtest_always,0,&wr_sxerr,c); } break; default: panic("impossible SXCONN state %d",c->state); break; } } static void destroy_cxip(CXIP *cx) { if (cx->id != PL_NOID) remove_poll_id(cx->id); if (cx->fd >= 0) close(cx->fd); switch (cx->hdrphase) { case CXIP_HP_NAME: case CXIP_HP_DATA: free(cx->wb); break; case CXIP_HP_NONE: case CXIP_HP_HDR: case CXIP_HP_LENS: break; } free(cx); } static void unlink_destroy_cxip(CXIP *cx) { DLL_UNLINK(cx,cx->r->ip); destroy_cxip(cx); } static void cx_start_try(CXIP *); /* forward */ static void cx_start_fail(CXIP *cx) { DLL_UNLINK(cx,cx->r->ip); (*cx->fail)(cx->arg); destroy_cxip(cx); } static void wr_cx_hdr(int id __attribute__((__unused__)), void *cxv) { CXIP *cx; int l; Xauth *a; cx = cxv; l = write(cx->fd,cx->wb+cx->wp,cx->wl-cx->wp); if (l < 0) { switch (errno) { case EWOULDBLOCK: case EINTR: return; } cx_start_fail(cx); return; } cx->wp += l; while (cx->wp >= cx->wl) { switch (cx->hdrphase) { case CXIP_HP_HDR: cx->hdrphase = CXIP_HP_LENS; cx->wb = &cx->lens[0]; a = cx->r->authv[cx->i]; X_put_2(cx->hdr[0],cx->wb,a?a->name_length:0); X_put_2(cx->hdr[0],cx->wb+2,a?a->data_length:0); X_put_2(cx->hdr[0],cx->wb+4,0); cx->wp = 0; cx->wl = 6; break; case CXIP_HP_LENS: cx->hdrphase = CXIP_HP_NAME; a = cx->r->authv[cx->i]; if (a) { l = (a->name_length + 3) & ~3; cx->wb = malloc(l); bzero(cx->wb,l); bcopy(a->name,cx->wb,a->name_length); cx->wl = l; } else { cx->wb = 0; cx->wl = 0; } cx->wp = 0; break; case CXIP_HP_NAME: cx->hdrphase = CXIP_HP_DATA; a = cx->r->authv[cx->i]; if (a) { l = (a->data_length + 3) & ~3; free(cx->wb); cx->wb = malloc(l); bzero(cx->wb,l); bcopy(a->data,cx->wb,a->data_length); cx->wl = l; } else { cx->wb = 0; cx->wl = 0; } cx->wp = 0; break; case CXIP_HP_DATA: DLL_UNLINK(cx,cx->r->ip); (*cx->done)(cx->fd,cx->arg); cx->fd = -1; destroy_cxip(cx); return; break; default: panic("impossible CXIP hdrphase %d",cx->hdrphase); break; } } } static void cx_start_ok(CXIP *cx, int fd) { cx->fd = fd; if (cx->id != PL_NOID) remove_poll_id(cx->id); cx->hdrphase = CXIP_HP_HDR; cx->wb = &cx->hdr[0]; cx->wl = 6; cx->wp = 0; cx->id = add_poll_fd(fd,&rwtest_never,&rwtest_always,0,&wr_cx_hdr,cx); } static void cx_start_next(CXIP *cx) { cx->i ++; if (cx->i >= cx->r->nsin) { cx_start_fail(cx); } else { cx_start_try(cx); } } static void cx_start_chk(int id, void *cxv) { CXIP *cx; int err; socklen_t errlen; cx = cxv; remove_poll_id(id); cx->id = PL_NOID; errlen = sizeof(err); if (getsockopt(cx->fd,SOL_SOCKET,SO_ERROR,&err,&errlen) < 0) { if (VERB(X)) verb(X,"[cx_start_chk: getsockopt: %s]\r\n",strerror(errno)); close(cx->fd); cx->fd = -1; cx_start_next(cx); } else if (err) { if (VERB(X)) verb(X,"[cx_start_chk: connect: %s]\r\n",strerror(err)); close(cx->fd); cx->fd = -1; cx_start_next(cx); } else { if (VERB(X)) verb(X,"[cx_start_chk: success %d]\r\n",cx->fd); cx_start_ok(cx,cx->fd); } } static void cx_start_try(CXIP *cx) { int s; struct sockaddr_in sin; s = socket(AF_INET,SOCK_STREAM,0); if (s < 0) { if (VERB(X)) verb(X,"[cx_start_try: socket: %s]\r\n",strerror(errno)); cx_start_fail(cx); return; } sin = cx->r->sinv[cx->i]; fcntl(s,F_SETFL,fcntl(s,F_GETFL,0)|O_NONBLOCK); if (connect(s,(const void *)&sin,sizeof(sin)) < 0) { if (errno == EINPROGRESS) { cx->fd = s; cx->id = add_poll_fd(s,&rwtest_never,&rwtest_always,0,&cx_start_chk,cx); return; } if (VERB(X)) verb(X,"[cx_start_try: connect %s/%u: %s]\r\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),strerror(errno)); cx_start_fail(cx); } else { if (VERB(X)) verb(X,"[cx_start_try: success %d]\r\n",s); cx_start_ok(cx,s); } } #ifndef X11_NO_TCP static void kind_tcp__accept(int id __attribute__((__unused__)), void *pv) { KP_TCP *p; int new; struct sockaddr_in sin; socklen_t sinlen; SXCONN *c; p = pv; if (p->magic != KP_TCP_MAGIC) panic("kind_tcp_accept: bad magic"); if (p->fd < 0) return; sinlen = sizeof(sin); new = accept(p->fd,(void *)&sin,&sinlen); if (new < 0) return; c = malloc(sizeof(SXCONN)); c->fd = new; c->id = add_poll_fd(new,&rwtest_always,&rwtest_never,&rd_sxconn,0,c); c->state = SXS_HDR; c->name = 0; c->data = 0; c->buf = &c->hdr[0]; c->err = 0; c->ioptr = 0; c->len = 12; c->kind = &xckinds[XCKX_TCP]; c->req = p->r; DLL_LINK_HEAD(c,p->r->conns); } static int kind_tcp_prep(SFXREQ *r) { KP_TCP *p; const char *hn; int ns; struct sockaddr_in *sv; int s; s = socket(AF_INET,SOCK_STREAM,0); if (s < 0) return(0); hn = config_str("local-host-name"); if (get_sin_vector(hn,&ns,&sv)) { close(s); return(0); } p = malloc(sizeof(KP_TCP)); p->magic = KP_TCP_MAGIC; p->r = r; p->sinv = sv; p->nsin = ns; p->fd = s; p->id = PL_NOID; r->kindprivs[XCKX_TCP] = p; return(1); } static int kind_tcp_start(SFXREQ *r, int dno) { KP_TCP *p; struct sockaddr_in sin; p = r->kindprivs[XCKX_TCP]; if (p->magic != KP_TCP_MAGIC) panic("kind_tcp_start: bad magic"); if (p->fd < 0) { p->fd = socket(AF_INET,SOCK_STREAM,0); if (p->fd < 0) return(0); } bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(6000+dno); if (bind(p->fd,(void *)&sin,sizeof(sin)) < 0) { close(p->fd); p->fd = -1; return(0); } fcntl(p->fd,F_SETFD,1); fcntl(p->fd,F_SETFL,fcntl(p->fd,F_GETFL,0)|O_NONBLOCK); listen(p->fd,10); return(1); } static void kind_tcp_unstart(SFXREQ *r) { KP_TCP *p; p = r->kindprivs[XCKX_TCP]; if (p->magic != KP_TCP_MAGIC) panic("kind_tcp_unstart: bad magic"); close(p->fd); p->fd = -1; } static void kind_tcp_go(SFXREQ *r, const char *cookie) { void *pv; KP_TCP *p; int i; char *dn; pv = r->kindprivs[XCKX_TCP]; if (! pv) panic("kind_tcp_go: nil pointer"); p = pv; if (p->magic != KP_TCP_MAGIC) panic("kind_tcp_go: bad magic"); for (i=p->nsin-1;i>=0;i--) { asprintf(&dn,"%s:%d",inet_ntoa(p->sinv[i].sin_addr),r->dispno); run_xauth("add",dn,"MIT-MAGIC-COOKIE-1",cookie,(char *)0); free(dn); } p->id = add_poll_fd(p->fd,&rwtest_always,&rwtest_never,&kind_tcp__accept,0,p); } static int kind_tcp_listening(SFXREQ *r) { return(!!r->kindprivs[XCKX_TCP]); } static void kind_tcp_disable(SFXREQ *r) { void *pv; KP_TCP *p; int i; char *dn; pv = r->kindprivs[XCKX_TCP]; if (! pv) return; p = pv; if (p->magic != KP_TCP_MAGIC) panic("kind_tcp_disable: bad magic"); if (p->id != PL_NOID) { remove_poll_id(p->id); for (i=p->nsin-1;i>=0;i--) { asprintf(&dn,"%s:%d",inet_ntoa(p->sinv[i].sin_addr),r->dispno); run_xauth("remove",dn,(char *)0); free(dn); } p->id = PL_NOID; } if (p->fd >= 0) { close(p->fd); p->fd = -1; } free(p->sinv); p->sinv = 0; p->magic = KP_NO_MAGIC; free(p); r->kindprivs[XCKX_TCP] = 0; } #endif #ifndef X11_NO_LOCAL static void kind_local__accept(int id __attribute__((__unused__)), void *pv) { KP_LOCAL *p; int new; struct sockaddr_storage ss; socklen_t sslen; SXCONN *c; p = pv; if (p->fd < 0) return; if (p->magic != KP_LOCAL_MAGIC) panic("kind_local_accept: bad magic"); sslen = sizeof(ss); new = accept(p->fd,(void *)&ss,&sslen); if (new < 0) return; c = malloc(sizeof(SXCONN)); c->fd = new; c->id = add_poll_fd(new,&rwtest_always,&rwtest_never,&rd_sxconn,0,c); c->state = SXS_HDR; c->name = 0; c->data = 0; c->buf = &c->hdr[0]; c->err = 0; c->ioptr = 0; c->len = 12; c->kind = &xckinds[XCKX_LOCAL]; c->req = p->r; DLL_LINK_HEAD(c,p->r->conns); } static int kind_local_prep(SFXREQ *r) { KP_LOCAL *p; int s; struct stat stb; if (stat(local_dir_path,&stb) < 0) return(0); if ((stb.st_mode & S_IFMT) != S_IFDIR) return(0); if ((stb.st_mode & 0777) != 0777) { if (stb.st_uid == geteuid()) { if ((stb.st_mode & 0700) != 0700) return(0); } else if (stb.st_gid == getegid()) { if ((stb.st_mode & 0070) != 0070) return(0); } else { if ((stb.st_mode & 0007) != 0007) return(0); } } s = socket(AF_LOCAL,SOCK_STREAM,0); if (s < 0) return(0); p = malloc(sizeof(KP_LOCAL)); p->magic = KP_LOCAL_MAGIC; p->r = r; p->fd = s; p->id = PL_NOID; p->path = 0; r->kindprivs[XCKX_LOCAL] = p; return(1); } static int kind_local_start(SFXREQ *r, int dno) { KP_LOCAL *p; struct sockaddr_un *sun; socklen_t sunlen; char *path; p = r->kindprivs[XCKX_LOCAL]; if (p->magic != KP_LOCAL_MAGIC) panic("kind_local_start: bad magic"); if (p->fd < 0) { p->fd = socket(AF_LOCAL,SOCK_STREAM,0); if (p->fd < 0) return(0); } asprintf(&path,"%s/X%d",local_dir_path,dno); sunlen = sizeof(*sun) - sizeof(sun->sun_path) + strlen(path); sun = malloc(sunlen+1); sun->sun_family = AF_LOCAL; sun->sun_len = sunlen; strcpy(&sun->sun_path[0],path); if (bind(p->fd,(void *)sun,sunlen) < 0) { close(p->fd); p->fd = -1; free(path); free(sun); return(0); } fcntl(p->fd,F_SETFD,1); fcntl(p->fd,F_SETFL,fcntl(p->fd,F_GETFL,0)|O_NONBLOCK); listen(p->fd,10); free(sun); p->path = path; return(1); } static void kind_local_unstart(SFXREQ *r) { KP_LOCAL *p; p = r->kindprivs[XCKX_TCP]; if (p->magic != KP_TCP_MAGIC) panic("kind_local_unstart: bad magic"); close(p->fd); p->fd = -1; unlink(p->path); free(p->path); p->path = 0; } static void kind_local_go(SFXREQ *r, const char *cookie) { void *pv; KP_LOCAL *p; char *dn; pv = r->kindprivs[XCKX_LOCAL]; if (! pv) panic("kind_local_go: nil pointer"); p = pv; if (p->magic != KP_LOCAL_MAGIC) panic("kind_local_go: bad magic"); asprintf(&dn,"%s/unix:%d",config_str("local-host-name"),r->dispno); run_xauth("add",dn,"MIT-MAGIC-COOKIE-1",cookie,(char *)0); free(dn); p->id = add_poll_fd(p->fd,&rwtest_always,&rwtest_never,&kind_local__accept,0,p); } static int kind_local_listening(SFXREQ *r) { return(!!r->kindprivs[XCKX_LOCAL]); } static void kind_local_disable(SFXREQ *r) { void *pv; KP_LOCAL *p; char *dn; pv = r->kindprivs[XCKX_LOCAL]; if (! pv) return; p = pv; if (p->magic != KP_LOCAL_MAGIC) panic("kind_local_disable: bad magic"); if (p->id != PL_NOID) { remove_poll_id(p->id); asprintf(&dn,"%s/unix:%d",config_str("local-host-name"),r->dispno); run_xauth("remove",dn,(char *)0); free(dn); p->id = PL_NOID; } if (p->fd >= 0) { close(p->fd); p->fd = -1; unlink(p->path); } p->magic = KP_NO_MAGIC; free(p); r->kindprivs[XCKX_LOCAL] = 0; } #endif CFXREQ *X_forward_req(void) { CFXREQ *r; char *dv; char *cp; char *dp; char *ep; char *hn; unsigned long int dispno; unsigned long int scrno; int i; dv = getenv("DISPLAY"); if (! dv) return(0); cp = index(dv,':'); if (! cp) return(0); dispno = strtoul(cp+1,&dp,0); if ((dp == cp+1) || (*dp && (*dp != '.'))) return(0); if (*dp) { scrno = strtoul(dp+1,&ep,0); if ((ep == dp+1) || *ep) return(0); } else { scrno = 0; } hn = (cp == dv) ? strdup("127.0.0.1") : blk_to_cstr(dv,cp-dv); r = malloc(sizeof(CFXREQ)); if (get_sin_vector(hn,&r->nsin,&r->sinv)) { free(hn); free(r); return(0); } free(hn); r->authv = malloc(r->nsin*sizeof(Xauth *)); for (i=0;insin;i++) { r->sinv[i].sin_port = htons(6000+dispno); r->authv[i] = XauGetAuthByAddr(FamilyInternet,4,(void *)&r->sinv[i].sin_addr,dp-(cp+1),cp+1,0,0); } r->scrno = scrno; r->ip = 0; return(r); } void X_forward_done(CFXREQ *r) { int i; while (r->ip) { CXIP *cx; cx = r->ip; unlink_destroy_cxip(cx); } free(r->sinv); for (i=r->nsin-1;i>=0;i--) if (r->authv[i]) XauDisposeAuth(r->authv[i]); free(r->authv); free(r); } void *X_fwdreq_gen(void *opp, CFXREQ *r) { return(put_uint32(opp,r->scrno)); } /* * It would be nice to establish listeners for non-TCP X connections * here. Unfortunately, as far as I know, there is no standard for * other kinds of connections, so it's difficult for us - not part of * the X installation - to know how to do that. * * We just depend on some de-facto standards. Specifically, we assume * that if /tmp/.X11-unix/ exists, it contains AF_LOCAL sockets X%d. */ SFXREQ *X_fwdreq_start( const void *data, int datalen, const void **restp, int *restlenp, void (*fail)(const void *, const char *, ...), void (*gotfd)(int, unsigned char *, void *), void *arg, unsigned int flags ) { unsigned int scrno; SFXREQ *r; int i; int ok[NXCKINDS]; int anyok; parse_data(data,datalen,fail, PP_UINT32(&scrno), PP_REST(restp,restlenp) ); r = malloc(sizeof(SFXREQ)); r->hostname = strdup(config_str("local-host-name")); r->scrno = scrno; r->gotfd = gotfd; r->arg = arg; r->kindprivs = malloc(NXCKINDS*sizeof(void *)); anyok = 0; for (i=NXCKINDS-1;i>=0;i--) { if ((*xckinds[i].prep)(r)) { ok[i] = 1; anyok = 1; } else { r->kindprivs[i] = 0; } } if (! anyok) { free(r->kindprivs); free(r); return(0); } for <"dispno"> (r->dispno=25;;r->dispno++) { for (i=0;idispno)) { for (i--;i>=0;i--) if (ok[i]) (*xckinds[i].unstart)(r); continue <"dispno">; } } random_data(&r->cookie[0],16); r->gotfd = gotfd; r->arg = arg; r->conns = 0; r->flags = flags; return(r); } } void X_fwdreq_abort(SFXREQ *r) { sfxreq_disable(r); while (r->conns) { SXCONN *c; c = r->conns; DLL_UNLINK(c,r->conns); destroy_sxconn(r->conns); } free(r->hostname); free(r); } void X_fwdreq_go(SFXREQ *r) { int i; char cookietext[33]; for (i=0;i<16;i++) sprintf(&cookietext[i+i],"%02x",r->cookie[i]); for (i=NXCKINDS-1;i>=0;i--) (*xckinds[i].go)(r,&cookietext[0]); } void X_fwdreq_env(SFXREQ *r, void (*setvar)(const char *, const char *)) { char *t; asprintf(&t,"%s:%d.%d",r->hostname,r->dispno,r->scrno); (*setvar)("DISPLAY",t); free(t); } void X_start_connect(CFXREQ *r, const unsigned char *hdr, void (*done)(int, void *), void (*fail)(void *), void *arg) { CXIP *cx; cx = malloc(sizeof(CXIP)); DLL_LINK_HEAD(cx,r->ip); cx->r = r; cx->i = 0; cx->fd = -1; cx->id = PL_NOID; cx->done = done; cx->fail = fail; cx->arg = arg; cx->hdrphase = CXIP_HP_NONE; bcopy(hdr,&cx->hdr[0],6); cx_start_try(cx); } #endif void X_put_2(char order, void *data, unsigned short int v) { switch (order) { case 0x42: ((unsigned char *)data)[0] = v >> 8; ((unsigned char *)data)[1] = v & 0xff; break; case 0x6c: ((unsigned char *)data)[0] = v & 0xff; ((unsigned char *)data)[1] = v >> 8; break; default: panic("X_put_2: bad byte order %#x\n",order); break; } } int X_auth_len(const unsigned char *hdr) { return( X_pad_roundup(x_get16(hdr[0],&hdr[6])) + X_pad_roundup(x_get16(hdr[0],&hdr[8])) ); } int X_auth_match( const unsigned char *hdr, const void *proto_data, int proto_len, const void *cookie_data, int cookie_len ) { return( (x_get16(hdr[0],&hdr[6]) == proto_len) && (x_get16(hdr[0],&hdr[8]) == cookie_len) && !bcmp(&hdr[12],proto_data,proto_len) && !bcmp(&hdr[12+X_pad_roundup(proto_len)],cookie_data,cookie_len) ); } unsigned int X_pad_roundup(unsigned int v) { return((v+3U)&~3U); }