int main(int argc, char *argv[]) { struct sigevent sev; mqd_t mqd; struct mq_attr attr; void *buffer; ssize_t numRead; sigset_t blockMask; siginfo_t si; if (argc != 2 || strcmp(argv[1], "--help") == 0) usageErr("%s mq-name\n", argv[0]); mqd = mq_open(argv[1], O_RDONLY | O_NONBLOCK); if (mqd == (mqd_t) -1) errExit("mq_open"); /* Determine mq_msgsize for message queue, and allocate an input buffer of that size */ if (mq_getattr(mqd, &attr) == -1) errExit("mq_getattr"); buffer = malloc(attr.mq_msgsize); if (buffer == NULL) errExit("malloc"); /* Block the signal that we'll accept using sigwaitinfo() */ sigemptyset(&blockMask); sigaddset(&blockMask, NOTIFY_SIG); if (sigprocmask(SIG_BLOCK, &blockMask, NULL) == -1) errExit("sigprocmask"); /* Set up message notification using the signal NOTIFY_SIG */ sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = NOTIFY_SIG; sev.sigev_value.sival_ptr = &mqd; /* This allows us to obtain a pointer to 'mqd' in the siginfo_t structure returned by sigwaitinfo() */ if (mq_notify(mqd, &sev) == -1) errExit("mq_notify"); for (;;) { /* Wait for a signal; when it is received, display associated information */ if (sigwaitinfo(&blockMask, &si) == -1)errExit("sigwaitinfo"); printf("Accepted signal:\n"); printf(" si_signo = %d\n", si.si_signo); printf(" si_pid = %ld\n", (long) si.si_pid); printf(" si_uid = %ld\n", (long) si.si_uid); printf(" si_code = %d (%s)\n", si.si_code, (si.si_code == SI_MESGQ) ? "SI_MESGQ" : "???"); printf(" *sival_ptr = %p\n\n", si.si_value.sival_ptr); /* Reestablish message notification */ if (mq_notify(mqd, &sev) == -1) errExit("mq_notify"); /* Although only one signal might have been queued (if NOTIFY_SIG is a standard signal) we might have received multiple messages, so use nonblocking mq_receive() calls inside a loop to read as many messages as possible. */ while ((numRead = mq_receive(mqd, buffer, attr.mq_msgsize, NULL)) >= 0) printf("Read %ld bytes\n", (long) numRead); if (errno != EAGAIN) /* Unexpected error */ errExit("mq_receive"); } }
int main(int argc, char *argv[]) { sigset_t pending, blocked; const int numSecs = 5; struct sigaction sa; /* Set up a handler for SIGINT */ printf("Setting up handler for SIGINT\n"); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handler; if (sigaction(SIGINT, &sa, NULL) == -1) errExit("sigaction"); /* Block SIGINT for a while */ sigemptyset(&blocked); sigaddset(&blocked, SIGINT); if (sigprocmask(SIG_SETMASK, &blocked, NULL) == -1) errExit("sigprocmask"); printf("BLOCKING SIGINT for %d seconds\n", numSecs); sleep(numSecs); /* Display mask of pending signals */ if (sigpending(&pending) == -1) errExit("sigpending"); printf("PENDING signals are: \n"); printSigset(stdout, "\t\t", &pending); /* Now ignore SIGINT */ sleep(2); printf("Ignoring SIGINT\n"); if (signal(SIGINT, SIG_IGN) == SIG_ERR) errExit("signal"); /* Redisplay mask of pending signals */ if (sigpending(&pending) == -1) errExit("sigpending"); if (sigismember(&pending, SIGINT)) { printf("SIGINT is now pending\n"); } else { printf("PENDING signals are: \n"); printSigset(stdout, "\t\t", &pending); } sleep(2); /* Reestablish SIGINT handler */ printf("Reestablishing handler for SIGINT\n"); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = handler; if (sigaction(SIGINT, &sa, NULL) == -1) errExit("sigaction"); sleep(2); /* And unblock SIGINT */ printf("UNBLOCKING SIGINT\n"); sigemptyset(&blocked); if (sigprocmask(SIG_SETMASK, &blocked, NULL) == -1) errExit("sigprocmask"); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { struct utmpx ut; char *devName; if (argc < 2 || strcmp(argv[1], "--help") == 0) usageErr("%s username [sleep-time]\n", argv[0]); /* Initialize login record for utmp and wtmp files */ memset(&ut, 0, sizeof(struct utmpx)); ut.ut_type = USER_PROCESS; /* This is a user login */ strncpy(ut.ut_user, argv[1], sizeof(ut.ut_user)); if (time((time_t *) &ut.ut_tv.tv_sec) == -1) errExit("time"); /* Stamp with current time */ ut.ut_pid = getpid(); /* Set ut_line and ut_id based on the terminal associated with 'stdin'. This code assumes terminals named "/dev/[pt]t[sy]*". The "/dev/" dirname is 5 characters; the "[pt]t[sy]" filename prefix is 3 characters (making 8 characters in all). */ devName = ttyname(STDIN_FILENO); if (devName == NULL) errExit("ttyname"); if (strlen(devName) <= 8) /* Should never happen */ fatal("Terminal name is too short: %s", devName); strncpy(ut.ut_line, devName + 5, sizeof(ut.ut_line)); strncpy(ut.ut_id, devName + 8, sizeof(ut.ut_id)); printf("Creating login entries in utmp and wtmp\n"); printf(" using pid %ld, line %.*s, id %.*s\n", (long) ut.ut_pid, (int) sizeof(ut.ut_line), ut.ut_line, (int) sizeof(ut.ut_id), ut.ut_id); setutxent(); /* Rewind to start of utmp file */ if (pututxline(&ut) == NULL) /* Write login record to utmp */ errExit("pututxline"); updwtmpx(_PATH_WTMP, &ut); /* Append login record to wtmp */ /* Sleep a while, so we can examine utmp and wtmp files */ sleep((argc > 2) ? getInt(argv[2], GN_NONNEG, "sleep-time") : 15); /* Now do a "logout"; use values from previously initialized 'ut', except for changes below */ ut.ut_type = DEAD_PROCESS; /* Required for logout record */ time((time_t *) &ut.ut_tv.tv_sec); /* Stamp with logout time */ memset(&ut.ut_user, 0, sizeof(ut.ut_user)); /* Logout record has null username */ printf("Creating logout entries in utmp and wtmp\n"); setutxent(); /* Rewind to start of utmp file */ if (pututxline(&ut) == NULL) /* Overwrite previous utmp record */ errExit("pututxline"); updwtmpx(_PATH_WTMP, &ut); /* Append logout record to wtmp */ endutxent(); exit(EXIT_SUCCESS); }
// mon_pid: pid of sandbox to be monitored, 0 if all sandboxes are included void pid_read(pid_t mon_pid) { if (pids == NULL) { FILE *fp = fopen("/proc/sys/kernel/pid_max", "r"); if (fp) { int val; if (fscanf(fp, "%d", &val) == 1) { if (val >= max_pids) max_pids = val + 1; } fclose(fp); } pids = malloc(sizeof(Process) * max_pids); if (pids == NULL) errExit("malloc"); } memset(pids, 0, sizeof(Process) * max_pids); pid_t mypid = getpid(); DIR *dir; if (!(dir = opendir("/proc"))) { // sleep 2 seconds and try again sleep(2); if (!(dir = opendir("/proc"))) { fprintf(stderr, "Error: cannot open /proc directory\n"); exit(1); } } pid_t child = -1; struct dirent *entry; char *end; while (child < 0 && (entry = readdir(dir))) { pid_t pid = strtol(entry->d_name, &end, 10); pid %= max_pids; if (end == entry->d_name || *end) continue; if (pid == mypid) continue; // open stat file char *file; if (asprintf(&file, "/proc/%u/status", pid) == -1) { perror("asprintf"); exit(1); } FILE *fp = fopen(file, "r"); if (!fp) { free(file); continue; } // look for firejail executable name char buf[PIDS_BUFLEN]; while (fgets(buf, PIDS_BUFLEN - 1, fp)) { if (strncmp(buf, "Name:", 5) == 0) { char *ptr = buf + 5; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { ptr++; } if (*ptr == '\0') { fprintf(stderr, "Error: cannot read /proc file\n"); exit(1); } if (mon_pid == 0 && strncmp(ptr, "firejail", 8) == 0) { pids[pid].level = 1; } else if (mon_pid == pid && strncmp(ptr, "firejail", 8) == 0) { pids[pid].level = 1; } // else if (mon_pid == 0 && strncmp(ptr, "lxc-execute", 11) == 0) { // pids[pid].level = 1; // } // else if (mon_pid == pid && strncmp(ptr, "lxc-execute", 11) == 0) { // pids[pid].level = 1; // } else pids[pid].level = -1; } if (strncmp(buf, "State:", 6) == 0) { if (strstr(buf, "(zombie)")) pids[pid].zombie = 1; } else if (strncmp(buf, "PPid:", 5) == 0) { char *ptr = buf + 5; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { ptr++; } if (*ptr == '\0') { fprintf(stderr, "Error: cannot read /proc file\n"); exit(1); } unsigned parent = atoi(ptr); parent %= max_pids; if (pids[parent].level > 0) { pids[pid].level = pids[parent].level + 1; } pids[pid].parent = parent; } else if (strncmp(buf, "Uid:", 4) == 0) { char *ptr = buf + 5; while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) { ptr++; } if (*ptr == '\0') { fprintf(stderr, "Error: cannot read /proc file\n"); exit(1); } pids[pid].uid = atoi(ptr); break; } } fclose(fp); free(file); } closedir(dir); pid_t pid; for (pid = 0; pid < max_pids; pid++) { int parent = pids[pid].parent; if (pids[parent].level > 0) { pids[pid].level = pids[parent].level + 1; } } }
int main(int argc,char *argv[]) { char cmd[CMD_SIZE]; pid_t childPid; sigset_t blockMask,emptyMask; struct sigaction sa; setbuf(stdout,NULL); /* Disable buffering of stdout */ memset(cmd,0,CMD_SIZE); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = sigHandler; if (sigaction(SIGCHLD,&sa,NULL) == -1) { errExit("sigaction"); } /* Block SIGCHLD to prevent its delivery if a child terminates before the parent commences the sigsupsend() */ sigemptyset(&blockMask); sigaddset(&blockMask,SIGCHLD); if (sigprocmask(SIG_SETMASK,&blockMask,NULL) == -1) { errExit("sigprocmask"); } printf("Parent PID=%ld\n",(long)getpid()); switch(childPid = fork()) { case -1: errExit("fork"); case 0: /* Child: immediately exits to become zombie */ printf("Child (PID=%ld) exiting\n",(long)getpid()); _exit(EXIT_SUCCESS); default: /* Parent */ sigemptyset(&emptyMask); if (sigsuspend(&emptyMask) == -1 && errno != EINTR) { errExit("sigsuspend"); } snprintf(cmd,CMD_SIZE,"ps -c | grep %s",basename(argv[0])); cmd[CMD_SIZE - 1] = '\0'; /* Ensure string is null-terminated */ system(cmd); /* View zombie child */ /* Now send the "sure kill" signal to the zombie */ if (kill(childPid,SIGKILL) == -1) { errMsg("kill"); } sleep(10); /* Give child a chance to react to signal */ printf("After sending SIGKILL to zombie (PID=%ld):\n",(long)childPid); system(cmd); /* View zombie child again */ exit(EXIT_SUCCESS); } }
int main(int argc, char *argv[]) { char buf[BUF_SIZE]; int outbound[2]; /* Pipe to send data from parent to child */ int inbound[2]; /* Pipe to send data from child to parent */ int j; ssize_t cnt; if (pipe(outbound) == -1) errExit("pipe"); if (pipe(inbound) == -1) errExit("pipe"); switch (fork()) { case -1: errExit("fork"); case 0: /* Child */ /* Close unused pipe descriptors */ if (close(outbound[1]) == -1) errExit("close"); if (close(inbound[0]) == -1) errExit("close"); /* Read data from outbound pipe, convert to uppercase, and send back to parent on inbound pipe */ while ((cnt = read(outbound[0], buf, BUF_SIZE)) > 0) { for (j = 0; j < cnt; j++) buf[j] = toupper((unsigned char) buf[j]); if (write(inbound[1], buf, cnt) == -1) errExit("write"); } if (cnt == -1) errExit("read"); _exit(EXIT_SUCCESS); default: /* Close unused pipe descriptors */ if (close(outbound[0]) == -1) errExit("close"); if (close(inbound[1]) == -1) errExit("close"); /* Read data from stdin, send to the child via the outbound pipe, read the results back from the child on the inbound pipe, and print them on stdout */ while ((cnt = read(STDIN_FILENO, buf, BUF_SIZE)) > 0) { if (write(outbound[1], buf, cnt) == -1) errExit("write"); cnt = read(inbound[0], buf, BUF_SIZE); if (cnt == -1) errExit("read"); if (cnt > 0) if (write(STDOUT_FILENO, buf, cnt) == -1) errExit("write"); } if (cnt == -1) errExit("read"); /* Exiting will close write end of outbound pipe, so that child see EOF */ exit(EXIT_SUCCESS); } }
int main(int argc, char *argv[]) { int epfd,ready,fd,s,j,numOpenFds; struct epoll_event ev; struct epoll_event evlist[MAX_EVENTS]; char buf[MAX_BUF]; //使用说明 if(argc < 2|| strcmp(argv[1], "--help") == 0) usageErr("%s file ..\n",argv[0]); epfd = epoll_create(argc - 1); if(epfd==-1) errExit("epoll_create"); //open each file on command line, and add it to the "interest list" for the epoll instance for(j = 1;j<argc; ++j) { fd = open(argv[j],O_RDONLY); if(fd == -1) errExit("open"); printf("Opened \"%s\" on fd %d\n",argv[j],fd); ev.events = EPOLLIN; //Only interested in input events ev.data.fd = fd; if(epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev) == -1) errExit("epoll_ctl"); } numOpenFds = argc -1; while(numOpenFds > 0) { //fetch up to MAX_EVENTS items from the ready list printf("About to epoll_wait()\n"); ready = epoll_wait(epfd,evlist,MAX_EVENTS, -1); if(ready == -1) { if(errno == EINTR) continue; else errExit("epoll_wait"); } printf("Ready: %d\n",ready); //deal with returned list of events for(j=0;j<ready;++j) { printf(" fd=%d;events: %s%s%s\n",evlist[j].data.fd, (evlist[j].events & EPOLLIN) ? "EPOLLIN" :"", (evlist[j].events & EPOLLHUP)? "EPOLLHUP":"", (evlist[j].events & EPOLLERR)? "EPOLLERR":""); if(evlist[j].events & EPOLLIN) { s = read(evlist[j].data.fd, buf ,MAX_BUF); if(s == -1) errExit("read"); printf(" read %d bytes:%.*s\n",s,s,buf); } else if(evlist[j].events & (EPOLLHUP | EPOLLERR)) { /*if EPOLLIN and EPOLLHUP were both set, then there might be more than MAX_BUF bytes to read. Therefore, we close the file descriptor only if EPOLLIN wsa not set. We'll read further bytes after the next epoll_wait().*/ printf(" closing fd %d\n",evlist[j].data.fd); if(close(evlist[j].data.fd) == -1) errExit("close"); numOpenFds--; } } } printf("All file descriptors closed,bye!\n"); exit(EXIT_SUCCESS); }
static void sanitize_group(void) { struct stat s; if (stat("/etc/group", &s) == -1) return; assert(gid_min); if (arg_debug) printf("Sanitizing /etc/group, GID_MIN %d\n", gid_min); if (is_link("/etc/group")) { fprintf(stderr, "Error: invalid /etc/group\n"); exit(1); } FILE *fpin = NULL; FILE *fpout = NULL; // open files /* coverity[toctou] */ fpin = fopen("/etc/group", "r"); if (!fpin) goto errout; fpout = fopen(RUN_GROUP_FILE, "w"); if (!fpout) goto errout; // read the file line by line char buf[MAXBUF]; gid_t mygid = getgid(); while (fgets(buf, MAXBUF, fpin)) { // comments and empty lines if (*buf == '\0' || *buf == '#') continue; // sample line: // pulse:x:115:netblue,bingo // drop lines with uid > 1000 and not the current user group char *ptr = buf; // advance to uid while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; if (*ptr == '\0') goto errout; // process uid int gid; int rv = sscanf(ptr, "%d:", &gid); if (rv == 0 || gid < 0) goto errout; assert(gid_min); if (gid < gid_min || gid == 65534) { // on Debian platforms 65534 is group nogroup if (copy_line(fpout, buf, ptr)) goto errout; continue; } if ((gid_t) gid != mygid) { continue; // skip line } if (copy_line(fpout, buf, ptr)) goto errout; } fclose(fpin); SET_PERMS_STREAM(fpout, 0, 0, 0644); fclose(fpout); // mount-bind tne new group file if (mount(RUN_GROUP_FILE, "/etc/group", "none", MS_BIND, "mode=400,gid=0") < 0) errExit("mount"); fs_logger("create /etc/group"); return; errout: fwarning("failed to clean up /etc/group\n"); if (fpin) fclose(fpin); if (fpout) fclose(fpout); }
static void processInotifyEvents(int *inotifyFd) { char buf[INOTIFY_READ_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event)))); ssize_t numRead, nr; char *evp; size_t cnt; int evLen; int firstTry; int j; struct sigaction sa; /* SIGALRM handler is designed simply to interrupt read() */ sigemptyset(&sa.sa_mask); sa.sa_handler = alarmHandler; sa.sa_flags = 0; if (sigaction(SIGALRM, &sa, NULL) == -1) errExit("sigaction"); firstTry = 1; /* Read some events from inotify file descriptor */ cnt = (readBufferSize > 0) ? readBufferSize : INOTIFY_READ_BUF_LEN; numRead = read(*inotifyFd, buf, cnt); if (numRead == -1) errExit("read"); if (numRead == 0) { fprintf(stderr, "read() from inotify fd returned 0!"); exit(EXIT_FAILURE); } inotifyReadCnt++; logMessage(VB_NOISY, "\n==========> Read %d: got %zd bytes\n", inotifyReadCnt, numRead); /* Process each event in the buffer returned by read() */ for (evp = buf; evp < buf + numRead; ) { evLen = processNextInotifyEvent(inotifyFd, evp, buf + numRead - evp, firstTry); if (evLen > 0) { evp += evLen; firstTry = 1; } else { /* We got here because an IN_MOVED_FROM event was found at the end of a previously read buffer and that event may be part of an "intra-tree" rename(), meaning that we should check if there is a subsequent IN_MOVED_TO event with the same cookie value. We left that event unprocessed and we will now try to read some more events, delaying for a short time, to give the associated IN_MOVED_IN event (if there is one) a chance to arrive. However, we only want to do this once: if the read() below fails to gather further events, then when we reprocess the IN_MOVED_FROM we should treat it as though this is an out-of-tree rename(). Thus, we set 'firstTry' to 0 for the next processNextInotifyEvent() call. */ struct sigaction sa; int savedErrno; firstTry = 0; numRead = buf + numRead - evp; /* Shuffle remaining bytes to start of buffer */ for (j = 0; j < numRead; j++) buf[j] = evp[j]; /* Do a read with timeout, to allow next events (if any) to arrive */ sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = alarmHandler; /* Set a timeout for read(). Some rough testing suggests that a 2-millisecond timeout is sufficient to ensure that, in around 99.8% of cases, we get the IN_MOVED_TO event (if there is one) that matched an IN_MOVED_FROM event, even in a highly dynamic directory tree. This number may, of course, warrant tuning on different hardware and in environments with different filesystem activity levels. */ ualarm(2000, 0); nr = read(*inotifyFd, buf + numRead, INOTIFY_READ_BUF_LEN - numRead); savedErrno = errno; /* In case ualarm() should change errno */ ualarm(0, 0); /* Cancel alarm */ errno = savedErrno; if (nr == -1 && errno != EINTR) errExit("read"); if (nr == 0) { fprintf(stderr, "read() from inotify fd returned 0!"); exit(EXIT_FAILURE); } if (errno != -1) { numRead += nr; inotifyReadCnt++; logMessage(VB_NOISY, "\n==========> SECONDARY Read %d: got %zd bytes\n", inotifyReadCnt, nr); } else { /* EINTR */ logMessage(VB_NOISY, "\n==========> SECONDARY Read got nothing\n"); } evp = buf; /* Start again at beginning of buffer */ } } }
// check profile line; if line == 0, this was generated from a command line option // return 1 if the command is to be added to the linked list of profile commands // return 0 if the command was already executed inside the function int profile_check_line(char *ptr, int lineno, const char *fname) { EUID_ASSERT(); // check ignore list int i; for (i = 0; i < MAX_PROFILE_IGNORE; i++) { if (cfg.profile_ignore[i] == NULL) break; if (strncmp(ptr, cfg.profile_ignore[i], strlen(cfg.profile_ignore[i])) == 0) return 0; // ignore line } if (strncmp(ptr, "ignore ", 7) == 0) { char *str = strdup(ptr + 7); if (*str == '\0') { fprintf(stderr, "Error: invalid ignore option\n"); exit(1); } // find an empty entry in profile_ignore array int j; for (j = 0; j < MAX_PROFILE_IGNORE; j++) { if (cfg.profile_ignore[j] == NULL) break; } if (j >= MAX_PROFILE_IGNORE) { fprintf(stderr, "Error: maximum %d --ignore options are permitted\n", MAX_PROFILE_IGNORE); exit(1); } // ... and configure it else cfg.profile_ignore[j] = str; return 0; } // mkdir if (strncmp(ptr, "mkdir ", 6) == 0) { fs_mkdir(ptr + 6); return 0; } // sandbox name else if (strncmp(ptr, "name ", 5) == 0) { cfg.name = ptr + 5; if (strlen(cfg.name) == 0) { fprintf(stderr, "Error: invalid sandbox name\n"); exit(1); } return 0; } else if (strcmp(ptr, "ipc-namespace") == 0) { arg_ipc = 1; return 0; } // seccomp, caps, private, user namespace else if (strcmp(ptr, "noroot") == 0) { #if HAVE_USERNS if (checkcfg(CFG_USERNS)) check_user_namespace(); else fprintf(stderr, "Warning: user namespace feature is disabled in Firejail configuration file\n"); #endif return 0; } else if (strcmp(ptr, "nonewprivs") == 0) { arg_nonewprivs = 1; return 0; } else if (strcmp(ptr, "seccomp") == 0) { #ifdef HAVE_SECCOMP if (checkcfg(CFG_SECCOMP)) arg_seccomp = 1; else fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); #endif return 0; } else if (strcmp(ptr, "caps") == 0) { arg_caps_default_filter = 1; return 0; } else if (strcmp(ptr, "caps.drop all") == 0) { arg_caps_drop_all = 1; return 0; } else if (strcmp(ptr, "shell none") == 0) { arg_shell_none = 1; return 0; } else if (strcmp(ptr, "tracelog") == 0) { arg_tracelog = 1; return 0; } else if (strcmp(ptr, "private") == 0) { arg_private = 1; return 0; } else if (strcmp(ptr, "private-dev") == 0) { arg_private_dev = 1; return 0; } else if (strcmp(ptr, "private-tmp") == 0) { arg_private_tmp = 1; return 0; } else if (strcmp(ptr, "nogroups") == 0) { arg_nogroups = 1; return 0; } else if (strcmp(ptr, "nosound") == 0) { arg_nosound = 1; arg_private_dev = 1; return 0; } else if (strcmp(ptr, "netfilter") == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) arg_netfilter = 1; else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "netfilter ", 10) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { arg_netfilter = 1; arg_netfilter_file = strdup(ptr + 10); if (!arg_netfilter_file) errExit("strdup"); check_netfilter_file(arg_netfilter_file); } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "netfilter6 ", 11) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { arg_netfilter6 = 1; arg_netfilter6_file = strdup(ptr + 11); if (!arg_netfilter6_file) errExit("strdup"); check_netfilter_file(arg_netfilter6_file); } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strcmp(ptr, "net none") == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { arg_nonetwork = 1; cfg.bridge0.configured = 0; cfg.bridge1.configured = 0; cfg.bridge2.configured = 0; cfg.bridge3.configured = 0; cfg.interface0.configured = 0; cfg.interface1.configured = 0; cfg.interface2.configured = 0; cfg.interface3.configured = 0; } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "net ", 4) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { #ifdef HAVE_NETWORK_RESTRICTED // compile time restricted networking if (getuid() != 0) { fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n"); exit(1); } #endif // run time restricted networking if (checkcfg(CFG_RESTRICTED_NETWORK) && getuid() != 0) { fprintf(stderr, "Error: only \"net none\" is allowed to non-root users\n"); exit(1); } if (strcmp(ptr + 4, "lo") == 0) { fprintf(stderr, "Error: cannot attach to lo device\n"); exit(1); } Bridge *br; if (cfg.bridge0.configured == 0) br = &cfg.bridge0; else if (cfg.bridge1.configured == 0) br = &cfg.bridge1; else if (cfg.bridge2.configured == 0) br = &cfg.bridge2; else if (cfg.bridge3.configured == 0) br = &cfg.bridge3; else { fprintf(stderr, "Error: maximum 4 network devices are allowed\n"); exit(1); } net_configure_bridge(br, ptr + 4); } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "iprange ", 8) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { Bridge *br = last_bridge_configured(); if (br == NULL) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } if (br->iprange_start || br->iprange_end) { fprintf(stderr, "Error: cannot configure the IP range twice for the same interface\n"); exit(1); } // parse option arguments char *firstip = ptr + 8; char *secondip = firstip; while (*secondip != '\0') { if (*secondip == ',') break; secondip++; } if (*secondip == '\0') { fprintf(stderr, "Error: invalid IP range\n"); exit(1); } *secondip = '\0'; secondip++; // check addresses if (atoip(firstip, &br->iprange_start) || atoip(secondip, &br->iprange_end) || br->iprange_start >= br->iprange_end) { fprintf(stderr, "Error: invalid IP range\n"); exit(1); } if (in_netrange(br->iprange_start, br->ip, br->mask) || in_netrange(br->iprange_end, br->ip, br->mask)) { fprintf(stderr, "Error: IP range addresses not in network range\n"); exit(1); } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } // from here else if (strncmp(ptr, "mac ", 4) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { Bridge *br = last_bridge_configured(); if (br == NULL) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } if (mac_not_zero(br->macsandbox)) { fprintf(stderr, "Error: cannot configure the MAC address twice for the same interface\n"); exit(1); } // read the address if (atomac(ptr + 4, br->macsandbox)) { fprintf(stderr, "Error: invalid MAC address\n"); exit(1); } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "mtu ", 4) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { Bridge *br = last_bridge_configured(); if (br == NULL) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } if (sscanf(ptr + 4, "%d", &br->mtu) != 1 || br->mtu < 576 || br->mtu > 9198) { fprintf(stderr, "Error: invalid mtu value\n"); exit(1); } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "ip ", 3) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { Bridge *br = last_bridge_configured(); if (br == NULL) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } if (br->arg_ip_none || br->ipsandbox) { fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); exit(1); } // configure this IP address for the last bridge defined if (strcmp(ptr + 3, "none") == 0) br->arg_ip_none = 1; else { if (atoip(ptr + 3, &br->ipsandbox)) { fprintf(stderr, "Error: invalid IP address\n"); exit(1); } } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "ip6 ", 4) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { Bridge *br = last_bridge_configured(); if (br == NULL) { fprintf(stderr, "Error: no network device configured\n"); exit(1); } if (br->arg_ip_none || br->ip6sandbox) { fprintf(stderr, "Error: cannot configure the IP address twice for the same interface\n"); exit(1); } // configure this IP address for the last bridge defined // todo: verify ipv6 syntax br->ip6sandbox = ptr + 4; // if (atoip(argv[i] + 5, &br->ipsandbox)) { // fprintf(stderr, "Error: invalid IP address\n"); // exit(1); // } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } else if (strncmp(ptr, "defaultgw ", 10) == 0) { #ifdef HAVE_NETWORK if (checkcfg(CFG_NETWORK)) { if (atoip(ptr + 10, &cfg.defaultgw)) { fprintf(stderr, "Error: invalid IP address\n"); exit(1); } } else fprintf(stderr, "Warning: networking features are disabled in Firejail configuration file\n"); #endif return 0; } if (strncmp(ptr, "protocol ", 9) == 0) { #ifdef HAVE_SECCOMP if (checkcfg(CFG_SECCOMP)) protocol_store(ptr + 9); else fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); #endif return 0; } if (strncmp(ptr, "env ", 4) == 0) { env_store(ptr + 4); return 0; } // seccomp drop list on top of default list if (strncmp(ptr, "seccomp ", 8) == 0) { #ifdef HAVE_SECCOMP if (checkcfg(CFG_SECCOMP)) { arg_seccomp = 1; cfg.seccomp_list = strdup(ptr + 8); if (!cfg.seccomp_list) errExit("strdup"); } else fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); #endif return 0; } // seccomp drop list without default list if (strncmp(ptr, "seccomp.drop ", 13) == 0) { #ifdef HAVE_SECCOMP if (checkcfg(CFG_SECCOMP)) { arg_seccomp = 1; cfg.seccomp_list_drop = strdup(ptr + 13); if (!cfg.seccomp_list_drop) errExit("strdup"); } else fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); #endif return 0; } // seccomp keep list if (strncmp(ptr, "seccomp.keep ", 13) == 0) { #ifdef HAVE_SECCOMP if (checkcfg(CFG_SECCOMP)) { arg_seccomp = 1; cfg.seccomp_list_keep= strdup(ptr + 13); if (!cfg.seccomp_list_keep) errExit("strdup"); } else fprintf(stderr, "Warning: user seccomp feature is disabled in Firejail configuration file\n"); #endif return 0; } // caps drop list if (strncmp(ptr, "caps.drop ", 10) == 0) { arg_caps_drop = 1; arg_caps_list = strdup(ptr + 10); if (!arg_caps_list) errExit("strdup"); // verify seccomp list and exit if problems if (caps_check_list(arg_caps_list, NULL)) exit(1); return 0; } // caps keep list if (strncmp(ptr, "caps.keep ", 10) == 0) { arg_caps_keep = 1; arg_caps_list = strdup(ptr + 10); if (!arg_caps_list) errExit("strdup"); // verify seccomp list and exit if problems if (caps_check_list(arg_caps_list, NULL)) exit(1); return 0; } // hostname if (strncmp(ptr, "hostname ", 9) == 0) { cfg.hostname = ptr + 9; return 0; } // dns if (strncmp(ptr, "dns ", 4) == 0) { uint32_t dns; if (atoip(ptr + 4, &dns)) { fprintf(stderr, "Error: invalid DNS server IP address\n"); return 1; } if (cfg.dns1 == 0) cfg.dns1 = dns; else if (cfg.dns2 == 0) cfg.dns2 = dns; else if (cfg.dns3 == 0) cfg.dns3 = dns; else { fprintf(stderr, "Error: up to 3 DNS servers can be specified\n"); return 1; } return 0; } // cpu affinity if (strncmp(ptr, "cpu ", 4) == 0) { read_cpu_list(ptr + 4); return 0; } // nice value if (strncmp(ptr, "nice ", 4) == 0) { cfg.nice = atoi(ptr + 5); if (getuid() != 0 &&cfg.nice < 0) cfg.nice = 0; arg_nice = 1; return 0; } // cgroup if (strncmp(ptr, "cgroup ", 7) == 0) { set_cgroup(ptr + 7); return 0; } // writable-etc if (strcmp(ptr, "writable-etc") == 0) { if (cfg.etc_private_keep) { fprintf(stderr, "Error: private-etc and writable-etc are mutually exclusive\n"); exit(1); } arg_writable_etc = 1; return 0; } // writable-var if (strcmp(ptr, "writable-var") == 0) { arg_writable_var = 1; return 0; } // private directory if (strncmp(ptr, "private ", 8) == 0) { cfg.home_private = ptr + 8; fs_check_private_dir(); arg_private = 1; return 0; } // private /etc list of files and directories if (strncmp(ptr, "private-etc ", 12) == 0) { if (arg_writable_etc) { fprintf(stderr, "Error: --private-etc and --writable-etc are mutually exclusive\n"); exit(1); } cfg.etc_private_keep = ptr + 12; fs_check_etc_list(); arg_private_etc = 1; return 0; } // private /bin list of files if (strncmp(ptr, "private-bin ", 12) == 0) { cfg.bin_private_keep = ptr + 12; arg_private_bin = 1; fs_check_bin_list(); return 0; } // filesystem bind if (strncmp(ptr, "bind ", 5) == 0) { #ifdef HAVE_BIND if (checkcfg(CFG_BIND)) { if (getuid() != 0) { fprintf(stderr, "Error: --bind option is available only if running as root\n"); exit(1); } // extract two directories char *dname1 = ptr + 5; char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories if (dname2 == NULL) { fprintf(stderr, "Error: missing second directory for bind\n"); exit(1); } // check directories invalid_filename(dname1); invalid_filename(dname2); if (strstr(dname1, "..") || strstr(dname2, "..")) { fprintf(stderr, "Error: invalid file name.\n"); exit(1); } if (is_link(dname1) || is_link(dname2)) { fprintf(stderr, "Symbolic links are not allowed for bind command\n"); exit(1); } // insert comma back *(dname2 - 1) = ','; return 1; } else { fprintf(stderr, "Warning: bind feature is disabled in Firejail configuration file\n"); return 0; } #else return 0; #endif } // rlimit if (strncmp(ptr, "rlimit", 6) == 0) { if (strncmp(ptr, "rlimit-nofile ", 14) == 0) { ptr += 14; if (not_unsigned(ptr)) { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } sscanf(ptr, "%u", &cfg.rlimit_nofile); arg_rlimit_nofile = 1; } else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) { ptr += 13; if (not_unsigned(ptr)) { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } sscanf(ptr, "%u", &cfg.rlimit_nproc); arg_rlimit_nproc = 1; } else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) { ptr += 13; if (not_unsigned(ptr)) { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } sscanf(ptr, "%u", &cfg.rlimit_fsize); arg_rlimit_fsize = 1; } else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) { ptr += 18; if (not_unsigned(ptr)) { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } sscanf(ptr, "%u", &cfg.rlimit_sigpending); arg_rlimit_sigpending = 1; } else { fprintf(stderr, "Invalid rlimit option on line %d\n", lineno); exit(1); } return 0; } // read-write if (strncmp(ptr, "read-write ", 11) == 0) { if (getuid() != 0) { fprintf(stderr, "Error: read-write command is available only for root user\n"); exit(1); } fs_rdwr_add(ptr + 11); return 0; } // rest of filesystem if (strncmp(ptr, "blacklist ", 10) == 0) ptr += 10; else if (strncmp(ptr, "blacklist-nolog ", 16) == 0) ptr += 16; else if (strncmp(ptr, "noblacklist ", 12) == 0) ptr += 12; else if (strncmp(ptr, "whitelist ", 10) == 0) { #ifdef HAVE_WHITELIST if (checkcfg(CFG_WHITELIST)) { arg_whitelist = 1; ptr += 10; } else return 0; #else return 0; #endif } else if (strncmp(ptr, "read-only ", 10) == 0) ptr += 10; else if (strncmp(ptr, "tmpfs ", 6) == 0) { if (getuid() != 0) { fprintf(stderr, "Error: tmpfs available only when running the sandbox as root\n"); exit(1); } ptr += 6; } else { if (lineno == 0) fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr); else if (fname != NULL) fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname); else fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno); exit(1); } // some characters just don't belong in filenames invalid_filename(ptr); if (strstr(ptr, "..")) { if (lineno == 0) fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr); else if (fname != NULL) fprintf(stderr, "Error: line %d in %s is invalid\n", lineno, fname); else fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno); exit(1); } return 1; }
static void sanitize_passwd(void) { struct stat s; if (stat("/etc/passwd", &s) == -1) return; assert(uid_min); if (arg_debug) printf("Sanitizing /etc/passwd, UID_MIN %d\n", uid_min); if (is_link("/etc/passwd")) { fprintf(stderr, "Error: invalid /etc/passwd\n"); exit(1); } FILE *fpin = NULL; FILE *fpout = NULL; // open files /* coverity[toctou] */ fpin = fopen("/etc/passwd", "r"); if (!fpin) goto errout; fpout = fopen(RUN_PASSWD_FILE, "w"); if (!fpout) goto errout; // read the file line by line char buf[MAXBUF]; uid_t myuid = getuid(); while (fgets(buf, MAXBUF, fpin)) { // comments and empty lines if (*buf == '\0' || *buf == '#') continue; // sample line: // www-data:x:33:33:www-data:/var/www:/bin/sh // drop lines with uid > 1000 and not the current user char *ptr = buf; // advance to uid while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; char *ptr1 = ptr; ptr++; while (*ptr != ':' && *ptr != '\0') ptr++; if (*ptr == '\0') goto errout; ptr++; if (*ptr == '\0') goto errout; // process uid int uid; int rv = sscanf(ptr, "%d:", &uid); if (rv == 0 || uid < 0) goto errout; assert(uid_min); if (uid < uid_min || uid == 65534) { // on Debian platforms user nobody is 65534 fprintf(fpout, "%s", buf); continue; } if ((uid_t) uid != myuid) { // store user name - necessary to process /etc/group *ptr1 = '\0'; char *user = strdup(buf); if (!user) errExit("malloc"); ulist_add(user); continue; // skip line } fprintf(fpout, "%s", buf); } fclose(fpin); SET_PERMS_STREAM(fpout, 0, 0, 0644); fclose(fpout); // mount-bind tne new password file if (mount(RUN_PASSWD_FILE, "/etc/passwd", "none", MS_BIND, "mode=400,gid=0") < 0) errExit("mount"); fs_logger("create /etc/passwd"); return; errout: fwarning("failed to clean up /etc/passwd\n"); if (fpin) fclose(fpin); if (fpout) fclose(fpout); }
int main(int argc, char *argv[]) { struct timeval start, finish; struct timespec request, remain; struct sigaction sa; int s, flags; if (argc < 3 || strcmp(argv[1], "--help") == 0) usageErr("%s secs nanosecs [a]\n", argv[0]); /* Allow SIGINT handler to interrupt clock_nanosleep() */ sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = sigintHandler; if (sigaction(SIGINT, &sa, NULL) == -1) errExit("sigaction"); /* If more than three command-line arguments, use TIMER_ABSTIME flag */ flags = (argc > 3) ? TIMER_ABSTIME : 0; if (flags == TIMER_ABSTIME) { if (clock_gettime(CLOCK_REALTIME, &request) == -1) errExit("clock_gettime"); printf("Initial CLOCK_REALTIME value: %ld.%09ld\n", (long) request.tv_sec, (long) request.tv_nsec); request.tv_sec += getLong(argv[1], 0, "secs"); request.tv_nsec += getLong(argv[2], 0, "nanosecs"); if (request.tv_nsec >= 1000000000) { request.tv_sec += request.tv_nsec / 1000000000; request.tv_nsec %= 1000000000; } } else { /* Relative sleep */ request.tv_sec = getLong(argv[1], 0, "secs"); request.tv_nsec = getLong(argv[2], 0, "nanosecs"); } if (gettimeofday(&start, NULL) == -1) errExit("gettimeofday"); for (;;) { s = clock_nanosleep(CLOCK_REALTIME, flags, &request, &remain); if (s != 0 && s != EINTR) errExitEN(s, "clock_nanosleep"); if (s == EINTR) printf("Interrupted... "); if (gettimeofday(&finish, NULL) == -1) errExit("gettimeofday"); printf("Slept: %.6f secs", finish.tv_sec - start.tv_sec + (finish.tv_usec - start.tv_usec) / 1000000.0); if (s == 0) break; /* sleep completed */ if (flags != TIMER_ABSTIME) { printf("... Remaining: %ld.%09ld", (long) remain.tv_sec, remain.tv_nsec); request = remain; } printf("... Restarting\n"); } printf("\nSleep complete\n"); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { char *username, *password, *encrypted, *p; struct passwd *pwd; struct spwd *spwd; Boolean authOk; size_t len; long lnmax; /* Determine size of buffer required for a username, and allocate it */ lnmax = sysconf(_SC_LOGIN_NAME_MAX); if (lnmax == -1) /* If limit is indeterminate */ lnmax = 256; /* make a guess */ username = malloc(lnmax); if (username == NULL) errExit("malloc"); printf("Username: "******"couldn't get password record"); spwd = getspnam(username); if (spwd == NULL && errno == EACCES) fatal("no permission to read shadow password file"); if (spwd != NULL) /* If there is a shadow password record */ pwd->pw_passwd = spwd->sp_pwdp; /* Use the shadow password */ password = getpass("Password: "******"crypt"); authOk = strcmp(encrypted, pwd->pw_passwd) == 0; if (!authOk) { printf("Incorrect password\n"); exit(EXIT_FAILURE); } printf("Successfully authenticated: UID=%ld\n", (long) pwd->pw_uid); /* Now do authenticated work... */ exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { int semid, key, perms; struct sembuf sops[2]; if (argc != 2 || strcmp(argv[1], "--help") == 0) usageErr("%s sem-op\n", argv[0]); key = 12345; perms = S_IRUSR | S_IWUSR; semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms); if (semid != -1) { /* Successfully created the semaphore */ union semun arg; struct sembuf sop; sleep(5); printf("%ld: created semaphore\n", (long) getpid()); arg.val = 0; /* So initialize it to 0 */ if (semctl(semid, 0, SETVAL, arg) == -1) errExit("semctl 1"); printf("%ld: initialized semaphore\n", (long) getpid()); /* Perform a "no-op" semaphore operation - changes sem_otime so other processes can see we've initialized the set. */ sop.sem_num = 0; /* Operate on semaphore 0 */ sop.sem_op = 0; /* Wait for value to equal 0 */ sop.sem_flg = 0; if (semop(semid, &sop, 1) == -1) errExit("semop"); printf("%ld: completed dummy semop()\n", (long) getpid()); } else { /* We didn't create the semaphore set */ if (errno != EEXIST) { /* Unexpected error from semget() */ errExit("semget 1"); } else { /* Someone else already created it */ const int MAX_TRIES = 10; int j; union semun arg; struct semid_ds ds; semid = semget(key, 1, perms); /* So just get ID */ if (semid == -1) errExit("semget 2"); printf("%ld: got semaphore key\n", (long) getpid()); /* Wait until another process has called semop() */ arg.buf = &ds; for (j = 0; j < MAX_TRIES; j++) { printf("Try %d\n", j); if (semctl(semid, 0, IPC_STAT, arg) == -1) errExit("semctl 2"); if (ds.sem_otime != 0) /* Semop() performed? */ break; /* Yes, quit loop */ sleep(1); /* If not, wait and retry */ } if (ds.sem_otime == 0) /* Loop ran to completion! */ fatal("Existing semaphore not initialized"); } } /* Now perform some operation on the semaphore */ sops[0].sem_num = 0; /* Operate on semaphore 0... */ sops[0].sem_op = getInt(argv[1], 0, "sem-op"); sops[0].sem_flg = 0; if (semop(semid, sops, 1) == -1) errExit("semop"); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { int epfd, ready, fd, s, j, numOpenFds; struct epoll_event ev; struct epoll_event evlist[MAX_EVENTS]; char buf[MAX_BUF]; if (argc < 2 || strcmp(argv[1], "--help") == 0) usageErr("%s file...\n", argv[0]); epfd = epoll_create(argc - 1); if (epfd == -1) errExit("epoll_create"); /* Open each file on command line, and add it to the "interest list" for the epoll instance */ for (j = 1; j < argc; j++) { fd = open(argv[j], O_RDONLY); if (fd == -1) errExit("open"); printf("Opened \"%s\" on fd %d\n", argv[j], fd); ev.events = EPOLLIN; /* Only interested in input events */ ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) errExit("epoll_ctl"); } numOpenFds = argc - 1; while (numOpenFds > 0) { /* Fetch up to MAX_EVENTS items from the ready list of the epoll instance */ printf("About to epoll_wait()\n"); ready = epoll_wait(epfd, evlist, MAX_EVENTS, -1); if (ready == -1) { if (errno == EINTR) continue; /* Restart if interrupted by signal */ else errExit("epoll_wait"); } printf("Ready: %d\n", ready); /* Deal with returned list of events */ for (j = 0; j < ready; j++) { printf(" fd=%d; events: %s%s%s\n", evlist[j].data.fd, (evlist[j].events & EPOLLIN) ? "EPOLLIN " : "", (evlist[j].events & EPOLLHUP) ? "EPOLLHUP " : "", (evlist[j].events & EPOLLERR) ? "EPOLLERR " : ""); if (evlist[j].events & EPOLLIN) { s = read(evlist[j].data.fd, buf, MAX_BUF); if (s == -1) errExit("read"); printf(" read %d bytes: %.*s\n", s, s, buf); } else if (evlist[j].events & (EPOLLHUP | EPOLLERR)) { /* After the epoll_wait(), EPOLLIN and EPOLLHUP may both have been set. But we'll only get here, and thus close the file descriptor, if EPOLLIN was not set. This ensures that all outstanding input (possibly more than MAX_BUF bytes) is consumed (by further loop iterations) before the file descriptor is closed. */ printf(" closing fd %d\n", evlist[j].data.fd); if (close(evlist[j].data.fd) == -1) errExit("close"); numOpenFds--; } } } printf("All file descriptors closed; bye\n"); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { fd_set rfds; int opt; int inotifyFd; /* Parse command-line options */ verboseMask = 0; checkCache = 0; dumpCache = 0; stopFile = NULL; abortOnCacheProblem = 0; while ((opt = getopt(argc, argv, "a:dxl:v:b:")) != -1) { switch (opt) { case 'a': abortOnCacheProblem = 1; stopFile = optarg; break; case 'x': checkCache = 1; break; case 'd': dumpCache = 1; break; case 'v': verboseMask = atoi(optarg); break; case 'b': readBufferSize = atoi(optarg); break; case 'l': logfp = fopen(optarg, "w+"); if (logfp == NULL) errExit("fopen"); setbuf(logfp, NULL); break; default: usageError(argv[0]); } } if (optind >= argc) usageError(argv[0]); /* Save a copy of the directories on the command line */ copyRootDirPaths(&argv[optind]); /* Create an inotify instance and populate it with entries for directory named on command line */ inotifyFd = reinitialize(-1); /* Loop to handle inotify events and keyboard commands */ printf("%s> ", argv[0]); fflush(stdout); for (;;) { FD_ZERO(&rfds); FD_SET(STDIN_FILENO, &rfds); FD_SET(inotifyFd, &rfds); if (select(inotifyFd + 1, &rfds, NULL, NULL, NULL) == -1) errExit("select"); if (FD_ISSET(STDIN_FILENO, &rfds)) { executeCommand(&inotifyFd); printf("%s> ", argv[0]); fflush(stdout); } if (FD_ISSET(inotifyFd, &rfds)) processInotifyEvents(&inotifyFd); } exit(EXIT_SUCCESS); }
/******************** gettCmdList ************************************** int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt) Purpose: Parse through the token array to determine the commands. It saves the beginning and ending subscripts for each command's arguments. It also determines whether the command has a redirected stdin and/or stdout. Parameters: O Cmd cmdM[] array of commands I Token tokenM[] array of tokens for the input test I int iTokenCnt number of entries in tokenM Returns: Count of number of entries in cmdM. Notes: - commands are separated by commas **************************************************************************/ int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt) { int i; // subscript to current token char cChar; // current character in input text int iCmdCnt = 0; // count of number of entries in cmdM // Iterate through the array of tokens. We actually // go to one item beyond the end so that we can process // the last token normally. (We pretend there is an // ending token after the last token.) for (i = 1; i <= iTokenCnt; i += 1) { Cmd *pCmd = &(cmdM[iCmdCnt]); if (i == iTokenCnt) cChar = ','; // pretend an ending delim else cChar = tokenM[i][0]; switch(cChar) { case ',': // delimiter between commands if (pCmd->iBeginIdx == 0) errExit("no command, cmd arg: %d\n", i); // If we haven't yet marked the end of the command's // arguments, assume it is right before the comma. // Note that redirection also set the iEndIdx. if (pCmd->iEndIdx == 0) pCmd->iEndIdx = i-1; // Check for no command arguments if (pCmd->iBeginIdx > pCmd->iEndIdx) { // no args pCmd->iBeginIdx = 0; pCmd->iEndIdx = -1; } iCmdCnt += 1; break; case '<': // stdin redirection if (i+1 >= iTokenCnt) // need another arg errExit("redirect requires additional arg, cmd arg: %d\n", i); pCmd->iStdinRedirectIdx = i+1; // If we haven't yet marked the end of the command's // arguments, assume it is right before the <. if (pCmd->iEndIdx == 0) pCmd->iEndIdx = i-1; break; case '>': if (i+1 >= iTokenCnt) // need another arg errExit("redirect requires additional arg, cmd arg: %d\n", i); pCmd->iStdoutRedirectIdx = i+1; // If we haven't yet marked the end of the command's // arguments, assume it is right before the >. if (pCmd->iEndIdx == 0) pCmd->iEndIdx = i-1; break; default: // check if at the beginning of the command if (pCmd->iBeginIdx == 0) { // not comma, <, > if iBeginIdx is 0, we need to record // where the arguments might begin strcpy(pCmd->szCmdNm, tokenM[i]); pCmd->iBeginIdx = i+1; } } } return iCmdCnt; }
static void copyRootDirPaths(char *argv[]) { char **p; int j, k; struct stat sb; p = argv; numRootDirs = 0; /* Count the number of root paths, and check that the paths are valid */ for (p = argv; *p != NULL; p++) { /* Check that command-line arguments are directories */ if (lstat(*p, &sb) == -1) { fprintf(stderr, "lstat() failed on '%s'\n", *p); exit(EXIT_FAILURE); } if (! S_ISDIR(sb.st_mode)) { fprintf(stderr, "'%s' is not a directory\n", *p); exit(EXIT_FAILURE); } numRootDirs++; } /* Create a copy of the root directory pathnames */ rootDirPaths = calloc(numRootDirs, sizeof(char *)); if (rootDirPaths == NULL) errExit("calloc"); rootDirStat = calloc(numRootDirs, sizeof(struct stat)); if (rootDirPaths == NULL) errExit("calloc"); for (j = 0; j < numRootDirs; j++) { rootDirPaths[j] = strdup(argv[j]); if (rootDirPaths[j] == NULL) errExit("strdup"); /* If the same filesystem object appears more than once in the command line, this will cause confusion if we later try to zap an object from the set of root paths. So, reject such duplicates now. Note that we can't just do simple string comparisons of the arguments, since different pathname strings may refer to the same filesystem object (e.g., "mydir" and "./mydir"). So, we use stat() to compare i-node numbers and containing device IDs. */ if (lstat(argv[j], &rootDirStat[j]) == -1) errExit("lstat"); for (k = 0; k < j; k++) { if ((rootDirStat[j].st_ino == rootDirStat[k].st_ino) && (rootDirStat[j].st_dev == rootDirStat[k].st_dev)) { fprintf(stderr, "Duplicate filesystem objects: %s, %s\n", argv[j], argv[k]); exit(EXIT_FAILURE); } } } ignoreRootDirs = 0; }
int main(int argc, char *argv[]) { fd_set readfds; int ready, nfds, flags; struct timeval timeout; struct timeval *pto; struct sigaction sa; char ch; int fd, j; if (argc < 2 || strcmp(argv[1], "--help") == 0) usageErr("%s {timeout|-} fd...\n" "\t\t('-' means infinite timeout)\n", argv[0]); /* Initialize 'timeout', 'readfds', and 'nfds' for select() */ if (strcmp(argv[1], "-") == 0) { pto = NULL; /* Infinite timeout */ } else { pto = &timeout; timeout.tv_sec = getLong(argv[1], 0, "timeout"); timeout.tv_usec = 0; /* No microseconds */ } nfds = 0; /* Build the 'readfds' from the fd numbers given in command line */ FD_ZERO(&readfds); for (j = 2; j < argc; j++) { fd = getInt(argv[j], 0, "fd"); if (fd >= FD_SETSIZE) cmdLineErr("file descriptor exceeds limit (%d)\n", FD_SETSIZE); if (fd >= nfds) nfds = fd + 1; /* Record maximum fd + 1 */ FD_SET(fd, &readfds); } /* Create pipe before establishing signal handler to prevent race */ if (pipe(pfd) == -1) errExit("pipe"); FD_SET(pfd[0], &readfds); /* Add read end of pipe to 'readfds' */ nfds = max(nfds, pfd[0] + 1); /* And adjust 'nfds' if required */ /* Make read and write ends of pipe nonblocking */ flags = fcntl(pfd[0], F_GETFL); if (flags == -1) errExit("fcntl-F_GETFL"); flags |= O_NONBLOCK; /* Make read end nonblocking */ if (fcntl(pfd[0], F_SETFL, flags) == -1) errExit("fcntl-F_SETFL"); flags = fcntl(pfd[1], F_GETFL); if (flags == -1) errExit("fcntl-F_GETFL"); flags |= O_NONBLOCK; /* Make write end nonblocking */ if (fcntl(pfd[1], F_SETFL, flags) == -1) errExit("fcntl-F_SETFL"); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; /* Restart interrupted reads()s */ sa.sa_handler = handler; if (sigaction(SIGINT, &sa, NULL) == -1) errExit("sigaction"); while ((ready = select(nfds, &readfds, NULL, NULL, pto)) == -1 && errno == EINTR) continue; /* Restart if interrupted by signal */ if (ready == -1) /* Unexpected error */ errExit("select"); if (FD_ISSET(pfd[0], &readfds)) { /* Handler was called */ printf("A signal was caught\n"); for (;;) { /* Consume bytes from pipe */ if (read(pfd[0], &ch, 1) == -1) { if (errno == EAGAIN) break; /* No more bytes */ else errExit("read"); /* Some other error */ } /* Perform any actions that should be taken in response to signal */ } } /* Examine file descriptor sets returned by select() to see which other file descriptors are ready */ printf("ready = %d\n", ready); for (j = 2; j < argc; j++) { fd = getInt(argv[j], 0, "fd"); printf("%d: %s\n", fd, FD_ISSET(fd, &readfds) ? "r" : ""); } /* And check if read end of pipe is ready */ printf("%d: %s (read end of pipe)\n", pfd[0], FD_ISSET(pfd[0], &readfds) ? "r" : ""); if (pto != NULL) printf("timeout after select(): %ld.%03ld\n", (long) timeout.tv_sec, (long) timeout.tv_usec / 1000); exit(EXIT_SUCCESS); }
static int fs_copydir(const char *infname, const struct stat *st, int ftype, struct FTW *sftw) { (void) st; (void) sftw; assert(infname); assert(*infname != '\0'); assert(outpath); assert(*outpath != '\0'); assert(inpath); // check size limit if (size_limit_reached) return 0; char *outfname; if (asprintf(&outfname, "%s%s", outpath, infname + strlen(inpath)) == -1) errExit("asprintf"); //printf("outpaht %s\n", outpath); //printf("inpath %s\n", inpath); //printf("infname %s\n", infname); //printf("outfname %s\n\n", outfname); // don't copy it if we already have the file struct stat s; if (stat(outfname, &s) == 0) { if (first) first = 0; else fprintf(stderr, "Warning fcopy: skipping %s, file already present\n", infname); free(outfname); return 0; } // extract mode and ownership if (stat(infname, &s) != 0) { fprintf(stderr, "Warning fcopy: skipping %s, cannot find inode\n", infname); free(outfname); return 0; } uid_t uid = s.st_uid; gid_t gid = s.st_gid; mode_t mode = s.st_mode; // recalculate size if ((s.st_size + size_cnt) > COPY_LIMIT) { fprintf(stderr, "Error fcopy: size limit of %dMB reached\n", (COPY_LIMIT / 1024) / 1024); size_limit_reached = 1; free(outfname); return 0; } file_cnt++; size_cnt += s.st_size; if(ftype == FTW_F) { copy_file(infname, outfname, mode, uid, gid); } else if (ftype == FTW_D) { mkdir_attr(outfname, mode, uid, gid); } else if (ftype == FTW_SL) { copy_link(infname, outfname, mode, uid, gid); } return(0); }
static void install_filter(void) { struct sock_filter filter[] = { /* Load architecture */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, arch))), /* Kill process if the architecture is not what we expect */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 1, 0), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), /* Load system call number */ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, (offsetof(struct seccomp_data, nr))), /* Allow system calls other than open() */ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_open, 1, 0), BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), /* Kill process on open() */ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL) }; struct sock_fprog prog = { .len = (unsigned short) (sizeof(filter) / sizeof(filter[0])), .filter = filter, }; if (seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog) == -1) errExit("seccomp"); } int main(int argc, char *argv[]) { int j, nloops; if (argc < 2) { fprintf(stderr, "Usage: %s <num-loops> [x]\n", argv[0]); fprintf(stderr, " (use 'x' to run with BPF filter applied)\n"); exit(EXIT_FAILURE); } if (argc > 2) { printf("Appling BPF filter\n"); if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) errExit("prctl"); install_filter(); } nloops = atoi(argv[1]); for (j = 0; j < nloops; j++) getppid(); exit(EXIT_SUCCESS); }
// scan interface (--scan option) void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { assert(dev); assert(ifip); // printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", // dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); if (strlen(dev) > IFNAMSIZ) { fprintf(stderr, "Error: invalid network device name %s\n", dev); exit(1); } // find interface mac address int sock; if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) errExit("socket"); struct ifreq ifr; memset(&ifr, 0, sizeof (ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ); if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) errExit("ioctl"); close(sock); uint8_t mac[6]; memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); // open layer2 socket if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) errExit("socket"); // try all possible ip addresses in ascending order uint32_t range = ~ifmask + 1; // the number of potential addresses // this software is not supported for /31 networks if (range < 4) { fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); close(sock); return; } uint32_t dest = (ifip & ifmask) + 1; uint32_t last = dest + range - 1; uint32_t src = htonl(ifip); // wait not more than one second for an answer int header_printed = 0; uint32_t last_ip = 0; struct timeval ts; ts.tv_sec = 2; // 2 seconds receive timeout ts.tv_usec = 0; while (1) { fd_set rfds; FD_ZERO(&rfds); FD_SET(sock, &rfds); fd_set wfds; FD_ZERO(&wfds); FD_SET(sock, &wfds); int maxfd = sock; uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc memset(frame, 0, ETH_FRAME_LEN); int nready; if (dest < last) nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); else nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); if (nready < 0) errExit("select"); if (nready == 0) { // timeout break; } if (FD_ISSET(sock, &wfds) && dest < last) { // configure layer2 socket address information struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) errExit("if_nametoindex"); addr.sll_family = AF_PACKET; memcpy (addr.sll_addr, mac, 6); addr.sll_halen = htons(6); // build the arp packet header ArpHdr hdr; memset(&hdr, 0, sizeof(hdr)); hdr.htype = htons(1); hdr.ptype = htons(ETH_P_IP); hdr.hlen = 6; hdr.plen = 4; hdr.opcode = htons(1); //ARPOP_REQUEST memcpy(hdr.sender_mac, mac, 6); memcpy(hdr.sender_ip, (uint8_t *)&src, 4); uint32_t dst = htonl(dest); memcpy(hdr.target_ip, (uint8_t *)&dst, 4); // buiild ethernet frame uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc memset(frame, 0, sizeof(frame)); frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; memcpy(frame + 6, mac, 6); frame[12] = ETH_P_ARP / 256; frame[13] = ETH_P_ARP % 256; memcpy (frame + 14, &hdr, sizeof(hdr)); // send packet int len; if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) errExit("send"); //printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); fflush(0); dest++; } if (FD_ISSET(sock, &rfds)) { // read the incoming packet int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); if (len < 0) { perror("recvfrom"); } // parse the incomming packet if ((unsigned int) len < 14 + sizeof(ArpHdr)) continue; // look only at ARP packets if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) continue; ArpHdr hdr; memcpy(&hdr, frame + 14, sizeof(ArpHdr)); if (hdr.opcode == htons(2)) { // check my mac and my address if (memcmp(mac, hdr.target_mac, 6) != 0) continue; uint32_t ip; memcpy(&ip, hdr.target_ip, 4); if (ip != src) continue; memcpy(&ip, hdr.sender_ip, 4); ip = ntohl(ip); if (ip == last_ip) // filter duplicates continue; last_ip = ip; // printing if (header_printed == 0) { printf(" Network scan:\n"); // print parent interface if (cfg.bridge0.configured && cfg.bridge0.ip && cfg.bridge0.macvlan && (cfg.bridge0.ip & cfg.bridge0.mask) == (ifip & cfg.bridge0.mask)) printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", PRINT_MAC(cfg.bridge0.mac), PRINT_IP(cfg.bridge0.ip)); if (cfg.bridge1.configured && cfg.bridge1.ip && cfg.bridge1.macvlan && (cfg.bridge1.ip & cfg.bridge1.mask) == (ifip & cfg.bridge1.mask)) printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", PRINT_MAC(cfg.bridge1.mac), PRINT_IP(cfg.bridge1.ip)); if (cfg.bridge2.configured && cfg.bridge2.ip && cfg.bridge2.macvlan && (cfg.bridge2.ip & cfg.bridge2.mask) == (ifip & cfg.bridge2.mask)) printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", PRINT_MAC(cfg.bridge2.mac), PRINT_IP(cfg.bridge2.ip)); if (cfg.bridge3.configured && cfg.bridge3.ip && cfg.bridge3.macvlan && (cfg.bridge3.ip & cfg.bridge3.mask) == (ifip & cfg.bridge3.mask)) printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", PRINT_MAC(cfg.bridge3.mac), PRINT_IP(cfg.bridge3.ip)); header_printed = 1; } printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); } } } close(sock); }
int main(int argc, char *argv[]) { struct msghdr msgh; struct iovec iov; struct ucred *ucredp, ucred; int data, lfd, sfd, optval, opt; ssize_t nr; Boolean useDatagramSocket; union { struct cmsghdr cmh; char control[CMSG_SPACE(sizeof(struct ucred))]; /* Space large enough to hold a ucred structure */ } control_un; struct cmsghdr *cmhp; socklen_t len; /* Parse command-line arguments */ useDatagramSocket = FALSE; while ((opt = getopt(argc, argv, "d")) != -1) { switch (opt) { case 'd': useDatagramSocket = TRUE; break; default: usageErr("%s [-d]\n" " -d use datagram socket\n", argv[0]); } } /* Create socket bound to well-known address */ if (remove(SOCK_PATH) == -1 && errno != ENOENT) errExit("remove-%s", SOCK_PATH); if (useDatagramSocket) { printf("Receiving via datagram socket\n"); sfd = unixBind(SOCK_PATH, SOCK_DGRAM); if (sfd == -1) errExit("unixBind"); } else { printf("Receiving via stream socket\n"); lfd = unixListen(SOCK_PATH, 5); if (lfd == -1) errExit("unixListen"); sfd = accept(lfd, NULL, 0); if (sfd == -1) errExit("accept"); } /* We must set the SO_PASSCRED socket option in order to receive credentials */ optval = 1; if (setsockopt(sfd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) errExit("setsockopt"); /* Set 'control_un' to describe ancillary data that we want to receive */ control_un.cmh.cmsg_len = CMSG_LEN(sizeof(struct ucred)); control_un.cmh.cmsg_level = SOL_SOCKET; control_un.cmh.cmsg_type = SCM_CREDENTIALS; /* Set 'msgh' fields to describe 'control_un' */ msgh.msg_control = control_un.control; msgh.msg_controllen = sizeof(control_un.control); /* Set fields of 'msgh' to point to buffer used to receive (real) data read by recvmsg() */ msgh.msg_iov = &iov; msgh.msg_iovlen = 1; iov.iov_base = &data; iov.iov_len = sizeof(int); msgh.msg_name = NULL; /* We don't need address of peer */ msgh.msg_namelen = 0; /* Receive real plus ancillary data */ nr = recvmsg(sfd, &msgh, 0); if (nr == -1) errExit("recvmsg"); printf("recvmsg() returned %ld\n", (long) nr); if (nr > 0) printf("Received data = %d\n", data); /* Extract credentials information from received ancillary data */ cmhp = CMSG_FIRSTHDR(&msgh); if (cmhp == NULL || cmhp->cmsg_len != CMSG_LEN(sizeof(struct ucred))) fatal("bad cmsg header / message length"); if (cmhp->cmsg_level != SOL_SOCKET) fatal("cmsg_level != SOL_SOCKET"); if (cmhp->cmsg_type != SCM_CREDENTIALS) fatal("cmsg_type != SCM_CREDENTIALS"); ucredp = (struct ucred *) CMSG_DATA(cmhp); printf("Received credentials pid=%ld, uid=%ld, gid=%ld\n", (long) ucredp->pid, (long) ucredp->uid, (long) ucredp->gid); /* The Linux-specific, read-only SO_PEERCRED socket option returns credential information about the peer, as described in socket(7) */ len = sizeof(struct ucred); if (getsockopt(sfd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) errExit("getsockopt"); printf("Credentials from SO_PEERCRED: pid=%ld, euid=%ld, egid=%ld\n", (long) ucred.pid, (long) ucred.uid, (long) ucred.gid); exit(EXIT_SUCCESS); }
// returns 0 if the address is not in use, -1 otherwise int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr) { if (strlen(dev) > IFNAMSIZ) { fprintf(stderr, "Error: invalid network device name %s\n", dev); exit(1); } if (arg_debug) printf("Trying %d.%d.%d.%d ...\n", PRINT_IP(destaddr)); // find interface address int sock; if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) errExit("socket"); srcaddr = htonl(srcaddr); destaddr = htonl(destaddr); // Find interface MAC address struct ifreq ifr; memset(&ifr, 0, sizeof (ifr)); strncpy(ifr.ifr_name, dev, IFNAMSIZ); if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) errExit("ioctl"); close(sock); // configure layer2 socket address information struct sockaddr_ll addr; memset(&addr, 0, sizeof(addr)); if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) errExit("if_nametoindex"); addr.sll_family = AF_PACKET; memcpy (addr.sll_addr, ifr.ifr_hwaddr.sa_data, 6); addr.sll_halen = htons(6); // build the arp packet header ArpHdr hdr; memset(&hdr, 0, sizeof(hdr)); hdr.htype = htons(1); hdr.ptype = htons(ETH_P_IP); hdr.hlen = 6; hdr.plen = 4; hdr.opcode = htons(1); //ARPOP_REQUEST memcpy(hdr.sender_mac, ifr.ifr_hwaddr.sa_data, 6); memcpy(hdr.sender_ip, (uint8_t *)&srcaddr, 4); memcpy(hdr.target_ip, (uint8_t *)&destaddr, 4); // buiild ethernet frame uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc memset(frame, 0, sizeof(frame)); frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; memcpy(frame + 6, ifr.ifr_hwaddr.sa_data, 6); frame[12] = ETH_P_ARP / 256; frame[13] = ETH_P_ARP % 256; memcpy (frame + 14, &hdr, sizeof(hdr)); // open layer2 socket if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) errExit("socket"); int len; if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) errExit("send"); fflush(0); // wait not more than one second for an answer fd_set fds; FD_ZERO(&fds); FD_SET(sock, &fds); int maxfd = sock; struct timeval ts; ts.tv_sec = 1; // 1 second wait time ts.tv_usec = 0; while (1) { int nready = select(maxfd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts); if (nready < 0) errExit("select"); else if (nready == 0) { // timeout close(sock); return 0; } else { // read the incoming packet int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); if (len < 0) { perror("recvfrom"); close(sock); return -1; } // parse the incomming packet if ((unsigned int) len < 14 + sizeof(ArpHdr)) continue; if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) continue; memcpy(&hdr, frame + 14, sizeof(ArpHdr)); if (hdr.opcode == htons(1)) continue; if (hdr.opcode == htons(2)) { // check my mac and my address if (memcmp(ifr.ifr_hwaddr.sa_data, hdr.target_mac, 6) != 0) continue; uint32_t ip; memcpy(&ip, hdr.target_ip, 4); if (ip != srcaddr) { continue; } close(sock); return -1; } } } // it will never get here! close(sock); return -1; }
void netfilter(const char *fname) { // default filter char *filter = client_filter; // custom filter int allocated = 0; if (fname) { // buffer the filter struct stat s; if (stat(fname, &s) == -1) { fprintf(stderr, "Error: cannot find network filter file\n"); exit(1); } filter = malloc(s.st_size + 1); // + '\0' memset(filter, 0, s.st_size + 1); if (!filter) errExit("malloc"); FILE *fp = fopen(fname, "r"); if (!fp) { fprintf(stderr, "Error: cannot open network filter file\n"); exit(1); } size_t sz = fread(filter, 1, s.st_size, fp); if (sz != s.st_size) { fprintf(stderr, "Error: cannot read network filter file\n"); exit(1); } fclose(fp); allocated = 1; } // mount a tempfs on top of /tmp directory if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /tmp"); // create the filter file FILE *fp = fopen("/tmp/netfilter", "w"); if (!fp) { fprintf(stderr, "Error: cannot open /tmp/netfilter file\n"); exit(1); } fprintf(fp, "%s\n", filter); fclose(fp); // push filter int rv; if (arg_debug) printf("Installing network filter:\n%s\n", filter); rv = system("/sbin/iptables-restore < /tmp/netfilter"); if (rv == -1) { fprintf(stderr, "Error: failed to configure network filter.\n"); exit(1); } if (arg_debug) rv = system("/sbin/iptables -vL"); // unmount /tmp umount("/tmp"); if (allocated) free(filter); }
void fs_private_dev(void){ // install a new /dev directory if (arg_debug) printf("Mounting tmpfs on /dev\n"); // create DRI_DIR fs_build_mnt_dir(); // keep a copy of dev directory if (mkdir(RUN_DEV_DIR, 0755) == -1) errExit("mkdir"); if (chmod(RUN_DEV_DIR, 0755) == -1) errExit("chmod"); ASSERT_PERMS(RUN_DEV_DIR, 0, 0, 0755); if (mount("/dev", RUN_DEV_DIR, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/dri"); // create DEVLOG_FILE int have_devlog = 0; struct stat s; if (stat("/dev/log", &s) == 0) { have_devlog = 1; FILE *fp = fopen(RUN_DEVLOG_FILE, "w"); if (!fp) have_devlog = 0; else { fprintf(fp, "\n"); fclose(fp); if (mount("/dev/log", RUN_DEVLOG_FILE, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); } } // mount tmpfs on top of /dev if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) errExit("mounting /dev"); fs_logger("tmpfs /dev"); deventry_mount(); // bring back /dev/log if (have_devlog) { FILE *fp = fopen("/dev/log", "w"); if (fp) { fprintf(fp, "\n"); fclose(fp); if (mount(RUN_DEVLOG_FILE, "/dev/log", NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mounting /dev/log"); fs_logger("clone /dev/log"); } } if (mount(RUN_RO_DIR, RUN_DEV_DIR, "none", MS_BIND, "mode=400,gid=0") < 0) errExit("disable /dev/snd"); // create /dev/shm if (arg_debug) printf("Create /dev/shm directory\n"); if (mkdir("/dev/shm", 01777) == -1) errExit("mkdir"); // mkdir sets only the file permission bits if (chmod("/dev/shm", 01777) < 0) errExit("chmod"); ASSERT_PERMS("/dev/shm", 0, 0, 01777); fs_logger("mkdir /dev/shm"); // create devices create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5 fs_logger("mknod /dev/zero"); create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3 fs_logger("mknod /dev/null"); create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7 fs_logger("mknod /dev/full"); create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8 fs_logger("mknod /dev/random"); create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9 fs_logger("mknod /dev/urandom"); create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0 fs_logger("mknod /dev/tty"); #if 0 create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0"); create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1"); #endif // pseudo-terminal if (mkdir("/dev/pts", 0755) == -1) errExit("mkdir"); if (chmod("/dev/pts", 0755) == -1) errExit("chmod"); ASSERT_PERMS("/dev/pts", 0, 0, 0755); fs_logger("mkdir /dev/pts"); create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2"); fs_logger("mknod /dev/pts/ptmx"); create_link("/dev/pts/ptmx", "/dev/ptmx"); // code before github issue #351 // mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts // if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0) // errExit("mounting /dev/pts"); // mount /dev/pts gid_t ttygid = get_tty_gid(); char *data; if (asprintf(&data, "newinstance,gid=%d,mode=620,ptmxmode=0666", (int) ttygid) == -1) errExit("asprintf"); if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, data) < 0) errExit("mounting /dev/pts"); free(data); fs_logger("clone /dev/pts"); #if 0 // stdin, stdout, stderr create_link("/proc/self/fd", "/dev/fd"); create_link("/proc/self/fd/0", "/dev/stdin"); create_link("/proc/self/fd/1", "/dev/stdout"); create_link("/proc/self/fd/2", "/dev/stderr"); #endif }
int main(int argc, char *argv[]) { struct sigaction sa; int fd, events, fnum; const int NOTIFY_SIG = SIGRTMIN; char *p; if (argc < 2 || strcmp(argv[1], "--help") == 0) usageError(argv[0], NULL); /* Establish handler for notification signal */ sa.sa_sigaction = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_SIGINFO; /* So handler gets siginfo_t arg. */ if (sigaction(NOTIFY_SIG, &sa, NULL) == -1) errExit("sigaction"); for (fnum = 1; fnum < argc; fnum++) { p = strchr(argv[fnum], ':'); /* Look for optional ':' */ if (p == NULL) { /* Default is all events + multishot */ events = DN_ACCESS | DN_ATTRIB | DN_CREATE | DN_DELETE | DN_MODIFY | DN_RENAME | DN_MULTISHOT; } else { /* ':' present, parse event chars */ *p = '\0'; /* Terminates directory component */ events = 0; for (p++; *p != '\0'; p++) { switch (*p) { case 'a': events |= DN_ACCESS; break; case 'A': events |= DN_ATTRIB; break; case 'c': events |= DN_CREATE; break; case 'd': events |= DN_DELETE; break; case 'm': events |= DN_MODIFY; break; case 'r': events |= DN_RENAME; break; case 'M': events |= DN_MULTISHOT; break; default: usageError(argv[0], "Bad event character\n"); } } } /* Obtain a file descriptor for the directory to be monitored */ fd = open(argv[fnum], O_RDONLY); if (fd == -1) errExit("open"); printf("opened '%s' as file descriptor %d\n", argv[fnum], fd); /* Use alternate signal instead of SIGIO for dnotify events */ if (fcntl(fd, F_SETSIG, NOTIFY_SIG) == -1) errExit("fcntl - F_SETSIG"); /* Enable directory change notifications */ if (fcntl(fd, F_NOTIFY, events) == -1) errExit("fcntl-F_NOTIFY"); printf("events: %o\n", (unsigned int) events); } for (;;) pause(); /* Wait for events */ }
void join(pid_t pid, int argc, char **argv, int index) { EUID_ASSERT(); char *homedir = cfg.homedir; extract_command(argc, argv, index); // if the pid is that of a firejail process, use the pid of the first child process EUID_ROOT(); char *comm = pid_proc_comm(pid); EUID_USER(); if (comm) { if (strcmp(comm, "firejail") == 0) { pid_t child; if (find_child(pid, &child) == 0) { pid = child; printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid); } } free(comm); } // check privileges for non-root users uid_t uid = getuid(); if (uid != 0) { uid_t sandbox_uid = pid_get_uid(pid); if (uid != sandbox_uid) { fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n"); exit(1); } } EUID_ROOT(); // in user mode set caps seccomp, cpu, cgroup, etc if (getuid() != 0) { extract_caps_seccomp(pid); extract_cpu(pid); extract_cgroup(pid); extract_nogroups(pid); extract_user_namespace(pid); } // set cgroup if (cfg.cgroup) // not available for uid 0 set_cgroup(cfg.cgroup); // join namespaces if (arg_join_network) { if (join_namespace(pid, "net")) exit(1); } else if (arg_join_filesystem) { if (join_namespace(pid, "mnt")) exit(1); } else { if (join_namespace(pid, "ipc")) exit(1); if (join_namespace(pid, "net")) exit(1); if (join_namespace(pid, "pid")) exit(1); if (join_namespace(pid, "uts")) exit(1); if (join_namespace(pid, "mnt")) exit(1); } pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // chroot into /proc/PID/root directory char *rootdir; if (asprintf(&rootdir, "/proc/%d/root", pid) == -1) errExit("asprintf"); int rv; if (!arg_join_network) { rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option if (rv == 0) printf("changing root to %s\n", rootdir); } prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died if (chdir("/") < 0) errExit("chdir"); if (homedir) { struct stat s; if (stat(homedir, &s) == 0) { /* coverity[toctou] */ if (chdir(homedir) < 0) errExit("chdir"); } } // set cpu affinity if (cfg.cpus) // not available for uid 0 set_cpu_affinity(); // set caps filter if (apply_caps == 1) // not available for uid 0 caps_set(caps); #ifdef HAVE_SECCOMP // set protocol filter if (getuid() != 0) protocol_filter_load(RUN_PROTOCOL_CFG); if (cfg.protocol) { // not available for uid 0 protocol_filter(); } // set seccomp filter if (apply_seccomp == 1) // not available for uid 0 seccomp_set(); #endif // fix qt 4.8 if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0) errExit("setenv"); if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc, errExit("setenv"); // mount user namespace or drop privileges if (arg_noroot) { // not available for uid 0 if (arg_debug) printf("Joining user namespace\n"); if (join_namespace(1, "user")) exit(1); } else drop_privs(arg_nogroups); // nogroups not available for uid 0 // set prompt color to green //export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] ' if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0) errExit("setenv"); // run cmdline trough /bin/bash if (cfg.command_line == NULL) { struct stat s; // replace the process with a shell if (stat("/bin/bash", &s) == 0) execlp("/bin/bash", "/bin/bash", NULL); else if (stat("/usr/bin/zsh", &s) == 0) execlp("/usr/bin/zsh", "/usr/bin/zsh", NULL); else if (stat("/bin/csh", &s) == 0) execlp("/bin/csh", "/bin/csh", NULL); else if (stat("/bin/sh", &s) == 0) execlp("/bin/sh", "/bin/sh", NULL); // no shell found, print an error and exit fprintf(stderr, "Error: no POSIX shell found\n"); sleep(5); exit(1); } else { // run the command supplied by the user int cwd = 0; if (cfg.cwd) { if (chdir(cfg.cwd) == 0) cwd = 1; } if (!cwd) { if (chdir("/") < 0) errExit("chdir"); if (cfg.homedir) { struct stat s; if (stat(cfg.homedir, &s) == 0) { if (chdir(cfg.homedir) < 0) errExit("chdir"); } } } char *arg[5]; arg[0] = "/bin/bash"; arg[1] = "-c"; if (arg_debug) printf("Starting %s\n", cfg.command_line); if (!arg_doubledash) { arg[2] = cfg.command_line; arg[3] = NULL; } else { arg[2] = "--"; arg[3] = cfg.command_line; arg[4] = NULL; } execvp("/bin/bash", arg); } // it will never get here!!! } // wait for the child to finish waitpid(child, NULL, 0); exit(0); }
int main(int argc, char *argv[]) { uint32_t seqNum; char reqLenStr[INT_LEN]; /* Length of requested sequence */ char seqNumStr[INT_LEN]; /* Start of granted sequence */ struct sockaddr_storage claddr; int lfd, cfd, optval, reqLen; socklen_t addrlen; struct addrinfo hints; struct addrinfo *result, *rp; #define ADDRSTRLEN (NI_MAXHOST + NI_MAXSERV + 10) char addrStr[ADDRSTRLEN]; char host[NI_MAXHOST]; char service[NI_MAXSERV]; if (argc > 1 && strcmp(argv[1], "--help") == 0) usageErr("%s [init-seq-num]\n", argv[0]); seqNum = (argc > 1) ? getInt(argv[1], 0, "init-seq-num") : 0; if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) errExit("signal"); /* Call getaddrinfo() to obtain a list of addresses that we can try binding to */ memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_canonname = NULL; hints.ai_addr = NULL; hints.ai_next = NULL; hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; /* Allows IPv4 or IPv6 */ hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; /* Wildcard IP address; service name is numeric */ if (getaddrinfo(NULL, PORT_NUM, &hints, &result) != 0) errExit("getaddrinfo"); /* Walk through returned list until we find an address structure that can be used to successfully create and bind a socket */ optval = 1; for (rp = result; rp != NULL; rp = rp->ai_next) { lfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (lfd == -1) continue; /* On error, try next address */ if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) errExit("setsockopt"); if (bind(lfd, rp->ai_addr, rp->ai_addrlen) == 0) break; /* Success */ /* bind() failed: close this socket and try next address */ close(lfd); } if (rp == NULL) fatal("Could not bind socket to any address"); if (listen(lfd, BACKLOG) == -1) errExit("listen"); freeaddrinfo(result); for (;;) { /* Handle clients iteratively */ /* Accept a client connection, obtaining client's address */ addrlen = sizeof(struct sockaddr_storage); cfd = accept(lfd, (struct sockaddr *) &claddr, &addrlen); if (cfd == -1) { errMsg("accept"); continue; } if (getnameinfo((struct sockaddr *) &claddr, addrlen, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) snprintf(addrStr, ADDRSTRLEN, "(%s, %s)", host, service); else snprintf(addrStr, ADDRSTRLEN, "(?UNKNOWN?)"); printf("Connection from %s\n", addrStr); /* Read client request, send sequence number back */ if (readLine(cfd, reqLenStr, INT_LEN) <= 0) { close(cfd); continue; /* Failed read; skip request */ } reqLen = atoi(reqLenStr); if (reqLen <= 0) { /* Watch for misbehaving clients */ close(cfd); continue; /* Bad request; skip it */ } snprintf(seqNumStr, INT_LEN, "%d\n", seqNum); if (write(cfd, &seqNumStr, strlen(seqNumStr)) != strlen(seqNumStr)) fprintf(stderr, "Error on write"); seqNum += reqLen; /* Update sequence number */ if (close(cfd) == -1) /* Close connection */ errMsg("close"); } }
// disable shm in pulseaudio void pulseaudio_init(void) { struct stat s; // do we have pulseaudio in the system? if (stat("/etc/pulse/client.conf", &s) == -1) { if (arg_debug) printf("/etc/pulse/client.conf not found\n"); return; } // create the new user pulseaudio directory if (mkdir(RUN_PULSE_DIR, 0700) == -1) errExit("mkdir"); // make it a mount point and add mount flags if (mount(RUN_PULSE_DIR, RUN_PULSE_DIR, NULL, MS_BIND, NULL) < 0 || mount(NULL, RUN_PULSE_DIR, NULL, MS_NOEXEC|MS_NODEV|MS_NOSUID|MS_BIND|MS_REMOUNT, NULL) < 0) errExit("mount RUN_PULSE_DIR"); // create the new client.conf file char *pulsecfg = NULL; if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) errExit("asprintf"); if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) // root needed errExit("copy_file"); FILE *fp = fopen(pulsecfg, "a+"); if (!fp) errExit("fopen"); fprintf(fp, "%s", "\nenable-shm = no\n"); SET_PERMS_STREAM(fp, getuid(), getgid(), 0644); fclose(fp); // hand over the directory to the user if (set_perms(RUN_PULSE_DIR, getuid(), getgid(), 0700)) errExit("set_perms"); // create ~/.config/pulse directory if not present char *dir1; if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) errExit("asprintf"); if (lstat(dir1, &s) == -1) { pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop privileges drop_privs(0); int rv = mkdir(dir1, 0755); if (rv == 0) { if (set_perms(dir1, getuid(), getgid(), 0755)) {;} // do nothing } #ifdef HAVE_GCOV __gcov_flush(); #endif _exit(0); } // wait for the child to finish waitpid(child, NULL, 0); fs_logger2("create", dir1); } else { // we expect a user owned directory if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: user .config is a symbolic link\n"); else fprintf(stderr, "Error: user .config is not a directory owned by the current user\n"); exit(1); } } free(dir1); if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) errExit("asprintf"); if (lstat(dir1, &s) == -1) { pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // drop privileges drop_privs(0); int rv = mkdir(dir1, 0700); if (rv == 0) { if (set_perms(dir1, getuid(), getgid(), 0700)) {;} // do nothing } #ifdef HAVE_GCOV __gcov_flush(); #endif _exit(0); } // wait for the child to finish waitpid(child, NULL, 0); fs_logger2("create", dir1); } else { // we expect a user owned directory if (!S_ISDIR(s.st_mode) || s.st_uid != getuid()) { if (S_ISLNK(s.st_mode)) fprintf(stderr, "Error: user .config/pulse is a symbolic link\n"); else fprintf(stderr, "Error: user .config/pulse is not a directory owned by the current user\n"); exit(1); } } free(dir1); // if we have ~/.config/pulse mount the new directory, else set environment variable. char *homeusercfg; if (asprintf(&homeusercfg, "%s/.config/pulse", cfg.homedir) == -1) errExit("asprintf"); if (stat(homeusercfg, &s) == 0) { // get a file descriptor for ~/.config/pulse, fails if there is any symlink int fd = safe_fd(homeusercfg, O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); if (fd == -1) errExit("safe_fd"); // confirm the actual mount destination is owned by the user if (fstat(fd, &s) == -1 || s.st_uid != getuid()) errExit("fstat"); // mount via the link in /proc/self/fd char *proc; if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) errExit("asprintf"); if (mount(RUN_PULSE_DIR, proc, "none", MS_BIND, NULL) < 0) errExit("mount pulseaudio"); fs_logger2("tmpfs", homeusercfg); free(proc); close(fd); // check /proc/self/mountinfo to confirm the mount is ok MountData *mptr = get_last_mount(); if (strcmp(mptr->dir, homeusercfg) != 0 || strcmp(mptr->fstype, "tmpfs") != 0) errLogExit("invalid pulseaudio mount"); char *p; if (asprintf(&p, "%s/client.conf", homeusercfg) == -1) errExit("asprintf"); fs_logger2("create", p); free(p); } else { // set environment if (setenv("PULSE_CLIENTCONFIG", pulsecfg, 1) < 0) errExit("setenv"); } free(pulsecfg); free(homeusercfg); }