Don't call radius_xlat if there's no request pointer
[freeradius.git] / src / main / exec.c
1 /*
2  * exec.c       Execute external programs.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2004,2006  The FreeRADIUS server project
21  */
22
23 #include <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/rad_assert.h>
28
29 #include <sys/file.h>
30
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #ifdef HAVE_SYS_WAIT_H
36 #       include <sys/wait.h>
37 #endif
38 #ifndef WEXITSTATUS
39 #       define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
40 #endif
41 #ifndef WIFEXITED
42 #       define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
43 #endif
44
45 #define MAX_ARGV (256)
46 /*
47  *      Execute a program on successful authentication.
48  *      Return 0 if exec_wait == 0.
49  *      Return the exit code of the called program if exec_wait != 0.
50  *      Return -1 on fork/other errors in the parent process.
51  */
52 int radius_exec_program(const char *cmd, REQUEST *request,
53                         int exec_wait,
54                         char *user_msg, int msg_len,
55                         VALUE_PAIR *input_pairs,
56                         VALUE_PAIR **output_pairs,
57                         int shell_escape)
58 {
59         VALUE_PAIR *vp;
60         char mycmd[1024];
61         char answer[4096];
62         char argv_buf[4096];
63         char *argv[MAX_ARGV];
64         const char *from;
65         char *p, *to;
66         int pd[2];
67         pid_t pid, child_pid;
68         int argc = -1;
69         int comma = 0;
70         int status;
71         int i;
72         int n, left, done;
73
74         if (user_msg) *user_msg = '\0';
75         if (output_pairs) *output_pairs = NULL;
76
77         if (strlen(cmd) > (sizeof(mycmd) - 1)) {
78                 radlog(L_ERR|L_CONS, "Command line is too long");
79                 return -1;
80         }
81
82         /*
83          *      Check for bad escapes.
84          */
85         if (cmd[strlen(cmd) - 1] == '\\') {
86                 radlog(L_ERR|L_CONS, "Command line has final backslash, without a following character");
87                 return -1;
88         }
89
90         strlcpy(mycmd, cmd, sizeof(mycmd));
91
92         /*
93          *      Split the string into argv's BEFORE doing radius_xlat...
94          */
95         from = cmd;
96         to = mycmd;
97         argc = 0;
98         while (*from) {
99                 int length;
100
101                 /*
102                  *      Skip spaces.
103                  */
104                 if ((*from == ' ') || (*from == '\t')) {
105                         from++;
106                         continue;
107                 }
108
109                 argv[argc] = to;
110                 argc++;
111
112                 if (argc >= (MAX_ARGV - 1)) break;
113
114                 /*
115                  *      Copy the argv over to our buffer.
116                  */
117                 while (*from && (*from != ' ') && (*from != '\t')) {
118                         if (to >= mycmd + sizeof(mycmd) - 1) {
119                                 return -1; /* ran out of space */
120                         }
121
122                         switch (*from) {
123                         case '"':
124                         case '\'':
125                                 length = rad_copy_string(to, from);
126                                 if (length < 0) {
127                                         radlog(L_ERR|L_CONS, "Invalid string passed as argument for external program");
128                                         return -1;
129                                 }
130                                 from += length;
131                                 to += length;
132                                 break;
133
134                         case '%':
135                                 if (from[1] == '{') {
136                                         *(to++) = *(from++);
137
138                                         length = rad_copy_variable(to, from);
139                                         if (length < 0) {
140                                                 radlog(L_ERR|L_CONS, "Invalid variable expansion passed as argument for external program");
141                                                 return -1;
142                                         }
143                                         from += length;
144                                         to += length;
145                                 } else { /* FIXME: catch %%{ ? */
146                                         *(to++) = *(from++);
147                                 }
148                                 break;
149
150                         default:
151                                 *(to++) = *(from++);
152                         }
153                 } /* end of string, or found a space */
154
155                 *(to++) = '\0'; /* terminate the string */
156         }
157
158         /*
159          *      We have to have SOMETHING, at least.
160          */
161         if (argc <= 0) {
162                 radlog(L_ERR, "Exec-Program: empty command line.");
163                 return -1;
164         }
165
166         /*
167          *      Expand each string, as appropriate.
168          */
169         to = argv_buf;
170         left = sizeof(argv_buf);
171         for (i = 0; i < argc; i++) {
172                 int sublen;
173
174                 /*
175                  *      Don't touch argv's which won't be translated.
176                  */
177                 if (strchr(argv[i], '%') == NULL) continue;
178
179                 if (!request) continue;
180
181                 sublen = radius_xlat(to, left - 1, argv[i], request, NULL);
182                 if (sublen <= 0) {
183                         /*
184                          *      Fail to be backwards compatible.
185                          *
186                          *      It's yucky, but it won't break anything,
187                          *      and it won't cause security problems.
188                          */
189                         sublen = 0;
190                 }
191
192                 argv[i] = to;
193                 to += sublen;
194                 *(to++) = '\0';
195                 left -= sublen;
196                 left--;
197
198                 if (left <= 0) {
199                         radlog(L_ERR, "Exec-Program: Ran out of space while expanding arguments.");
200                         return -1;
201                 }
202         }
203         argv[argc] = NULL;
204
205         /*
206          *      Open a pipe for child/parent communication, if necessary.
207          */
208         if (exec_wait) {
209                 if (pipe(pd) != 0) {
210                         radlog(L_ERR|L_CONS, "Couldn't open pipe: %s",
211                                strerror(errno));
212                         return -1;
213                 }
214         } else {
215                 /*
216                  *      We're not waiting, so we don't look for a
217                  *      message, or VP's.
218                  */
219                 user_msg = NULL;
220                 output_pairs = NULL;
221         }
222
223         if (exec_wait) {
224                 pid = rad_fork();       /* remember PID */
225         } else {
226                 pid = fork();           /* don't wait */
227         }
228
229         if (pid == 0) {
230 #define MAX_ENVP 1024
231                 int devnull;
232                 char *envp[MAX_ENVP];
233                 int envlen;
234                 char buffer[1024];
235
236                 /*
237                  *      Child process.
238                  *
239                  *      We try to be fail-safe here. So if ANYTHING
240                  *      goes wrong, we exit with status 1.
241                  */
242
243                 /*
244                  *      Open STDIN to /dev/null
245                  */
246                 devnull = open("/dev/null", O_RDWR);
247                 if (devnull < 0) {
248                         radlog(L_ERR|L_CONS, "Failed opening /dev/null: %s\n",
249                                strerror(errno));
250                         exit(1);
251                 }
252                 dup2(devnull, STDIN_FILENO);
253
254                 /*
255                  *      Only massage the pipe handles if the parent
256                  *      has created them.
257                  */
258                 if (exec_wait) {
259                         /*
260                          *      pd[0] is the FD the child will read from,
261                          *      which we don't want.
262                          */
263                         if (close(pd[0]) != 0) {
264                                 radlog(L_ERR|L_CONS, "Can't close pipe: %s",
265                                        strerror(errno));
266                                 exit(1);
267                         }
268
269                         /*
270                          *      pd[1] is the FD that the child will write to,
271                          *      so we make it STDOUT.
272                          */
273                         if (dup2(pd[1], STDOUT_FILENO) != 1) {
274                                 radlog(L_ERR|L_CONS, "Can't dup stdout: %s",
275                                        strerror(errno));
276                                 exit(1);
277                         }
278
279                 } else {        /* no pipe, STDOUT should be /dev/null */
280                         dup2(devnull, STDOUT_FILENO);
281                 }
282
283                 /*
284                  *      If we're not debugging, then we can't do
285                  *      anything with the error messages, so we throw
286                  *      them away.
287                  *
288                  *      If we are debugging, then we want the error
289                  *      messages to go to the STDERR of the server.
290                  */
291                 if (debug_flag == 0) {
292                         dup2(devnull, STDERR_FILENO);
293                 }
294                 close(devnull);
295
296                 /*
297                  *      The server may have MANY FD's open.  We don't
298                  *      want to leave dangling FD's for the child process
299                  *      to play funky games with, so we close them.
300                  */
301                 closefrom(3);
302
303                 /*
304                  *      Set up the environment variables.
305                  *      We're in the child, and it will exit in 4 lines
306                  *      anyhow, so memory allocation isn't an issue.
307                  */
308                 envlen = 0;
309
310                 for (vp = input_pairs; vp != NULL; vp = vp->next) {
311                         /*
312                          *      Hmm... maybe we shouldn't pass the
313                          *      user's password in an environment
314                          *      variable...
315                          */
316                         snprintf(buffer, sizeof(buffer), "%s=", vp->name);
317                         if (shell_escape) {
318                                 for (p = buffer; *p != '='; p++) {
319                                         if (*p == '-') {
320                                                 *p = '_';
321                                         } else if (isalpha((int) *p)) {
322                                                 *p = toupper(*p);
323                                         }
324                                 }
325                         }
326
327                         n = strlen(buffer);
328                         vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape);
329
330                         envp[envlen++] = strdup(buffer);
331
332                         /*
333                          *      Don't add too many attributes.
334                          */
335                         if (envlen == (MAX_ENVP - 1)) break;
336                 }
337                 envp[envlen] = NULL;
338
339                 execve(argv[0], argv, envp);
340                 radlog(L_ERR, "Exec-Program: FAILED to execute %s: %s",
341                        argv[0], strerror(errno));
342                 exit(1);
343         }
344
345         /*
346          *      Parent process.
347          */
348         if (pid < 0) {
349                 radlog(L_ERR|L_CONS, "Couldn't fork %s: %s",
350                        argv[0], strerror(errno));
351                 return -1;
352         }
353
354         /*
355          *      We're not waiting, exit, and ignore any child's status.
356          */
357         if (!exec_wait) {
358                 return 0;
359         }
360
361         /*
362          *      Close the FD to which the child writes it's data.
363          *
364          *      If we can't close it, then we close pd[0], and return an
365          *      error.
366          */
367         if (close(pd[1]) != 0) {
368                 radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno));
369                 close(pd[0]);
370                 return -1;
371         }
372
373         /*
374          *      Read from the pipe until we doesn't get any more or
375          *      until the message is full.
376          */
377         done = 0;
378         left = sizeof(answer) - 1;
379         while (1) {
380                 status = read(pd[0], answer + done, left);
381                 /*
382                  *      Nothing more to read: stop.
383                  */
384                 if (status == 0) {
385                         break;
386                 }
387
388                 /*
389                  *      Error: See if we have to continue.
390                  */
391                 if (status < 0) {
392                         /*
393                          *      We were interrupted: continue reading.
394                          */
395                         if (errno == EINTR) {
396                                 continue;
397                         }
398
399                         /*
400                          *      There was another error.  Most likely
401                          *      The child process has finished, and
402                          *      exited.
403                          */
404                         break;
405                 }
406
407                 done += status;
408                 left -= status;
409                 if (left <= 0) break;
410         }
411         answer[done] = 0;
412
413         /*
414          *      Make sure that the writer can't block while writing to
415          *      a pipe that no one is reading from anymore.
416          */
417         close(pd[0]);
418
419         DEBUG2("Exec-Program output: %s", answer);
420
421         /*
422          *      Parse the output, if any.
423          */
424         if (done) {
425                 n = T_OP_INVALID;
426                 if (output_pairs) {
427                         /*
428                          *      For backwards compatibility, first check
429                          *      for plain text (user_msg).
430                          */
431                         vp = NULL;
432                         n = userparse(answer, &vp);
433                         if (vp) {
434                                 pairfree(&vp);
435                         }
436                 }
437
438                 if (n == T_OP_INVALID) {
439                         DEBUG("Exec-Program-Wait: plaintext: %s", answer);
440                         if (user_msg) {
441                                 strlcpy(user_msg, answer, msg_len);
442                         }
443                 } else {
444                         /*
445                          *      HACK: Replace '\n' with ',' so that
446                          *      userparse() can parse the buffer in
447                          *      one go (the proper way would be to
448                          *      fix userparse(), but oh well).
449                          */
450                         for (p = answer; *p; p++) {
451                                 if (*p == '\n') {
452                                         *p = comma ? ' ' : ',';
453                                         p++;
454                                         comma = 0;
455                                 }
456                                 if (*p == ',') comma++;
457                         }
458
459                         /*
460                          *      Replace any trailing comma by a NUL.
461                          */
462                         if (answer[strlen(answer) - 1] == ',') {
463                                 answer[strlen(answer) - 1] = '\0';
464                         }
465
466                         radlog(L_DBG,"Exec-Program-Wait: value-pairs: %s", answer);
467                         if (userparse(answer, &vp) == T_OP_INVALID) {
468                                 radlog(L_ERR, "Exec-Program-Wait: %s: unparsable reply", cmd);
469
470                         } else {
471                                 /*
472                                  *      Tell the caller about the value
473                                  *      pairs.
474                                  */
475                                 *output_pairs = vp;
476                         }
477                 } /* else the answer was a set of VP's, not a text message */
478         } /* else we didn't read anything from the child */
479
480         /*
481          *      Call rad_waitpid (should map to waitpid on non-threaded
482          *      or single-server systems).
483          */
484         child_pid = rad_waitpid(pid, &status);
485         if (child_pid == 0) {
486                 radlog(L_DBG, "Exec-Program: Timeout waiting for child");
487                 return 2;
488         }
489
490         if (child_pid == pid) {
491                 if (WIFEXITED(status)) {
492                         status = WEXITSTATUS(status);
493                         radlog(L_DBG, "Exec-Program: returned: %d", status);
494                         return status;
495                 }
496         }
497
498         radlog(L_ERR|L_CONS, "Exec-Program: Abnormal child exit: %s",
499                strerror(errno));
500         return 1;
501 }