diff options
| -rw-r--r-- | xlators/features/marker/utils/src/gsyncd.c | 205 | ||||
| -rw-r--r-- | xlators/features/marker/utils/syncdaemon/gsyncd.py | 15 | 
2 files changed, 182 insertions, 38 deletions
diff --git a/xlators/features/marker/utils/src/gsyncd.c b/xlators/features/marker/utils/src/gsyncd.c index ec1d3a65a98..cebca1aeaae 100644 --- a/xlators/features/marker/utils/src/gsyncd.c +++ b/xlators/features/marker/utils/src/gsyncd.c @@ -32,52 +32,67 @@  #include "run.h"  #define _GLUSTERD_CALLED_ "_GLUSTERD_CALLED_" +#define _GSYNCD_DISPATCHED_ "_GSYNCD_DISPATCHED_"  #define GSYNCD_CONF "geo-replication/gsyncd.conf" +#define RSYNC "rsync" + +int restricted = 0;  static int -config_wanted (int argc, char **argv) +duplexpand (void **buf, size_t tsiz, size_t *len)  { -        char *evas = NULL; -        char *oa = NULL; -        int i      = 0; -        int one_more_arg = 0; +        size_t osiz = tsiz * *len; -        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_); -                return 0; -        } +        *buf = realloc (*buf, osiz << 1); +        if (!buf) +                return -1; -        for (i = 1; i < argc; i++) { -                /* -c found, see if it has an argument */ -                if (one_more_arg) { -                        if (argv[i][0] != '-') -                                return 0; -                        one_more_arg = 0; -                } +        memset ((char *)*buf + osiz, 0, osiz); +        *len <<= 1; -                if ((strcmp (argv[i], "-c") && strcmp (argv[i], "--config-file")) == 0) { -                        one_more_arg = 1; -                        continue; -                } +        return 0; +} + +static int +str2argv (char *str, char ***argv) +{ +        char *p         = NULL; +        int argc        = 0; +        size_t argv_len = 32; +        int ret         = 0; + +        assert (str); +        str = strdup (str); +        if (!str) +                return -1; -                oa = strtail (argv[i], "-c"); -                if (oa && !*oa) -                        oa = NULL; -                if (!oa) -                        oa = strtail (argv[i], "--config-file="); -                if (oa) -                        return 0; +        *argv = calloc (argv_len, sizeof (**argv)); +        if (!*argv) +                goto error; + +        while ((p = strtok (str, " "))) { +                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 1; +        return argc; + + error: +        fprintf (stderr, "out of memory\n"); +        return -1;  } -int -main(int argc, char **argv) +static int +invoke_gsyncd (int argc, char **argv)  {          char config_file[PATH_MAX] = {0,};          size_t gluster_workdir_len = 0; @@ -86,7 +101,8 @@ main(int argc, char **argv)          int j                      = 0;          char *nargv[argc + 4]; -        if (config_wanted (argc, argv)) { +        if (restricted) { +                /* in restricted mode we forcibly use the system-wide config */                  runinit (&runner);                  runner_add_args (&runner, SBIN_DIR"/gluster",                                   "--log-file=/dev/stderr", "system::", "getwd", @@ -106,18 +122,21 @@ main(int argc, char **argv)                          config_file[gluster_workdir_len] = '/';                          strcat (config_file, GSYNCD_CONF);                  } else -                        config_file[0] = '\0'; +                        goto error; + +                if (setenv ("_GSYNCD_RESTRICTED_", "1", 1) == -1) +                        goto error;          }          j = 0;          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;          } -        for (i = 1; i < argc; i++) -                nargv[j++] = argv[i];          nargv[j++] = NULL;          execvp (PYTHON, nargv); @@ -129,3 +148,113 @@ main(int argc, char **argv)          fprintf (stderr, "gsyncd initializaion failed\n");          return 1;  } + +static int +invoke_rsync (int argc, char **argv) +{ +        int i = 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 || +              (i == argc - 2 && strcmp (argv[i], ".") == 0 && argv[i + 1][0] == '/'))) +                goto error; + +        /* XXX a proper check would involve the following: +         * - require rsync to not protect args (ie. pass target in command line) +         * - find out proper synchronization target by: +         *   - looking up sshd process we origin from +         *   - within its children, find the gsyncd process +         *     that maintains the aux mount +         *   - find out mount directory by checking the working directory +         *     of the gsyncd process +         * - demand that rsync target equals to sync target +         * +         * As of now, what we implement is dispatching rsync invocation to +         * our system rsync, that handles the cardinal issue of controlling +         * remote-requested command invocations. +         */ + +        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; + +        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; +} diff --git a/xlators/features/marker/utils/syncdaemon/gsyncd.py b/xlators/features/marker/utils/syncdaemon/gsyncd.py index 9cae4d407f4..6747acbce6f 100644 --- a/xlators/features/marker/utils/syncdaemon/gsyncd.py +++ b/xlators/features/marker/utils/syncdaemon/gsyncd.py @@ -190,6 +190,8 @@ def main_i():      op.add_option('--canonicalize-escape-url', dest='url_print', action='callback', callback=store_local_curry('canon_esc'))      tunables = [ norm(o.get_opt_string()[2:]) for o in op.option_list if o.callback in (store_abs, 'store_true', None) and o.get_opt_string() not in ('--version', '--help') ] +    remote_tunables = [ 'listen', 'go_daemon', 'timeout', 'session_owner', 'config_file' ] +    rq_remote_tunables = { 'listen': True }      # precedence for sources of values: 1) commandline, 2) cfg file, 3) defaults      # -- for this to work out we need to tell apart defaults from explicitly set @@ -206,6 +208,19 @@ def main_i():          sys.stderr.write(op.get_usage() + "\n")          sys.exit(1) +    if os.getenv('_GSYNCD_RESTRICTED_'): +        allopts = {} +        allopts.update(opts.__dict__) +        allopts.update(rconf) +        bannedtuns = set(allopts.keys()) - set(remote_tunables) +        if bannedtuns: +            raise GsyncdError('following tunables cannot be set with restricted SSH invocaton: ' + \ +                              ', '.join(bannedtuns)) +        for k, v in rq_remote_tunables.items(): +            if not k in allopts or allopts[k] != v: +                raise GsyncdError('tunable %s is not set to value %s required for restricted SSH invocaton' % \ +                                  (k, v)) +      if getattr(confdata, 'rx', None):          # peers are regexen, don't try to parse them          canon_peers = args  | 
