mirror of the now-defunct rocklinux.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

456 lines
10 KiB

#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 proc %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;
}