diff --git a/.gitignore b/.gitignore index 4384999..a31ee5d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ obj/ bin/ isolate test* -rootfs \ No newline at end of file +rootfs +a.out diff --git a/Makefile b/Makefile index f8d38c7..5c4dc86 100644 --- a/Makefile +++ b/Makefile @@ -7,31 +7,66 @@ ROOTFSIMAGEURL := https://dl-cdn.alpinelinux.org/alpine/v3.15/releases/x86_64/al # Target Binary TARGET := isolate +USERNS := userns +MOUNTNS := mountns +UTILS := utils +MOUNTNS := mountns +PIDNS := pidns # Directories SRCDIR :=src INCDIR := include +OBJDIR := obj ROOTFSDIR := rootfs # Compile Time Flags CFLAGS := -Wall -Wextra -Werror=format-security -grecord-gcc-switches -fstack-clash-protection -pipe -g -O2 -D_GNU_SOURCE #Defauilt Make -all: $(TARGET) rootfs +all: rootfs utils pidns mountns mountns userns $(TARGET) rootfs: @$(RM) -rf $(ROOTFSIMAGE) $(ROOTFSDIR) @mkdir -p $(ROOTFSDIR) - @echo "Fetching Ubuntu rootfs image" && wget -q --show-progress $(ROOTFSIMAGEURL) -O $(ROOTFSIMAGE) - @echo "Extracting Rootfs" && tar -xzf $(ROOTFSIMAGE) -C $(ROOTFSDIR) && echo "Done!" + @echo "[+] Fetching Alpine rootfs image" && wget -q --show-progress $(ROOTFSIMAGEURL) -O $(ROOTFSIMAGE) + @echo "[+] Extracting Rootfs" && tar -xzf $(ROOTFSIMAGE) -C $(ROOTFSDIR) && echo "Done!" @$(RM) -rf $(ROOTFSIMAGE) -$(TARGET): $(SRCDIR)/$(TARGET).c - $(CC) $(CFLAGS) -I $(INCDIR) -o $(TARGET) $(SRCDIR)/$(TARGET).c + +utils: $(SRCDIR)/$(UTILS).c + @echo "[+] Compiling Program Utils" + @mkdir -p $(OBJDIR) + $(CC) $(CFLAGS) -I ${INCDIR} -c -o $(OBJDIR)/$(UTILS).o $(SRCDIR)/$(UTILS).c + + +pidns: $(SRCDIR)/$(PIDNS).c + @mkdir -p $(OBJDIR) + @echo "[+] Compiling PID Namespace Program" + $(CC) $(CFLAGS) -I ${INCDIR} -c -o $(OBJDIR)/$(PIDNS).o $(SRCDIR)/$(PIDNS).c + + +mountns: $(SRCDIR)/$(MOUNTNS).c + @mkdir -p $(OBJDIR) + @echo "[+] Compiling Mount Namespace Program" + $(CC) $(CFLAGS) -I ${INCDIR} -c -o $(OBJDIR)/$(MOUNTNS).o $(SRCDIR)/$(MOUNTNS).c + +userns: $(SRCDIR)/$(USERNS).c + @mkdir -p $(OBJDIR) + @echo "[+] Compiling User Namespace Program" + $(CC) $(CFLAGS) -I ${INCDIR} -c -o $(OBJDIR)/$(USERNS).o $(SRCDIR)/$(USERNS).c + + +$(TARGET): $(SRCDIR)/$(TARGET).c $(OBJDIR)/$(USERNS).o $(OBJDIR)/$(MOUNTNS).o $(OBJDIR)/$(UTILS).o + @echo "[+] Compiling" + $(CC) $(CFLAGS) -I ${INCDIR} -c -o $(OBJDIR)/$(TARGET).o $(SRCDIR)/$(TARGET).c + $(CC) $(CFLAGS) $(OBJDIR)/$(USERNS).o $(OBJDIR)/$(MOUNTNS).o $(OBJDIR)/$(TARGET).o $(OBJDIR)/$(UTILS).o $(OBJDIR)/$(PIDNS).o -o $(TARGET) + + + #Non-File Targets -.PHONY: clean rootfs +.PHONY: clean rootfs #Clean only Objecst clean: - @$(RM) -rf $(TARGET) $(ROOTFSDIR) + @$(RM) -rf $(TARGET) $(ROOTFSDIR) $(OBJDIR) diff --git a/include/isolate.h b/include/isolate.h deleted file mode 100644 index 887c078..0000000 --- a/include/isolate.h +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "utils.h" - -// Struct used for storing commands and parent-child processs communication -struct params { - int fd[2]; // Array to serve as "Pipe" - char **argv; // Command to execute -}; - -// Print command to be run in isolation -static inline void print_isolated_cmd(int argc, char **argv){ - int i; - for(i=0;ifd[0],buf,2)!= 2){ - exit_on_error("Failed to read from pipe while awaiting 'setup done' from main"); - } - - prepare_mount_ns(ROOTFS); - - // Close reading end of the pipe once done - if(close(params->fd[0])){ - exit_on_error("Failed to read close pipe :("); - } - - // Drop superuser privileges - if ((setuid(0)==-1) || setgid(0) == -1){ - exit_on_error("Error setting privileges :("); - } - - char **argv = params->argv; - char *cmd = argv[0]; - - if (execvp(cmd,argv)==-1){ - exit_on_error("Cannot execute command In Isolation:("); - return -1; - } - - exit(EXIT_SUCCESS); - return 0; -} \ No newline at end of file diff --git a/include/mountns.h b/include/mountns.h index 6a91b00..2b0ba5e 100644 --- a/include/mountns.h +++ b/include/mountns.h @@ -1,46 +1,13 @@ #pragma once -#include -#include -#include -#include -#include -#include "proc_ns.h" -#include "utils.h" +#ifndef __MOUNT_NS +#define __MOUNT_NS -static void prepare_mount_ns(char* rootfs){ - const char *mnt = rootfs; +#include +#include - // mount --bind rootfs rootfs - if(mount(rootfs, mnt, FSTYPE, MS_BIND,"") < 0){ - exit_on_error("Failed to mount rootfs"); - } +#define ROOTFS "rootfs" // Name of rootfs directory +#define FSTYPE "ext4" // FS Type + +int prepare_mountns(void); - // cd rootfs - if(chdir(mnt) < 0){ - exit_on_error("Failed to change directory"); - } - - // mkdir put_old - const char *put_old = ".put_old"; - if((mkdir(put_old,0777) != 0) && (errno != EEXIST)){ - exit_on_error("Could not create .put_old"); - } - - // pivot_root . .put_old - if(syscall(SYS_pivot_root,".",put_old)<0){ - exit_on_error("Could Not Pivot Root"); - } - - // cd / - if (chdir("/") < 0){ - exit_on_error("Could not change directory to /"); - } - - //prepare proc fs - prepare_procfs(); - - //umount .put_old - if(umount2(put_old, MNT_DETACH)){ - exit_on_error("Failed to umount .put_old"); - } -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/networkns.h b/include/networkns.h new file mode 100644 index 0000000..0778991 --- /dev/null +++ b/include/networkns.h @@ -0,0 +1,172 @@ +/* The Holy Bible which helped me through this: +https://maz-programmersdiary.blogspot.com/2011/09/netlink-sockets.html + +God protect the precious soul who wrote this +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" + +// Netlink Message Payload +#define MAX_PAYLOAD 1024 + +/* +Struct referencing the Netlink message +See https://github.com/shemminger/iproute2/blob/main/ip/ip_common.h +*/ +struct iplink_req{ + // See:https://datatracker.ietf.org/doc/html/rfc3549#section-2.3.2 + struct nlmsghdr n; // Netlink message header + // See https://elixir.bootlin.com/linux/v4.9/source/include/uapi/linux/rtnetlink.h#L481 + struct ifinfomsg i; + char buf[MAX_PAYLOAD]; +}; + +// Check response from Kernel +void check_response(int sock_fd){ + struct iovec iov; + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1 + }; + +} + + +// send Netlink message +static void send_nlmsg(int sock_fd, struct nlmsghdr *n){ + struct iovec iov = { + .iov_base = n, /* Starting address */ + .iov_len = n->nlmsg_len /* Starting address */ + }; + + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = &iov, + .msg_iovlen = 1 + }; + + n->nlmsg_seq++; + + if (sendmsg(sock_fd, &msg, 0) < 0){ + exit_on_error("[-] Netlink Request Failed"); + } + + check_response(sock_fd); +} + +// Create veth device +static int create_veth(int sock_fd, char *ifname, char *peername){ + /* Taken from source code of ip command + See Link 1060: https://github.com/shemminger/iproute2/blob/main/ip/iplink.c + */ + struct iplink_req req = { + /* From netlink(3) + NLMSG_LENGTH() + Given the payload length, len, this macro returns the aligned length to store + in the nlmsg_len field of the nlmsghdr. + */ + + .n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)), + /* From netlink(7) + NLM_F_REQUEST: Must be set on all request messages + NLM_F_ACK: Request for an acknowledgement on success. + NLM_F_CREATE: Create new device if it doesn't already exist + NLM_F_EXCL: Don't replace if the object already exists + */ + .n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_EXCL, + /* From rtnetlink(7) + RTM_NEWLINK: Create a specific network interface. + */ + .n.nlmsg_type = RTM_NEWLINK, + + // Set socket type to Netlink + .i.ifi_family = PF_NETLINK, + }; + + + struct nlmsghdr *n = &req.n; + int maxlen = sizeof(req); + + /* From libnetlink(3): + int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) + Add a variable length attribute of type type and with value data and alen length to netlink message n, + which is part of a buffer of length maxlen. data is copied. + + From https://www.infradead.org/~tgr/libnl/doc/route.html: + IFLA_IFNAME: Used to add link name attribute + */ + if (addattr_l(n, maxlen, IFLA_IFNAME, ifname, strlen(ifname)+1) <0){ + exit_on_error("Could Not Setup veth pair"); + } + + /* Create a network bridge + Add Attribute + See: https://unix.stackexchange.com/questions/441877/identify-if-a-network-interface-is-a-veth-using-sys-class-net + Also, I highly recommend refering to the Bible mentioned at the start of this code for the upcoming sections + */ + struct rtattr *linkinfo; + struct rtattr *linkdata; + struct rtattr *peerinfo; + linkinfo = addattr_nest(n, maxlen, IFLA_LINKINFO); + + // Specify a veth type device + if (addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, DEVICETYPE, strlen(DEVICETYPE)+1) < 0 ){ + addattr_nest_end(n, linkinfo); + exit_on_error("[-] Could not create veth device :("); + } + + // Add another nested attribute data + linkdata = addattr_nest(n, maxlen, IFLA_INFO_DATA); + + // Add nested attribute containing peer name + peerinfo = addattr_nest(n, maxlen, VETH_INFO_PEER); + n->nlmsg_len += sizeof(struct ifinfomsg); + + if (addattr_l(n, maxlen, IFLA_IFNAME, peername, strlen(peername) + 1)<0){ + addattr_nest_end(n, linkdata); + exit_on_error("[-] Could not add peer"); + } + + // End attributes + addattr_nest_end(n, peerinfo); + addattr_nest_end(n, linkdata); + addattr_nest_end(n, linkinfo); + + // Send message + send_nlmsg(sock_fd, n); + return 0; +} + +// Prepare Network Namespace +static int prepare_network_ns(){ + /* + PF_NETLINK: Netlink socket + SOCK_RAW: Raw Socket, requires sudo privileges + SOCK_CLOEXEC: Avoids a race condition between getting a new socket from accept and setting the FD_CLOEXEC flag afterwards. + NETLINK_ROUTE: Communication channel between user-space routing dæmon and kernel packet forwarding module. + + PS: PF_NETLINK and AF_NETLINK point to the same value as can be seen in socket.h: + #define PF_NETLINK AF_NETLINK + */ + int s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if(s < 0){ + exit_on_error("Failed to create Netlink socket"); + } + + if (create_veth(s, VETH0, VETH1)!= 0){ + exit_on_error("Could not create interface pair"); + } + + return 0; +} diff --git a/include/pidns.h b/include/pidns.h new file mode 100644 index 0000000..a187f14 --- /dev/null +++ b/include/pidns.h @@ -0,0 +1,10 @@ +#pragma once +#ifndef __PROC_NS +#define __PROC_NS +#include +#include +#include +#include "utils.h" + +int prepare_pidns(void); +#endif \ No newline at end of file diff --git a/include/proc_ns.h b/include/proc_ns.h deleted file mode 100644 index d1dc058..0000000 --- a/include/proc_ns.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include -#include -#include "utils.h" - -static void prepare_procfs(){ - // mkdir /proc - if ((mkdir("/proc", 0555) != 0) && (errno != EEXIST)){ - exit_on_error("Failed to mkdir /proc"); - } - - // mount proc - if (mount("proc", "/proc", "proc", 0, "")){ - exit_on_error("Failed to mount proc"); - } -} \ No newline at end of file diff --git a/include/userns.h b/include/userns.h index 2a79c14..dc1aa1d 100644 --- a/include/userns.h +++ b/include/userns.h @@ -1,59 +1,12 @@ +#pragma once +#ifndef __PREPARE_USER_NS #include #include -#include -#include "utils.h" #include +#include -void write_to_file(char* path_to_file, char *line_to_write){ - FILE *f = fopen(path_to_file,"w"); - if (f == NULL){ - exit_on_error("Failed to open file for writing:("); - } - if (fwrite(line_to_write, 1, strlen(line_to_write), f) == 0){ - exit_on_error("Failed to write to file :("); - } - if (fclose(f) != 0){ - exit_on_error("Failed to close file :("); - } -} - -static void prepare_user_ns(int pid){ - char* path; - char* line; - - /* Write UID to /proc/pid/uid_map - Format: - 0 UID 1 - - This ensures that under isolated conditions, we have UID 1 - */ - if ((asprintf(&path, "/proc/%d/uid_map", pid) < 0) || (asprintf(&line, "0 %d 1\n", UID) < 0)){ - exit_on_error("Could not write to file :("); - } - write_to_file(path, line); - - // Disable the setgroups system call - memset(path, 0, (int)sizeof(path)); - memset(line, 0, (int)sizeof(line)); - if ((asprintf(&path, "/proc/%d/setgroups", pid) < 0) || (asprintf(&line, "deny") < 0)){ - exit_on_error("Function asprintf() failed :("); - } - write_to_file(path, line); - - /* Write UID to /proc/pid/gid_map - Format: - 0 UID 1 - - This ensures that under isolated conditions, we have GID 1 - */ - memset(path, 0, (int)sizeof(path)); - memset(line, 0, (int)sizeof(line)); +#include "utils.h" - if ((asprintf(&path, "/proc/%d/gid_map", pid) < 0) || (asprintf(&line, "0 %d 1\n", UID) < 0)){ - exit_on_error("Could not write to file :("); - } - write_to_file(path, line); +int prepare_user_ns(int pid); - free(path); - free(line); -} \ No newline at end of file +#endif \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index 43ce71f..c7683d2 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,5 +1,12 @@ -#include #pragma once + +#ifndef __UTILS__ +#define __UTILS__ + +#include +#include +#include + #define RED(string) "\x1b[31m" string "\x1b[0m" #define GREEN(string) "\x1b[32m" string "\x1b[0m" #define YELLOW(string) "\x1b[33m" string "\x1b[0m" @@ -7,10 +14,14 @@ #define MAGENTA(string) "\x1b[35m" string "\x1b[0m" #define CYAN(string) "\x1b[36m" string "\x1b[0m" -#define exit_on_error(msg) ({fprintf(stderr,"[" RED("-") "] %s\n",msg); exit(EXIT_FAILURE);}) +#define UID 1000 +#define VETH0 "veth0" // The interface facing the root namespace +#define VETH1 "veth1" // The interface facing the network namespace +#define IP_0 "10.1.1.1" // IP address associated with VETH0 +#define IP_1 "10.1.1.2" // IP address associated with VETH1 +#define NETMASK "255.255.255.0" // Netmask of our virtual network +#define DEVICETYPE "veth" -#define STACKSIZE 1024*1024 +void exit_on_error(const char *format, ...); -#define UID 1000 -#define ROOTFS "rootfs" -#define FSTYPE "ext4" \ No newline at end of file +#endif \ No newline at end of file diff --git a/src/isolate.c b/src/isolate.c index 57bed15..9d84077 100644 --- a/src/isolate.c +++ b/src/isolate.c @@ -1,17 +1,34 @@ // Necessary headers -#include #include +#include +#include #include #include -#include -#include +#include #include +#include +#include + // User-defined headers -#include "mountns.h" -#include "isolate.h" #include "utils.h" +#include "mountns.h" #include "userns.h" +//#include "networkns.h" + + +// Flags used in name space creation +#define NSFLAGS (SIGCHLD | CLONE_NEWUTS | CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWNET) + +// Stack size to store child process stack +#define STACKSIZE 1024*1024 + + +// Struct used for storing commands and parent-child processs communication +struct params { + int fd[2]; // Array to serve as "Pipe" + char **argv; // Command to execute +}; // Extract the command (along with the given arguments) to be run in isolation @@ -22,61 +39,135 @@ int parse_args(int argc, char* argv[], struct params *params){ return argc-1; } -// Check if the program is being run as root -void check_root(){ - if (geteuid()!=0){ - fprintf(stderr,"[" RED("-") "] The Program Must Be Run As " RED("ROOT") "\n"); - exit(EXIT_FAILURE); + +// Print command to be run in isolation +void print_isolated_cmd(int argc, char **argv){ + int i; + for(i=0; ifd[0],buf,2)!= 2){ + fprintf(stderr, "[" RED("!") "] Failed to read from pipe while awaiting "RED("'setup done'")" from main"); + return -1; + } + + if (prepare_mountns() != 0){ + fprintf(stderr,"[" RED("!") "] Failed to create "RED("Mount") "namespace\n"); + return -1; + } + fprintf(stdout,"[" GREEN("i") "] Successfully created " GREEN("Mount") " namespace\n"); + + // Close reading end of the pipe once done + if(close(params->fd[0])){ + fprintf(stderr, "[" RED("!") "] Failed to close pipe\n"); + return -1; + } + + // Drop superuser privileges + if ((setuid(0)==-1) || setgid(0) == -1){ + fprintf(stderr, "[" RED("!") "] Could not set privileges\n"); + return -1; + } + + char **argv = params->argv; + char *cmd = argv[0]; + + if (execvp(cmd,argv)==-1){ + fprintf(stderr,"[" RED("!")"] Cannot execute command in Isolation :(\n"); + return -1; + } + + exit(EXIT_SUCCESS); + return 0; +} + +// Main Functions int main(int argc, char* argv[]){ fprintf(stdout,"[" GREEN("+") "] " CYAN("1s0lat3") " by " MAGENTA("@whokilleddb")"\n"); - + + // Check if program is being run as root - check_root(); + if (geteuid()!=0){ + exit_on_error("["RED("-")"] Program must be run as "RED("ROOT")"\n"); + } + // Create a variable to store commands to be run in isolation and pipes struct params cli_params; - memset(&cli_params, 0, sizeof(struct params)); - + if(memset(&cli_params, 0, sizeof(struct params)) == NULL){ + exit_on_error("[" RED("-") "] The function " RED("memset()") " failed\n"); + } + + + // Check and parse command line arguments int result = parse_args(argc, argv, &cli_params); if (result == -1){ fprintf(stderr,"[" RED("-") "] Nothing To Do :(\n"); fprintf(stdout,"[" CYAN(">") "] Usage: %s "BLUE("COMMAND")"\n",argv[0]); exit(EXIT_FAILURE); } - + + + // Print command line arguments fprintf(stdout,"[" MAGENTA(">") "] Command to be run in " CYAN("1s0lati0n")": "); print_isolated_cmd(result, cli_params.argv ); - // Create pipes + + // Create pipes for communication with child process if(pipe(cli_params.fd) < 0){ - exit_on_error(":("); + exit_on_error("[" RED("-") "] Failed to create "RED("pipe()")"\n"); } + // Setup memory for the child process char *stack; char *stackhead; - stack = (char *)malloc(STACKSIZE); if (!stack){ - exit_on_error("Failed to allocate memory for the child thread :("); + exit_on_error("[" RED("-") "] Failed to allocate stack memory for child process\n"); + } + + if(memset(stack,0,STACKSIZE) == NULL){ + exit_on_error("[" RED("!") "] The function " RED("memset()") " failed\n"); } - memset(stack,0,STACKSIZE); stackhead = stack + STACKSIZE ; + // When the command exits, it leaves a return status code // Start with cloning the UTS Namespace - int cmd_pid = clone(cmd_exec, stackhead, SIGCHLD | CLONE_NEWUTS | CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWPID, &cli_params); + int cmd_pid = clone(cmd_exec, stackhead, NSFLAGS, &cli_params); + // Check if clone was successful if (cmd_pid < 0){ free(stack); - exit_on_error("Failed To Clone :("); + exit_on_error("[" RED("!") "] The function " RED("clone()") " failed\n"); } - // Prepare User Namespace - prepare_user_ns(cmd_pid); + fprintf(stdout,"[" GREEN("i") "] Successfully created " GREEN("Hostname") " namespace\n"); + + + // Prepare User Namespace + if (prepare_user_ns(cmd_pid) != 0){ + exit_on_error("[" RED("!") "] Failed to create " RED("User") " namespace\n"); + } + fprintf(stdout,"[" GREEN("i") "] Successfully created " GREEN("User") " namespace\n"); + // Send 'setup done' signal to Child process if (write(cli_params.fd[1],"OK",2)!=2){ @@ -84,12 +175,14 @@ int main(int argc, char* argv[]){ exit_on_error("Cannot Send 'setup done' to child process"); } + // Close pipe after writing if (close(cli_params.fd[1])<0){ free(stack); exit_on_error("Cannot close pipe :("); } + // Wait for child process to complete execution if(waitpid(cmd_pid,NULL,0) == -1){ char buff[50]; @@ -99,6 +192,8 @@ int main(int argc, char* argv[]){ exit_on_error(buff); } + + // Free Memory like a good boi free(stack); fprintf(stdout,"[" GREEN("+") "] Bye :D\n"); return 0; diff --git a/src/mountns.c b/src/mountns.c new file mode 100644 index 0000000..399af19 --- /dev/null +++ b/src/mountns.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include +#include "pidns.h" +#include "mountns.h" +#include "utils.h" + +// Prepare Mount Namespace +int prepare_mountns(void){ + // mount --bind rootfs rootfs + if(mount(ROOTFS, ROOTFS, FSTYPE, MS_BIND,"") < 0){ + fprintf(stderr,"[" RED("!") "] Failed to mount" RED("%s") "\n", ROOTFS); + return -1; + } + + // cd rootfs + if(chdir(ROOTFS) < 0){ + fprintf(stderr,"[" RED("!") "] Failed to change directory\n"); + return -1; + } + + // mkdir put_old + const char *put_old = ".put_old"; + if((mkdir(put_old,0777) != 0) && (errno != EEXIST)){ + fprintf(stderr,"[" RED("!") "] Could not create "RED(".put_old") "\n"); + return -1; + } + + // pivot_root . .put_old + if(syscall(SYS_pivot_root,".",put_old)<0){ + fprintf(stderr,"[" RED("!") "] Could not "RED("PIVOT ROOT") "\n"); + return -1; + } + + // cd / + if (chdir("/") < 0){ + fprintf(stderr,"[" RED("!") "] Change Directory operation failed\n"); + return -1; + } + + //prepare proc fs + if (prepare_pidns() != 0){ + return -1; + } + fprintf(stdout,"[" GREEN("i") "] Successfully created " GREEN("PID") " namespace\n"); + + //umount .put_old + if(umount2(put_old, MNT_DETACH)){ + fprintf(stderr,"[" RED("!") "] Failed to unmount "RED(".put_old")"\n"); + return -1; + } + + // remove .put_old + if( rmdir(put_old) < 0){ + fprintf(stderr,"[" RED("!") "] Failed to remove "RED(".put_old")"\n"); + return -1; + } + return 0; +} \ No newline at end of file diff --git a/src/pidns.c b/src/pidns.c new file mode 100644 index 0000000..54f5b12 --- /dev/null +++ b/src/pidns.c @@ -0,0 +1,19 @@ +#include +#include +#include +#include "utils.h" + +int prepare_pidns(void){ + // mkdir /proc + if ((mkdir("/proc", 0555) != 0) && (errno != EEXIST)){ + fprintf(stderr,"[" RED("!") "] Failed to create " RED("/proc") "directory\n"); + return -1; + } + + // mount proc + if (mount("proc", "/proc", "proc", 0, "")){ + fprintf(stderr,"[" RED("!") "] Failed to mount the " RED("/proc") "directory\n"); + return -1; + } + return 0; +} \ No newline at end of file diff --git a/src/userns.c b/src/userns.c new file mode 100644 index 0000000..62579e1 --- /dev/null +++ b/src/userns.c @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include "userns.h" + +int write_to_file(char* path_to_file, char *line_to_write){ + FILE *f = fopen(path_to_file,"w"); + if (f == NULL){ + fprintf(stderr,"[" RED("-") "] Could not open %s for writing\n", path_to_file); + return -1; + } + if (fwrite(line_to_write, 1, strlen(line_to_write), f) == 0){ + fprintf(stderr,"[" RED("-") "] Could not write to %s\n", path_to_file); + return -1; + } + if (fclose(f) != 0){ + fprintf(stderr,"[" RED("-") "] Could not close %s\n", path_to_file); + return -1; + } + return 0; +} + +int prepare_user_ns(int pid){ + char* path; + char* line; + + /* Write UID to /proc/pid/uid_map + Format: + 0 UID 1 + + This ensures that under isolated conditions, we have UID 1 + */ + if ((asprintf(&path, "/proc/%d/uid_map", pid) < 0) || (asprintf(&line, "0 %d 1\n", UID) < 0)){ + fprintf(stderr,"[" RED("-") "] out of memory\n"); + return -1; + } + + // Write tp uid_map + if (write_to_file(path, line) != 0){ + fprintf(stderr,"[" RED("-") "] Could not prepare " RED("uid_map") "\n"); + return -1; + } + + // Zero out memory + if(memset(path, 0, (int)sizeof(path)) == NULL || memset(line, 0, (int)sizeof(line)) == NULL){ + fprintf(stderr,"[" RED("-") "] The function " RED("memset()") " failed\n"); + return -1; + } + + // Disable the setgroups system call + if ((asprintf(&path, "/proc/%d/setgroups", pid) < 0) || (asprintf(&line, "deny") < 0)){ + fprintf(stderr,"[" RED("-") "] out of memory\n"); + return -1; + } + + + // Write tp uid_map + if (write_to_file(path, line) != 0){ + fprintf(stderr,"[" RED("-") "] Could not disable " RED("setgroups") " syscall\n"); + return -1; + } + + + // Zero out memory + if(memset(path, 0, (int)sizeof(path)) == NULL || memset(line, 0, (int)sizeof(line)) == NULL){ + fprintf(stderr,"[" RED("-") "] The function " RED("memset()") " failed\n"); + return -1; + } + + /* Write UID to /proc/pid/gid_map + Format: + 0 UID 1 + + This ensures that under isolated conditions, we have GID 1 + */ + if ((asprintf(&path, "/proc/%d/gid_map", pid) < 0) || (asprintf(&line, "0 %d 1\n", UID) < 0)){ + fprintf(stderr,"[" RED("-") "] out of memory\n"); + return -1; + } + + // Write tp uid_map + if (write_to_file(path, line) != 0){ + fprintf(stderr,"[" RED("-") "] Could not prepare " RED("gid_map") "\n"); + return -1; + } + + free(path); + free(line); + return 0; +} + + diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..afb7a1a --- /dev/null +++ b/src/utils.c @@ -0,0 +1,14 @@ +#include +#include +#include +#include "utils.h" + +void exit_on_error(const char *format, ...){ + va_list args; + + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + + exit(EXIT_FAILURE); +} \ No newline at end of file