sfakeroot

manipulate files faking root privileges
git clone git://git.vx21.xyz/sfakeroot
Log | Files | Refs | README | LICENSE

sfakeroot.c (19801B)


      1 /* sfakeroot: manipulate files faking root privileges
      2  *
      3  * Copyright © 2020 Richard Ipsum
      4  *
      5  * This program is free software: you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation, version 3 of the License.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16  *
     17  */
     18 
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include <stdbool.h>
     22 #include <limits.h>
     23 #include <dirent.h>
     24 #include <unistd.h>
     25 #include <string.h>
     26 #include <errno.h>
     27 #include <arpa/inet.h>
     28 #include <sys/types.h>
     29 #include <sys/socket.h>
     30 #include <sys/wait.h>
     31 #include <sys/un.h>
     32 #include <signal.h>
     33 
     34 #include "sfakeroot.h"
     35 
     36 #include <sys/syscall.h>
     37 
     38 #define FALLBACK_SHELL "/bin/sh"
     39 #define SONAME "libsfakeroot.so"
     40 
     41 #ifdef __linux__
     42 #ifndef __x86_64__
     43 #error "Architectures other than amd64 are not supported on Linux"
     44 #endif
     45 /*./x86_64-linux-gnu/asm/unistd_64.h:#define __NR_newfstatat 262 */
     46 #define SYS_fstatat 262
     47 #define SESSION_FILE_LINE_FMT "%lu,%u,%u,%u\n"
     48 #else
     49 #define SESSION_FILE_LINE_FMT "%llu,%u,%u,%u\n"
     50 #endif
     51 
     52 static char *argv0;
     53 
     54 struct sfakeroot_list {
     55     struct sfakeroot_ent *first;
     56     struct sfakeroot_ent *last;
     57 };
     58 
     59 struct sfakeroot_list ents;
     60 
     61 struct cmdline_options {
     62     bool require_session_file;
     63     char *session_file;
     64 };
     65 
     66 struct cmdline_arguments {
     67     struct cmdline_options options;
     68     char **args;
     69     size_t args_len;
     70 };
     71 
     72 static void xsend(int sockfd, struct sfakeroot_msg *m)
     73 {
     74     if (sfakeroot_sendmsg(sockfd, m) == -1) {
     75         exit(1);
     76     }
     77 }
     78 
     79 static struct sfakeroot_ent *lookupent(struct sfakeroot_list *list, ino_t st_ino)
     80 {
     81     for (struct sfakeroot_ent *e = list->first; e != NULL; e = e->next) {
     82         debug("checking st_ino %llu\n", e->st.st_ino);
     83         if (e->st.st_ino == st_ino) {
     84             return e;
     85         }
     86     }
     87 
     88     return NULL;
     89 }
     90 
     91 static struct sfakeroot_ent *addent(struct sfakeroot_list *list,
     92                                     const struct stat *s, bool stale)
     93 {
     94     struct sfakeroot_ent *ent;
     95 
     96     ent = malloc(sizeof (*ent));
     97     if (ent == NULL) {
     98         return NULL;
     99     }
    100 
    101     debug("add ent: %llu\n", s->st_ino);
    102 
    103     ent->stale = stale;
    104     ent->next = NULL;
    105     ent->st = *s;
    106 
    107     if (list->first == NULL) {
    108         list->first = ent;
    109         goto out;
    110     }
    111 
    112     if (list->last != NULL) {
    113         list->last->next = ent;
    114     }
    115 
    116 out:
    117     list->last = ent;
    118     return ent;
    119 }
    120 
    121 static int real_stat_internal(int fd, const char *path, struct stat *sb,
    122                               int flag, const char *working_dir, int *errno_out,
    123                               int sysno)
    124 {
    125     int cwdfd, ret;
    126     DIR *cwd = opendir(".");
    127 
    128     if (cwd == NULL) {
    129         *errno_out = errno;
    130         return -1;
    131     }
    132 
    133     debug("chdir %s\n", working_dir);
    134     cwdfd = dirfd(cwd);
    135     if (chdir(working_dir) == -1) {
    136         *errno_out = errno;
    137         closedir(cwd);
    138         return -1;
    139     }
    140 
    141     debug("do syscall\n");
    142     switch (sysno) {
    143         case SYS_fstatat:
    144             ret = syscall(sysno, fd, path, sb, flag);
    145             break;
    146         case SYS_stat:
    147         case SYS_lstat:
    148             ret = syscall(sysno, path, sb);
    149             break;
    150         default:
    151             ret = -1;
    152             errno = EINVAL;
    153             break;
    154     }
    155 
    156     *errno_out = errno;
    157     fchdir(cwdfd);
    158     closedir(cwd);
    159     return ret;
    160 }
    161 
    162 static int real_stat(const char *path, struct stat *sb, bool islstat,
    163                      char *working_dir, int *errno_out)
    164 {
    165     int sysno = islstat ? SYS_lstat : SYS_stat;
    166     return real_stat_internal(-1, path, sb, 0, working_dir, errno_out, sysno);
    167 }
    168 
    169 static int real_fstatat(int fd, const char *path, struct stat *sb, int flag,
    170                         const char *working_dir, int *errno_out)
    171 {
    172     return real_stat_internal(fd, path, sb, flag, working_dir,
    173                               errno_out, SYS_fstatat);
    174 }
    175 
    176 static int real_fstat(int fd, struct stat *sb, int *errno_out)
    177 {
    178     int ret = syscall(SYS_fstat, fd, sb);
    179     *errno_out = errno;
    180     return ret;
    181 }
    182 
    183 static void handle_stat(struct sfakeroot_msg *m)
    184 {
    185     struct sfakeroot_ent *ent;
    186     int errno_sv = 0;
    187 
    188     debug("handle_fstat\n");
    189 
    190     switch (m->type) {
    191         case SFAKEROOT_MSG_FSTAT:
    192             m->retcode = real_fstat(m->fd, &m->st, &errno_sv);
    193             break;
    194         case SFAKEROOT_MSG_STAT:
    195         case SFAKEROOT_MSG_LSTAT:
    196             m->retcode = real_stat(m->path, &m->st,
    197                                    m->type == SFAKEROOT_MSG_LSTAT,
    198                                    m->working_dir, &errno_sv);
    199             break;
    200         case SFAKEROOT_MSG_FSTATAT:
    201             m->retcode = real_fstatat(m->fd, m->path, &m->st, m->flag,
    202                                       m->working_dir, &errno_sv);
    203             break;
    204         default:
    205             debug("non stat message in handle_stat\n");
    206             return;
    207     }
    208 
    209     if (m->retcode != 0) {
    210         m->reterrno = errno_sv;
    211         return;
    212     }
    213 
    214     if ((ent = lookupent(&ents, m->st.st_ino)) != NULL) {
    215         if (ent->stale) {
    216             /* update the entry with the new stat data */
    217             m->st.st_uid = ent->st.st_uid;
    218             m->st.st_gid = ent->st.st_gid;
    219             m->st.st_mode = ent->st.st_mode;
    220             ent->st = m->st;
    221             ent->stale = false;
    222         }
    223         else {
    224             m->st = ent->st;
    225         }
    226         if ((int) m->st.st_uid == -1) {
    227             m->st.st_uid = 0;
    228         }
    229         if ((int) m->st.st_gid == -1) {
    230             m->st.st_gid = 0;
    231         }
    232         return;
    233     }
    234 
    235     m->st.st_uid = 0;
    236     m->st.st_gid = 0;
    237 }
    238 
    239 static void handle_perms_change(struct sfakeroot_msg *m)
    240 {
    241     struct stat s;
    242     int errno_sv;
    243     struct sfakeroot_ent *ent;
    244 
    245     debug("handle_perms_change: m->path: %s, m->working_dir: %s\n",
    246           m->path, m->working_dir);
    247 
    248     switch (m->type) {
    249         case SFAKEROOT_MSG_LCHOWN:
    250             m->retcode = real_stat(m->path, &s, true, m->working_dir, &errno_sv);
    251             break;
    252         case SFAKEROOT_MSG_CHOWN:
    253         case SFAKEROOT_MSG_CHMOD:
    254             m->retcode = real_stat(m->path, &s, false, m->working_dir, &errno_sv);
    255             break;
    256         case SFAKEROOT_MSG_FCHOWNAT:
    257             m->retcode = real_fstatat(m->fd, m->path, &s, m->flag,
    258                                       m->working_dir, &errno_sv);
    259             break;
    260         case SFAKEROOT_MSG_FCHOWN:
    261             m->retcode = real_fstat(m->fd, &s, &errno_sv);
    262             break;
    263         default:
    264             debug("non chown/chmod message in handle_perms_change\n");
    265             return;
    266     }
    267 
    268     if (m->retcode == -1) {
    269         debug("stat returned error\n");
    270         m->reterrno = errno_sv;
    271         return;
    272     }
    273 
    274     /* path exists */
    275     switch (m->type) {
    276         case SFAKEROOT_MSG_LCHOWN:
    277         case SFAKEROOT_MSG_CHOWN:
    278         case SFAKEROOT_MSG_FCHOWNAT:
    279             s.st_uid = m->uid;
    280             s.st_gid = m->gid;
    281             break;
    282         case SFAKEROOT_MSG_CHMOD:
    283             s.st_mode = (s.st_mode & S_IFMT) | m->mode;
    284             break;
    285         default:
    286             debug("non chown/chmod message in handle_perms_change\n");
    287             return;
    288     }
    289 
    290     if ((ent = lookupent(&ents, s.st_ino)) != NULL) {
    291         uid_t u = ent->st.st_uid;
    292         gid_t g = ent->st.st_gid;
    293         ent->st = s;
    294         ent->st.st_uid = ((int) m->uid == -1) ? u : m->uid;
    295         ent->st.st_gid = ((int) m->gid == -1) ? g : m->gid;
    296         m->retcode = 0;
    297         return;
    298     }
    299 
    300     s.st_uid = m->uid;
    301     s.st_gid = m->gid;
    302 
    303     if (addent(&ents, &s, false) == NULL) {
    304         m->reterrno = ENOMEM;
    305         m->retcode = -1;
    306         return;
    307     }
    308 
    309     m->retcode = 0;
    310 }
    311 
    312 /* listen for new connections */
    313 static int sfakeroot_create_listener(const char *session_socket_path)
    314 {
    315     static struct sockaddr_un sa = {.sun_family = AF_UNIX};
    316     socklen_t sock_namelen;
    317     int sock;
    318 
    319     sock = socket(AF_UNIX, SOCK_STREAM, 0);
    320     if (sock == -1) {
    321         fprintf(stderr, "%s: socket: %s\n", argv0, strerror(errno));
    322         exit(1);
    323     }
    324 
    325     strlcpy(sa.sun_path, session_socket_path, sizeof (sa.sun_path));
    326     if (unlink(session_socket_path) == -1 && errno != ENOENT) {
    327         fprintf(stderr, "%s: unlink \"%s\" failed: %s\n",
    328                 argv0, sa.sun_path, strerror(errno));
    329         exit(1);
    330     }
    331 
    332     sock_namelen = strlen(sa.sun_path) + 1 + sizeof (sa.sun_family);
    333     if (bind(sock, (struct sockaddr *) &sa, sock_namelen) == -1) {
    334         fprintf(stderr, "%s: bind: %s\n", argv0, strerror(errno));
    335         exit(1);
    336     }
    337 
    338     if (listen(sock, 21) == -1) {
    339         fprintf(stderr, "%s: listen: %s\n", argv0, strerror(errno));
    340         exit(1);
    341     }
    342 
    343     return sock;
    344 }
    345 
    346 static int write_session_to_file(const char *save_path)
    347 {
    348     FILE *f = fopen(save_path, "w");
    349 
    350     debug("write session to `%s'\n", save_path);
    351 
    352     if (f == NULL) {
    353         goto error;
    354     }
    355 
    356     for (struct sfakeroot_ent *p = ents.first; p != NULL; p = p->next) {
    357         struct stat *s = &p->st;
    358         if (fprintf(f, SESSION_FILE_LINE_FMT, s->st_ino, s->st_uid, s->st_gid, s->st_mode) < 0) {
    359             goto error;
    360         }
    361         debug(SESSION_FILE_LINE_FMT, s->st_ino, s->st_uid, s->st_gid, s->st_mode);
    362     }
    363 
    364     if (fclose(f) != 0) {
    365         goto error;
    366     }
    367 
    368     return 0;
    369 
    370 error:
    371     fprintf(stderr, "%s: error saving session to `%s': %s\n",
    372             argv0, save_path, strerror(errno));
    373     return -1;
    374 }
    375 
    376 static int sfakeroot_finish_session(const struct cmdline_arguments *args)
    377 {
    378     if (args->options.session_file != NULL) {
    379         if (write_session_to_file(args->options.session_file) == -1) {
    380             return -1;
    381         }
    382     }
    383 
    384     return 0;
    385 }
    386 
    387 int estrtol(const char *s, long *out)
    388 {
    389     char *e;
    390 
    391     long n = strtol(s, &e, 10);
    392 
    393     if (*s == '\0' || *e != '\0') {
    394         fprintf(stderr, "%s: estrtol: `%s' not a number\n", argv0, s);
    395         return -1;
    396     }
    397 
    398     if ((errno == ERANGE && (n == LONG_MIN || n == LONG_MAX))) {
    399         fprintf(stderr, "%s: estrtol: `%s' out of range\n", argv0, s);
    400         return -1;
    401     }
    402 
    403     *out = n;
    404     return 0;
    405 }
    406 
    407 static int sfakeroot_load_session_from_file(const struct cmdline_arguments *args)
    408 {
    409     char *line = NULL, *session_filepath;
    410     size_t n = 0;
    411     ssize_t count;
    412     FILE *f;
    413 
    414     session_filepath = args->options.session_file;
    415     f = fopen(session_filepath, "r");
    416     if (f == NULL) {
    417         char *errfmt = "%s: couldn't open session file `%s': %s\n";
    418         debug(errfmt, argv0, session_filepath, strerror(errno));
    419         if (args->options.require_session_file) {
    420             fprintf(stderr, errfmt, argv0, session_filepath, strerror(errno));
    421             return -1;
    422         }
    423         return 0;
    424     }
    425 
    426     debug("loading session from `%s'\n", session_filepath);
    427 
    428     while ((count = getline(&line, &n, f)) != -1) {
    429         struct stat s = {0};
    430         long values[4];
    431         int i = 0;
    432         line[count - 1] = '\0'; /* strip '\n' */
    433         for (char *s = strtok(line, ","); s != NULL && i < 4; s = strtok(NULL, ","), i++) {
    434             if (estrtol(s, values + i) == -1) {
    435                 free(line);
    436                 return -1;
    437             }
    438         }
    439         s.st_ino = (ino_t) values[0];
    440         s.st_uid = (uid_t) values[1];
    441         s.st_gid = (gid_t) values[2];
    442         s.st_mode = (mode_t) values[3];
    443         addent(&ents, &s, true);
    444     }
    445 
    446     if (ferror(f)) {
    447         fprintf(stderr, "%s: error reading from `%s': %s\n",
    448                 argv0, session_filepath, strerror(errno));
    449         free(line);
    450         return -1;
    451     }
    452 
    453     free(line);
    454     return 0;
    455 }
    456 
    457 static void sfakeroot_server(int pipewfd, const char *session_socket_path,
    458                              const struct cmdline_arguments *args)
    459 {
    460     static struct sockaddr_un sa;
    461     int listen_sock, sock;
    462     socklen_t namelen;
    463 
    464     if (args->options.session_file != NULL) {
    465         if (sfakeroot_load_session_from_file(args) == -1) {
    466             exit(1);
    467         }
    468     }
    469 
    470     listen_sock = sfakeroot_create_listener(session_socket_path);
    471 
    472     /* close write end of pipe to indicate to parent process that we
    473      * are now ready to accept incoming connections
    474      */
    475     close(pipewfd);
    476 
    477     for (;;) {
    478         struct sfakeroot_msg m;
    479 
    480         sock = accept(listen_sock, (struct sockaddr *) &sa, &namelen);
    481         if (sock == -1) {
    482             fprintf(stderr, "%s: accept: %s\n", argv0, strerror(errno));
    483             exit(1);
    484         }
    485         if (sfakeroot_recvmsg(sock, &m) == -1) {
    486             fprintf(stderr, "eof?\n");
    487             continue;
    488         }
    489 
    490         switch (m.type) {
    491             case SFAKEROOT_MSG_FSTAT:
    492             case SFAKEROOT_MSG_LSTAT:
    493             case SFAKEROOT_MSG_STAT:
    494             case SFAKEROOT_MSG_FSTATAT:
    495                 handle_stat(&m);
    496                 break;
    497             case SFAKEROOT_MSG_CHOWN:
    498             case SFAKEROOT_MSG_LCHOWN:
    499             case SFAKEROOT_MSG_CHMOD:
    500             // TODO: fchmod
    501             // TODO: fchmodat
    502             case SFAKEROOT_MSG_FCHOWN:
    503             case SFAKEROOT_MSG_FCHOWNAT:
    504                 handle_perms_change(&m);
    505                 break;
    506             case SFAKEROOT_MSG_FINISH:
    507                 if (sfakeroot_finish_session(args) == -1) {
    508                     exit(1);
    509                 }
    510                 exit(0);
    511         }
    512         xsend(sock, &m);
    513         close(sock);
    514     }
    515 }
    516 
    517 static int sfakeroot_daemon(const char *session_socket_path,
    518                             const struct cmdline_arguments *args)
    519 {
    520     int pipefds[2];
    521     char buf[1];
    522     pid_t pid;
    523     int status;
    524 
    525     if (pipe(pipefds) == -1) {
    526         fprintf(stderr, "%s: pipe: %s\n", argv0, strerror(errno));
    527         return -1;
    528     }
    529 
    530     switch ((pid = fork())) {
    531         case 0:
    532             /* child */
    533             close(pipefds[0]); /* close read end */
    534             if (daemon(1, 1) == -1) {
    535                 fprintf(stderr, "%s: failed to daemonise: %s\n",
    536                         argv0, strerror(errno));
    537                 exit(1);
    538             }
    539             sfakeroot_server(pipefds[1], session_socket_path, args);
    540             break;
    541         case -1:
    542             fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno));
    543             return -1;
    544         default:
    545             /* parent */
    546             close(pipefds[1]); /* close write end */
    547             /* This read will block until the child process signals it
    548              * is ready, i.e. it's ready to accept connections from us.
    549              */
    550             if (read(pipefds[0], buf, sizeof (buf)) == -1) {
    551                 fprintf(stderr, "%s: error reading from pipe: %s\n",
    552                         argv0, strerror(errno));
    553                 return -1;
    554             }
    555             if (waitpid(pid, &status, 0) == -1) {
    556                 fprintf(stderr, "%s: waitpid: %s\n", argv0, strerror(errno));
    557                 return -1;
    558             }
    559             if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
    560                 fprintf(stderr, "%s: sfakeroot daemon unexpected condition\n",
    561                         argv0);
    562                 return -1;
    563             }
    564     }
    565 
    566     return 0;
    567 }
    568 
    569 int sfakeroot_session_open(void);
    570 
    571 static int setenvvars(const char *session_socket_path)
    572 {
    573     char *wd = SFAKEROOT_LIBDIR, *path = NULL;
    574     size_t len;
    575 
    576     len = snprintf(NULL, 0, "%s/%s", wd, SONAME);
    577     path = malloc(len + 1);
    578     if (path == NULL) {
    579         goto error;
    580     }
    581     snprintf(path, len + 1, "%s/%s", wd, SONAME);
    582 
    583     if (setenv("LD_PRELOAD", path, 1) == -1) {
    584         goto error;
    585     }
    586     free(path);
    587 
    588     if (setenv("SFAKEROOT_SOCKET_PATH", session_socket_path, 1) == -1) {
    589         goto error;
    590     }
    591 
    592     return 0;
    593 
    594 error:
    595     fprintf(stderr, "%s: setenv: %s\n", argv0, strerror(errno));
    596     return -1;
    597 }
    598 
    599 static void usage(void)
    600 {
    601     fprintf(stderr, "usage: %s [-Ff file]\n", argv0);
    602 }
    603 
    604 static void parse_options(int argc, char *argv[], struct cmdline_arguments *args)
    605 {
    606     int ch;
    607     extern int optind;
    608     extern char *optarg;
    609 
    610     while ((ch = getopt(argc, argv, "F:f:")) != -1) {
    611         switch (ch) {
    612             case 'F':
    613                 args->options.require_session_file = true;
    614                 /* fallthrough */
    615             case 'f':
    616                 args->options.session_file = optarg;
    617                 break;
    618             default:
    619                 usage();
    620                 exit(1);
    621         }
    622     }
    623 
    624     args->args = argv + optind;
    625     args->args_len = argc - optind;
    626 }
    627 
    628 int main(int argc, char *argv[])
    629 {
    630     int sockfd, status, exit_status, len;
    631     pid_t pid;
    632     struct sfakeroot_msg m;
    633     char *sargv[] = {FALLBACK_SHELL, NULL}, *shell, **exec_argvp;
    634     char tempdir_path[8192] = "/tmp/sfakeroot.XXXXXXXXXX";
    635     char session_socket_path[8192];
    636     static struct cmdline_arguments args;
    637 
    638     argv0 = argv[0];
    639     parse_options(argc, argv, &args);
    640 
    641     if ((shell = getenv("SHELL")) != NULL) {
    642         sargv[0] = shell;
    643     }
    644 
    645     if (mkdtemp(tempdir_path) == NULL) {
    646         fprintf(stderr, "%s: mkdtemp: %s\n", argv0, strerror(errno));
    647         exit_status = 1;
    648         goto cleanup;
    649     }
    650 
    651     len = snprintf(session_socket_path, sizeof (session_socket_path), "%s/%s",
    652                    tempdir_path, "uds.sock");
    653     if (len == -1) {
    654         fprintf(stderr, "%s: snprintf\n", argv0);
    655         exit_status = 1;
    656         goto cleanup;
    657     }
    658     else if ((size_t) len > sizeof (session_socket_path)) {
    659         fprintf(stderr, "%s: path too long\n", argv0);
    660         exit_status = 1;
    661         goto cleanup;
    662     }
    663 
    664     if (setenvvars(session_socket_path) == -1) {
    665         exit_status = 1;
    666         goto cleanup;
    667     }
    668 
    669     if (!sfakeroot_daemon_running()) {
    670         if (sfakeroot_daemon(session_socket_path, &args) == -1) {
    671             exit_status = 1;
    672             goto cleanup;
    673         }
    674     }
    675 
    676     exec_argvp = args.args_len > 0 ? args.args : sargv;
    677     switch (pid = fork()) {
    678         case 0:
    679             execvp(exec_argvp[0], exec_argvp);
    680             fprintf(stderr, "%s: exec `%s': %s\n",
    681                     argv0, exec_argvp[0], strerror(errno));
    682             switch (errno) {
    683                 case EACCES:
    684                     exit_status = 126;
    685                     goto cleanup;
    686                 case ENOENT:
    687                     exit_status = 127;
    688                     goto cleanup;
    689                 default:
    690                     exit_status = 1;
    691                     goto cleanup;
    692             }
    693         case -1:
    694             fprintf(stderr, "%s: fork: %s\n", argv0, strerror(errno));
    695             exit_status = 1;
    696             goto cleanup;
    697         default:
    698             waitpid(pid, &status, 0);
    699             debug("instructing daemon to finish...\n");
    700             m.type = SFAKEROOT_MSG_FINISH;
    701             sockfd = sfakeroot_session_open();
    702             if (sockfd == -1) {
    703                 fprintf(stderr, "%s: failed to open session\n", argv0);
    704                 exit_status = 1;
    705                 goto cleanup;
    706             }
    707             if (sfakeroot_sendmsg(sockfd, &m) == -1) {
    708                 exit_status = 1;
    709                 goto cleanup;
    710             }
    711             close(sockfd);
    712             if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
    713                 exit_status = WEXITSTATUS(status);
    714                 goto cleanup;
    715             }
    716             if (WIFSIGNALED(status)) {
    717                 exit_status = 127 + WTERMSIG(status);
    718                 goto cleanup;
    719             }
    720             else if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) {
    721                 exit_status = 1;
    722                 goto cleanup;
    723             }
    724             break;
    725     }
    726 
    727     exit_status = 0;
    728 
    729 cleanup:
    730     unlink(session_socket_path);
    731     rmdir(tempdir_path);
    732     return exit_status;
    733 }