sockroot

Login to a secured (chrooted) shell via a UNIX-socket
Log | Files | Refs

sockroot.c (3648B)


      1 #include <errno.h>
      2 #include <signal.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <string.h>
      6 #include <sys/socket.h>
      7 #include <sys/stat.h>
      8 #include <sys/un.h>
      9 #include <sys/wait.h>
     10 #include <unistd.h>
     11 
     12 #define SERVER_SOCK_FILE "sockroot.sock"
     13 #define CHROOT           "test/"
     14 #define EXECUTE          "/bin/sh"
     15 #define SOCKET_LIMIT     64
     16 
     17 int processes[SOCKET_LIMIT];
     18 int processes_size = 0;
     19 
     20 void handle_client(int client) {
     21 	int pid;
     22 
     23 	if (processes_size == SOCKET_LIMIT) {
     24 		fprintf(stderr, "error: cannot accept client: process-limit reached\n");
     25 		return;
     26 	}
     27 
     28 	while ((pid = fork()) == -1) {
     29 		fprintf(stderr, "warning: fork for process: %s, waiting 1sec...\n", strerror(errno));
     30 		sleep(1);
     31 	}
     32 
     33 	if (pid == 0) {    // is child
     34 		dup2(client, 0);
     35 		dup2(client, 1);
     36 		dup2(client, 2);
     37 		close(client);
     38 
     39 		if (chroot(CHROOT) == -1) {
     40 			fprintf(stderr, "error: cannot chroot to '" CHROOT "': %s\n", strerror(errno));
     41 			_exit(1);
     42 		}
     43 		if (chdir("/") == -1) {
     44 			fprintf(stderr, "error: cannot chdir to '/': %s\n", strerror(errno));
     45 			_exit(1);
     46 		}
     47 
     48 		execl(EXECUTE, EXECUTE, "-i", NULL);
     49 		fprintf(stderr, "error: cannot execute '" EXECUTE "': %s\n", strerror(errno));
     50 		_exit(1);
     51 	}
     52 
     53 	close(client);
     54 
     55 	printf("new connection: %d\n", pid);
     56 
     57 	for (int i = 0; i < SOCKET_LIMIT; i++) {
     58 		if (processes[i] == 0) {
     59 			processes[i] = pid;
     60 			processes_size++;
     61 			return;
     62 		}
     63 	}
     64 }
     65 
     66 void on_child(int signal) {
     67 	(void) signal;
     68 
     69 	int pid, status;
     70 	if ((pid = wait(&status)) == -1) {
     71 		fprintf(stderr, "error: cannot await child: %s\n", strerror(errno));
     72 		return;
     73 	}
     74 	printf("%d terminated\n", pid);
     75 
     76 	for (int i = 0; i < SOCKET_LIMIT; i++) {
     77 		if (processes[i] == pid) {
     78 			processes[i] = 0;
     79 			processes_size--;
     80 			return;
     81 		}
     82 	}
     83 	fprintf(stderr, "error: cannot find %d in process-table\n", pid);
     84 }
     85 
     86 void on_interrupt(int signal) {
     87 	(void) signal;
     88 
     89 	for (int i = 0; i < SOCKET_LIMIT; i++) {
     90 		if (processes[i] > 0 && kill(processes[i], SIGTERM) == -1) {
     91 			fprintf(stderr, "warning: unable to terminate %d: %s\n", processes[i], strerror(errno));
     92 		}
     93 	}
     94 
     95 	while (processes_size > 0) {
     96 		printf("awaiting %d processes to stop\n", processes_size);
     97 		sleep(1);
     98 	}
     99 
    100 	if (processes_size > 0) {
    101 		printf("kill %d left over processes\n", processes_size);
    102 		for (int i = 0; i < SOCKET_LIMIT; i++) {
    103 			if (processes[i] > 0 && kill(processes[i], SIGKILL) == -1) {
    104 				fprintf(stderr, "warning: unable to kill %d: %s\n", processes[i], strerror(errno));
    105 			}
    106 		}
    107 		sleep(1);
    108 	}
    109 	exit(0);
    110 }
    111 
    112 int main(void) {
    113 	if (unlink(SERVER_SOCK_FILE) == -1 && errno != ENOENT) {
    114 		fprintf(stderr, "error: cannot unlink socket: %s\n", strerror(errno));
    115 		return 1;
    116 	}
    117 
    118 	int server;
    119 
    120 	if ((server = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
    121 		fprintf(stderr, "error: cannot create socket: %s\n", strerror(errno));
    122 		return 1;
    123 	}
    124 
    125 	umask(0);
    126 
    127 	// bind socket to address
    128 	struct sockaddr_un addr = { 0 };
    129 	addr.sun_family         = AF_UNIX;
    130 	strncpy(addr.sun_path, SERVER_SOCK_FILE, sizeof(addr.sun_path));
    131 	if (bind(server, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
    132 		fprintf(stderr, "error: cannot bind to socket: %s\n", strerror(errno));
    133 		return 1;
    134 	}
    135 
    136 	// listen for connections
    137 	if (listen(server, 5) == -1) {
    138 		fprintf(stderr, "error: cannot listen socket: %s\n", strerror(errno));
    139 		return 1;
    140 	}
    141 
    142 	for (int i = 0; i < SOCKET_LIMIT; i++) {
    143 		processes[i] = 0;
    144 	}
    145 
    146 	signal(SIGINT, on_interrupt);
    147 	signal(SIGCHLD, on_child);
    148 
    149 	while (1) {
    150 		int client_fd;
    151 		if ((client_fd = accept(server, NULL, NULL)) == -1) {
    152 			fprintf(stderr, "error: cannot accept client from socket: %s\n", strerror(errno));
    153 			continue;
    154 		}
    155 		handle_client(client_fd);
    156 	}
    157 
    158 	return 0;
    159 }