|
|
#include <stdint.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#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; }
|