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