diff options
Diffstat (limited to 'xlators/features/marker/utils')
-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 ec1d3a65a..cebca1aea 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 9cae4d407..6747acbce 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 |