aboutgitcodelistschat:MatrixIRC
path: root/seitan.c
blob: 17ec63ed1825580d9164fe1fcb700f86deef2c75 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                            


                                                                    
                                                                  

                                  

                                               







                   
                    



                   
                   




                        
                      
                      
                     







                            
                   

                       
                 
 

                      
                    

                                                  

                                                   

                                                         



                         
                     
                

                  

  
                   
 



                                                                                           
                                                                                                


                                                                                                             
                           

 




                                                                     
                        
                                                                      







                                                         


                                                   





                                                      




                                            
                                          


                                                              




                                                                          


                        
 
                                                                 
                                                



                                                                        

                                    

                                                             

 
                                                                

                                


                                                 
                                                    
 

                                    

                                                               
                              
 


                                                                

                            
                                

                                      
                                
 
                    



























                                                     
                                 












                                                                                

















                                                                         

                               
                           
                                                    
                                                                  
                                           
                                
                             
                            
                        
                       
                 
 
                           

                                 
                                      
 
                                                              

                                                  

                  

                            

                                                                            
                                            

                                                                           
                                                                   

                                                                           
                                             

                                           
                                              
                                         
                                                                                             
                                                     
                                                
                                                                  
         
 

                                             

                              

                                                                   
 









                                                                       
                 


                                                 
         
 
                                     








                                                                           
 
// SPDX-License-Identifier: GPL-2.0-or-later

/* SEITAN - Syscall Expressive Interpreter, Transformer and Notifier
 *
 * seitan.c - Wait for processes, listen for syscalls, handle them
 *
 * Copyright (c) 2022 Red Hat GmbH
 * Authors: Alice Frosi <afrosi@redhat.com>
 * 	    Stefano Brivio <sbrivio@redhat.com>
 */

#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <signal.h>
#include <getopt.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>

#include "common.h"
#include "gluten.h"
#include "operations.h"
#include "util.h"

#define EPOLL_EVENTS 8

/* Seitan options */
static struct option options[] = {
	{ "input", required_argument, NULL, 'i' },
	{ "pid", optional_argument, NULL, 'p' },
	{ "socket", optional_argument, NULL, 's' },
	{ "socket-user", optional_argument, NULL, 'u' },
	{ "socket-group", optional_argument, NULL, 'g' },
};

struct arguments {
	char *input_file;
	char *socket;
	int pid;
	uid_t uid;
	gid_t gid;
};

static void usage()
{
	printf("seitan: monitor for processes on seccomp events and executor for actions\n"
	       "Example:  setain -pid <pid> -i <input file>\n"
	       "Usage:\n"
	       "\t-i, --input:\tAction input file\n"
	       "\t-p, --pid:\tPid of process to monitor (cannot be used together with socket)\n"
	       "\t-s, --socket:\tSocket to pass the seccomp notifier fd (cannot be used together with pid)\n"
	       "\t-u, --socket-user:\t User to set for the socket (cannot be used together with pid)\n"
	       "\t-g, --socket-group:\t Group to set for the socket (cannot be used together with pid)\n");
	exit(EXIT_FAILURE);
}

static void parse(int argc, char **argv, struct arguments *arguments)
{
	int option_index = 0;
	int oc;
	if (arguments == NULL)
		usage();
	while ((oc = getopt_long(argc, argv, ":i:o:p:s:u:g:", options,
				 &option_index)) != -1) {
		switch (oc) {
		case 'p':
			arguments->pid = atoi(optarg);
			break;
		case 'i':
			arguments->input_file = optarg;
			break;
		case 's':
			arguments->socket = optarg;
			break;
		case 'u':
			arguments->uid = atoi(optarg);
			break;
		case 'g':
			arguments->gid = atoi(optarg);
			break;
		default:
			usage();
		}
	}
	if (arguments->input_file == NULL) {
		err("missing input file");
		usage();
	}
	if (arguments->socket != NULL && arguments->pid > 0) {
		err("the socket and pid options cannot be used together");
		usage();
	}
	if (arguments->socket == NULL && arguments->pid < 0) {
		err("select one of the options between socket and pid");
		usage();
	}
}

static int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
			     unsigned int flags)
{
	return syscall(__NR_pidfd_send_signal, pidfd, sig, info, flags);
}

static void unblock_eater(int pidfd)
{
	if (pidfd_send_signal(pidfd, SIGCONT, NULL, 0) == -1)
		die("  pidfd_send_signal");
}

static int create_socket(const char *path, uid_t uid, gid_t gid)
{
	struct sockaddr_un addr;
	int ret, conn;
	int fd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (fd < 0)
		die("  error creating UNIX socket");

	strcpy(addr.sun_path, path);
	addr.sun_family = AF_UNIX;
	ret = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
	if (ret < 0)
		die("  bind");

	ret = lchown(path, uid, gid);
	if (ret < 0)
		die("  failed to the permission on the socket");
	ret = listen(fd, 1);
	if (ret < 0)
		die("  listen");
	conn = accept(fd, NULL, NULL);
	if (conn < 0)
		die("  accept");

	return conn;
}

static int recvfd(int sockfd)
{
	struct msghdr msgh;
	struct iovec iov;
	int data, fd;
	ssize_t nr;

	union {
		char buf[CMSG_SPACE(sizeof(int))];
		struct cmsghdr align;
	} controlMsg;
	struct cmsghdr *cmsgp;

	msgh.msg_name = NULL;
	msgh.msg_namelen = 0;

	msgh.msg_iov = &iov;
	msgh.msg_iovlen = 1;
	iov.iov_base = &data;
	iov.iov_len = sizeof(int);

	msgh.msg_control = controlMsg.buf;
	msgh.msg_controllen = sizeof(controlMsg.buf);

	nr = recvmsg(sockfd, &msgh, 0);
	if (nr == -1)
		die("  recvmsg");

	cmsgp = CMSG_FIRSTHDR(&msgh);

	if (cmsgp == NULL || cmsgp->cmsg_len != CMSG_LEN(sizeof(int)) ||
	    cmsgp->cmsg_level != SOL_SOCKET || cmsgp->cmsg_type != SCM_RIGHTS) {
		errno = EINVAL;
		return -1;
	}

	memcpy(&fd, CMSG_DATA(cmsgp), sizeof(int));
	return fd;
}

static void gluten_relocate(struct gluten *g)
{
	unsigned i;

	for (i = 0; i < 256; i++) {
		void **p;

		switch (g->header.relocation[i].type) {
		case OFFSET_DATA:
			p = gluten_write_ptr(g, g->header.relocation[i]);
			*p = (intptr_t)*p + g->data;
			break;
		default:
			;
		}
	}
}

int main(int argc, char **argv)
{
	char req_b[BUFSIZ];
	struct epoll_event ev, events[EPOLL_EVENTS];
	struct seccomp_notif *req = (struct seccomp_notif *)req_b;
	struct arguments arguments = { 0 };
	char path[PATH_MAX + 1];
	int fd = -1, epollfd;
	int pidfd, notifier;
	struct gluten g;
	int notifierfd;
	int n, i;

	arguments.pid = -1;
	arguments.uid = getuid();
	arguments.gid = getgid();
	parse(argc, argv, &arguments);

	fd = open(arguments.input_file, O_CLOEXEC | O_RDONLY);
	if (read(fd, &g, sizeof(g)) != sizeof(g))
		die("Failed to read gluten file");
	close(fd);

	gluten_relocate(&g);

	if (arguments.pid > 0) {
		if ((pidfd = syscall(SYS_pidfd_open, arguments.pid, 0)) < 0)
			die("  pidfd_open");
		snprintf(path, sizeof(path), "/proc/%d/fd", arguments.pid);
		if ((notifierfd = find_fd_seccomp_notifier(path)) < 0)
			die("  failed getting fd of the notifier");
		if ((notifier = syscall(SYS_pidfd_getfd, pidfd, notifierfd,
					0)) < 0)
			die("  pidfd_getfd");
		/* Unblock seitan-loader */
		unblock_eater(pidfd);
	} else if (arguments.socket != NULL) {
		unlink(arguments.socket);
		if ((fd = create_socket(arguments.socket, arguments.uid, arguments.gid)) < 0)
			die("  creating the socket");
		if ((notifier = recvfd(fd)) < 0)
			die("  failed recieving the notifier fd");
	}

	if ((epollfd = epoll_create1(0)) < 0)
		die("  epoll_create");
	ev.events = EPOLLIN;
	ev.data.fd = notifier;
	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, notifier, &ev) == -1)
		die("  epoll_ctl: notifier");

loop:
	n = epoll_wait(epollfd, events, EPOLL_EVENTS, -1);
	if (n < 0)
		die("waiting for seccomp events: %s", strerror(errno));

	for (i = 0; i < n; i++) {
		if (events[i].events & EPOLLHUP) {
			if (fd >= 0)
				unlink(arguments.socket);
			exit(EXIT_SUCCESS);
		}

		if (events[i].events & EPOLLERR)
			die("error on notifier");
	}

	memset(req, 0, sizeof(*req));
	if (ioctl(notifier, SECCOMP_IOCTL_NOTIF_RECV, req) < 0)
		die("receiving seccomp notification: %s", strerror(errno));

	if (eval(&g, req, notifier))
		err("an error occured during the evaluation");

	goto loop;

	return 0;
}