diff options
Diffstat (limited to 'geo-replication/src/gsyncd.c')
| -rw-r--r-- | geo-replication/src/gsyncd.c | 367 | 
1 files changed, 367 insertions, 0 deletions
diff --git a/geo-replication/src/gsyncd.c b/geo-replication/src/gsyncd.c new file mode 100644 index 00000000000..9c4a5bdffb3 --- /dev/null +++ b/geo-replication/src/gsyncd.c @@ -0,0 +1,367 @@ +/* +   Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com> +   This file is part of GlusterFS. + +   This file is licensed to you under your choice of the GNU Lesser +   General Public License, version 3 or any later version (LGPLv3 or +   later), or the GNU General Public License, version 2 (GPLv2), in all +   cases as published by the Free Software Foundation. +*/ + +#ifndef _CONFIG_H +#define _CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <sys/param.h> /* for PATH_MAX */ + +/* NOTE (USE_LIBGLUSTERFS): + * ------------------------ + * When USE_LIBGLUSTERFS debugging sumbol is passed; perform + * glusterfs translator like initialization so that glusterfs + * globals, contexts are valid when glustefs api's are invoked. + * We unconditionally pass then while building gsyncd binary. + */ +#ifdef USE_LIBGLUSTERFS +#include "glusterfs.h" +#include "globals.h" +#endif + +#include "common-utils.h" +#include "run.h" +#include "procdiggy.h" + +#define _GLUSTERD_CALLED_ "_GLUSTERD_CALLED_" +#define _GSYNCD_DISPATCHED_ "_GSYNCD_DISPATCHED_" +#define GSYNCD_CONF "geo-replication/gsyncd.conf" +#define GSYNCD_PY "gsyncd.py" +#define RSYNC "rsync" + +int restricted = 0; + +static int +duplexpand (void **buf, size_t tsiz, size_t *len) +{ +        size_t osiz = tsiz * *len; +        char *p = realloc (*buf, osiz << 1); +        if (!p) { +                free(*buf); +                return -1; +        } + +        memset (p + osiz, 0, osiz); +        *buf = p; +        *len <<= 1; + +        return 0; +} + +static int +str2argv (char *str, char ***argv) +{ +        char *p         = NULL; +        char *savetok   = NULL; +        int argc        = 0; +        size_t argv_len = 32; +        int ret         = 0; + +        assert (str); +        str = strdup (str); +        if (!str) +                return -1; + +        *argv = calloc (argv_len, sizeof (**argv)); +        if (!*argv) +                goto error; + +        while ((p = strtok_r (str, " ", &savetok))) { +                str = NULL; + +                argc++; +                if (argc == argv_len) { +                        ret = duplexpand ((void *)argv, +                                          sizeof (**argv), +                                          &argv_len); +                        if (ret == -1) +                                goto error; +                } +                (*argv)[argc - 1] = p; +        } + +        return argc; + + error: +        fprintf (stderr, "out of memory\n"); +        return -1; +} + +static int +invoke_gsyncd (int argc, char **argv) +{ +        char config_file[PATH_MAX] = {0,}; +        size_t gluster_workdir_len = 0; +        runner_t runner            = {0,}; +        int i                      = 0; +        int j                      = 0; +        char *nargv[argc + 4]; +        char *python = NULL; + +        if (restricted) { +                size_t len; +                /* in restricted mode we forcibly use the system-wide config */ +                runinit (&runner); +                runner_add_args (&runner, SBIN_DIR"/gluster", +                                 "--log-file=-", "system::", "getwd", +                                 NULL); +                runner_redir (&runner, STDOUT_FILENO, RUN_PIPE); +                if (runner_start (&runner) == 0 && +                    fgets (config_file, PATH_MAX, +                           runner_chio (&runner, STDOUT_FILENO)) != NULL && +                    (len = strlen (config_file)) && +                    config_file[len - 1] == '\n' && +                    runner_end (&runner) == 0) +                        gluster_workdir_len = len - 1; + +                if (gluster_workdir_len) { +                        if (gluster_workdir_len + 1 + strlen (GSYNCD_CONF) + 1 > +                            PATH_MAX) +                                goto error; +                        config_file[gluster_workdir_len] = '/'; +                        strcat (config_file, GSYNCD_CONF); +                } else +                        goto error; + +                if (setenv ("_GSYNCD_RESTRICTED_", "1", 1) == -1) +                        goto error; +        } + +        if (chdir ("/") == -1) +                goto error; + +        j = 0; +        python = getenv("PYTHON"); +        if(!python) +                python = PYTHON; +        nargv[j++] = python; +        nargv[j++] = GSYNCD_PREFIX"/python/syncdaemon/"GSYNCD_PY; +        for (i = 1; i < argc; i++) +                nargv[j++] = argv[i]; +        if (config_file[0]) { +                nargv[j++] = "-c"; +                nargv[j++] = config_file; +        } +        nargv[j++] = NULL; + +        execvp (python, nargv); + +        fprintf (stderr, "exec of '%s' failed\n", python); +        return 127; + + error: +        fprintf (stderr, "gsyncd initializaion failed\n"); +        return 1; +} + + +static int +find_gsyncd (pid_t pid, pid_t ppid, char *name, void *data) +{ +        char buf[NAME_MAX * 2] = {0,}; +        char path[PATH_MAX]    = {0,}; +        char *p                = NULL; +        int zeros              = 0; +        int ret                = 0; +        int fd                 = -1; +        pid_t *pida            = (pid_t *)data; + +        if (ppid != pida[0]) +                return 0; + +        sprintf (path, PROC"/%d/cmdline", pid); +        fd = open (path, O_RDONLY); +        if (fd == -1) +                return 0; +        ret = read (fd, buf, sizeof (buf)); +        close (fd); +        if (ret == -1) +                return 0; +        for (zeros = 0, p = buf; zeros < 2 && p < buf + ret; p++) +                zeros += !*p; + +        ret = 0; +        switch (zeros) { +        case 2: +                if ((strcmp (basename (buf), basename (PYTHON)) || +                     strcmp (basename (buf + strlen (buf) + 1), GSYNCD_PY)) == 0) { +                        ret = 1; +                        break; +                } +                /* fallthrough */ +        case 1: +                if (strcmp (basename (buf), GSYNCD_PY) == 0) +                        ret = 1; +        } + +        if (ret == 1) { +                if (pida[1] != -1) { +                        fprintf (stderr, GSYNCD_PY" sibling is not unique"); +                        return -1; +                } +                pida[1] = pid; +        } + +        return 0; +} + +static int +invoke_rsync (int argc, char **argv) +{ +        int i                  = 0; +        char path[PATH_MAX]    = {0,}; +        pid_t pid              = -1; +        pid_t ppid             = -1; +        pid_t pida[]           = {-1, -1}; +        char *name             = NULL; +        char buf[PATH_MAX + 1] = {0,}; +        int ret                = 0; + +        assert (argv[argc] == NULL); + +        if (argc < 2 || strcmp (argv[1], "--server") != 0) +                goto error; + +        for (i = 2; i < argc && argv[i][0] == '-'; i++); + +        if (!(i == argc - 2 && strcmp (argv[i], ".") == 0 && argv[i + 1][0] == '/')) { +                fprintf (stderr, "need an rsync invocation without protected args\n"); +                goto error; +        } + +        /* look up sshd we are spawned from */ +        for (pid = getpid () ;; pid = ppid) { +                ppid = pidinfo (pid, &name); +                if (ppid < 0) { +                        fprintf (stderr, "sshd ancestor not found\n"); +                        goto error; +                } +                if (strcmp (name, "sshd") == 0) { +                        GF_FREE (name); +                        break; +                } +                GF_FREE (name); +        } +        /* look up "ssh-sibling" gsyncd */ +        pida[0] = pid; +        ret = prociter (find_gsyncd, pida); +        if (ret == -1 || pida[1] == -1) { +                fprintf (stderr, "gsyncd sibling not found\n"); +                goto error; +        } +        /* check if rsync target matches gsyncd target */ +        sprintf (path, PROC"/%d/cwd", pida[1]); +        ret = readlink (path, buf, sizeof (buf)); +        if (ret == -1 || ret == sizeof (buf)) +                goto error; +        if (strcmp (argv[argc - 1], "/") == 0 /* root dir cannot be a target */ || +            (strcmp (argv[argc - 1], path) /* match against gluster target */ && +             strcmp (argv[argc - 1], buf) /* match against file target */) != 0) { +                fprintf (stderr, "rsync target does not match "GEOREP" session\n"); +                goto error; +        } + +        argv[0] = RSYNC; + +        execvp (RSYNC, argv); + +        fprintf (stderr, "exec of "RSYNC" failed\n"); +        return 127; + + error: +        fprintf (stderr, "disallowed "RSYNC" invocation\n"); +        return 1; +} + + +struct invocable { +        char *name; +        int (*invoker) (int argc, char **argv); +}; + +struct invocable invocables[] = { +        { "rsync",  invoke_rsync  }, +        { "gsyncd", invoke_gsyncd }, +        { NULL, NULL} +}; + +int +main (int argc, char **argv) +{ +        char *evas          = NULL; +        struct invocable *i = NULL; +        char *b             = NULL; +        char *sargv         = NULL; + +#ifdef USE_LIBGLUSTERFS +        glusterfs_ctx_t *ctx = NULL; + +        ctx = glusterfs_ctx_new (); +        if (!ctx) +                return ENOMEM; + +        if (glusterfs_globals_init (ctx)) +                return 1; + +        THIS->ctx = ctx; +#endif + +        evas = getenv (_GLUSTERD_CALLED_); +        if (evas && strcmp (evas, "1") == 0) +                /* OK, we know glusterd called us, no need to look for further config +                 * ... altough this conclusion should not inherit to our children +                 */ +                unsetenv (_GLUSTERD_CALLED_); +        else { +                /* we regard all gsyncd invocations unsafe +                 * that do not come from glusterd and +                 * therefore restrict it +                 */ +                restricted = 1; + +                if (!getenv (_GSYNCD_DISPATCHED_)) { +                        evas = getenv ("SSH_ORIGINAL_COMMAND"); +                        if (evas) +                                sargv = evas; +                        else { +                                evas = getenv ("SHELL"); +                                if (evas && strcmp (basename (evas), "gsyncd") == 0 && +                                    argc == 3 && strcmp (argv[1], "-c") == 0) +                                        sargv = argv[2]; +                        } +                } + +        } + +        if (!(sargv && restricted)) +                return invoke_gsyncd (argc, argv); + +        argc = str2argv (sargv, &argv); +        if (argc == -1 || setenv (_GSYNCD_DISPATCHED_, "1", 1) == -1) { +                fprintf (stderr, "internal error\n"); +                return 1; +        } + +        b = basename (argv[0]); +        for (i = invocables; i->name; i++) { +                if (strcmp (b, i->name) == 0) +                        return i->invoker (argc, argv); +        } + +        fprintf (stderr, "invoking %s in restricted SSH session is not allowed\n", +                 b); + +        return 1; +}  | 
