glusterfs
407 строк · 9.4 Кб
1/*
2Copyright (c) 2011-2012 Red Hat, Inc. <http://www.redhat.com>
3This file is part of GlusterFS.
4
5This file is licensed to you under your choice of the GNU Lesser
6General Public License, version 3 or any later version (LGPLv3 or
7later), or the GNU General Public License, version 2 (GPLv2), in all
8cases as published by the Free Software Foundation.
9*/
10#include <glusterfs/compat.h>11#include <glusterfs/syscall.h>12
13#include <stdio.h>14#include <stdlib.h>15#include <string.h>16#include <sys/param.h> /* for PATH_MAX */17#include <unistd.h>18
19/* NOTE (USE_LIBGLUSTERFS):
20* ------------------------
21* When USE_LIBGLUSTERFS debugging sumbol is passed; perform
22* glusterfs translator like initialization so that glusterfs
23* globals, contexts are valid when glustefs api's are invoked.
24* We unconditionally pass then while building gsyncd binary.
25*/
26#ifdef USE_LIBGLUSTERFS27#include <glusterfs/defaults.h>28#include <glusterfs/globals.h>29#endif30
31#include "procdiggy.h"32#include <glusterfs/run.h>33
34#define _GLUSTERD_CALLED_ "_GLUSTERD_CALLED_"35#define _GSYNCD_DISPATCHED_ "_GSYNCD_DISPATCHED_"36#define GSYNCD_CONF_TEMPLATE "geo-replication/gsyncd_template.conf"37#define GSYNCD_PY "gsyncd.py"38#define RSYNC "rsync"39
40int restricted = 0;41
42static int43duplexpand(void **buf, size_t tsiz, size_t *len)44{
45size_t osiz = tsiz * *len;46char *p = realloc(*buf, osiz << 1);47if (!p) {48return -1;49}50
51memset(p + osiz, 0, osiz);52*buf = p;53*len <<= 1;54
55return 0;56}
57
58static int59str2argv(char *str, char ***argv)60{
61char *p = NULL;62char *savetok = NULL;63char *temp = NULL;64char *temp1 = NULL;65int argc = 0;66size_t argv_len = 32;67int ret = 0;68int i = 0;69
70assert(str);71temp = str = strdup(str);72if (!str)73goto error;74
75*argv = calloc(argv_len, sizeof(**argv));76if (!*argv)77goto error;78
79while ((p = strtok_r(str, " ", &savetok))) {80str = NULL;81
82argc++;83if (argc == argv_len) {84ret = duplexpand((void *)argv, sizeof(**argv), &argv_len);85if (ret == -1)86goto error;87}88temp1 = strdup(p);89if (!temp1)90goto error;91(*argv)[argc - 1] = temp1;92}93
94free(temp);95return argc;96
97error:98fprintf(stderr, "out of memory\n");99free(temp);100for (i = 0; i < argc - 1; i++)101free((*argv)[i]);102free(*argv);103return -1;104}
105
106static int107invoke_gsyncd(int argc, char **argv)108{
109int i = 0;110int j = 0;111char *nargv[argc + 4];112char *python = NULL;113
114if (chdir("/") == -1)115goto error;116
117j = 0;118python = getenv("PYTHON");119if (!python)120python = PYTHON;121nargv[j++] = python;122nargv[j++] = GSYNCD_PREFIX "/python/syncdaemon/" GSYNCD_PY;123for (i = 1; i < argc; i++)124nargv[j++] = argv[i];125
126nargv[j++] = NULL;127
128execvp(python, nargv);129
130fprintf(stderr, "exec of '%s' failed\n", python);131return 127;132
133error:134fprintf(stderr, "gsyncd initializaion failed\n");135return 1;136}
137
138static int139find_gsyncd(pid_t pid, pid_t ppid, char *name, void *data)140{
141char buf[NAME_MAX * 2] = {1420,143};144char path[PATH_MAX] = {1450,146};147char *p = NULL;148int zeros = 0;149int ret = 0;150int fd = -1;151pid_t *pida = (pid_t *)data;152
153if (ppid != pida[0])154return 0;155
156snprintf(path, sizeof path, PROC "/%d/cmdline", pid);157fd = open(path, O_RDONLY);158if (fd == -1)159return 0;160ret = sys_read(fd, buf, sizeof(buf));161sys_close(fd);162if (ret == -1)163return 0;164for (zeros = 0, p = buf; zeros < 2 && p < buf + ret; p++)165zeros += !*p;166
167ret = 0;168switch (zeros) {169case 2:170if ((strcmp(basename(buf), basename(PYTHON)) ||171strcmp(basename(buf + strlen(buf) + 1), GSYNCD_PY)) == 0) {172ret = 1;173break;174}175/* fallthrough */176case 1:177if (strcmp(basename(buf), GSYNCD_PY) == 0)178ret = 1;179}180
181if (ret == 1) {182if (pida[1] != -1) {183fprintf(stderr, GSYNCD_PY " sibling is not unique");184return -1;185}186pida[1] = pid;187}188
189return 0;190}
191
192static int193invoke_rsync(int argc, char **argv)194{
195int i = 0;196char path[PATH_MAX] = {1970,198};199pid_t pid = -1;200pid_t ppid = -1;201pid_t pida[] = {-1, -1};202char *name = NULL;203char buf[PATH_MAX + 1] = {2040,205};206int ret = 0;207
208assert(argv[argc] == NULL);209
210if (argc < 2 || strcmp(argv[1], "--server") != 0)211goto error;212
213for (i = 2; i < argc && argv[i][0] == '-'; i++)214;215
216if (!(i == argc - 2 && strcmp(argv[i], ".") == 0 &&217argv[i + 1][0] == '/')) {218fprintf(stderr, "need an rsync invocation without protected args\n");219goto error;220}221
222/* look up sshd we are spawned from */223for (pid = getpid();; pid = ppid) {224ppid = pidinfo(pid, &name);225if (ppid < 0) {226fprintf(stderr, "sshd ancestor not found\n");227goto error;228}229if (strcmp(name, "sshd") == 0) {230GF_FREE(name);231break;232}233GF_FREE(name);234}235/* look up "ssh-sibling" gsyncd */236pida[0] = pid;237ret = prociter(find_gsyncd, pida);238if (ret == -1 || pida[1] == -1) {239fprintf(stderr, "gsyncd sibling not found\n");240goto error;241}242/* check if rsync target matches gsyncd target */243snprintf(path, sizeof path, PROC "/%d/cwd", pida[1]);244ret = sys_readlink(path, buf, sizeof(buf));245if (ret == -1 || ret == sizeof(buf))246goto error;247
248buf[ret] = '\0';249
250if (strcmp(argv[argc - 1], "/") == 0 /* root dir cannot be a target */ ||251(strcmp(argv[argc - 1], path) /* match against gluster target */ &&252strcmp(argv[argc - 1], buf) /* match against file target */) != 0) {253fprintf(stderr, "rsync target does not match " GEOREP " session\n");254goto error;255}256
257argv[0] = RSYNC;258
259execvp(RSYNC, argv);260
261fprintf(stderr, "exec of " RSYNC " failed\n");262return 127;263
264error:265fprintf(stderr, "disallowed " RSYNC " invocation\n");266return 1;267}
268
269static int270invoke_gluster(int argc, char **argv)271{
272int i = 0;273int j = 0;274int optsover = 0;275char *ov = NULL;276
277for (i = 1; i < argc; i++) {278ov = strtail(argv[i], "--");279if (ov && !optsover) {280if (*ov == '\0')281optsover = 1;282continue;283}284switch (++j) {285case 1:286if (strcmp(argv[i], "volume") != 0)287goto error;288break;289case 2:290if (strcmp(argv[i], "info") != 0)291goto error;292break;293case 3:294break;295default:296goto error;297}298}299
300argv[0] = SBIN_DIR "/gluster";301execv(SBIN_DIR "/gluster", argv);302fprintf(stderr, "exec of gluster failed\n");303return 127;304
305error:306fprintf(stderr, "disallowed gluster invocation\n");307return 1;308}
309
310struct invocable {311char *name;312int (*invoker)(int argc, char **argv);313};314
315struct invocable invocables[] = {{"rsync", invoke_rsync},316{"gsyncd", invoke_gsyncd},317{"gluster", invoke_gluster},318{NULL, NULL}};319
320int
321main(int argc, char **argv)322{
323int ret = -1;324char *evas = NULL;325struct invocable *i = NULL;326char *b = NULL;327char *sargv = NULL;328int j = 0;329
330#ifdef USE_LIBGLUSTERFS331glusterfs_ctx_t *ctx = NULL;332
333ctx = glusterfs_ctx_new();334if (!ctx)335return ENOMEM;336
337if (glusterfs_globals_init(ctx))338return 1;339
340THIS->ctx = ctx;341ret = default_mem_acct_init(THIS);342if (ret) {343fprintf(stderr, "internal error: mem accounting failed\n");344return 1;345}346#endif347
348evas = getenv(_GLUSTERD_CALLED_);349if (evas && strcmp(evas, "1") == 0)350/* OK, we know glusterd called us, no need to look for further config351*...although this conclusion should not inherit to our children
352*/
353unsetenv(_GLUSTERD_CALLED_);354else {355/* we regard all gsyncd invocations unsafe356* that do not come from glusterd and
357* therefore restrict it
358*/
359restricted = 1;360
361if (!getenv(_GSYNCD_DISPATCHED_)) {362evas = getenv("SSH_ORIGINAL_COMMAND");363if (evas)364sargv = evas;365else {366evas = getenv("SHELL");367if (evas && strcmp(basename(evas), "gsyncd") == 0 &&368argc == 3 && strcmp(argv[1], "-c") == 0)369sargv = argv[2];370}371}372}373
374if (!(sargv && restricted))375return invoke_gsyncd(argc, argv);376
377argc = str2argv(sargv, &argv);378
379if (argc == -1) {380fprintf(stderr, "internal error\n");381return 1;382}383
384if (setenv(_GSYNCD_DISPATCHED_, "1", 1) == -1) {385fprintf(stderr, "internal error\n");386goto out;387}388
389if ( argc >= 1 )390b = basename(argv[0]);391else392goto out;393
394for (i = invocables; i->name; i++) {395if (strcmp(b, i->name) == 0)396return i->invoker(argc, argv);397}398
399fprintf(stderr, "invoking %s in restricted SSH session is not allowed\n",400b);401
402out:403for (j = 1; j < argc; j++)404free(argv[j]);405free(argv);406return 1;407}
408