1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2006-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2019-2019 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Manipulation routines for RunScript list
24  *
25  * Eric Bollengier, May 2006
26  */
27 
28 #include "include/bareos.h"
29 #include "include/jcr.h"
30 #include "runscript.h"
31 #include "lib/berrno.h"
32 #include "lib/util.h"
33 
34 /*
35  * This function pointer is set only by the Director (dird.c),
36  * and is not set in the File daemon, because the File
37  * daemon cannot run console commands.
38  */
39 bool (*console_command)(JobControlRecord* jcr, const char* cmd) = NULL;
40 
DuplicateRunscript(RunScript * src)41 RunScript* DuplicateRunscript(RunScript* src)
42 {
43   Dmsg0(500, "runscript: creating new RunScript object from other\n");
44 
45   RunScript* copy = new RunScript(*src);
46 
47   copy->command.clear();
48   copy->SetCommand(src->command, src->cmd_type);
49   copy->SetTarget(src->target);
50 
51   return copy;
52 }
53 
FreeRunscript(RunScript * script)54 void FreeRunscript(RunScript* script)
55 {
56   Dmsg0(500, "runscript: freeing RunScript object\n");
57   delete script;
58 }
59 
ScriptDirAllowed(JobControlRecord * jcr,RunScript * script,alist * allowed_script_dirs)60 static bool ScriptDirAllowed(JobControlRecord* jcr,
61                              RunScript* script,
62                              alist* allowed_script_dirs)
63 {
64   char *bp, *allowed_script_dir = nullptr;
65   bool allowed = false;
66   PoolMem script_dir(PM_FNAME);
67 
68   /*
69    * If there is no explicit list of allowed dirs allow any dir.
70    */
71   if (!allowed_script_dirs) { return true; }
72 
73   /*
74    * Determine the dir the script is in.
75    */
76   PmStrcpy(script_dir, script->command.c_str());
77   if ((bp = strrchr(script_dir.c_str(), '/'))) { *bp = '\0'; }
78 
79   /*
80    * Make sure there are no relative path elements in script dir by which the
81    * user tries to escape the allowed dir checking. For scripts we only allow
82    * absolute paths.
83    */
84   if (strstr(script_dir.c_str(), "..")) {
85     Dmsg1(200, "ScriptDirAllowed: relative pathnames not allowed: %s\n",
86           script_dir.c_str());
87     return false;
88   }
89 
90   /*
91    * Match the path the script is in against the list of allowed script
92    * directories.
93    */
94   foreach_alist (allowed_script_dir, allowed_script_dirs) {
95     if (Bstrcasecmp(script_dir.c_str(), allowed_script_dir)) {
96       allowed = true;
97       break;
98     }
99   }
100 
101   Dmsg2(200,
102         "ScriptDirAllowed: script %s %s allowed by Allowed Script Dir setting",
103         script->command.c_str(), (allowed) ? "" : "NOT");
104 
105   return allowed;
106 }
107 
RunScripts(JobControlRecord * jcr,alist * runscripts,const char * label,alist * allowed_script_dirs)108 int RunScripts(JobControlRecord* jcr,
109                alist* runscripts,
110                const char* label,
111                alist* allowed_script_dirs)
112 {
113   RunScript* script = nullptr;
114   bool runit;
115   int when;
116 
117   Dmsg2(200, "runscript: running all RunScript object (%s) JobStatus=%c\n",
118         label, jcr->JobStatus);
119 
120   if (strstr(label, NT_("Before"))) {
121     when = SCRIPT_Before;
122   } else if (bstrcmp(label, NT_("ClientAfterVSS"))) {
123     when = SCRIPT_AfterVSS;
124   } else {
125     when = SCRIPT_After;
126   }
127 
128   if (runscripts == NULL) {
129     Dmsg0(100, "runscript: WARNING RUNSCRIPTS list is NULL\n");
130     return 0;
131   }
132 
133   foreach_alist (script, runscripts) {
134     Dmsg5(200,
135           "runscript: try to run (Target=%s, OnSuccess=%i, OnFailure=%i, "
136           "CurrentJobStatus=%c, command=%s)\n",
137           NSTDPRNT(script->target), script->on_success, script->on_failure,
138           jcr->JobStatus, NSTDPRNT(script->command));
139     runit = false;
140 
141     if (!script->IsLocal()) {
142       if (jcr->is_JobType(JT_ADMIN)) {
143         Jmsg(jcr, M_WARNING, 0,
144              "Invalid runscript definition (command=%s). Admin Jobs only "
145              "support local runscripts.\n",
146              script->command.c_str());
147       }
148     } else {
149       if ((script->when & SCRIPT_Before) && (when & SCRIPT_Before)) {
150         if ((script->on_success
151              && (jcr->JobStatus == JS_Running || jcr->JobStatus == JS_Created))
152             || (script->on_failure
153                 && (JobCanceled(jcr) || jcr->JobStatus == JS_Differences))) {
154           Dmsg4(200, "runscript: Run it because SCRIPT_Before (%s,%i,%i,%c)\n",
155                 script->command.c_str(), script->on_success, script->on_failure,
156                 jcr->JobStatus);
157           runit = true;
158         }
159       }
160 
161       if ((script->when & SCRIPT_AfterVSS) && (when & SCRIPT_AfterVSS)) {
162         if ((script->on_success && (jcr->JobStatus == JS_Blocked))
163             || (script->on_failure && JobCanceled(jcr))) {
164           Dmsg4(200,
165                 "runscript: Run it because SCRIPT_AfterVSS (%s,%i,%i,%c)\n",
166                 script->command.c_str(), script->on_success, script->on_failure,
167                 jcr->JobStatus);
168           runit = true;
169         }
170       }
171 
172       if ((script->when & SCRIPT_After) && (when & SCRIPT_After)) {
173         if ((script->on_success && jcr->IsTerminatedOk())
174             || (script->on_failure
175                 && (JobCanceled(jcr) || jcr->JobStatus == JS_Differences))) {
176           Dmsg4(200, "runscript: Run it because SCRIPT_After (%s,%i,%i,%c)\n",
177                 script->command.c_str(), script->on_success, script->on_failure,
178                 jcr->JobStatus);
179           runit = true;
180         }
181       }
182     }
183 
184     /*
185      * We execute it
186      */
187     if (runit) {
188       if (!ScriptDirAllowed(jcr, script, allowed_script_dirs)) {
189         Dmsg1(200,
190               "runscript: Not running script %s because its not in one of the "
191               "allowed scripts dirs\n",
192               script->command.c_str());
193         Jmsg(jcr, M_ERROR, 0,
194              _("Runscript: run %s \"%s\" could not execute, "
195                "not in one of the allowed scripts dirs\n"),
196              label, script->command.c_str());
197         jcr->setJobStatus(JS_ErrorTerminated);
198         goto bail_out;
199       }
200 
201       script->Run(jcr, label);
202     }
203   }
204 
205 bail_out:
206   return 1;
207 }
208 
SetCommand(const std::string & cmd,int acmd_type)209 void RunScript::SetCommand(const std::string& cmd, int acmd_type)
210 {
211   Dmsg1(500, "runscript: setting command = %s\n", NSTDPRNT(cmd));
212 
213   if (cmd.empty()) { return; }
214 
215   command = cmd;
216   cmd_type = acmd_type;
217 }
218 
SetTarget(const std::string & client_name)219 void RunScript::SetTarget(const std::string& client_name)
220 {
221   Dmsg1(500, "runscript: setting target = %s\n", NSTDPRNT(client_name));
222 
223   target = client_name;
224 }
225 
Run(JobControlRecord * jcr,const char * name)226 bool RunScript::Run(JobControlRecord* jcr, const char* name)
227 {
228   Dmsg1(100, "runscript: running a RunScript object type=%d\n", cmd_type);
229   POOLMEM* ecmd = GetPoolMemory(PM_FNAME);
230   int status;
231   Bpipe* bpipe;
232   PoolMem line(PM_NAME);
233 
234   ecmd
235       = edit_job_codes(jcr, ecmd, command.c_str(), "", this->job_code_callback);
236   Dmsg1(100, "runscript: running '%s'...\n", ecmd);
237   Jmsg(jcr, M_INFO, 0, _("%s: run %s \"%s\"\n"),
238        cmd_type == SHELL_CMD ? "shell command" : "console command", name, ecmd);
239 
240   switch (cmd_type) {
241     case SHELL_CMD:
242       bpipe = OpenBpipe(ecmd, 0, "r");
243       FreePoolMemory(ecmd);
244 
245       if (bpipe == NULL) {
246         BErrNo be;
247         Jmsg(jcr, M_ERROR, 0, _("Runscript: %s could not execute. ERR=%s\n"),
248              name, be.bstrerror());
249         goto bail_out;
250       }
251 
252       while (fgets(line.c_str(), line.size(), bpipe->rfd)) {
253         StripTrailingJunk(line.c_str());
254         Jmsg(jcr, M_INFO, 0, _("%s: %s\n"), name, line.c_str());
255       }
256 
257       status = CloseBpipe(bpipe);
258 
259       if (status != 0) {
260         BErrNo be;
261         Jmsg(jcr, M_ERROR, 0,
262              _("Runscript: %s returned non-zero status=%d. ERR=%s\n"), name,
263              be.code(status), be.bstrerror(status));
264         goto bail_out;
265       }
266 
267       Dmsg0(100, "runscript OK\n");
268       break;
269     case CONSOLE_CMD:
270       if (console_command) {               /* can we run console command? */
271         if (!console_command(jcr, ecmd)) { /* yes, do so */
272           goto bail_out;
273         }
274       }
275       break;
276   }
277   return true;
278 
279 bail_out:
280   /* cancel running job properly */
281   if (fail_on_error) { jcr->setJobStatus(JS_ErrorTerminated); }
282   Dmsg1(100, "runscript failed. fail_on_error=%d\n", fail_on_error);
283   return false;
284 }
285 
FreeRunscripts(alist * runscripts)286 void FreeRunscripts(alist* runscripts)
287 {
288   Dmsg0(500, "runscript: freeing all RUNSCRIPTS object\n");
289 
290   RunScript* r = nullptr;
291   foreach_alist (r, runscripts) {
292     FreeRunscript(r);
293   }
294 }
295 
Debug() const296 void RunScript::Debug() const
297 {
298   Dmsg0(200, "runscript: debug\n");
299   Dmsg0(200, _(" --> RunScript\n"));
300   Dmsg1(200, _("  --> Command=%s\n"), NSTDPRNT(command));
301   Dmsg1(200, _("  --> Target=%s\n"), NSTDPRNT(target));
302   Dmsg1(200, _("  --> RunOnSuccess=%u\n"), on_success);
303   Dmsg1(200, _("  --> RunOnFailure=%u\n"), on_failure);
304   Dmsg1(200, _("  --> FailJobOnError=%u\n"), fail_on_error);
305   Dmsg1(200, _("  --> RunWhen=%u\n"), when);
306 }
307 
SetJobCodeCallback(job_code_callback_t arg_job_code_callback)308 void RunScript::SetJobCodeCallback(job_code_callback_t arg_job_code_callback)
309 {
310   this->job_code_callback = arg_job_code_callback;
311 }
312