]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add 'allowed scripts directory' directory for the FD
authorMichal Rakowski <michal.rakowski@baculasystems.com>
Sat, 16 Oct 2021 11:09:24 +0000 (13:09 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 14 Sep 2023 11:57:02 +0000 (13:57 +0200)
bacula/src/filed/fd_plugins.c
bacula/src/filed/fd_snapshot.c
bacula/src/filed/job.c
bacula/src/filed/protos.h
bacula/src/jcr.h
bacula/src/lib/bpipe.c

index 54c859ac5229a0ffe4130156635b879f761e4a05..88e13f411152c409a6a87648370dcfbf41dab546 100644 (file)
@@ -2310,7 +2310,10 @@ static bRC baculaAddExclude(bpContext *ctx, const char *file)
    /* Set the Exclude context */
    set_incexe(jcr, bctx->exclude);
 
-   add_file_to_fileset(jcr, file, true);
+   if (add_file_to_fileset(jcr, file, true) != state_include) {
+      Dmsg1(100, "Failed to add exclude file=%s\n", file);
+      return bRC_Error;
+   }
 
    /* Restore the current context */
    set_incexe(jcr, old);
@@ -2350,7 +2353,10 @@ static bRC baculaAddInclude(bpContext *ctx, const char *file)
    }
 
    set_incexe(jcr, bctx->include);
-   add_file_to_fileset(jcr, file, true);
+   if (add_file_to_fileset(jcr, file, true) != state_include) {
+      Dmsg1(100, "Failed to add include file=%s\n", file);
+      return bRC_Error;
+   }
 
    /* Restore the current context */
    set_incexe(jcr, old);
@@ -2414,7 +2420,10 @@ static bRC baculaAddPlugin(bpContext *ctx, const char *file)
    }
 
    set_incexe(jcr, bctx->include);
-   add_file_to_fileset(jcr, file, false);
+   if (add_file_to_fileset(jcr, file, false) != state_include) {
+      Dmsg1(100, "Failed to plugin=%s\n", file);
+      return bRC_Error;
+   }
 
    /* Restore the current context */
    set_incexe(jcr, old);
index 980b1c1b2b05a954bf54302bd48d8664a7dfbb4d..1e4b4083863bebcba5405603b8ecaae145669052 100644 (file)
@@ -1495,8 +1495,11 @@ bool snapshot_manager::create_snapshots()
                set_incexe(jcr, exclude);
             }
             Mmsg(t, "%s", elt->snap->SnapDirectory);
+            if (add_file_to_fileset(jcr, t.c_str(), true) != state_include) {
+               Jmsg(jcr, M_ERROR, 0, "   Failed to exclude=%s\n", t.c_str());
+               return false;
+            }
             Dmsg1(DT_SNAPSHOT|10, "Excluding %s\n", t.c_str());
-            add_file_to_fileset(jcr, t.c_str(), true);
          }
       }
    }
index 0fda15effbc2c08db4b7e3abb21781824a0695e9..c91caba82b250cf5970e9758bb787ee75946c809 100644 (file)
@@ -403,6 +403,12 @@ static void *handle_director_request(BSOCK *dir)
                 quit = true;
                 break;
             }
+
+            /* Set per-director list of allowed directories for scripts/program being run */
+            if (jcr->director && jcr->director->allowed_script_dirs) {
+               jcr->allowed_script_dirs = jcr->director->allowed_script_dirs;
+            }
+
             Dmsg1(100, "Executing Dir %s command.\n", dir->msg);
             if (!cmds[i].func(jcr)) {         /* do command */
                quit = true;         /* error or fully terminated, get out */
@@ -1455,7 +1461,7 @@ static void append_file(JCR *jcr, findINCEXE *incexe,
  * Add fname to include/exclude fileset list. First check for
  * | and < and if necessary perform command.
  */
-void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
+int add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
 {
    findFILESET *fileset = jcr->ff->fileset;
    char *p;
@@ -1480,7 +1486,7 @@ void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
          Jmsg(jcr, M_FATAL, 0, _("Cannot run program: %s. ERR=%s\n"),
             p, be.bstrerror());
          free_pool_memory(fn);
-         return;
+         return state_error;
       }
       free_pool_memory(fn);
       while (fgets(buf, sizeof(buf), bpipe->rfd)) {
@@ -1493,7 +1499,7 @@ void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
          berrno be;
          Jmsg(jcr, M_FATAL, 0, _("Error running program: %s. stat=%d: ERR=%s\n"),
             p, be.code(stat), be.bstrerror(stat));
-         return;
+         return state_error;
       }
       break;
    case '<':
@@ -1504,7 +1510,7 @@ void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
          Jmsg(jcr, M_FATAL, 0,
               _("Cannot open FileSet input file: %s. ERR=%s\n"),
             p, be.bstrerror());
-         return;
+         return state_error;
       }
       while (fgets(buf, sizeof(buf), ffd)) {
          strip_trailing_junk(buf);
@@ -1516,6 +1522,9 @@ void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file)
       append_file(jcr, fileset->incexe, fname, is_file);
       break;
    }
+
+   //TODO check all calls for add_file_to_fileset() to check for ret code now
+   return state_include;
 }
 
 findINCEXE *get_incexe(JCR *jcr)
@@ -1755,13 +1764,11 @@ static void add_fileset(JCR *jcr, const char *item)
       break;
    case 'F':                             /* file = */
       /* File item to include or exclude list */
-      state = state_include;
-      add_file_to_fileset(jcr, item, true);
+      state = add_file_to_fileset(jcr, item, true);
       break;
    case 'P':                              /* plugin */
       /* Plugin item to include list */
-      state = state_include;
-      add_file_to_fileset(jcr, item, false);
+      state = add_file_to_fileset(jcr, item, false);
       break;
    case 'R':                              /* regex */
       state = add_regex_to_fileset(jcr, item, subcode);
index 3848933219b983dbce05665f042acec869d40ad9..b9c643ba4ed1dbb9e7adf7bb7db2eb42aac1ba7c 100644 (file)
@@ -99,7 +99,7 @@ findINCEXE *new_preinclude(JCR *jcr);
 findINCEXE *get_incexe(JCR *jcr);
 void set_incexe(JCR *jcr, findINCEXE *incexe);
 void new_options(JCR *jcr, findINCEXE *incexe);
-void add_file_to_fileset(JCR *jcr, const char *fname, bool is_file);
+int add_file_to_fileset(JCR *jcr, const char *fname, bool is_file);
 int add_options_to_fileset(JCR *jcr, const char *item);
 int add_wild_to_fileset(JCR *jcr, const char *item, int type);
 int add_regex_to_fileset(JCR *jcr, const char *item, int type);
index b40ad7be8a0b4b70456936532b659a99b53e5f81..53d50569ca518eb59268f19434ba1014053188a1 100644 (file)
@@ -274,6 +274,8 @@ public:
    pthread_mutex_t msg_queue_mutex;   /* message queue mutex */
    bool dequeuing_msgs;               /* Set when dequeuing messages */
    alist job_end_push;                /* Job end pushed calls */
+   alist *allowed_script_dirs;        /* Daemon-specific Allowed directory list to run
+                                         scripts/programs from */
    POOLMEM *VolumeName;               /* Volume name desired -- pool_memory */
    POOLMEM *errmsg;                   /* edited error message */
    char Job[MAX_NAME_LENGTH];         /* Unique name of this Job */
index e1833b32e8a2d8685507116b020936f0243d70f3..80de02714d3f6414ca7e8b61dfaac4ce30fba577 100644 (file)
@@ -70,6 +70,24 @@ void build_sh_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg)
    *bargc = 3;
 }
 
+int forbidden_chars[] = {
+   '$', '!', ';', '\\', '&', '|', '<', '>', '`', '(', ')'
+};
+
+static bool check_for_forbidden_chars(const char *str)
+{
+   bool ret = true;
+   int size = sizeof(forbidden_chars) / sizeof(int);
+   for (int i=0; i<size; i++) {
+      if (strchr(str, forbidden_chars[i]) != NULL) {
+         ret = false;
+         break;
+      }
+   }
+
+   return ret;
+}
+
 /*
  * Run an external program. Optionally wait a specified number
  *   of seconds. Program killed if wait exceeded. We open
@@ -84,12 +102,9 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode, char *envp[])
    POOLMEM *tprog;
    int mode_map = 0;
    BPIPE *bpipe;
+   JCR *jcr;
    int save_errno;
-
-#if !defined(HAVE_FCNTL_F_CLOSEM) && !defined(HAVE_CLOSEFROM)
-   struct rlimit rl;
-   int64_t rlimitResult=0;
-#endif
+   bool allowed = false;
 
    if (!prog || !*prog) {
       /* execve(3) A component of the file does not name an existing file or file is an empty string. */
@@ -97,6 +112,43 @@ BPIPE *open_bpipe(char *prog, int wait, const char *mode, char *envp[])
       return NULL;
    }
 
+   /* Get JCR from the thread context.
+    * It's needed for per-daemon specific data, e.g. list of allowed scripts/programs directories. */
+   jcr = get_jcr_from_tsd();
+   if (!jcr) {
+      allowed = true; /* Allow everything */
+   } else if (jcr->allowed_script_dirs) {
+      /* JCR has some allowed script directories set, so we need to check if command matches it */
+      char *dir, *p;
+      foreach_alist(dir, jcr->allowed_script_dirs) {
+         if ((p = b_path_match(prog, dir)) == prog) {
+            /* Path is ok, now check if program contains forbidden characters */
+            //TODO too many ifs, refactor that
+            if (!check_for_forbidden_chars(prog)) {
+               errno = berr_not_allowed_char;
+               allowed = false;
+               break;
+            } else {
+               allowed = true;
+            }
+            break;
+         }
+      }
+
+      if (!allowed && errno != berr_not_allowed_char) {
+         errno = berr_not_allowed_path;
+      }
+
+   } else {
+      /* Nothing specified, so we should allow to run scripts from every dir provided */
+      allowed = true;
+   }
+
+   /* Check if script/program can be executed */
+   if (!allowed) {
+      return NULL;
+   }
+
    bpipe = (BPIPE *)malloc(sizeof(BPIPE));
    memset(bpipe, 0, sizeof(BPIPE));
    if (strchr(mode,'r')) mode_map|=MODE_READ;