#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TCPPORT 28336 #define CMD_EXEC 1 #define CMD_ADDARG 2 #define CMD_ADDENV 3 #define CMD_CHDIR 4 #define CMD_CHROOT 5 #define CMD_EXIT 6 #define CMD_BUILDID 7 #define CMD_WRITE0 1000 #define CMD_CLOSE0 2000 #define CMD_ENABLE0 3000 struct pn_pkg { uint32_t len; uint32_t type; char data[]; }; struct sbuf; struct sbuf { struct sbuf *next; int len, c; struct pn_pkg *p; char *d; }; struct sbuf *recv4p = 0; struct sbuf *send2p = 0; int sbuf_c = 0; void conn_send(int conn, int len, int type, char *data) { struct pn_pkg *p = malloc( sizeof(struct pn_pkg) + len ); int i, rc; p->len = htonl(len); p->type = htonl(type); memcpy(p->data, data, len); len += sizeof(struct pn_pkg); for (i = 0; i < len; i += rc) { rc = write(conn, (char*)p + i, len - i); if ( rc <= 0 ) { fprintf(stderr, "network write error: %s\n", strerror(errno)); exit(-1); } } free(p); } int conn_read(int conn, char *buf, int len, int nonblock) { int f = -1; if ( nonblock ) { f = fcntl(conn, F_GETFL, 0); fcntl(conn, F_SETFL, f | O_NONBLOCK); } while (len) { int rc = read(conn, buf, len); if ( rc < 0 ) { if ( errno == EAGAIN ) break; fprintf(stderr, "network read error: %s\n", strerror(errno)); exit(-1); } if ( rc == 0 ) { fprintf(stderr, "connection closed by peer\n"); exit(-1); } if ( f != -1 ) fcntl(conn, F_SETFL, f); len -= rc; buf += rc; f = -1; } if ( f != -1 ) fcntl(conn, F_SETFL, f); return len; } struct pn_pkg *conn_recv(int conn, int nonblock) { struct pn_pkg *p = malloc( sizeof(struct pn_pkg) ); if ( conn_read(conn, (char*)p, 8, nonblock) ) { free(p); return 0; } p->len = ntohl(p->len); p->type = ntohl(p->type); p = realloc(p, sizeof(struct pn_pkg) + p->len); conn_read(conn, p->data, p->len, 0); return p; } void do_chroot(char *d, char *b) { char buf[1024]; char *e = malloc(strlen(d)+strlen(b)+2); int i; sprintf(e, "%s:%s", d, b); for (i=0; e[i]; i++) if ( e[i] == '/' ) e[i] = '_'; snprintf(buf, 1024, "%s/pseudonative_handler", e); if ( access(buf, F_OK) ) { mkdir(e, 0700); snprintf(buf, 1024, "mount -t nfs -o noac %s/build/%s %s", d, b, e); system(buf); snprintf(buf, 1024, "mount -t nfs -o noac %s %s/ROCK/loop", d, e); system(buf); snprintf(buf, 1024, "mount -t nfs -o noac %s/config %s/ROCK/config", d, e); system(buf); snprintf(buf, 1024, "mount -t nfs -o noac %s/download %s/ROCK/download", d, e); system(buf); snprintf(buf, 1024, "mount -t proc none %s/proc", e); system(buf); } chdir(e); chroot("."); free(e); } void do_session(int conn) { char *argv[1024], *bid="", *exe = "/bin/true"; int f0[2], f1[2], f2[2], m, f, argc = 0; int open_0 = 1, open_1 = 1, open_2 = 1; int schedule_close_0 = 0, enable_0 = 0; unsigned char retval; struct timeval tv; fd_set rfds, wfds; struct pn_pkg *p; clearenv(); signal(SIGCHLD, SIG_DFL); signal(SIGPIPE, SIG_IGN); while (1) { if ( (p = conn_recv(conn, 0)) == 0 ) { fprintf(stderr, "\nnetwork read error: EOF\n"); exit(-1); } switch (p->type) { case CMD_EXEC: printf("\nEXE: %s", p->data); exe = strdup(p->data); break; case CMD_ADDARG: printf(m == p->type ? " %s" : "\nARG: %s", p->data); argv[argc++] = strdup(p->data); break; case CMD_ADDENV: printf(m == p->type ? "." : "\nENV: ."); putenv(strdup(p->data)); break; case CMD_CHDIR: printf("\nCHD: %s", p->data); chdir(p->data); break; case CMD_BUILDID: bid = strdup(p->data); break; case CMD_CHROOT: printf("\nMNT: %s %s", p->data, bid); do_chroot(p->data, bid); break; } if ((m=p->type) == CMD_EXEC) { free(p); break; } free(p); } pipe(f0); pipe(f1); pipe(f2); printf("\n"); fflush(stderr); fflush(stdout); if (!fork()) { dup2(f0[0], 0); close(f0[0]); close(f0[1]); dup2(f1[1], 1); close(f1[0]); close(f1[1]); dup2(f2[1], 2); close(f2[0]); close(f2[1]); signal(SIGPIPE, SIG_DFL); argv[argc] = 0; execv(exe, argv); fprintf(stderr, "Can't execute %s: %s\n", exe, strerror(errno)); exit(-1); } close(f0[0]); close(f1[1]); close(f2[1]); /* This is usefull for reading strace dumps, etc. */ #if 0 printf("FDS: fd0=%d, fd1=%d, fd2=%d, conn=%d\n", f0[1], f1[0], f2[0], conn); #endif f = fcntl(f0[1], F_GETFL, 0); fcntl(f0[1], F_SETFL, f | O_NONBLOCK); f = fcntl(f1[0], F_GETFL, 0); fcntl(f1[0], F_SETFL, f | O_NONBLOCK); f = fcntl(f2[0], F_GETFL, 0); fcntl(f2[0], F_SETFL, f | O_NONBLOCK); while (open_1 || open_2) { char buf[512]; int rc; FD_ZERO(&rfds); FD_ZERO(&wfds); m = f0[1]; if ( conn > m ) m = conn; FD_SET(conn, &rfds); if ( open_1 ) { if ( f1[0] > m ) m = f1[0]; FD_SET(f1[0], &rfds); } if ( open_2 ) { if ( f2[0] > m ) m = f2[0]; FD_SET(f2[0], &rfds); } write(1, "?", 1); tv.tv_sec = 1; tv.tv_usec = 0; if ( schedule_close_0 < 2 && (send2p || !enable_0) ){ FD_SET(f0[1], &wfds); rc = select(m+1, &rfds, &wfds, 0, &tv); } else rc = select(m+1, &rfds, 0, 0, &tv); if ( rc < 0 && errno != EINTR ) { fprintf(stderr, "select error: %s\n", strerror(errno)); exit(-1); } /* FIXME: This should only be triggered if the proc is actually * sleeping (read(), poll(), select(), etc) on input */ if ( !enable_0 && FD_ISSET(f0[1], &wfds) ) { write(1, "E", 1); conn_send(conn, 0, CMD_ENABLE0, 0); enable_0 = 1; } nextread: if ( schedule_close_0 == 2 ) goto skip_send2p; while ( send2p ) { struct sbuf *t; while ( send2p->len > 0 ) { rc = write(f0[1], send2p->d, send2p->len); if ( rc <= 0 ) { if ( errno == EAGAIN ) goto skip_send2p; if ( errno == EPIPE ) { write(1, "[P]", 3); conn_send(conn, 0, CMD_CLOSE0, 0); schedule_close_0 = 2; goto skip_send2p; } fprintf(stderr, "write error (%d): %s\n", f0[1], strerror(errno)); exit(-1); } send2p->len -= rc; send2p->d += rc; fflush(stdout); } write(1, "X", 1); send2p = (t = send2p)->next; free(t->p); free(t); } if ( !send2p ) { recv4p = 0; if ( schedule_close_0 ) { schedule_close_0 = 2; write(1, "[X]", 3); close(f0[1]); } } skip_send2p:; if ( (p = conn_recv(conn, 1)) ) { if ( p->type == CMD_WRITE0 && open_0 ) { struct sbuf *t = malloc(sizeof(struct sbuf)); t->next = 0; t->p = p; t->len = p->len; t->d = p->data; t->c = sbuf_c++; if ( recv4p ) recv4p->next = t; else send2p = t; recv4p = t; write(1, "0", 1); goto nextread; } if ( p->type == CMD_CLOSE0 ) { write(1, "[0]", 3); if ( !schedule_close_0 ) schedule_close_0 = 1; open_0 = 0; } if ( p->type == CMD_CLOSE0+1 ) { write(1, "[P1]", 4); close(f1[0]); open_1 = 0; } if ( p->type == CMD_CLOSE0+2 ) { write(1, "[P2]", 4); close(f2[0]); open_2 = 0; } free(p); goto nextread; } if( open_1 ) { rc = read(f1[0], buf, 512); if ( rc > 0 ) { write(1, "1", 1); conn_send(conn, rc, CMD_WRITE0+1, buf); goto nextread; } else if (rc == 0) { write(1, "[1]", 3); conn_send(conn, 0, CMD_CLOSE0+1, 0); open_1 = 0; } else if ( errno != EAGAIN ) { perror("Error on read from f1:"); } } if( open_2 ) { rc = read(f2[0], buf, 512); if ( rc > 0 ) { write(1, "2", 1); conn_send(conn, rc, CMD_WRITE0+2, buf); goto nextread; } else if (rc == 0) { write(1, "[2]", 3); conn_send(conn, 0, CMD_CLOSE0+2, 0); open_2 = 0; } else if ( errno != EAGAIN ) { perror("Error on read from f2:"); } } } write(1, "\n", 1); wait(&m); retval = WEXITSTATUS(m); conn_send(conn, 1, CMD_EXIT, &retval); printf("RET: %d\n", retval); } int main(int argc, char **argv) { struct sockaddr_in addr; int listenfd, fd; printf("\n"); printf("**********************************************************************\n"); printf("* *\n"); printf("* ROCK Linux Pseudo-Native Daemon by Clifford Wolf *\n"); printf("* *\n"); printf("* This daemon will create subdirectories in the current working *\n"); printf("* directory and mount NFS shares there. There is no authentication *\n"); printf("* implemented - so everyone who can reach the TCP port can also *\n"); printf("* execute commands. So secure the port using iptables! *\n"); printf("* *\n"); printf("**********************************************************************\n"); printf("\n"); signal(SIGCHLD, SIG_IGN); if ( (listenfd=socket(AF_INET, SOCK_STREAM, 0)) == -1 ) { fprintf(stderr, "socket: %s\n", strerror(errno)); return 1; } bzero(&addr,sizeof(addr)); addr.sin_family=AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(TCPPORT); if ( bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ) { fprintf(stderr, "bind: %s\n", strerror(errno)); return 1; } if ( listen(listenfd, 5) == -1 ) { fprintf(stderr, "listen: %s\n", strerror(errno)); return 1; } printf("Listening on TCP port %d ...\n", TCPPORT); while (1) { if ( (fd=accept(listenfd, NULL, NULL)) == -1 ) { fprintf(stderr, "accept: %s\n", strerror(errno)); return 1; } if ( !fork() ) { fprintf(stderr, "\nconnection %d opened.", (int)getpid()); do_session(fd); return 0; } else close(fd); } return 0; }